Repository: wright-group/WrightTools

Issue:
@property methods should return tuples
the following should return tuples, not lists: `axes`, `axis_expressions`, `axis_names`, `channel_names`, `channels`, `variable_names`, `variables`


Discussion:

Resolving patch:
diff --git a/WrightTools/data/_channel.py b/WrightTools/data/_channel.py
--- a/WrightTools/data/_channel.py
+++ b/WrightTools/data/_channel.py
@@ -79,7 +79,7 @@ def natural_name(self):
     @natural_name.setter
     def natural_name(self, value):
         index = wt_kit.get_index(self.parent.channel_names, self.natural_name)
-        new = self.parent.channel_names
+        new = list(self.parent.channel_names)
         new[index] = value
         self.parent.channel_names = new
         self.attrs['name'] = value
diff --git a/WrightTools/data/_data.py b/WrightTools/data/_data.py
--- a/WrightTools/data/_data.py
+++ b/WrightTools/data/_data.py
@@ -46,7 +46,7 @@ def __init__(self, *args, **kwargs):
         kwargs.pop('axis_names', None)
         kwargs.pop('channel_names', None)
         kwargs.pop('constant_names', None)
-        self.axes = []
+        self._axes = []
         Group.__init__(self, *args, **kwargs)
         for identifier in self.attrs.get('axes', []):
             identifier = identifier.decode()
@@ -56,7 +56,7 @@ def __init__(self, *args, **kwargs):
                 expression = expression.replace(i, identifier_to_operator[i])
             expression = expression.replace(' ', '')  # remove all whitespace
             axis = Axis(self, expression, units.strip())
-            self.axes.append(axis)
+            self._axes.append(axis)
         # the following are populated if not already recorded
         self.channel_names
         self.constant_names
@@ -70,22 +70,26 @@ def __repr__(self):
         return '<WrightTools.Data \'{0}\' {1} at {2}>'.format(
             self.natural_name, str(self.axis_names), '::'.join([self.filepath, self.name]))
 
+    @property
+    def axes(self):
+        return tuple(self._axes)
+
     @property
     def axis_expressions(self):
         """Axis expressions."""
-        return [a.expression for a in self.axes]
+        return tuple(a.expression for a in self._axes)
 
     @property
     def axis_names(self):
         """Axis names."""
-        return [a.natural_name for a in self.axes]
+        return tuple(a.natural_name for a in self._axes)
 
     @property
     def channel_names(self):
         """Channel names."""
         if 'channel_names' not in self.attrs.keys():
             self.attrs['channel_names'] = np.array([], dtype='S')
-        return [s.decode() for s in self.attrs['channel_names']]
+        return tuple(s.decode() for s in self.attrs['channel_names'])
 
     @channel_names.setter
     def channel_names(self, value):
@@ -95,29 +99,29 @@ def channel_names(self, value):
     @property
     def channels(self):
         """Channels."""
-        return [self[n] for n in self.channel_names]
+        return tuple(self[n] for n in self.channel_names)
 
     @property
     def constant_names(self):
         """Constant names."""
         if 'constant_names' not in self.attrs.keys():
             self.attrs['constant_names'] = np.array([], dtype='S')
-        return [s.decode() for s in self.attrs['constant_names']]
+        return tuple(s.decode() for s in self.attrs['constant_names'])
 
     @property
     def constants(self):
         """Constants."""
-        return [self[n] for n in self.constant_names]
+        return tuple(self[n] for n in self.constant_names)
 
     @property
     def datasets(self):
         """Datasets."""
-        return [v for _, v in self.items() if isinstance(v, h5py.Dataset)]
+        return tuple(v for _, v in self.items() if isinstance(v, h5py.Dataset))
 
     @property
     def dimensionality(self):
         """Get dimensionality of Data object."""
-        return len(self.axes)
+        return len(self._axes)
 
     @property
     def info(self):
@@ -173,14 +177,14 @@ def source(self):
     @property
     def units(self):
         """All axis units."""
-        return tuple(a.units for a in self.axes)
+        return tuple(a.units for a in self._axes)
 
     @property
     def variable_names(self):
         """Variable names."""
         if 'variable_names' not in self.attrs.keys():
             self.attrs['variable_names'] = np.array([], dtype='S')
-        return [s.decode() for s in self.attrs['variable_names']]
+        return tuple(s.decode() for s in self.attrs['variable_names'])
 
     @variable_names.setter
     def variable_names(self, value):
