prompt: 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()
  
  ```
  
golden_diff: 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
  
verification_info: {"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"}]}