We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
setter for null
Currently null is not settable on a channel

It can be worked around with `channel.attrs['null']`

--- 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/_channel.py`
Content:
```
1 """Channel class and associated."""
2 
3 
4 # --- import --------------------------------------------------------------------------------------
5 
6 
7 import numpy as np
8 
9 import h5py
10 
11 from .. import kit as wt_kit
12 from .._dataset import Dataset
13 
14 
15 # --- class ---------------------------------------------------------------------------------------
16 
17 
18 class Channel(Dataset):
19     """Channel."""
20 
21     class_name = 'Channel'
22 
23     def __init__(self, parent, id, *, units=None, null=None, signed=None, label=None,
24                  label_seed=None, **kwargs):
25         """Construct a channel object.
26 
27         Parameters
28         ----------
29         values : array-like
30             Values.
31         name : string
32             Channel name.
33         units : string (optional)
34             Channel units. Default is None.
35         null : number (optional)
36             Channel null. Default is None (0).
37         signed : booelan (optional)
38             Channel signed flag. Default is None (guess).
39         label : string.
40             Label. Default is None.
41         label_seed : list of strings
42             Label seed. Default is None.
43         **kwargs
44             Additional keyword arguments are added to the attrs dictionary
45             and to the natural namespace of the object (if possible).
46         """
47         self._parent = parent
48         super().__init__(id)
49         self.label = label
50         self.label_seed = label_seed
51         self.units = units
52         self.dimensionality = len(self.shape)
53         # attrs
54         self.attrs.update(kwargs)
55         self.attrs['name'] = h5py.h5i.get_name(self.id).decode().split('/')[-1]
56         self.attrs['class'] = 'Channel'
57         if signed is not None:
58             self.attrs['signed'] = signed
59         if null is not None:
60             self.attrs['null'] = null
61         for key, value in self.attrs.items():
62             identifier = wt_kit.string2identifier(key)
63             if not hasattr(self, identifier):
64                 setattr(self, identifier, value)
65 
66     @property
67     def minor_extent(self):
68         """Minimum deviation from null."""
69         return min((self.max() - self.null, self.null - self.min()))
70 
71     @property
72     def natural_name(self):
73         """Natural name of the dataset. May be different from name."""
74         try:
75             assert self._natural_name is not None
76         except (AssertionError, AttributeError):
77             self._natural_name = self.attrs['name']
78         finally:
79             return self._natural_name
80 
81     @natural_name.setter
82     def natural_name(self, value):
83         index = wt_kit.get_index(self.parent.channel_names, self.natural_name)
84         new = list(self.parent.channel_names)
85         new[index] = value
86         self.parent.channel_names = new
87         self.attrs['name'] = value
88         self._natural_name = None
89 
90     @property
91     def null(self):
92         if 'null' not in self.attrs.keys():
93             self.attrs['null'] = 0
94         return self.attrs['null']
95 
96     @property
97     def major_extent(self):
98         """Maximum deviation from null."""
99         return max((self.max() - self.null, self.null - self.min()))
100 
101     @property
102     def signed(self):
103         if 'signed' not in self.attrs.keys():
104             self.attrs['signed'] = False
105         return self.attrs['signed']
106 
107     @signed.setter
108     def signed(self, value):
109         self.attrs['signed'] = value
110 
111     def mag(self):
112         """Channel magnitude (maximum deviation from null)."""
113         return self.major_extent
114 
115     def normalize(self):
116         """Normalize a Channel, set `null` to 0 and the mag to 1."""
117         def f(dataset, s, null, mag):
118             dataset[s] -= null
119             dataset[s] /= mag
120         if self.signed:
121             mag = self.mag()
122         else:
123             mag = self.max()
124         self.chunkwise(f, null=self.null, mag=mag)
125         self._null = 0
126 
127     def trim(self, neighborhood, method='ztest', factor=3, replace='nan',
128              verbose=True):
129         """Remove outliers from the dataset.
130 
131         Identifies outliers by comparing each point to its
132         neighbors using a statistical test.
133 
134         Parameters
135         ----------
136         neighborhood : list of integers
137             Size of the neighborhood in each dimension. Length of the list must
138             be equal to the dimensionality of the channel.
139         method : {'ztest'} (optional)
140             Statistical test used to detect outliers. Default is ztest.
141 
142             ztest
143                 Compare point deviation from neighborhood mean to neighborhood
144                 standard deviation.
145 
146         factor : number (optional)
147             Tolerance factor.  Default is 3.
148         replace : {'nan', 'mean', 'mask', number} (optional)
149             Behavior of outlier replacement. Default is nan.
150 
151             nan
152                 Outliers are replaced by numpy nans.
153 
154             mean
155                 Outliers are replaced by the mean of its neighborhood.
156 
157             mask
158                 Array is masked at outliers.
159 
160             number
161                 Array becomes given number.
162 
163         Returns
164         -------
165         list of tuples
166             Indicies of trimmed outliers.
167 
168         See Also
169         --------
170         clip
171             Remove pixels outside of a certain range.
172         """
173         raise NotImplementedError
174         outliers = []
175         means = []
176         # find outliers
177         for idx in np.ndindex(self.shape):
178             slices = []
179             for i, di, size in zip(idx, neighborhood, self.shape):
180                 start = max(0, i - di)
181                 stop = min(size, i + di + 1)
182                 slices.append(slice(start, stop, 1))
183             neighbors = self[slices]
184             mean = np.nanmean(neighbors)
185             limit = np.nanstd(neighbors) * factor
186             if np.abs(self[idx] - mean) > limit:
187                 outliers.append(idx)
188                 means.append(mean)
189         # replace outliers
190         i = tuple(zip(*outliers))
191         if replace == 'nan':
192             self[i] = np.nan
193         elif replace == 'mean':
194             self[i] = means
195         elif replace == 'mask':
196             self[:] = np.ma.array(self[:])
197             self[i] = np.ma.masked
198         elif type(replace) in [int, float]:
199             self[i] = replace
200         else:
201             raise KeyError('replace must be one of {nan, mean, mask} or some number')
202         # finish
203         if verbose:
204             print('%i outliers removed' % len(outliers))
205         return outliers
206
```
--- 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/_channel.py b/WrightTools/data/_channel.py
--- a/WrightTools/data/_channel.py
+++ b/WrightTools/data/_channel.py
@@ -93,6 +93,10 @@
             self.attrs['null'] = 0
         return self.attrs['null']
 
+    @null.setter
+    def null(self, value):
+        self.attrs['null'] = value
+
     @property
     def major_extent(self):
         """Maximum deviation from null."""

{"golden_diff": "diff --git a/WrightTools/data/_channel.py b/WrightTools/data/_channel.py\n--- a/WrightTools/data/_channel.py\n+++ b/WrightTools/data/_channel.py\n@@ -93,6 +93,10 @@\n             self.attrs['null'] = 0\n         return self.attrs['null']\n \n+    @null.setter\n+    def null(self, value):\n+        self.attrs['null'] = value\n+\n     @property\n     def major_extent(self):\n         \"\"\"Maximum deviation from null.\"\"\"\n", "issue": "setter for null\nCurrently null is not settable on a channel\r\n\r\nIt can be worked around with `channel.attrs['null']`\n", "before_files": [{"content": "\"\"\"Channel class and associated.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nimport h5py\n\nfrom .. import kit as wt_kit\nfrom .._dataset import Dataset\n\n\n# --- class ---------------------------------------------------------------------------------------\n\n\nclass Channel(Dataset):\n    \"\"\"Channel.\"\"\"\n\n    class_name = 'Channel'\n\n    def __init__(self, parent, id, *, units=None, null=None, signed=None, label=None,\n                 label_seed=None, **kwargs):\n        \"\"\"Construct a channel object.\n\n        Parameters\n        ----------\n        values : array-like\n            Values.\n        name : string\n            Channel name.\n        units : string (optional)\n            Channel units. Default is None.\n        null : number (optional)\n            Channel null. Default is None (0).\n        signed : booelan (optional)\n            Channel signed flag. Default is None (guess).\n        label : string.\n            Label. Default is None.\n        label_seed : list of strings\n            Label seed. Default is None.\n        **kwargs\n            Additional keyword arguments are added to the attrs dictionary\n            and to the natural namespace of the object (if possible).\n        \"\"\"\n        self._parent = parent\n        super().__init__(id)\n        self.label = label\n        self.label_seed = label_seed\n        self.units = units\n        self.dimensionality = len(self.shape)\n        # attrs\n        self.attrs.update(kwargs)\n        self.attrs['name'] = h5py.h5i.get_name(self.id).decode().split('/')[-1]\n        self.attrs['class'] = 'Channel'\n        if signed is not None:\n            self.attrs['signed'] = signed\n        if null is not None:\n            self.attrs['null'] = null\n        for key, value in self.attrs.items():\n            identifier = wt_kit.string2identifier(key)\n            if not hasattr(self, identifier):\n                setattr(self, identifier, value)\n\n    @property\n    def minor_extent(self):\n        \"\"\"Minimum deviation from null.\"\"\"\n        return min((self.max() - self.null, self.null - self.min()))\n\n    @property\n    def natural_name(self):\n        \"\"\"Natural name of the dataset. May be different from name.\"\"\"\n        try:\n            assert self._natural_name is not None\n        except (AssertionError, AttributeError):\n            self._natural_name = self.attrs['name']\n        finally:\n            return self._natural_name\n\n    @natural_name.setter\n    def natural_name(self, value):\n        index = wt_kit.get_index(self.parent.channel_names, self.natural_name)\n        new = list(self.parent.channel_names)\n        new[index] = value\n        self.parent.channel_names = new\n        self.attrs['name'] = value\n        self._natural_name = None\n\n    @property\n    def null(self):\n        if 'null' not in self.attrs.keys():\n            self.attrs['null'] = 0\n        return self.attrs['null']\n\n    @property\n    def major_extent(self):\n        \"\"\"Maximum deviation from null.\"\"\"\n        return max((self.max() - self.null, self.null - self.min()))\n\n    @property\n    def signed(self):\n        if 'signed' not in self.attrs.keys():\n            self.attrs['signed'] = False\n        return self.attrs['signed']\n\n    @signed.setter\n    def signed(self, value):\n        self.attrs['signed'] = value\n\n    def mag(self):\n        \"\"\"Channel magnitude (maximum deviation from null).\"\"\"\n        return self.major_extent\n\n    def normalize(self):\n        \"\"\"Normalize a Channel, set `null` to 0 and the mag to 1.\"\"\"\n        def f(dataset, s, null, mag):\n            dataset[s] -= null\n            dataset[s] /= mag\n        if self.signed:\n            mag = self.mag()\n        else:\n            mag = self.max()\n        self.chunkwise(f, null=self.null, mag=mag)\n        self._null = 0\n\n    def trim(self, neighborhood, method='ztest', factor=3, replace='nan',\n             verbose=True):\n        \"\"\"Remove outliers from the dataset.\n\n        Identifies outliers by comparing each point to its\n        neighbors using a statistical test.\n\n        Parameters\n        ----------\n        neighborhood : list of integers\n            Size of the neighborhood in each dimension. Length of the list must\n            be equal to the dimensionality of the channel.\n        method : {'ztest'} (optional)\n            Statistical test used to detect outliers. Default is ztest.\n\n            ztest\n                Compare point deviation from neighborhood mean to neighborhood\n                standard deviation.\n\n        factor : number (optional)\n            Tolerance factor.  Default is 3.\n        replace : {'nan', 'mean', 'mask', number} (optional)\n            Behavior of outlier replacement. Default is nan.\n\n            nan\n                Outliers are replaced by numpy nans.\n\n            mean\n                Outliers are replaced by the mean of its neighborhood.\n\n            mask\n                Array is masked at outliers.\n\n            number\n                Array becomes given number.\n\n        Returns\n        -------\n        list of tuples\n            Indicies of trimmed outliers.\n\n        See Also\n        --------\n        clip\n            Remove pixels outside of a certain range.\n        \"\"\"\n        raise NotImplementedError\n        outliers = []\n        means = []\n        # find outliers\n        for idx in np.ndindex(self.shape):\n            slices = []\n            for i, di, size in zip(idx, neighborhood, self.shape):\n                start = max(0, i - di)\n                stop = min(size, i + di + 1)\n                slices.append(slice(start, stop, 1))\n            neighbors = self[slices]\n            mean = np.nanmean(neighbors)\n            limit = np.nanstd(neighbors) * factor\n            if np.abs(self[idx] - mean) > limit:\n                outliers.append(idx)\n                means.append(mean)\n        # replace outliers\n        i = tuple(zip(*outliers))\n        if replace == 'nan':\n            self[i] = np.nan\n        elif replace == 'mean':\n            self[i] = means\n        elif replace == 'mask':\n            self[:] = np.ma.array(self[:])\n            self[i] = np.ma.masked\n        elif type(replace) in [int, float]:\n            self[i] = replace\n        else:\n            raise KeyError('replace must be one of {nan, mean, mask} or some number')\n        # finish\n        if verbose:\n            print('%i outliers removed' % len(outliers))\n        return outliers\n", "path": "WrightTools/data/_channel.py"}], "after_files": [{"content": "\"\"\"Channel class and associated.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nimport h5py\n\nfrom .. import kit as wt_kit\nfrom .._dataset import Dataset\n\n\n# --- class ---------------------------------------------------------------------------------------\n\n\nclass Channel(Dataset):\n    \"\"\"Channel.\"\"\"\n\n    class_name = 'Channel'\n\n    def __init__(self, parent, id, *, units=None, null=None, signed=None, label=None,\n                 label_seed=None, **kwargs):\n        \"\"\"Construct a channel object.\n\n        Parameters\n        ----------\n        values : array-like\n            Values.\n        name : string\n            Channel name.\n        units : string (optional)\n            Channel units. Default is None.\n        null : number (optional)\n            Channel null. Default is None (0).\n        signed : booelan (optional)\n            Channel signed flag. Default is None (guess).\n        label : string.\n            Label. Default is None.\n        label_seed : list of strings\n            Label seed. Default is None.\n        **kwargs\n            Additional keyword arguments are added to the attrs dictionary\n            and to the natural namespace of the object (if possible).\n        \"\"\"\n        self._parent = parent\n        super().__init__(id)\n        self.label = label\n        self.label_seed = label_seed\n        self.units = units\n        self.dimensionality = len(self.shape)\n        # attrs\n        self.attrs.update(kwargs)\n        self.attrs['name'] = h5py.h5i.get_name(self.id).decode().split('/')[-1]\n        self.attrs['class'] = 'Channel'\n        if signed is not None:\n            self.attrs['signed'] = signed\n        if null is not None:\n            self.attrs['null'] = null\n        for key, value in self.attrs.items():\n            identifier = wt_kit.string2identifier(key)\n            if not hasattr(self, identifier):\n                setattr(self, identifier, value)\n\n    @property\n    def minor_extent(self):\n        \"\"\"Minimum deviation from null.\"\"\"\n        return min((self.max() - self.null, self.null - self.min()))\n\n    @property\n    def natural_name(self):\n        \"\"\"Natural name of the dataset. May be different from name.\"\"\"\n        try:\n            assert self._natural_name is not None\n        except (AssertionError, AttributeError):\n            self._natural_name = self.attrs['name']\n        finally:\n            return self._natural_name\n\n    @natural_name.setter\n    def natural_name(self, value):\n        index = wt_kit.get_index(self.parent.channel_names, self.natural_name)\n        new = list(self.parent.channel_names)\n        new[index] = value\n        self.parent.channel_names = new\n        self.attrs['name'] = value\n        self._natural_name = None\n\n    @property\n    def null(self):\n        if 'null' not in self.attrs.keys():\n            self.attrs['null'] = 0\n        return self.attrs['null']\n\n    @null.setter\n    def null(self, value):\n        self.attrs['null'] = value\n\n    @property\n    def major_extent(self):\n        \"\"\"Maximum deviation from null.\"\"\"\n        return max((self.max() - self.null, self.null - self.min()))\n\n    @property\n    def signed(self):\n        if 'signed' not in self.attrs.keys():\n            self.attrs['signed'] = False\n        return self.attrs['signed']\n\n    @signed.setter\n    def signed(self, value):\n        self.attrs['signed'] = value\n\n    def mag(self):\n        \"\"\"Channel magnitude (maximum deviation from null).\"\"\"\n        return self.major_extent\n\n    def normalize(self):\n        \"\"\"Normalize a Channel, set `null` to 0 and the mag to 1.\"\"\"\n        def f(dataset, s, null, mag):\n            dataset[s] -= null\n            dataset[s] /= mag\n        if self.signed:\n            mag = self.mag()\n        else:\n            mag = self.max()\n        self.chunkwise(f, null=self.null, mag=mag)\n        self._null = 0\n\n    def trim(self, neighborhood, method='ztest', factor=3, replace='nan',\n             verbose=True):\n        \"\"\"Remove outliers from the dataset.\n\n        Identifies outliers by comparing each point to its\n        neighbors using a statistical test.\n\n        Parameters\n        ----------\n        neighborhood : list of integers\n            Size of the neighborhood in each dimension. Length of the list must\n            be equal to the dimensionality of the channel.\n        method : {'ztest'} (optional)\n            Statistical test used to detect outliers. Default is ztest.\n\n            ztest\n                Compare point deviation from neighborhood mean to neighborhood\n                standard deviation.\n\n        factor : number (optional)\n            Tolerance factor.  Default is 3.\n        replace : {'nan', 'mean', 'mask', number} (optional)\n            Behavior of outlier replacement. Default is nan.\n\n            nan\n                Outliers are replaced by numpy nans.\n\n            mean\n                Outliers are replaced by the mean of its neighborhood.\n\n            mask\n                Array is masked at outliers.\n\n            number\n                Array becomes given number.\n\n        Returns\n        -------\n        list of tuples\n            Indicies of trimmed outliers.\n\n        See Also\n        --------\n        clip\n            Remove pixels outside of a certain range.\n        \"\"\"\n        raise NotImplementedError\n        outliers = []\n        means = []\n        # find outliers\n        for idx in np.ndindex(self.shape):\n            slices = []\n            for i, di, size in zip(idx, neighborhood, self.shape):\n                start = max(0, i - di)\n                stop = min(size, i + di + 1)\n                slices.append(slice(start, stop, 1))\n            neighbors = self[slices]\n            mean = np.nanmean(neighbors)\n            limit = np.nanstd(neighbors) * factor\n            if np.abs(self[idx] - mean) > limit:\n                outliers.append(idx)\n                means.append(mean)\n        # replace outliers\n        i = tuple(zip(*outliers))\n        if replace == 'nan':\n            self[i] = np.nan\n        elif replace == 'mean':\n            self[i] = means\n        elif replace == 'mask':\n            self[:] = np.ma.array(self[:])\n            self[i] = np.ma.masked\n        elif type(replace) in [int, float]:\n            self[i] = replace\n        else:\n            raise KeyError('replace must be one of {nan, mean, mask} or some number')\n        # finish\n        if verbose:\n            print('%i outliers removed' % len(outliers))\n        return outliers\n", "path": "WrightTools/data/_channel.py"}]}