@@ -224,7 +228,7 @@ def bring_to_front(self, channel):
             Channel index or name.
         """
         channel_index = wt_kit.get_index(self.channel_names, channel)
-        new = self.channel_names
+        new = list(self.channel_names)
         new.insert(0, new.pop(channel_index))
         self.channel_names = new
 
@@ -284,13 +288,13 @@ def chop(self, *args, at={}, parent=None, verbose=True):
         args = list(args)
         for i, arg in enumerate(args):
             if isinstance(arg, int):
-                args[i] = self.axes[i].expression
+                args[i] = self._axes[i].expression
         # get output collection
         out = wt_collection.Collection(name='chop', parent=parent)
         # get output shape
         kept = args + list(at.keys())
-        kept_axes = [self.axes[self.axis_expressions.index(a)] for a in kept]
-        removed_axes = [a for a in self.axes if a not in kept_axes]
+        kept_axes = [self._axes[self.axis_expressions.index(a)] for a in kept]
+        removed_axes = [a for a in self._axes if a not in kept_axes]
         removed_shape = wt_kit.joint_shape(removed_axes)
         if removed_shape == ():
             removed_shape = (1,) * self.ndim
@@ -301,10 +305,10 @@ def chop(self, *args, at={}, parent=None, verbose=True):
             idx[np.array(removed_shape) == 1] = slice(None)
             for axis, point in at.items():
                 point, units = point
-                destination_units = self.axes[self.axis_names.index(axis)].units
+                destination_units = self._axes[self.axis_names.index(axis)].units
                 point = wt_units.converter(point, units, destination_units)
                 axis_index = self.axis_names.index(axis)
-                axis = self.axes[axis_index]
+                axis = self._axes[axis_index]
                 idx[axis_index] = np.argmin(np.abs(axis[tuple(idx)] - point))
             idx = tuple(idx)
             data = out.create_data(name='chop%03i' % i)
@@ -361,7 +365,7 @@ def collapse(self, axis, method='integrate'):
         for method, channel in zip(methods, self.channels):
             if method in ['int', 'integrate']:
                 channel[:] = np.trapz(
-                    y=channel[:], x=self.axes[axis_index][:], axis=axis_index)
+                    y=channel[:], x=self._axes[axis_index][:], axis=axis_index)
             elif method == 'sum':
                 channel[:] = np.nansum(channel[:], axis=axis_index)
             elif method in ['max', 'maximum']:
@@ -373,7 +377,7 @@ def collapse(self, axis, method='integrate'):
             else:
                 print('method not recognized in data.collapse')
         # cleanup ---------------------------------------------------------------------------------
-        self.axes.pop(axis_index)
+        self._axes.pop(axis_index)
         self._update_natural_namespace()
 
     def convert(self, destination_units, verbose=True):
@@ -432,7 +436,6 @@ def create_channel(self, name, values=None, units=None, **kwargs):
                                           chunks=True).id
         channel = Channel(self, dataset_id, units=units, **kwargs)
         # finish
-        self.channels.append(channel)
         self.attrs['channel_names'] = np.append(self.attrs['channel_names'], name.encode())
         self._update_natural_namespace()
         return channel
@@ -501,10 +504,10 @@ def divide(self, divisor, channel=0, divisor_channel=0):
         # transpose so axes of divisor are last (in order)
         axis_indicies = [self.axis_names.index(name) for name in divisor.axis_names]
         axis_indicies.reverse()
-        transpose_order = list(range(len(self.axes)))
+        transpose_order = list(range(len(self._axes)))
         for i in range(len(axis_indicies)):
             ai = axis_indicies[i]
-            ri = list(range(len(self.axes)))[-(i + 1)]
+            ri = list(range(len(self._axes)))[-(i + 1)]
             transpose_order[ri], transpose_order[ai] = transpose_order[ai], transpose_order[ri]
         self.transpose(transpose_order, verbose=False)
         # get own channel
@@ -529,7 +532,7 @@ def divide(self, divisor, channel=0, divisor_channel=0):
         self.transpose(transpose_order, verbose=False)
 
     def flush(self):
-        self.attrs['axes'] = [a.identity.encode() for a in self.axes]
+        self.attrs['axes'] = [a.identity.encode() for a in self._axes]
         super().flush()
 
     def get_nadir(self, channel=0):
@@ -559,7 +562,7 @@ def get_nadir(self, channel=0):
         arr = channel[:]
         idxs = np.unravel_index(arr.argmin(), arr.shape)
         # finish
-        return [a[i] for a, i in zip(self.axes, idxs)]
+        return [a[i] for a, i in zip(self._axes, idxs)]
 
     def get_zenith(self, channel=0):
         """
