We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
multiplication doesn't seem to work in d.transform
`d.transform('w2+w2+w2', 'w1', 'd2')` works.

`d.transform('3*w2', 'w1', 'd2')` does not work (even with varying spacing around the multiplication operator)
Part of the error that is raised:
```
  File "/home/darien/source/WrightTools/WrightTools/data/_data.py", line 1306, in transform
    axis = current.get(expression, Axis(self, expression))

  File "/home/darien/source/WrightTools/WrightTools/data/_axis.py", line 53, in __init__
    self.units = self.variables[0].units

  File "/home/darien/source/WrightTools/WrightTools/data/_axis.py", line 152, in variables
    return self._variables

AttributeError: 'Axis' object has no attribute '_variables'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `WrightTools/data/_axis.py`
Content:
```
1 """Axis class and associated."""
2 
3 
4 # --- import --------------------------------------------------------------------------------------
5 
6 
7 import re
8 import numexpr
9 import operator
10 import functools
11 
12 import numpy as np
13 
14 from .. import exceptions as wt_exceptions
15 from .. import kit as wt_kit
16 from .. import units as wt_units
17 
18 
19 # --- define --------------------------------------------------------------------------------------
20 
21 
22 operator_to_identifier = {}
23 operator_to_identifier['/'] = '__d__'
24 operator_to_identifier['='] = '__e__'
25 operator_to_identifier['-'] = '__m__'
26 operator_to_identifier['+'] = '__p__'
27 operator_to_identifier['*'] = '__t__'
28 identifier_to_operator = {value: key for key, value in operator_to_identifier.items()}
29 operators = ''.join(operator_to_identifier.keys())
30 
31 
32 # --- class ---------------------------------------------------------------------------------------
33 
34 
35 class Axis(object):
36     """Axis class."""
37 
38     def __init__(self, parent, expression, units=None):
39         """Data axis.
40 
41         Parameters
42         ----------
43         parent : WrightTools.Data
44             Parent data object.
45         expression : string
46             Axis expression.
47         units : string (optional)
48             Axis units. Default is None.
49         """
50         self.parent = parent
51         self.expression = expression
52         if units is None:
53             self.units = self.variables[0].units
54         else:
55             self.units = units
56 
57     def __getitem__(self, index):
58         vs = {}
59         for variable in self.variables:
60             arr = variable[index]
61             vs[variable.natural_name] = wt_units.converter(arr, variable.units, self.units)
62         return numexpr.evaluate(self.expression.split('=')[0], local_dict=vs)
63 
64     def __repr__(self):
65         return '<WrightTools.Axis {0} ({1}) at {2}>'.format(self.expression, str(self.units),
66                                                             id(self))
67 
68     @property
69     def _leaf(self):
70         out = self.expression
71         if self.units is not None:
72             out += ' ({0}) {1}'.format(self.units, self.shape)
73         return out
74 
75     @property
76     def full(self):
77         arr = self[:]
78         for i in range(arr.ndim):
79             if arr.shape[i] == 1:
80                 arr = np.repeat(arr, self.parent.shape[i], axis=i)
81         return arr
82 
83     @property
84     def identity(self):
85         return self.natural_name + ' {%s}' % self.units
86 
87     @property
88     def label(self):
89         symbol = wt_units.get_symbol(self.units)
90         label = r'$\mathsf{' + self.expression
91         for v in self.variables:
92             label = label.replace(v.natural_name, '%s_{%s}' % (symbol, v.label))
93         if self.units_kind:
94             units_dictionary = getattr(wt_units, self.units_kind)
95             label += r'\,'
96             label += r'\left('
97             label += units_dictionary[self.units][2]
98             label += r'\right)'
99         else:
100             pass
101         label += r'}$'
102         return label
103 
104     @property
105     def natural_name(self):
106         name = self.expression.strip()
107         for op in operators:
108             name = name.replace(op, operator_to_identifier[op])
109         return name
110 
111     @property
112     def ndim(self):
113         """Get number of dimensions."""
114         try:
115             assert self._ndim is not None
116         except (AssertionError, AttributeError):
117             self._ndim = self.variables[0].ndim
118         finally:
119             return self._ndim
120 
121     @property
122     def points(self):
123         """Squeezed array."""
124         return np.squeeze(self[:])
125 
126     @property
127     def shape(self):
128         """Shape."""
129         return wt_kit.joint_shape(*self.variables)
130 
131     @property
132     def size(self):
133         """Size."""
134         return functools.reduce(operator.mul, self.shape)
135 
136     @property
137     def units_kind(self):
138         """Units kind."""
139         return wt_units.kind(self.units)
140 
141     @property
142     def variables(self):
143         """Variables."""
144         try:
145             assert self._variables is not None
146         except (AssertionError, AttributeError):
147             pattern = '|'.join(map(re.escape, operators))
148             keys = re.split(pattern, self.expression)
149             indices = [self.parent.variable_names.index(key) for key in keys]
150             self._variables = [self.parent.variables[i] for i in indices]
151         finally:
152             return self._variables
153 
154     def convert(self, destination_units, *, convert_variables=False):
155         """Convert axis to destination_units.
156 
157         Parameters
158         ----------
159         destination_units : string
160             Destination units.
161         convert_variables : boolean (optional)
162             Toggle conversion of stored arrays. Default is False.
163         """
164         if not wt_units.is_valid_conversion(self.units, destination_units):
165             kind = wt_units.kind(self.units)
166             valid = list(wt_units.dicts[kind].keys())
167             raise wt_exceptions.UnitsError(valid, destination_units)
168         if convert_variables:
169             for v in self.variables:
170                 v.convert(destination_units)
171         self.units = destination_units
172 
173     def max(self):
174         """Axis max."""
175         return np.max(self[:])
176 
177     def min(self):
178         """Axis min."""
179         return np.min(self[:])
180
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/WrightTools/data/_axis.py b/WrightTools/data/_axis.py
--- a/WrightTools/data/_axis.py
+++ b/WrightTools/data/_axis.py
@@ -106,7 +106,7 @@
         name = self.expression.strip()
         for op in operators:
             name = name.replace(op, operator_to_identifier[op])
