Skip to content

Commit 930109d

Browse files
ilevkivskyigvanrossum
authored andcommitted
Fix the problem with copy and deepcopy; also improve pickling a bit (#311)
Fixes #306 I also improved pickling a bit, but as you could see from tests, the list of things that could be pickled is much shorter than list of those that can be copied/deepcopied (note that copy module in Python 3 treats classes as immutable, and therefore return the class unchanged)
1 parent 50d2b4d commit 930109d

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

python2/test_typing.py

+19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import sys
77
from unittest import TestCase, main, SkipTest
8+
from copy import copy, deepcopy
89

910
from typing import Any
1011
from typing import TypeVar, AnyStr
@@ -803,6 +804,24 @@ class C(B[int]):
803804
self.assertEqual(x.foo, 42)
804805
self.assertEqual(x.bar, 'abc')
805806
self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'})
807+
simples = [Any, Union, Tuple, Callable, ClassVar, List, typing.Iterable]
808+
for s in simples:
809+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
810+
z = pickle.dumps(s, proto)
811+
x = pickle.loads(z)
812+
self.assertEqual(s, x)
813+
814+
def test_copy_and_deepcopy(self):
815+
T = TypeVar('T')
816+
class Node(Generic[T]): pass
817+
things = [Any, Union[T, int], Tuple[T, int], Callable[..., T], Callable[[int], int],
818+
Tuple[Any, Any], Node[T], Node[int], Node[Any], typing.Iterable[T],
819+
typing.Iterable[Any], typing.Iterable[int], typing.Dict[int, str],
820+
typing.Dict[T, Any], ClassVar[int], ClassVar[List[T]], Tuple['T', 'T'],
821+
Union['T', int], List['T'], typing.Mapping['T', int]]
822+
for t in things:
823+
self.assertEqual(t, deepcopy(t))
824+
self.assertEqual(t, copy(t))
806825

807826
def test_errors(self):
808827
with self.assertRaises(TypeError):

python2/typing.py

+8
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ def __new__(cls, *args, **kwds):
182182
return self
183183
raise TypeError("Cannot instantiate %r" % cls)
184184

185+
def __reduce__(self):
186+
return _trim_name(type(self).__name__)
187+
185188

186189
class _ForwardRef(_TypingBase):
187190
"""Wrapper to hold a forward reference."""
@@ -1140,6 +1143,11 @@ def __instancecheck__(self, instance):
11401143
return issubclass(instance.__class__, self)
11411144
return False
11421145

1146+
def __copy__(self):
1147+
return self.__class__(self.__name__, self.__bases__, dict(self.__dict__),
1148+
self.__parameters__, self.__args__, self.__origin__,
1149+
self.__extra__, self.__orig_bases__)
1150+
11431151

11441152
# Prevent checks for Generic to crash when defining Generic.
11451153
Generic = None

src/test_typing.py

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import sys
66
from unittest import TestCase, main, skipUnless, SkipTest
7+
from copy import copy, deepcopy
78

89
from typing import Any
910
from typing import TypeVar, AnyStr
@@ -845,6 +846,24 @@ class C(B[int]):
845846
self.assertEqual(x.foo, 42)
846847
self.assertEqual(x.bar, 'abc')
847848
self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'})
849+
simples = [Any, Union, Tuple, Callable, ClassVar, List, typing.Iterable]
850+
for s in simples:
851+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
852+
z = pickle.dumps(s, proto)
853+
x = pickle.loads(z)
854+
self.assertEqual(s, x)
855+
856+
def test_copy_and_deepcopy(self):
857+
T = TypeVar('T')
858+
class Node(Generic[T]): ...
859+
things = [Union[T, int], Tuple[T, int], Callable[..., T], Callable[[int], int],
860+
Tuple[Any, Any], Node[T], Node[int], Node[Any], typing.Iterable[T],
861+
typing.Iterable[Any], typing.Iterable[int], typing.Dict[int, str],
862+
typing.Dict[T, Any], ClassVar[int], ClassVar[List[T]], Tuple['T', 'T'],
863+
Union['T', int], List['T'], typing.Mapping['T', int]]
864+
for t in things + [Any]:
865+
self.assertEqual(t, copy(t))
866+
self.assertEqual(t, deepcopy(t))
848867

849868
def test_errors(self):
850869
with self.assertRaises(TypeError):

src/typing.py

+8
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ def __new__(cls, *args, _root=False, **kwds):
190190
return self
191191
raise TypeError("Cannot instantiate %r" % cls)
192192

193+
def __reduce__(self):
194+
return _trim_name(type(self).__name__)
195+
193196

194197
class _ForwardRef(_TypingBase, _root=True):
195198
"""Wrapper to hold a forward reference."""
@@ -1051,6 +1054,11 @@ def __instancecheck__(self, instance):
10511054
# classes are supposed to be rare anyways.
10521055
return issubclass(instance.__class__, self)
10531056

1057+
def __copy__(self):
1058+
return self.__class__(self.__name__, self.__bases__, dict(self.__dict__),
1059+
self.__parameters__, self.__args__, self.__origin__,
1060+
self.__extra__, self.__orig_bases__)
1061+
10541062

10551063
# Prevent checks for Generic to crash when defining Generic.
10561064
Generic = None

0 commit comments

Comments
 (0)