We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[before h5] join takes set of points along each axis
currently join looks at each axis and tries to guess what the best evenly spaced points are

moving forward, join will simply take the set of all points in all data objects along each axis

interpolation will still be used for points that are not contained in any data set (within the convex hull, of course)

--- 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/_join.py`
Content:
```
1 """Join multiple data objects together."""
2 
3 
4 # --- import --------------------------------------------------------------------------------------
5 
6 
7 import numpy as np
8 
9 from ._data import Channel, Data
10 
11 
12 # --- define --------------------------------------------------------------------------------------
13 
14 __all__ = ['join']
15 
16 # --- functions -----------------------------------------------------------------------------------
17 
18 
19 def join(datas, method='first', verbose=True, **kwargs):
20     """Join a list of data objects together.
21 
22     For now datas must have identical dimensionalities (order and identity).
23 
24     Parameters
25     ----------
26     datas : list of data
27         The list of data objects to join together.
28     method : {'first', 'sum', 'max', 'min', 'mean'} (optional)
29         The method for how overlapping points get treated. Default is first,
30         meaning that the data object that appears first in datas will take
31         precedence.
32     verbose : bool (optional)
33         Toggle talkback. Default is True.
34 
35     Keyword Arguments
36     -----------------
37     axis objects
38         The axes of the new data object. If not supplied, the points of the
39         new axis will be guessed from the given datas.
40 
41     Returns
42     -------
43     data
44         A Data instance.
45     """
46     # TODO: a proper treatment of joining datas that have different dimensions
47     # with intellegent treatment of their constant dimensions. perhaps changing
48     # map_axis would be good for this. - Blaise 2015.10.31
49 
50     # copy datas so original objects are not changed
51     datas = [d.copy() for d in datas]
52     # get scanned dimensions
53     axis_names = []
54     axis_units = []
55     axis_objects = []
56     for data in datas:
57         for i, axis in enumerate(data.axes):
58             if axis.name in kwargs.keys():
59                 axis.convert(kwargs[axis.name].units)
60             if axis.points[0] > axis.points[-1]:
61                 data.flip(i)
62             if axis.name not in axis_names:
63                 axis_names.append(axis.name)
64                 axis_units.append(axis.units)
65                 axis_objects.append(axis)
66     # TODO: transpose to same dimension orders
67     # convert into same units
68     for data in datas:
69         for axis_name, axis_unit in zip(axis_names, axis_units):
70             for axis in data.axes:
71                 if axis.name == axis_name:
72                     axis.convert(axis_unit)
73     # get axis points
74     axis_points = []  # list of 1D arrays
75     for axis_name in axis_names:
76         if axis_name in kwargs.keys():
77             axis_points.append(kwargs[axis_name].points)
78             continue
79         all_points = np.array([])
80         step_sizes = []
81         for data in datas:
82             for axis in data.axes:
83                 if axis.name == axis_name:
84                     all_points = np.concatenate([all_points, axis.points])
85                     this_axis_min = np.nanmin(axis.points)
86                     this_axis_max = np.nanmax(axis.points)
87                     this_axis_number = float(axis.points.size) - 1
88                     step_size = (this_axis_max - this_axis_min) / this_axis_number
89                     step_sizes.append(step_size)
90         axis_min = np.nanmin(all_points)
91         axis_max = np.nanmax(all_points)
92         axis_step_size = min(step_sizes)
93         axis_n_points = np.ceil((axis_max - axis_min) / axis_step_size)
94         points = np.linspace(axis_min, axis_max, axis_n_points + 1)
95         axis_points.append(points)
96     # map datas to new points
97     for axis_index, axis_name in enumerate(axis_names):
98         for data in datas:
99             for axis in data.axes:
100                 if axis.name == axis_name:
101                     if not np.array_equiv(axis.points, axis_points[axis_index]):
102                         data.map_axis(axis_name, axis_points[axis_index])
103     # make new channel objects
104     channel_objects = []
105     n_channels = min([len(d.channels) for d in datas])
106     for channel_index in range(n_channels):
107         full = np.array([d.channels[channel_index].values for d in datas])
108         if method == 'first':
109             zis = np.full(full.shape[1:], np.nan)
110             for idx in np.ndindex(*full.shape[1:]):
111                 for data_index in range(len(full)):
112                     value = full[data_index][idx]
113                     if not np.isnan(value):
114                         zis[idx] = value
115                         break
116         elif method == 'sum':
117             zis = np.nansum(full, axis=0)
118             zis[zis == 0.] = np.nan
119         elif method == 'max':
120             zis = np.nanmax(full, axis=0)
121         elif method == 'min':
122             zis = np.nanmin(full, axis=0)
123         elif method == 'mean':
124             zis = np.nanmean(full, axis=0)
125         else:
126             raise ValueError("method %s not recognized" % method)
127         zis[np.isnan(full).all(axis=0)] = np.nan  # if all datas NaN, zis NaN
128         channel = Channel(zis, null=0.,
129                           signed=datas[0].channels[channel_index].signed,
130                           name=datas[0].channels[channel_index].name)
131         channel_objects.append(channel)
132     # make new data object
133     out = Data(axis_objects, channel_objects)
134     # finish
135     if verbose:
136         print(len(datas), 'datas joined to create new data:')
137         print('  axes:')
138         for axis in out.axes:
139             points = axis.points
140             print('    {0} : {1} points from {2} to {3} {4}'.format(
141                 axis.name, points.size, min(points), max(points), axis.units))
142         print('  channels:')
143         for channel in out.channels:
144             percent_nan = np.around(100. * (np.isnan(channel.values).sum() /
145                                             float(channel.values.size)), decimals=2)
146             print('    {0} : {1} to {2} ({3}% NaN)'.format(
147                 channel.name, channel.min(), channel.max(), percent_nan))
148     return out
149
```
--- 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/_join.py b/WrightTools/data/_join.py
--- a/WrightTools/data/_join.py
+++ b/WrightTools/data/_join.py
@@ -11,8 +11,10 @@
 
 # --- define --------------------------------------------------------------------------------------
 
+
 __all__ = ['join']
 
+
 # --- functions -----------------------------------------------------------------------------------
 
 
@@ -32,21 +34,11 @@
     verbose : bool (optional)
         Toggle talkback. Default is True.
 
-    Keyword Arguments
-    -----------------
-    axis objects
-        The axes of the new data object. If not supplied, the points of the
-        new axis will be guessed from the given datas.
-
     Returns
     -------
-    data
-        A Data instance.
+    WrightTools.data.Data
+        A new Data instance.
     """
-    # TODO: a proper treatment of joining datas that have different dimensions
-    # with intellegent treatment of their constant dimensions. perhaps changing
-    # map_axis would be good for this. - Blaise 2015.10.31
-
     # copy datas so original objects are not changed
     datas = [d.copy() for d in datas]
     # get scanned dimensions
@@ -63,7 +55,6 @@
                 axis_names.append(axis.name)
                 axis_units.append(axis.units)
                 axis_objects.append(axis)
-    # TODO: transpose to same dimension orders
     # convert into same units
     for data in datas:
         for axis_name, axis_unit in zip(axis_names, axis_units):
@@ -73,26 +64,11 @@
     # get axis points
     axis_points = []  # list of 1D arrays
     for axis_name in axis_names:
-        if axis_name in kwargs.keys():
-            axis_points.append(kwargs[axis_name].points)
-            continue
-        all_points = np.array([])
-        step_sizes = []
+        points = np.full((0), np.nan)
         for data in datas:
-            for axis in data.axes:
-                if axis.name == axis_name:
-                    all_points = np.concatenate([all_points, axis.points])
-                    this_axis_min = np.nanmin(axis.points)
-                    this_axis_max = np.nanmax(axis.points)
-                    this_axis_number = float(axis.points.size) - 1
-                    step_size = (this_axis_max - this_axis_min) / this_axis_number
-                    step_sizes.append(step_size)
-        axis_min = np.nanmin(all_points)
-        axis_max = np.nanmax(all_points)
-        axis_step_size = min(step_sizes)
-        axis_n_points = np.ceil((axis_max - axis_min) / axis_step_size)
-        points = np.linspace(axis_min, axis_max, axis_n_points + 1)
-        axis_points.append(points)
+            index = data.axis_names.index(axis_name)
+            points = np.hstack((points, data.axes[index].points))
+        axis_points.append(np.unique(points))
     # map datas to new points
     for axis_index, axis_name in enumerate(axis_names):
         for data in datas:

{"golden_diff": "diff --git a/WrightTools/data/_join.py b/WrightTools/data/_join.py\n--- a/WrightTools/data/_join.py\n+++ b/WrightTools/data/_join.py\n@@ -11,8 +11,10 @@\n \n # --- define --------------------------------------------------------------------------------------\n \n+\n __all__ = ['join']\n \n+\n # --- functions -----------------------------------------------------------------------------------\n \n \n@@ -32,21 +34,11 @@\n     verbose : bool (optional)\n         Toggle talkback. Default is True.\n \n-    Keyword Arguments\n-    -----------------\n-    axis objects\n-        The axes of the new data object. If not supplied, the points of the\n-        new axis will be guessed from the given datas.\n-\n     Returns\n     -------\n-    data\n-        A Data instance.\n+    WrightTools.data.Data\n+        A new Data instance.\n     \"\"\"\n-    # TODO: a proper treatment of joining datas that have different dimensions\n-    # with intellegent treatment of their constant dimensions. perhaps changing\n-    # map_axis would be good for this. - Blaise 2015.10.31\n-\n     # copy datas so original objects are not changed\n     datas = [d.copy() for d in datas]\n     # get scanned dimensions\n@@ -63,7 +55,6 @@\n                 axis_names.append(axis.name)\n                 axis_units.append(axis.units)\n                 axis_objects.append(axis)\n-    # TODO: transpose to same dimension orders\n     # convert into same units\n     for data in datas:\n         for axis_name, axis_unit in zip(axis_names, axis_units):\n@@ -73,26 +64,11 @@\n     # get axis points\n     axis_points = []  # list of 1D arrays\n     for axis_name in axis_names:\n-        if axis_name in kwargs.keys():\n-            axis_points.append(kwargs[axis_name].points)\n-            continue\n-        all_points = np.array([])\n-        step_sizes = []\n+        points = np.full((0), np.nan)\n         for data in datas:\n-            for axis in data.axes:\n-                if axis.name == axis_name:\n-                    all_points = np.concatenate([all_points, axis.points])\n-                    this_axis_min = np.nanmin(axis.points)\n-                    this_axis_max = np.nanmax(axis.points)\n-                    this_axis_number = float(axis.points.size) - 1\n-                    step_size = (this_axis_max - this_axis_min) / this_axis_number\n-                    step_sizes.append(step_size)\n-        axis_min = np.nanmin(all_points)\n-        axis_max = np.nanmax(all_points)\n-        axis_step_size = min(step_sizes)\n-        axis_n_points = np.ceil((axis_max - axis_min) / axis_step_size)\n-        points = np.linspace(axis_min, axis_max, axis_n_points + 1)\n-        axis_points.append(points)\n+            index = data.axis_names.index(axis_name)\n+            points = np.hstack((points, data.axes[index].points))\n+        axis_points.append(np.unique(points))\n     # map datas to new points\n     for axis_index, axis_name in enumerate(axis_names):\n         for data in datas:\n", "issue": "[before h5] join takes set of points along each axis\ncurrently join looks at each axis and tries to guess what the best evenly spaced points are\r\n\r\nmoving forward, join will simply take the set of all points in all data objects along each axis\r\n\r\ninterpolation will still be used for points that are not contained in any data set (within the convex hull, of course)\n", "before_files": [{"content": "\"\"\"Join multiple data objects together.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom ._data import Channel, Data\n\n\n# --- define --------------------------------------------------------------------------------------\n\n__all__ = ['join']\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef join(datas, method='first', verbose=True, **kwargs):\n    \"\"\"Join a list of data objects together.\n\n    For now datas must have identical dimensionalities (order and identity).\n\n    Parameters\n    ----------\n    datas : list of data\n        The list of data objects to join together.\n    method : {'first', 'sum', 'max', 'min', 'mean'} (optional)\n        The method for how overlapping points get treated. Default is first,\n        meaning that the data object that appears first in datas will take\n        precedence.\n    verbose : bool (optional)\n        Toggle talkback. Default is True.\n\n    Keyword Arguments\n    -----------------\n    axis objects\n        The axes of the new data object. If not supplied, the points of the\n        new axis will be guessed from the given datas.\n\n    Returns\n    -------\n    data\n        A Data instance.\n    \"\"\"\n    # TODO: a proper treatment of joining datas that have different dimensions\n    # with intellegent treatment of their constant dimensions. perhaps changing\n    # map_axis would be good for this. - Blaise 2015.10.31\n\n    # copy datas so original objects are not changed\n    datas = [d.copy() for d in datas]\n    # get scanned dimensions\n    axis_names = []\n    axis_units = []\n    axis_objects = []\n    for data in datas:\n        for i, axis in enumerate(data.axes):\n            if axis.name in kwargs.keys():\n                axis.convert(kwargs[axis.name].units)\n            if axis.points[0] > axis.points[-1]:\n                data.flip(i)\n            if axis.name not in axis_names:\n                axis_names.append(axis.name)\n                axis_units.append(axis.units)\n                axis_objects.append(axis)\n    # TODO: transpose to same dimension orders\n    # convert into same units\n    for data in datas:\n        for axis_name, axis_unit in zip(axis_names, axis_units):\n            for axis in data.axes:\n                if axis.name == axis_name:\n                    axis.convert(axis_unit)\n    # get axis points\n    axis_points = []  # list of 1D arrays\n    for axis_name in axis_names:\n        if axis_name in kwargs.keys():\n            axis_points.append(kwargs[axis_name].points)\n            continue\n        all_points = np.array([])\n        step_sizes = []\n        for data in datas:\n            for axis in data.axes:\n                if axis.name == axis_name:\n                    all_points = np.concatenate([all_points, axis.points])\n                    this_axis_min = np.nanmin(axis.points)\n                    this_axis_max = np.nanmax(axis.points)\n                    this_axis_number = float(axis.points.size) - 1\n                    step_size = (this_axis_max - this_axis_min) / this_axis_number\n                    step_sizes.append(step_size)\n        axis_min = np.nanmin(all_points)\n        axis_max = np.nanmax(all_points)\n        axis_step_size = min(step_sizes)\n        axis_n_points = np.ceil((axis_max - axis_min) / axis_step_size)\n        points = np.linspace(axis_min, axis_max, axis_n_points + 1)\n        axis_points.append(points)\n    # map datas to new points\n    for axis_index, axis_name in enumerate(axis_names):\n        for data in datas:\n            for axis in data.axes:\n                if axis.name == axis_name:\n                    if not np.array_equiv(axis.points, axis_points[axis_index]):\n                        data.map_axis(axis_name, axis_points[axis_index])\n    # make new channel objects\n    channel_objects = []\n    n_channels = min([len(d.channels) for d in datas])\n    for channel_index in range(n_channels):\n        full = np.array([d.channels[channel_index].values for d in datas])\n        if method == 'first':\n            zis = np.full(full.shape[1:], np.nan)\n            for idx in np.ndindex(*full.shape[1:]):\n                for data_index in range(len(full)):\n                    value = full[data_index][idx]\n                    if not np.isnan(value):\n                        zis[idx] = value\n                        break\n        elif method == 'sum':\n            zis = np.nansum(full, axis=0)\n            zis[zis == 0.] = np.nan\n        elif method == 'max':\n            zis = np.nanmax(full, axis=0)\n        elif method == 'min':\n            zis = np.nanmin(full, axis=0)\n        elif method == 'mean':\n            zis = np.nanmean(full, axis=0)\n        else:\n            raise ValueError(\"method %s not recognized\" % method)\n        zis[np.isnan(full).all(axis=0)] = np.nan  # if all datas NaN, zis NaN\n        channel = Channel(zis, null=0.,\n                          signed=datas[0].channels[channel_index].signed,\n                          name=datas[0].channels[channel_index].name)\n        channel_objects.append(channel)\n    # make new data object\n    out = Data(axis_objects, channel_objects)\n    # finish\n    if verbose:\n        print(len(datas), 'datas joined to create new data:')\n        print('  axes:')\n        for axis in out.axes:\n            points = axis.points\n            print('    {0} : {1} points from {2} to {3} {4}'.format(\n                axis.name, points.size, min(points), max(points), axis.units))\n        print('  channels:')\n        for channel in out.channels:\n            percent_nan = np.around(100. * (np.isnan(channel.values).sum() /\n                                            float(channel.values.size)), decimals=2)\n            print('    {0} : {1} to {2} ({3}% NaN)'.format(\n                channel.name, channel.min(), channel.max(), percent_nan))\n    return out\n", "path": "WrightTools/data/_join.py"}], "after_files": [{"content": "\"\"\"Join multiple data objects together.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom ._data import Channel, Data\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\n__all__ = ['join']\n\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef join(datas, method='first', verbose=True, **kwargs):\n    \"\"\"Join a list of data objects together.\n\n    For now datas must have identical dimensionalities (order and identity).\n\n    Parameters\n    ----------\n    datas : list of data\n        The list of data objects to join together.\n    method : {'first', 'sum', 'max', 'min', 'mean'} (optional)\n        The method for how overlapping points get treated. Default is first,\n        meaning that the data object that appears first in datas will take\n        precedence.\n    verbose : bool (optional)\n        Toggle talkback. Default is True.\n\n    Returns\n    -------\n    WrightTools.data.Data\n        A new Data instance.\n    \"\"\"\n    # copy datas so original objects are not changed\n    datas = [d.copy() for d in datas]\n    # get scanned dimensions\n    axis_names = []\n    axis_units = []\n    axis_objects = []\n    for data in datas:\n        for i, axis in enumerate(data.axes):\n            if axis.name in kwargs.keys():\n                axis.convert(kwargs[axis.name].units)\n            if axis.points[0] > axis.points[-1]:\n                data.flip(i)\n            if axis.name not in axis_names:\n                axis_names.append(axis.name)\n                axis_units.append(axis.units)\n                axis_objects.append(axis)\n    # convert into same units\n    for data in datas:\n        for axis_name, axis_unit in zip(axis_names, axis_units):\n            for axis in data.axes:\n                if axis.name == axis_name:\n                    axis.convert(axis_unit)\n    # get axis points\n    axis_points = []  # list of 1D arrays\n    for axis_name in axis_names:\n        points = np.full((0), np.nan)\n        for data in datas:\n            index = data.axis_names.index(axis_name)\n            points = np.hstack((points, data.axes[index].points))\n        axis_points.append(np.unique(points))\n    # map datas to new points\n    for axis_index, axis_name in enumerate(axis_names):\n        for data in datas:\n            for axis in data.axes:\n                if axis.name == axis_name:\n                    if not np.array_equiv(axis.points, axis_points[axis_index]):\n                        data.map_axis(axis_name, axis_points[axis_index])\n    # make new channel objects\n    channel_objects = []\n    n_channels = min([len(d.channels) for d in datas])\n    for channel_index in range(n_channels):\n        full = np.array([d.channels[channel_index].values for d in datas])\n        if method == 'first':\n            zis = np.full(full.shape[1:], np.nan)\n            for idx in np.ndindex(*full.shape[1:]):\n                for data_index in range(len(full)):\n                    value = full[data_index][idx]\n                    if not np.isnan(value):\n                        zis[idx] = value\n                        break\n        elif method == 'sum':\n            zis = np.nansum(full, axis=0)\n            zis[zis == 0.] = np.nan\n        elif method == 'max':\n            zis = np.nanmax(full, axis=0)\n        elif method == 'min':\n            zis = np.nanmin(full, axis=0)\n        elif method == 'mean':\n            zis = np.nanmean(full, axis=0)\n        else:\n            raise ValueError(\"method %s not recognized\" % method)\n        zis[np.isnan(full).all(axis=0)] = np.nan  # if all datas NaN, zis NaN\n        channel = Channel(zis, null=0.,\n                          signed=datas[0].channels[channel_index].signed,\n                          name=datas[0].channels[channel_index].name)\n        channel_objects.append(channel)\n    # make new data object\n    out = Data(axis_objects, channel_objects)\n    # finish\n    if verbose:\n        print(len(datas), 'datas joined to create new data:')\n        print('  axes:')\n        for axis in out.axes:\n            points = axis.points\n            print('    {0} : {1} points from {2} to {3} {4}'.format(\n                axis.name, points.size, min(points), max(points), axis.units))\n        print('  channels:')\n        for channel in out.channels:\n            percent_nan = np.around(100. * (np.isnan(channel.values).sum() /\n                                            float(channel.values.size)), decimals=2)\n            print('    {0} : {1} to {2} ({3}% NaN)'.format(\n                channel.name, channel.min(), channel.max(), percent_nan))\n    return out\n", "path": "WrightTools/data/_join.py"}]}