-        return name
+        return wt_kit.string2identifier(name)
 
     @property
     def ndim(self):
@@ -146,7 +146,10 @@
         except (AssertionError, AttributeError):
             pattern = '|'.join(map(re.escape, operators))
             keys = re.split(pattern, self.expression)
-            indices = [self.parent.variable_names.index(key) for key in keys]
+            indices = []
+            for key in keys:
+                if key in self.parent.variable_names:
+                    indices.append(self.parent.variable_names.index(key))
             self._variables = [self.parent.variables[i] for i in indices]
         finally:
             return self._variables

{"golden_diff": "diff --git a/WrightTools/data/_axis.py b/WrightTools/data/_axis.py\n--- a/WrightTools/data/_axis.py\n+++ b/WrightTools/data/_axis.py\n@@ -106,7 +106,7 @@\n         name = self.expression.strip()\n         for op in operators:\n             name = name.replace(op, operator_to_identifier[op])\n-        return name\n+        return wt_kit.string2identifier(name)\n \n     @property\n     def ndim(self):\n@@ -146,7 +146,10 @@\n         except (AssertionError, AttributeError):\n             pattern = '|'.join(map(re.escape, operators))\n             keys = re.split(pattern, self.expression)\n-            indices = [self.parent.variable_names.index(key) for key in keys]\n+            indices = []\n+            for key in keys:\n+                if key in self.parent.variable_names:\n+                    indices.append(self.parent.variable_names.index(key))\n             self._variables = [self.parent.variables[i] for i in indices]\n         finally:\n             return self._variables\n", "issue": "multiplication doesn't seem to work in d.transform\n`d.transform('w2+w2+w2', 'w1', 'd2')` works.\r\n\r\n`d.transform('3*w2', 'w1', 'd2')` does not work (even with varying spacing around the multiplication operator)\r\nPart of the error that is raised:\r\n```\r\n  File \"/home/darien/source/WrightTools/WrightTools/data/_data.py\", line 1306, in transform\r\n    axis = current.get(expression, Axis(self, expression))\r\n\r\n  File \"/home/darien/source/WrightTools/WrightTools/data/_axis.py\", line 53, in __init__\r\n    self.units = self.variables[0].units\r\n\r\n  File \"/home/darien/source/WrightTools/WrightTools/data/_axis.py\", line 152, in variables\r\n    return self._variables\r\n\r\nAttributeError: 'Axis' object has no attribute '_variables'\r\n```\n", "before_files": [{"content": "\"\"\"Axis class and associated.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport re\nimport numexpr\nimport operator\nimport functools\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\nfrom .. import kit as wt_kit\nfrom .. import units as wt_units\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\noperator_to_identifier = {}\noperator_to_identifier['/'] = '__d__'\noperator_to_identifier['='] = '__e__'\noperator_to_identifier['-'] = '__m__'\noperator_to_identifier['+'] = '__p__'\noperator_to_identifier['*'] = '__t__'\nidentifier_to_operator = {value: key for key, value in operator_to_identifier.items()}\noperators = ''.join(operator_to_identifier.keys())\n\n\n# --- class ---------------------------------------------------------------------------------------\n\n\nclass Axis(object):\n    \"\"\"Axis class.\"\"\"\n\n    def __init__(self, parent, expression, units=None):\n        \"\"\"Data axis.\n\n        Parameters\n        ----------\n        parent : WrightTools.Data\n            Parent data object.\n        expression : string\n            Axis expression.\n        units : string (optional)\n            Axis units. Default is None.\n        \"\"\"\n        self.parent = parent\n        self.expression = expression\n        if units is None:\n            self.units = self.variables[0].units\n        else:\n            self.units = units\n\n    def __getitem__(self, index):\n        vs = {}\n        for variable in self.variables:\n            arr = variable[index]\n            vs[variable.natural_name] = wt_units.converter(arr, variable.units, self.units)\n        return numexpr.evaluate(self.expression.split('=')[0], local_dict=vs)\n\n    def __repr__(self):\n        return '<WrightTools.Axis {0} ({1}) at {2}>'.format(self.expression, str(self.units),\n                                                            id(self))\n\n    @property\n    def _leaf(self):\n        out = self.expression\n        if self.units is not None:\n            out += ' ({0}) {1}'.format(self.units, self.shape)\n        return out\n\n    @property\n    def full(self):\n        arr = self[:]\n        for i in range(arr.ndim):\n            if arr.shape[i] == 1:\n                arr = np.repeat(arr, self.parent.shape[i], axis=i)\n        return arr\n\n    @property\n    def identity(self):\n        return self.natural_name + ' {%s}' % self.units\n\n    @property\n    def label(self):\n        symbol = wt_units.get_symbol(self.units)\n        label = r'$\\mathsf{' + self.expression\n        for v in self.variables:\n            label = label.replace(v.natural_name, '%s_{%s}' % (symbol, v.label))\n        if self.units_kind:\n            units_dictionary = getattr(wt_units, self.units_kind)\n            label += r'\\,'\n            label += r'\\left('\n            label += units_dictionary[self.units][2]\n            label += r'\\right)'\n        else:\n            pass\n        label += r'}$'\n        return label\n\n    @property\n    def natural_name(self):\n        name = self.expression.strip()\n        for op in operators:\n            name = name.replace(op, operator_to_identifier[op])\n        return name\n\n    @property\n    def ndim(self):\n        \"\"\"Get number of dimensions.\"\"\"\n        try:\n            assert self._ndim is not None\n        except (AssertionError, AttributeError):\n            self._ndim = self.variables[0].ndim\n        finally:\n            return self._ndim\n\n    @property\n    def points(self):\n        \"\"\"Squeezed array.\"\"\"\n        return np.squeeze(self[:])\n\n    @property\n    def shape(self):\n        \"\"\"Shape.\"\"\"\n        return wt_kit.joint_shape(*self.variables)\n\n    @property\n    def size(self):\n        \"\"\"Size.\"\"\"\n        return functools.reduce(operator.mul, self.shape)\n\n    @property\n    def units_kind(self):\n        \"\"\"Units kind.\"\"\"\n        return wt_units.kind(self.units)\n\n    @property\n    def variables(self):\n        \"\"\"Variables.\"\"\"\n        try:\n            assert self._variables is not None\n        except (AssertionError, AttributeError):\n            pattern = '|'.join(map(re.escape, operators))\n            keys = re.split(pattern, self.expression)\n            indices = [self.parent.variable_names.index(key) for key in keys]\n            self._variables = [self.parent.variables[i] for i in indices]\n        finally:\n            return self._variables\n\n    def convert(self, destination_units, *, convert_variables=False):\n        \"\"\"Convert axis to destination_units.\n\n        Parameters\n        ----------\n        destination_units : string\n            Destination units.\n        convert_variables : boolean (optional)\n            Toggle conversion of stored arrays. Default is False.\n        \"\"\"\n        if not wt_units.is_valid_conversion(self.units, destination_units):\n            kind = wt_units.kind(self.units)\n            valid = list(wt_units.dicts[kind].keys())\n            raise wt_exceptions.UnitsError(valid, destination_units)\n        if convert_variables:\n            for v in self.variables:\n                v.convert(destination_units)\n        self.units = destination_units\n\n    def max(self):\n        \"\"\"Axis max.\"\"\"\n        return np.max(self[:])\n\n    def min(self):\n        \"\"\"Axis min.\"\"\"\n        return np.min(self[:])\n", "path": "WrightTools/data/_axis.py"}], "after_files": [{"content": "\"\"\"Axis class and associated.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport re\nimport numexpr\nimport operator\nimport functools\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\nfrom .. import kit as wt_kit\nfrom .. import units as wt_units\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\noperator_to_identifier = {}\noperator_to_identifier['/'] = '__d__'\noperator_to_identifier['='] = '__e__'\noperator_to_identifier['-'] = '__m__'\noperator_to_identifier['+'] = '__p__'\noperator_to_identifier['*'] = '__t__'\nidentifier_to_operator = {value: key for key, value in operator_to_identifier.items()}\noperators = ''.join(operator_to_identifier.keys())\n\n\n# --- class ---------------------------------------------------------------------------------------\n\n\nclass Axis(object):\n    \"\"\"Axis class.\"\"\"\n\n    def __init__(self, parent, expression, units=None):\n        \"\"\"Data axis.\n\n        Parameters\n        ----------\n        parent : WrightTools.Data\n            Parent data object.\n        expression : string\n            Axis expression.\n        units : string (optional)\n            Axis units. Default is None.\n        \"\"\"\n        self.parent = parent\n        self.expression = expression\n        if units is None:\n            self.units = self.variables[0].units\n        else:\n            self.units = units\n\n    def __getitem__(self, index):\n        vs = {}\n        for variable in self.variables:\n            arr = variable[index]\n            vs[variable.natural_name] = wt_units.converter(arr, variable.units, self.units)\n        return numexpr.evaluate(self.expression.split('=')[0], local_dict=vs)\n\n    def __repr__(self):\n        return '<WrightTools.Axis {0} ({1}) at {2}>'.format(self.expression, str(self.units),\n                                                            id(self))\n\n    @property\n    def _leaf(self):\n        out = self.expression\n        if self.units is not None:\n            out += ' ({0}) {1}'.format(self.units, self.shape)\n        return out\n\n    @property\n    def full(self):\n        arr = self[:]\n        for i in range(arr.ndim):\n            if arr.shape[i] == 1:\n                arr = np.repeat(arr, self.parent.shape[i], axis=i)\n        return arr\n\n    @property\n    def identity(self):\n        return self.natural_name + ' {%s}' % self.units\n\n    @property\n    def label(self):\n        symbol = wt_units.get_symbol(self.units)\n        label = r'$\\mathsf{' + self.expression\n        for v in self.variables:\n            label = label.replace(v.natural_name, '%s_{%s}' % (symbol, v.label))\n        if self.units_kind:\n            units_dictionary = getattr(wt_units, self.units_kind)\n            label += r'\\,'\n            label += r'\\left('\n            label += units_dictionary[self.units][2]\n            label += r'\\right)'\n        else:\n            pass\n        label += r'}$'\n        return label\n\n    @property\n    def natural_name(self):\n        name = self.expression.strip()\n        for op in operators:\n            name = name.replace(op, operator_to_identifier[op])\n        return wt_kit.string2identifier(name)\n\n    @property\n    def ndim(self):\n        \"\"\"Get number of dimensions.\"\"\"\n        try:\n            assert self._ndim is not None\n        except (AssertionError, AttributeError):\n            self._ndim = self.variables[0].ndim\n        finally:\n            return self._ndim\n\n    @property\n    def points(self):\n        \"\"\"Squeezed array.\"\"\"\n        return np.squeeze(self[:])\n\n    @property\n    def shape(self):\n        \"\"\"Shape.\"\"\"\n        return wt_kit.joint_shape(*self.variables)\n\n    @property\n    def size(self):\n        \"\"\"Size.\"\"\"\n        return functools.reduce(operator.mul, self.shape)\n\n    @property\n    def units_kind(self):\n        \"\"\"Units kind.\"\"\"\n        return wt_units.kind(self.units)\n\n    @property\n    def variables(self):\n        \"\"\"Variables.\"\"\"\n        try:\n            assert self._variables is not None\n        except (AssertionError, AttributeError):\n            pattern = '|'.join(map(re.escape, operators))\n            keys = re.split(pattern, self.expression)\n            indices = []\n            for key in keys:\n                if key in self.parent.variable_names:\n                    indices.append(self.parent.variable_names.index(key))\n            self._variables = [self.parent.variables[i] for i in indices]\n        finally:\n            return self._variables\n\n    def convert(self, destination_units, *, convert_variables=False):\n        \"\"\"Convert axis to destination_units.\n\n        Parameters\n        ----------\n        destination_units : string\n            Destination units.\n        convert_variables : boolean (optional)\n            Toggle conversion of stored arrays. Default is False.\n        \"\"\"\n        if not wt_units.is_valid_conversion(self.units, destination_units):\n            kind = wt_units.kind(self.units)\n            valid = list(wt_units.dicts[kind].keys())\n            raise wt_exceptions.UnitsError(valid, destination_units)\n        if convert_variables:\n            for v in self.variables:\n                v.convert(destination_units)\n        self.units = destination_units\n\n    def max(self):\n        \"\"\"Axis max.\"\"\"\n        return np.max(self[:])\n\n    def min(self):\n        \"\"\"Axis min.\"\"\"\n        return np.min(self[:])\n", "path": "WrightTools/data/_axis.py"}]}