We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cannot pickle mne.preprocessing.ica.ICA anymore
With the current master (installed via `pip install git+https://github.com/mne-tools/mne-python.git#egg=mne`), I get an error when pickling an `mne.preprocessing.ica.ICA` object:

```
import pickle
import mne


print(mne.__version__)
ica = mne.preprocessing.ICA()
raw = mne.io.read_raw_edf("/Users/clemens/Downloads/testfiles/S001R04.edf", preload=True)
ica.fit(raw)
pickle.loads(pickle.dumps(ica))
# Traceback (most recent call last):
#   File "/Users/clemens/Repositories/mnelab/.venv/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3296, in run_code
#     exec(code_obj, self.user_global_ns, self.user_ns)
#   File "<ipython-input-8-a056623ee708>", line 1, in <module>
#     pickle.loads(pickle.dumps(ica))
# TypeError: __new__() missing 1 required positional argument: 'val'
```

The same code works with the latest released version 0.17.2. I didn't see any change to the `ICA` object that might be causing this (but maybe I'm missing something). If indeed nothing changed, could this behavior be due to how pip installs stuff from GitHub (vs. "proper" packages from PyPI)? If so, how can I fix this (I need to pickle because I want to use the `multiprocessing` module)?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mne/utils/_bunch.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """Bunch-related classes."""
3 # Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
4 #          Eric Larson <larson.eric.d@gmail.com>
5 #
6 # License: BSD (3-clause)
7 
8 from copy import deepcopy
9 
10 
11 ###############################################################################
12 # Create a Bunch class that acts like a struct (mybunch.key = val )
13 
14 class Bunch(dict):
15     """Dictionnary-like object thatexposes its keys as attributes."""
16 
17     def __init__(self, **kwargs):  # noqa: D102
18         dict.__init__(self, kwargs)
19         self.__dict__ = self
20 
21 
22 ###############################################################################
23 # A protected version that prevents overwriting
24 
25 class BunchConst(Bunch):
26     """Class to prevent us from re-defining constants (DRY)."""
27 
28     def __setattr__(self, attr, val):  # noqa: D105
29         if attr != '__dict__' and hasattr(self, attr):
30             raise AttributeError('Attribute "%s" already set' % attr)
31         super().__setattr__(attr, val)
32 
33 
34 ###############################################################################
35 # A version that tweaks the __repr__ of its values based on keys
36 
37 class BunchConstNamed(BunchConst):
38     """Class to provide nice __repr__ for our integer constants.
39 
40     Only supports string keys and int or float values.
41     """
42 
43     def __setattr__(self, attr, val):  # noqa: D105
44         assert isinstance(attr, str)
45         if isinstance(val, int):
46             val = NamedInt(attr, val)
47         elif isinstance(val, float):
48             val = NamedFloat(attr, val)
49         else:
50             assert isinstance(val, BunchConstNamed), type(val)
51         super().__setattr__(attr, val)
52 
53 
54 class Named(object):
55     """Provide shared methods for giving named-representation subclasses."""
56 
57     def __str__(self):  # noqa: D105
58         return '%s (%s)' % (super().__str__(), self._name)
59 
60     __repr__ = __str__
61 
62     # see https://stackoverflow.com/a/15774013/2175965
63     def __copy__(self):  # noqa: D105
64         cls = self.__class__
65         result = cls.__new__(cls)
66         result.__dict__.update(self.__dict__)
67         return result
68 
69     def __deepcopy__(self, memo):  # noqa: D105
70         cls = self.__class__
71         result = cls.__new__(cls, self._name, self)
72         memo[id(self)] = result
73         for k, v in self.__dict__.items():
74             setattr(result, k, deepcopy(v, memo))
75         return result
76 
77 
78 class NamedInt(Named, int):
79     """Int with a name in __repr__."""
80 
81     def __new__(cls, name, val):  # noqa: D102,D105
82         out = int.__new__(cls, val)
83         out._name = name
84         return out
85 
86 
87 class NamedFloat(Named, float):
88     """Float with a name in __repr__."""
89 
90     def __new__(cls, name, val):  # noqa: D102,D105
91         out = float.__new__(cls, val)
92         out._name = name
93         return out
94
```
--- 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/mne/utils/_bunch.py b/mne/utils/_bunch.py
--- a/mne/utils/_bunch.py
+++ b/mne/utils/_bunch.py
@@ -9,10 +9,10 @@
 
 
 ###############################################################################
-# Create a Bunch class that acts like a struct (mybunch.key = val )
+# Create a Bunch class that acts like a struct (mybunch.key = val)
 
 class Bunch(dict):
-    """Dictionnary-like object thatexposes its keys as attributes."""
+    """Dictionary-like object that exposes its keys as attributes."""
 
     def __init__(self, **kwargs):  # noqa: D102
         dict.__init__(self, kwargs)
@@ -51,9 +51,14 @@
         super().__setattr__(attr, val)
 
 
-class Named(object):
+class _Named(object):
     """Provide shared methods for giving named-representation subclasses."""
 
+    def __new__(cls, name, val):  # noqa: D102,D105
+        out = _named_subclass(cls).__new__(cls, val)
+        out._name = name
+        return out
+
     def __str__(self):  # noqa: D105
         return '%s (%s)' % (super().__str__(), self._name)
 
@@ -74,20 +79,25 @@
             setattr(result, k, deepcopy(v, memo))
         return result
 
+    def __getnewargs__(self):  # noqa: D105
+        return self._name, _named_subclass(self)(self)
+
+
+def _named_subclass(klass):
+    if not isinstance(klass, type):
+        klass = klass.__class__
+    subklass = klass.mro()[-2]
+    assert subklass in (int, float)
+    return subklass
+
 
-class NamedInt(Named, int):
+class NamedInt(_Named, int):
     """Int with a name in __repr__."""
 
-    def __new__(cls, name, val):  # noqa: D102,D105
-        out = int.__new__(cls, val)
-        out._name = name
-        return out
+    pass
 
 
-class NamedFloat(Named, float):
+class NamedFloat(_Named, float):
     """Float with a name in __repr__."""
 
-    def __new__(cls, name, val):  # noqa: D102,D105
-        out = float.__new__(cls, val)
-        out._name = name
-        return out
+    pass

{"golden_diff": "diff --git a/mne/utils/_bunch.py b/mne/utils/_bunch.py\n--- a/mne/utils/_bunch.py\n+++ b/mne/utils/_bunch.py\n@@ -9,10 +9,10 @@\n \n \n ###############################################################################\n-# Create a Bunch class that acts like a struct (mybunch.key = val )\n+# Create a Bunch class that acts like a struct (mybunch.key = val)\n \n class Bunch(dict):\n-    \"\"\"Dictionnary-like object thatexposes its keys as attributes.\"\"\"\n+    \"\"\"Dictionary-like object that exposes its keys as attributes.\"\"\"\n \n     def __init__(self, **kwargs):  # noqa: D102\n         dict.__init__(self, kwargs)\n@@ -51,9 +51,14 @@\n         super().__setattr__(attr, val)\n \n \n-class Named(object):\n+class _Named(object):\n     \"\"\"Provide shared methods for giving named-representation subclasses.\"\"\"\n \n+    def __new__(cls, name, val):  # noqa: D102,D105\n+        out = _named_subclass(cls).__new__(cls, val)\n+        out._name = name\n+        return out\n+\n     def __str__(self):  # noqa: D105\n         return '%s (%s)' % (super().__str__(), self._name)\n \n@@ -74,20 +79,25 @@\n             setattr(result, k, deepcopy(v, memo))\n         return result\n \n+    def __getnewargs__(self):  # noqa: D105\n+        return self._name, _named_subclass(self)(self)\n+\n+\n+def _named_subclass(klass):\n+    if not isinstance(klass, type):\n+        klass = klass.__class__\n+    subklass = klass.mro()[-2]\n+    assert subklass in (int, float)\n+    return subklass\n+\n \n-class NamedInt(Named, int):\n+class NamedInt(_Named, int):\n     \"\"\"Int with a name in __repr__.\"\"\"\n \n-    def __new__(cls, name, val):  # noqa: D102,D105\n-        out = int.__new__(cls, val)\n-        out._name = name\n-        return out\n+    pass\n \n \n-class NamedFloat(Named, float):\n+class NamedFloat(_Named, float):\n     \"\"\"Float with a name in __repr__.\"\"\"\n \n-    def __new__(cls, name, val):  # noqa: D102,D105\n-        out = float.__new__(cls, val)\n-        out._name = name\n-        return out\n+    pass\n", "issue": "Cannot pickle mne.preprocessing.ica.ICA anymore\nWith the current master (installed via `pip install git+https://github.com/mne-tools/mne-python.git#egg=mne`), I get an error when pickling an `mne.preprocessing.ica.ICA` object:\r\n\r\n```\r\nimport pickle\r\nimport mne\r\n\r\n\r\nprint(mne.__version__)\r\nica = mne.preprocessing.ICA()\r\nraw = mne.io.read_raw_edf(\"/Users/clemens/Downloads/testfiles/S001R04.edf\", preload=True)\r\nica.fit(raw)\r\npickle.loads(pickle.dumps(ica))\r\n# Traceback (most recent call last):\r\n#   File \"/Users/clemens/Repositories/mnelab/.venv/lib/python3.7/site-packages/IPython/core/interactiveshell.py\", line 3296, in run_code\r\n#     exec(code_obj, self.user_global_ns, self.user_ns)\r\n#   File \"<ipython-input-8-a056623ee708>\", line 1, in <module>\r\n#     pickle.loads(pickle.dumps(ica))\r\n# TypeError: __new__() missing 1 required positional argument: 'val'\r\n```\r\n\r\nThe same code works with the latest released version 0.17.2. I didn't see any change to the `ICA` object that might be causing this (but maybe I'm missing something). If indeed nothing changed, could this behavior be due to how pip installs stuff from GitHub (vs. \"proper\" packages from PyPI)? If so, how can I fix this (I need to pickle because I want to use the `multiprocessing` module)?\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Bunch-related classes.\"\"\"\n# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>\n#          Eric Larson <larson.eric.d@gmail.com>\n#\n# License: BSD (3-clause)\n\nfrom copy import deepcopy\n\n\n###############################################################################\n# Create a Bunch class that acts like a struct (mybunch.key = val )\n\nclass Bunch(dict):\n    \"\"\"Dictionnary-like object thatexposes its keys as attributes.\"\"\"\n\n    def __init__(self, **kwargs):  # noqa: D102\n        dict.__init__(self, kwargs)\n        self.__dict__ = self\n\n\n###############################################################################\n# A protected version that prevents overwriting\n\nclass BunchConst(Bunch):\n    \"\"\"Class to prevent us from re-defining constants (DRY).\"\"\"\n\n    def __setattr__(self, attr, val):  # noqa: D105\n        if attr != '__dict__' and hasattr(self, attr):\n            raise AttributeError('Attribute \"%s\" already set' % attr)\n        super().__setattr__(attr, val)\n\n\n###############################################################################\n# A version that tweaks the __repr__ of its values based on keys\n\nclass BunchConstNamed(BunchConst):\n    \"\"\"Class to provide nice __repr__ for our integer constants.\n\n    Only supports string keys and int or float values.\n    \"\"\"\n\n    def __setattr__(self, attr, val):  # noqa: D105\n        assert isinstance(attr, str)\n        if isinstance(val, int):\n            val = NamedInt(attr, val)\n        elif isinstance(val, float):\n            val = NamedFloat(attr, val)\n        else:\n            assert isinstance(val, BunchConstNamed), type(val)\n        super().__setattr__(attr, val)\n\n\nclass Named(object):\n    \"\"\"Provide shared methods for giving named-representation subclasses.\"\"\"\n\n    def __str__(self):  # noqa: D105\n        return '%s (%s)' % (super().__str__(), self._name)\n\n    __repr__ = __str__\n\n    # see https://stackoverflow.com/a/15774013/2175965\n    def __copy__(self):  # noqa: D105\n        cls = self.__class__\n        result = cls.__new__(cls)\n        result.__dict__.update(self.__dict__)\n        return result\n\n    def __deepcopy__(self, memo):  # noqa: D105\n        cls = self.__class__\n        result = cls.__new__(cls, self._name, self)\n        memo[id(self)] = result\n        for k, v in self.__dict__.items():\n            setattr(result, k, deepcopy(v, memo))\n        return result\n\n\nclass NamedInt(Named, int):\n    \"\"\"Int with a name in __repr__.\"\"\"\n\n    def __new__(cls, name, val):  # noqa: D102,D105\n        out = int.__new__(cls, val)\n        out._name = name\n        return out\n\n\nclass NamedFloat(Named, float):\n    \"\"\"Float with a name in __repr__.\"\"\"\n\n    def __new__(cls, name, val):  # noqa: D102,D105\n        out = float.__new__(cls, val)\n        out._name = name\n        return out\n", "path": "mne/utils/_bunch.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Bunch-related classes.\"\"\"\n# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>\n#          Eric Larson <larson.eric.d@gmail.com>\n#\n# License: BSD (3-clause)\n\nfrom copy import deepcopy\n\n\n###############################################################################\n# Create a Bunch class that acts like a struct (mybunch.key = val)\n\nclass Bunch(dict):\n    \"\"\"Dictionary-like object that exposes its keys as attributes.\"\"\"\n\n    def __init__(self, **kwargs):  # noqa: D102\n        dict.__init__(self, kwargs)\n        self.__dict__ = self\n\n\n###############################################################################\n# A protected version that prevents overwriting\n\nclass BunchConst(Bunch):\n    \"\"\"Class to prevent us from re-defining constants (DRY).\"\"\"\n\n    def __setattr__(self, attr, val):  # noqa: D105\n        if attr != '__dict__' and hasattr(self, attr):\n            raise AttributeError('Attribute \"%s\" already set' % attr)\n        super().__setattr__(attr, val)\n\n\n###############################################################################\n# A version that tweaks the __repr__ of its values based on keys\n\nclass BunchConstNamed(BunchConst):\n    \"\"\"Class to provide nice __repr__ for our integer constants.\n\n    Only supports string keys and int or float values.\n    \"\"\"\n\n    def __setattr__(self, attr, val):  # noqa: D105\n        assert isinstance(attr, str)\n        if isinstance(val, int):\n            val = NamedInt(attr, val)\n        elif isinstance(val, float):\n            val = NamedFloat(attr, val)\n        else:\n            assert isinstance(val, BunchConstNamed), type(val)\n        super().__setattr__(attr, val)\n\n\nclass _Named(object):\n    \"\"\"Provide shared methods for giving named-representation subclasses.\"\"\"\n\n    def __new__(cls, name, val):  # noqa: D102,D105\n        out = _named_subclass(cls).__new__(cls, val)\n        out._name = name\n        return out\n\n    def __str__(self):  # noqa: D105\n        return '%s (%s)' % (super().__str__(), self._name)\n\n    __repr__ = __str__\n\n    # see https://stackoverflow.com/a/15774013/2175965\n    def __copy__(self):  # noqa: D105\n        cls = self.__class__\n        result = cls.__new__(cls)\n        result.__dict__.update(self.__dict__)\n        return result\n\n    def __deepcopy__(self, memo):  # noqa: D105\n        cls = self.__class__\n        result = cls.__new__(cls, self._name, self)\n        memo[id(self)] = result\n        for k, v in self.__dict__.items():\n            setattr(result, k, deepcopy(v, memo))\n        return result\n\n    def __getnewargs__(self):  # noqa: D105\n        return self._name, _named_subclass(self)(self)\n\n\ndef _named_subclass(klass):\n    if not isinstance(klass, type):\n        klass = klass.__class__\n    subklass = klass.mro()[-2]\n    assert subklass in (int, float)\n    return subklass\n\n\nclass NamedInt(_Named, int):\n    \"\"\"Int with a name in __repr__.\"\"\"\n\n    pass\n\n\nclass NamedFloat(_Named, float):\n    \"\"\"Float with a name in __repr__.\"\"\"\n\n    pass\n", "path": "mne/utils/_bunch.py"}]}