Repository: keras-team/keras

Issue:
TypeError: DTypePolicy.__new__() when deepcopy(layer_instance)
Hello,

I use `Python==3.11.8` with `keras==3.1.1`.

When I create a layer instance and try to deepcopy this layer I receive the following error which did not happen before.


```python
>>> import keras
>>> import copy
>>> layer_obj = keras.layers.Dense(1)
>>> copy.deepcopy(layer_obj)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
            ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 146, in deepcopy
    y = copier(x, memo)
        ^^^^^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
                             ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 265, in _reconstruct
    y = func(*args)
        ^^^^^^^^^^^
  File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copyreg.py", line 105, in __newobj__
    return cls.__new__(cls, *args)
           ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: DTypePolicy.__new__() missing 1 required positional argument: 'name'
>>> >>> copy.deepcopy(layer_obj)
  File "<stdin>", line 1
    >>> copy.deepcopy(layer_obj)
    ^^
SyntaxError: invalid syntax
>>> Traceback (most recent call last):
  File "<stdin>", line 1
    Traceback (most recent call last):
               ^^^^^^^^^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
>>>   File "<stdin>", line 1, in <module>
  File "<stdin>", line 1
    File "<stdin>", line 1, in <module>
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
IndentationError: unexpected indent
>>>     y = _reconstruct(x, memo, *rv)
  File "<stdin>", line 1
    y = _reconstruct(x, memo, *rv)
IndentationError: unexpected indent
>>>         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 271, in _reconstruct
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 271, in _reconstruct
IndentationError: unexpected indent
>>>     state = deepcopy(state, memo)
  File "<stdin>", line 1
    state = deepcopy(state, memo)
IndentationError: unexpected indent
>>>             ^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 146, in deepcopy
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 146, in deepcopy
IndentationError: unexpected indent
>>>     y = copier(x, memo)
  File "<stdin>", line 1
    y = copier(x, memo)
IndentationError: unexpected indent
>>>         ^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 231, in _deepcopy_dict
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 231, in _deepcopy_dict
IndentationError: unexpected indent
>>>     y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "<stdin>", line 1
    y[deepcopy(key, memo)] = deepcopy(value, memo)
IndentationError: unexpected indent
>>>                              ^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 172, in deepcopy
IndentationError: unexpected indent
>>>     y = _reconstruct(x, memo, *rv)
  File "<stdin>", line 1
    y = _reconstruct(x, memo, *rv)
IndentationError: unexpected indent
>>>         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 265, in _reconstruct
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copy.py", line 265, in _reconstruct
IndentationError: unexpected indent
>>>     y = func(*args)
  File "<stdin>", line 1
    y = func(*args)
IndentationError: unexpected indent
>>>         ^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^
IndentationError: unexpected indent
>>>   File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copyreg.py", line 105, in __newobj__
  File "<stdin>", line 1
    File "/Users/romainegele/miniforge3/envs/dlp/lib/python3.11/copyreg.py", line 105, in __newobj__
IndentationError: unexpected indent
>>>     return cls.__new__(cls, *args)
  File "<stdin>", line 1
    return cls.__new__(cls, *args)
IndentationError: unexpected indent
>>>            ^^^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    ^^^^^^^^^^^^^^^^^^^^^^^
IndentationError: unexpected indent
>>> TypeError: DTypePolicy.__new__() missing 1 required positional argument: 'name'
```


Discussion:
Related to this line apparently: https://github.com/keras-team/keras/blob/v3.1.1/keras/dtype_policies/dtype_policy.py#L58

Resolving patch:
diff --git a/keras/dtype_policies/dtype_policy.py b/keras/dtype_policies/dtype_policy.py
--- a/keras/dtype_policies/dtype_policy.py
+++ b/keras/dtype_policies/dtype_policy.py
@@ -69,6 +69,10 @@ def __new__(cls, name):
             return FloatDTypePolicy(name)
         return super().__new__(cls)
 
+    def __getnewargs__(self):
+        # To support `copy`, `deepcopy` and `pickle`
+        return (self._name,)
+
     def __init__(self, name):
         self._name = name
         self._compute_dtype = backend.floatx()


Test patch:
diff --git a/keras/dtype_policies/dtype_policy_test.py b/keras/dtype_policies/dtype_policy_test.py
--- a/keras/dtype_policies/dtype_policy_test.py
+++ b/keras/dtype_policies/dtype_policy_test.py
@@ -61,6 +61,32 @@ def test_get_config_from_config(self):
         new_policy = DTypePolicy.from_config(config)
         self.assertEqual(new_policy.name, "mixed_float16")
 
+    def test_deepcopy(self):
+        """Test builtin serialization methods."""
+        import copy
+        import pickle
+
+        # copy.deepcopy
+        policy = DTypePolicy("mixed_float16")
+        copied_policy = copy.deepcopy(policy)
+        self.assertEqual(
+            repr(copied_policy), '<FloatDTypePolicy "mixed_float16">'
+        )
+        # copy.copy
+        copied_policy = copy.copy(policy)
+        self.assertEqual(
+            repr(copied_policy), '<FloatDTypePolicy "mixed_float16">'
+        )
+        # pickle
+        temp_dir = self.get_temp_dir()
+        with open(f"{temp_dir}/policy.pickle", "wb") as f:
+            pickle.dump(policy, f)
+        with open(f"{temp_dir}/policy.pickle", "rb") as f:
+            copied_policy = pickle.load(f)
+        self.assertEqual(
+            repr(copied_policy), '<FloatDTypePolicy "mixed_float16">'
+        )
+
 
 class FloatDTypePolicyTest(test_case.TestCase):
     def test_initialization_valid_name(self):