@@ -588,7 +591,7 @@ def get_zenith(self, channel=0):
         arr = channel[:]
         idxs = np.unravel_index(arr.argmax(), arr.shape)
         # finish
-        return [a[i] for a, i in zip(self.axes, idxs)]
+        return [a[i] for a, i in zip(self._axes, idxs)]
 
     def heal(self, channel=0, method='linear', fill_value=np.nan,
              verbose=True):
@@ -629,11 +632,11 @@ def heal(self, channel=0, method='linear', fill_value=np.nan,
                 raise TypeError("channel: expected {int, str}, got %s" % type(channel))
             channel = self.channels[channel_index]
             values = self.channels[channel_index][:]
-            points = [axis[:] for axis in self.axes]
+            points = [axis[:] for axis in self._axes]
             xi = tuple(np.meshgrid(*points, indexing='ij'))
             # 'undo' gridding
-            arr = np.zeros((len(self.axes) + 1, values.size))
-            for i in range(len(self.axes)):
+            arr = np.zeros((len(self._axes) + 1, values.size))
+            for i in range(len(self._axes)):
                 arr[i] = xi[i].flatten()
             arr[-1] = values.flatten()
             # remove nans
@@ -708,7 +711,7 @@ def level(self, channel, axis, npts, verbose=True):
         channel._null = 0.
         # print
         if verbose:
-            axis = self.axes[axis_index]
+            axis = self._axes[axis_index]
             if npts > 0:
                 points = axis[:npts]
             if npts < 0:
@@ -747,7 +750,7 @@ def map_axis(self, axis, points, input_units='same', edge_tolerance=0., verbose=
             axis_index = self.axis_names.index(axis)
         else:
             raise TypeError("axis: expected {int, str}, got %s" % type(axis))
-        axis = self.axes[axis_index]
+        axis = self._axes[axis_index]
         # get points ------------------------------------------------------------------------------
         if isinstance(points, int):
             points = np.linspace(axis[0], axis[-1], points)
@@ -760,9 +763,9 @@ def map_axis(self, axis, points, input_units='same', edge_tolerance=0., verbose=
         else:
             points = wt_units.converter(points, input_units, axis.units)
         # points must be ascending ----------------------------------------------------------------
-        flipped = np.zeros(len(self.axes), dtype=np.bool)
-        for i in range(len(self.axes)):
-            if self.axes[i][0] > self.axes[i][-1]:
+        flipped = np.zeros(len(self._axes), dtype=np.bool)
+        for i in range(len(self._axes)):
+            if self._axes[i][0] > self._axes[i][-1]:
                 self.flip(i)
                 flipped[i] = True
         # handle edge tolerance -------------------------------------------------------------------
@@ -772,11 +775,11 @@ def map_axis(self, axis, points, input_units='same', edge_tolerance=0., verbose=
             if new - edge_tolerance < old < new + edge_tolerance:
                 axis[index] = new
         # interpn data ----------------------------------------------------------------------------
-        old_points = [a[:] for a in self.axes]
-        new_points = [a[:] if a is not axis else points for a in self.axes]
-        if len(self.axes) == 1:
+        old_points = [a[:] for a in self._axes]
+        new_points = [a[:] if a is not axis else points for a in self._axes]
+        if len(self._axes) == 1:
             for channel in self.channels:
-                function = scipy.interpolate.interp1d(self.axes[0][:], channel[:])
+                function = scipy.interpolate.interp1d(self._axes[0][:], channel[:])
                 channel[:] = function(new_points[0])
         else:
             xi = tuple(np.meshgrid(*new_points, indexing='ij'))
@@ -787,7 +790,7 @@ def map_axis(self, axis, points, input_units='same', edge_tolerance=0., verbose=
                                                        bounds_error=False,
                                                        fill_value=np.nan)
         # cleanup ---------------------------------------------------------------------------------
-        for i in range(len(self.axes)):
+        for i in range(len(self._axes)):
             if not i == axis_index:
                 if flipped[i]:
                     self.flip(i)
@@ -876,7 +879,7 @@ def offset(self, points, offsets, along, offset_axis,
             axis_index = self.axis_names.index(along)
         else:
             raise TypeError("along: expected {int, str}, got %s" % type(along))
-        axis = self.axes[axis_index]
+        axis = self._axes[axis_index]
         # values & points -------------------------------------------------------------------------
         # get values, points, units
         if units == 'same':
@@ -906,8 +909,8 @@ def offset(self, points, offsets, along, offset_axis,
                              (int(left_pad_width), int(right_pad_width)), mode='edge')
         # do correction ---------------------------------------------------------------------------
         # transpose so axis is last
-        transpose_order = np.arange(len(self.axes))
-        transpose_order[axis_index] = len(self.axes) - 1
+        transpose_order = np.arange(len(self._axes))
+        transpose_order[axis_index] = len(self._axes) - 1
         transpose_order[-1] = axis_index
         self.transpose(transpose_order, verbose=False)
         # get offset axis index
@@ -918,8 +921,8 @@ def offset(self, points, offsets, along, offset_axis,
         else:
             raise TypeError("offset_axis: expected {int, str}, got %s" % type(offset_axis))
         # new points
-        new_points = [a[:] for a in self.axes]
-        old_offset_axis_points = self.axes[offset_axis_index][:]
+        new_points = [a[:] for a in self._axes]
+        old_offset_axis_points = self._axes[offset_axis_index][:]
         spacing = abs((old_offset_axis_points.max() - old_offset_axis_points.min()) /
                       float(len(old_offset_axis_points)))
         if mode == 'old':
@@ -936,11 +939,11 @@ def offset(self, points, offsets, along, offset_axis,
             new_offset_axis_points = np.linspace(_min, _max, n)
         new_points[offset_axis_index] = new_offset_axis_points
         new_xi = tuple(np.meshgrid(*new_points, indexing='ij'))
-        xi = tuple(np.meshgrid(*[a[:] for a in self.axes], indexing='ij'))
+        xi = tuple(np.meshgrid(*[a[:] for a in self._axes], indexing='ij'))
         for channel in self.channels:
             # 'undo' gridding
-            arr = np.zeros((len(self.axes) + 1, channel[:].size))
-            for i in range(len(self.axes)):
+            arr = np.zeros((len(self._axes) + 1, channel[:].size))
+            for i in range(len(self._axes)):
                 arr[i] = xi[i].flatten()
             arr[-1] = channel[:].flatten()
             # do corrections
@@ -953,7 +956,7 @@ def offset(self, points, offsets, along, offset_axis,
             out = griddata(tup, arr[-1], new_xi, method=method,
                            fill_value=np.nan, rescale=True)
             channel[:] = out
-        self.axes[offset_axis_index][:] = new_offset_axis_points
+        self._axes[offset_axis_index][:] = new_offset_axis_points
         # transpose out
         self.transpose(transpose_order, verbose=False)
         self._update_natural_namespace()
@@ -969,7 +972,7 @@ def remove_channel(self, channel, *, verbose=True):
             Toggle talkback. Default is True.
         """
         channel_index = wt_kit.get_index(self.channel_names, channel)
-        new = self.channel_names
+        new = list(self.channel_names)
         name = new.pop(channel_index)
         del self[name]
         self.channel_names = new
@@ -999,14 +1002,14 @@ def remove_variable(self, variable, *, implied=True, verbose=True):
                     removed.append(n)
         # check that axes will not be ruined
         for n in removed:
-            for a in self.axes:
+            for a in self._axes:
                 if n in a.expression:
                     message = '{0} is contained in axis {1}'.format(n, a.expression)
                     raise RuntimeError(message)
         # do removal
         for n in removed:
             variable_index = wt_kit.get_index(self.variable_names, n)
-            new = self.variable_names
+            new = list(self.variable_names)
             name = new.pop(variable_index)
             del self[name]
             self.variable_names = new
@@ -1043,7 +1046,7 @@ def rename_channels(self, *, verbose=True, **kwargs):
             # remove old references
             del self[k]
         # apply new references
-        names = self.channel_names
+        names = list(self.channel_names)
         for v, value in new.items():
             obj, index = value
             self[v] = obj
@@ -1095,7 +1098,7 @@ def rename_variables(self, *, implied=True, verbose=True, **kwargs):
             # remove old references
             del self[k]
         # apply new references
-        names = self.variable_names
+        names = list(self.variable_names)
         for v, value in new.items():
             obj, index = value
             self[v] = obj
@@ -1103,14 +1106,14 @@ def rename_variables(self, *, implied=True, verbose=True, **kwargs):
         self.variable_names = names
         # update axes
         units = self.units
-        new = self.axis_expressions
+        new = list(self.axis_expressions)
         for i, v in enumerate(kwargs.keys()):
             for j, n in enumerate(new):
                 new[j] = n.replace(v, '{%i}' % i)
         for i, n in enumerate(new):
             new[i] = n.format(*kwargs.values())
         self.transform(new)
-        for a, u in zip(self.axes, units):
+        for a, u in zip(self._axes, units):
             a.convert(u)
         # finish
         if verbose:
@@ -1153,7 +1156,7 @@ def smooth(self, factors, channel=None, verbose=True):
         if isinstance(factors, list):
             pass
         else:
-            dummy = np.zeros(len(self.axes))
+            dummy = np.zeros(len(self._axes))
             dummy[::] = factors
             factors = list(dummy)
         # get channels ----------------------------------------------------------------------------
@@ -1241,7 +1244,7 @@ def split(self, axis, positions, units='same', direction='below', parent=None, v
             axis_index = self.axis_names.index(axis)
         else:
             raise TypeError("axis: expected {int, str}, got %s" % type(axis))
-        axis = self.axes[axis_index]
+        axis = self._axes[axis_index]
         # indicies --------------------------------------------------------------------------------
         # positions must be iterable and should be a numpy array
         if type(positions) in [int, float]:
@@ -1282,7 +1285,7 @@ def split(self, axis, positions, units='same', direction='below', parent=None, v
                 attrs = dict(self.attrs)
                 attrs.pop('name', None)
                 new_data = outs.create_data(new_name, **attrs)
-                for ax in self.axes:
+                for ax in self._axes:
                     if ax != axis:
                         attrs = dict(ax.attrs)
                         attrs.pop('name', None)
@@ -1299,7 +1302,7 @@ def split(self, axis, positions, units='same', direction='below', parent=None, v
                 attrs = dict(self.attrs)
                 attrs.pop('name', None)
                 new_data = outs.create_data(new_name, **attrs)
-                for ax in self.axes:
+                for ax in self._axes:
                     if ax == axis:
                         slc = slice(start, stop)
                     else:
@@ -1363,10 +1366,10 @@ def subtract(self, subtrahend, channel=0, subtrahend_channel=0):
         # transpose so axes of divisor are last (in order)
         axis_indicies = [self.axis_names.index(name) for name in subtrahend.axis_names]
         axis_indicies.reverse()
-        transpose_order = range(len(self.axes))
+        transpose_order = range(len(self._axes))
         for i in range(len(axis_indicies)):
             ai = axis_indicies[i]
-            ri = range(len(self.axes))[-(i + 1)]
+            ri = range(len(self._axes))[-(i + 1)]
             transpose_order[ri], transpose_order[ai] = transpose_order[ai], transpose_order[ri]
         self.transpose(transpose_order, verbose=False)
         # get own channel
@@ -1435,13 +1438,13 @@ def transform(self, axes, verbose=True):
         # TODO: ensure that transform does not break data
         # create
         new = []
-        current = {a.expression: a for a in self.axes}
+        current = {a.expression: a for a in self._axes}
         for expression in axes:
             axis = current.get(expression, Axis(self, expression))
             new.append(axis)
-        self.axes = new
+        self._axes = new
         # units
-        for a in self.axes:
+        for a in self._axes:
             if a.units is None:
                 a.convert(a.variables[0].units)
         # finish
@@ -1469,7 +1472,7 @@ def zoom(self, factor, order=1, verbose=True):
         raise NotImplementedError
         import scipy.ndimage
         # axes
-        for axis in self.axes:
+        for axis in self._axes:
             axis[:] = scipy.ndimage.interpolation.zoom(axis[:], factor, order=order)
         # channels
         for channel in self.channels:


Test patch:
diff --git a/tests/data/chop.py b/tests/data/chop.py
--- a/tests/data/chop.py
+++ b/tests/data/chop.py
@@ -19,7 +19,7 @@ def test_2D_to_1D():
     assert len(chop) == 81
     for d in chop.values():
         assert d.w2.size == 81
-        assert d.axis_expressions == ['w2']
+        assert d.axis_expressions == ('w2',)
     data.close()
     chop.close()
 
@@ -31,7 +31,7 @@ def test_3D_to_1D():
     assert len(chop) == 385
     for d in chop.values():
         assert d.w2.size == 11
-        assert d.axis_expressions == ['w2']
+        assert d.axis_expressions == ('w2',)
     data.close()
     chop.close()
 
@@ -43,7 +43,7 @@ def test_3D_to_1D_at():
     assert len(chop) == 29
     for d in chop.values():
         assert d.w2.size == 11
-        assert d.axis_expressions == ['w2']
+        assert d.axis_expressions == ('w2',)
     data.close()
     chop.close()
 
@@ -56,7 +56,7 @@ def test_3D_to_2D():
     for d in chop.values():
         assert d.wm.size == 29
         assert d.w2.size == 11
-        assert d.axis_expressions == ['wm', 'w2']
+        assert d.axis_expressions == ('wm', 'w2',)
     data.close()
     chop.close()
 
@@ -68,7 +68,7 @@ def test_3D_to_2D_at():
     assert len(chop) == 1
     assert chop[0].wm.size == 29
     assert chop[0].w2.size == 11
-    assert chop[0].axis_expressions == ['wm', 'w2']
+    assert chop[0].axis_expressions == ('wm', 'w2',)
     data.close()
     chop.close()
 
diff --git a/tests/data/from_BrunoldrRaman.py b/tests/data/from_BrunoldrRaman.py
--- a/tests/data/from_BrunoldrRaman.py
+++ b/tests/data/from_BrunoldrRaman.py
@@ -15,6 +15,6 @@ def test_LDS821_514nm_80mW():
     p = datasets.BrunoldrRaman.LDS821_514nm_80mW
     data = wt.data.from_BrunoldrRaman(p)
     assert data.shape == (1340,)
-    assert data.axis_expressions == ['energy']
+    assert data.axis_expressions == ('energy',)
     assert data.units == ('wn',)
     data.close()
diff --git a/tests/data/from_COLORS.py b/tests/data/from_COLORS.py
--- a/tests/data/from_COLORS.py
+++ b/tests/data/from_COLORS.py
@@ -15,7 +15,7 @@ def test_v0p2_d1_d2_diagonal():
     p = datasets.COLORS.v0p2_d1_d2_diagonal
     data = wt.data.from_COLORS(p)
     assert data.shape == (21, 21)
-    assert data.axis_expressions == ['d1', 'd2']
+    assert data.axis_expressions == ('d1', 'd2',)
     assert data.units == ('fs', 'fs')
     data.close()
 
@@ -24,7 +24,7 @@ def test_v0p2_d1_d2_off_diagonal():
     p = datasets.COLORS.v0p2_d1_d2_off_diagonal
     data = wt.data.from_COLORS(p)
     assert data.shape == (21, 21)
-    assert data.axis_expressions == ['d1', 'd2']
+    assert data.axis_expressions == ('d1', 'd2',)
     assert data.units == ('fs', 'fs')
     data.close()
 
@@ -33,7 +33,7 @@ def test_v2p1_MoS2_TrEE_movie():
     ps = datasets.COLORS.v2p1_MoS2_TrEE_movie
     data = wt.data.from_COLORS(ps)
     assert data.shape == (41, 41, 23)
-    assert data.axis_expressions == ['w2', 'w1=wm', 'd2']
+    assert data.axis_expressions == ('w2', 'w1=wm', 'd2',)
     assert data.units == ('nm', 'nm', 'fs')
     data.close()
 
@@ -42,6 +42,6 @@ def test_v2p2_WL_wigner():
     p = datasets.COLORS.v2p2_WL_wigner
     data = wt.data.from_COLORS(p)
     assert data.shape == (241, 51)
-    assert data.axis_expressions == ['wm', 'd1']
+    assert data.axis_expressions == ('wm', 'd1',)
     assert data.units == ('nm', 'fs')
     data.close()
diff --git a/tests/data/from_Cary50.py b/tests/data/from_Cary50.py
--- a/tests/data/from_Cary50.py
+++ b/tests/data/from_Cary50.py
@@ -23,5 +23,5 @@ def test_CuPCtS_H2O_vis():
 
     data = datas[0]
     assert data.shape == (141,)
-    assert data.axis_names == ['wm']
+    assert data.axis_names == ('wm',)
     datas.close()
diff --git a/tests/data/from_JASCO.py b/tests/data/from_JASCO.py
--- a/tests/data/from_JASCO.py
+++ b/tests/data/from_JASCO.py
@@ -15,7 +15,7 @@ def test_PbSe_batch_1():
     p = datasets.JASCO.PbSe_batch_1
     data = wt.data.from_JASCO(p)
     assert data.shape == (1801,)
-    assert data.axis_expressions == ['energy']
+    assert data.axis_expressions == ('energy',)
     assert data.units == ('nm',)
     data.close()
 
@@ -24,7 +24,7 @@ def test_PbSe_batch_4_2012_02_21():
     p = datasets.JASCO.PbSe_batch_4_2012_02_21
     data = wt.data.from_JASCO(p)
     assert data.shape == (1251,)
-    assert data.axis_expressions == ['energy']
+    assert data.axis_expressions == ('energy',)
     assert data.units == ('nm',)
     data.close()
 
@@ -33,6 +33,6 @@ def test_PbSe_batch_4_2012_03_15():
     p = datasets.JASCO.PbSe_batch_4_2012_03_15
     data = wt.data.from_JASCO(p)
     assert data.shape == (1251,)
-    assert data.axis_expressions == ['energy']
+    assert data.axis_expressions == ('energy',)
     assert data.units == ('nm',)
     data.close()
diff --git a/tests/data/from_KENT.py b/tests/data/from_KENT.py
--- a/tests/data/from_KENT.py
+++ b/tests/data/from_KENT.py
@@ -18,7 +18,7 @@ def test_LDS821_TRSF():
     ignore = ['wm', 'd1', 'd2']
     data = wt.data.from_KENT(p, ignore=ignore)
     assert data.shape == (71, 71)
-    assert data.axis_names == ['w2', 'w1']
+    assert data.axis_names == ('w2', 'w1',)
     assert data.units == ('wn', 'wn')
     data.close()
 
@@ -28,7 +28,7 @@ def test_PbSe_2D_delay_A():
     p = datasets.KENT.PbSe_2D_delay_A
     data = wt.data.from_KENT(p, delay_tolerance=0.01)
     assert data.shape == (101, 151)
-    assert data.axis_names == ['d2', 'd1']
+    assert data.axis_names == ('d2', 'd1',)
     assert data.units == ('ps', 'ps')
     data.close()
 
@@ -37,6 +37,6 @@ def test_PbSe_2D_delay_B():
     p = datasets.KENT.PbSe_2D_delay_B
     data = wt.data.from_KENT(p, delay_tolerance=0.01)
     assert data.shape == (101, 101)
-    assert data.axis_names == ['d2', 'd1']
+    assert data.axis_names == ('d2', 'd1',)
     assert data.units == ('ps', 'ps')
     data.close()
diff --git a/tests/data/from_PyCMDS.py b/tests/data/from_PyCMDS.py
--- a/tests/data/from_PyCMDS.py
+++ b/tests/data/from_PyCMDS.py
@@ -15,7 +15,7 @@ def test_w1_000():
     p = datasets.PyCMDS.w1_000
     data = wt.data.from_PyCMDS(p)
     assert data.shape == (51,)
-    assert data.axis_expressions == ['w1']
+    assert data.axis_expressions == ('w1',)
     data.close()
 
 
@@ -23,7 +23,7 @@ def test_w1_wa_000():
     p = datasets.PyCMDS.w1_wa_000
     data = wt.data.from_PyCMDS(p)
     assert data.shape == (25, 256)
-    assert data.axis_expressions == ['w1=wm', 'wa']
+    assert data.axis_expressions == ('w1=wm', 'wa',)
     data.close()
 
 
@@ -31,7 +31,7 @@ def test_w2_w1_000():
     p = datasets.PyCMDS.w2_w1_000
     data = wt.data.from_PyCMDS(p)
     assert data.shape == (81, 81)
-    assert data.axis_expressions == ['w2', 'w1']
+    assert data.axis_expressions == ('w2', 'w1',)
     data.close()
 
 
@@ -39,7 +39,7 @@ def test_wm_w2_w1_000():
     p = datasets.PyCMDS.wm_w2_w1_000
     data = wt.data.from_PyCMDS(p)
     assert data.shape == (35, 11, 11)
-    assert data.axis_expressions == ['wm', 'w2', 'w1']
+    assert data.axis_expressions == ('wm', 'w2', 'w1',)
     data.close()
 
 
@@ -47,7 +47,7 @@ def test_wm_w2_w1_001():
     p = datasets.PyCMDS.wm_w2_w1_001
     data = wt.data.from_PyCMDS(p)
     assert data.shape == (29, 11, 11)
-    assert data.axis_expressions == ['wm', 'w2', 'w1']
+    assert data.axis_expressions == ('wm', 'w2', 'w1',)
     data.close()
 
 
diff --git a/tests/data/from_Tensor27.py b/tests/data/from_Tensor27.py
--- a/tests/data/from_Tensor27.py
+++ b/tests/data/from_Tensor27.py
@@ -18,5 +18,5 @@ def test_CuPCtS_powder_ATR():
     p = datasets.Tensor27.CuPCtS_powder_ATR
     data = wt.data.from_Tensor27(p)
     assert data.shape == (7259,)
-    assert data.axis_names == ['wm']
+    assert data.axis_names == ('wm',)
     data.close()
diff --git a/tests/data/from_shimadzu.py b/tests/data/from_shimadzu.py
--- a/tests/data/from_shimadzu.py
+++ b/tests/data/from_shimadzu.py
@@ -15,6 +15,6 @@ def test_MoS2_fromCzech2015():
     p = datasets.Shimadzu.MoS2_fromCzech2015
     data = wt.data.from_shimadzu(p)
     assert data.shape == (819,)
-    assert data.axis_expressions == ['energy']
+    assert data.axis_expressions == ('energy',)
     assert data.units == ('nm',)
     data.close()
diff --git a/tests/data/from_spcm.py b/tests/data/from_spcm.py
--- a/tests/data/from_spcm.py
+++ b/tests/data/from_spcm.py
@@ -18,5 +18,5 @@ def test_test_data():
     p = datasets.spcm.test_data
     data = wt.data.from_spcm(p)
     assert data.shape == (1024,)
-    assert data.axis_names == ['time']
+    assert data.axis_names == ('time',)
     data.close()
diff --git a/tests/data/remove_channel.py b/tests/data/remove_channel.py
--- a/tests/data/remove_channel.py
+++ b/tests/data/remove_channel.py
@@ -24,5 +24,5 @@ def test_remove_trailing_channels():
     data = wt.data.from_COLORS(p)
     while len(data.channel_names) > 1:
         data.remove_channel(-1)
-    assert data.channel_names == ['ai0']
+    assert data.channel_names == ('ai0',)
     data.close()
diff --git a/tests/data/remove_variable.py b/tests/data/remove_variable.py
--- a/tests/data/remove_variable.py
+++ b/tests/data/remove_variable.py
@@ -38,7 +38,7 @@ def test_index():
     p = datasets.KENT.LDS821_TRSF
     ignore = ['wm', 'd1', 'd2']
     data = wt.data.from_KENT(p, ignore=ignore)
-    assert data.variable_names == ['w2', 'w1', 'wm', 'd1', 'd2']
+    assert data.variable_names == ('w2', 'w1', 'wm', 'd1', 'd2',)
     data.remove_variable(-2)
-    assert data.variable_names == ['w2', 'w1', 'wm', 'd2']
+    assert data.variable_names == ('w2', 'w1', 'wm', 'd2',)
     data.close()
diff --git a/tests/data/rename_variables.py b/tests/data/rename_variables.py
--- a/tests/data/rename_variables.py
+++ b/tests/data/rename_variables.py
@@ -15,7 +15,7 @@ def test_simple():
     p = datasets.BrunoldrRaman.LDS821_514nm_80mW
     data = wt.data.from_BrunoldrRaman(p)
     data.rename_variables(energy='blaise')
-    assert data.variable_names == ['blaise']
+    assert data.variable_names == ('blaise',)
     assert data.variables[0].natural_name == 'blaise'
     assert 'blaise' in data.variables[0].name
     data.close()
@@ -48,7 +48,7 @@ def test_propagate_units():
 def test_updated_axes():
     p = datasets.COLORS.v2p2_WL_wigner
     data = wt.data.from_COLORS(p)
-    assert data.axis_expressions == ['wm', 'd1']
+    assert data.axis_expressions == ('wm', 'd1',)
     data.rename_variables(wm='mono')
-    assert data.axis_expressions == ['mono', 'd1']
+    assert data.axis_expressions == ('mono', 'd1',)
     data.close()
diff --git a/tests/data/transform.py b/tests/data/transform.py
--- a/tests/data/transform.py
+++ b/tests/data/transform.py
@@ -15,6 +15,6 @@ def test_w2_w1():
     p = datasets.PyCMDS.w2_w1_000
     data = wt.data.from_PyCMDS(p)
     data.transform(['w2', 'wm'])
-    assert data.axis_names == ['w2', 'wm']
+    assert data.axis_names == ('w2', 'wm',)
     assert data.w2.shape == (81, 1)
     assert data.wm.shape == (81, 81)