Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions Lib/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ def copy(x):
return _reconstruct(x, None, *rv)


_copy_atomic_types = {types.NoneType, int, float, bool, complex, str, tuple,
bytes, frozenset, type, range, slice, property,
_copy_atomic_types = frozenset({types.NoneType, int, float, bool, complex, str, tuple,
bytes, frozendict, frozenset, type, range, slice, property,
types.BuiltinFunctionType, types.EllipsisType,
types.NotImplementedType, types.FunctionType, types.CodeType,
weakref.ref, super}
_copy_builtin_containers = {list, dict, set, bytearray}
weakref.ref, super})
_copy_builtin_containers = frozenset({list, dict, set, bytearray})

def deepcopy(x, memo=None, _nil=[]):
def deepcopy(x, memo=None):
"""Deep copy operation on arbitrary Python objects.

See the module's __doc__ string for more info.
Expand All @@ -122,8 +122,8 @@ def deepcopy(x, memo=None, _nil=[]):
if memo is None:
memo = {}
else:
y = memo.get(d, _nil)
if y is not _nil:
y = memo.get(d, None)
if y is not None:
return y

copier = _deepcopy_dispatch.get(cls)
Expand Down Expand Up @@ -162,9 +162,9 @@ def deepcopy(x, memo=None, _nil=[]):
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y

_atomic_types = {types.NoneType, types.EllipsisType, types.NotImplementedType,
_atomic_types = frozenset({types.NoneType, types.EllipsisType, types.NotImplementedType,
int, float, bool, complex, bytes, str, types.CodeType, type, range,
types.BuiltinFunctionType, types.FunctionType, weakref.ref, property}
types.BuiltinFunctionType, types.FunctionType, weakref.ref, property})

_deepcopy_dispatch = d = {}

Expand Down Expand Up @@ -203,6 +203,21 @@ def _deepcopy_dict(x, memo, deepcopy=deepcopy):
return y
d[dict] = _deepcopy_dict

def _deepcopy_frozendict(x, memo, deepcopy=deepcopy):
y = {}
for key, value in x.items():
y[deepcopy(key, memo)] = deepcopy(value, memo)

# We're not going to put the frozendict in the memo, but it's still
# important we check for it, in case the frozendict contains recursive
# mutable structures.
try:
return memo[id(x)]
except KeyError:
pass
return frozendict(y)
d[frozendict] = _deepcopy_frozendict

def _deepcopy_method(x, memo): # Copy instance methods
return type(x)(x.__func__, deepcopy(x.__self__, memo))
d[types.MethodType] = _deepcopy_method
Expand Down Expand Up @@ -230,7 +245,7 @@ def _reconstruct(x, memo, func, args,
*, deepcopy=deepcopy):
deep = memo is not None
if deep and args:
args = (deepcopy(arg, memo) for arg in args)
args = [deepcopy(arg, memo) for arg in args]
y = func(*args)
if deep:
memo[id(x)] = y
Expand Down
133 changes: 70 additions & 63 deletions Lib/test/mapping_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from test import support


class BasicTestMappingProtocol(unittest.TestCase):
class BasicTestImmutableMappingProtocol(unittest.TestCase):
# This base class can be used to check that an object conforms to the
# mapping protocol

Expand All @@ -20,12 +20,9 @@ def _empty_mapping(self):
"""Return an empty mapping object"""
return self.type2test()
def _full_mapping(self, data):
"""Return a mapping object with the value contained in data
"""Return a mapping object with the values contained in data
dictionary"""
x = self._empty_mapping()
for key, value in data.items():
x[key] = value
return x
return self.type2test(data)

def __init__(self, *args, **kw):
unittest.TestCase.__init__(self, *args, **kw)
Expand Down Expand Up @@ -88,6 +85,72 @@ def check_iterandlist(iter, lst, ref):
self.assertEqual(d.get(knownkey, knownvalue), knownvalue)
self.assertNotIn(knownkey, d)

def test_constructor(self):
self.assertEqual(self._empty_mapping(), self._empty_mapping())

def test_bool(self):
self.assertTrue(not self._empty_mapping())
self.assertTrue(self.reference)
self.assertFalse(bool(self._empty_mapping()))
self.assertTrue(bool(self.reference))

def test_keys(self):
d = self._empty_mapping()
self.assertEqual(list(d.keys()), [])
d = self.reference
self.assertIn(list(self.inmapping.keys())[0], d.keys())
self.assertNotIn(list(self.other.keys())[0], d.keys())
self.assertRaises(TypeError, d.keys, None)

def test_values(self):
d = self._empty_mapping()
self.assertEqual(list(d.values()), [])

self.assertRaises(TypeError, d.values, None)

def test_items(self):
d = self._empty_mapping()
self.assertEqual(list(d.items()), [])

self.assertRaises(TypeError, d.items, None)

def test_len(self):
d = self._empty_mapping()
self.assertEqual(len(d), 0)

def test_getitem(self):
d = self.reference
self.assertEqual(d[list(self.inmapping.keys())[0]],
list(self.inmapping.values())[0])

self.assertRaises(TypeError, d.__getitem__)

# no test_fromkeys or test_copy as both os.environ and selves don't support it

def test_get(self):
d = self._empty_mapping()
self.assertIsNone(d.get(list(self.other.keys())[0]))
self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
d = self.reference
self.assertIsNone(d.get(list(self.other.keys())[0]))
self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
self.assertEqual(d.get(list(self.inmapping.keys())[0]),
list(self.inmapping.values())[0])
self.assertEqual(d.get(list(self.inmapping.keys())[0], 3),
list(self.inmapping.values())[0])
self.assertRaises(TypeError, d.get)
self.assertRaises(TypeError, d.get, None, None, None)


class BasicTestMappingProtocol(BasicTestImmutableMappingProtocol):
def _full_mapping(self, data):
"""Return a mapping object with the values contained in data
dictionary"""
x = self._empty_mapping()
for key, value in data.items():
x[key] = value
return x

def test_write(self):
# Test for write operations on mapping
p = self._empty_mapping()
Expand Down Expand Up @@ -130,46 +193,6 @@ def test_write(self):
p=self._empty_mapping()
self.assertRaises(KeyError, p.popitem)

def test_constructor(self):
self.assertEqual(self._empty_mapping(), self._empty_mapping())

def test_bool(self):
self.assertTrue(not self._empty_mapping())
self.assertTrue(self.reference)
self.assertTrue(bool(self._empty_mapping()) is False)
self.assertTrue(bool(self.reference) is True)

def test_keys(self):
d = self._empty_mapping()
self.assertEqual(list(d.keys()), [])
d = self.reference
self.assertIn(list(self.inmapping.keys())[0], d.keys())
self.assertNotIn(list(self.other.keys())[0], d.keys())
self.assertRaises(TypeError, d.keys, None)

def test_values(self):
d = self._empty_mapping()
self.assertEqual(list(d.values()), [])

self.assertRaises(TypeError, d.values, None)

def test_items(self):
d = self._empty_mapping()
self.assertEqual(list(d.items()), [])

self.assertRaises(TypeError, d.items, None)

def test_len(self):
d = self._empty_mapping()
self.assertEqual(len(d), 0)

def test_getitem(self):
d = self.reference
self.assertEqual(d[list(self.inmapping.keys())[0]],
list(self.inmapping.values())[0])

self.assertRaises(TypeError, d.__getitem__)

def test_update(self):
# mapping argument
d = self._empty_mapping()
Expand Down Expand Up @@ -265,22 +288,6 @@ def __next__(self):

self.assertRaises(ValueError, d.update, [(1, 2, 3)])

# no test_fromkeys or test_copy as both os.environ and selves don't support it

def test_get(self):
d = self._empty_mapping()
self.assertTrue(d.get(list(self.other.keys())[0]) is None)
self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
d = self.reference
self.assertTrue(d.get(list(self.other.keys())[0]) is None)
self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
self.assertEqual(d.get(list(self.inmapping.keys())[0]),
list(self.inmapping.values())[0])
self.assertEqual(d.get(list(self.inmapping.keys())[0], 3),
list(self.inmapping.values())[0])
self.assertRaises(TypeError, d.get)
self.assertRaises(TypeError, d.get, None, None, None)

def test_setdefault(self):
d = self._empty_mapping()
self.assertRaises(TypeError, d.setdefault)
Expand Down Expand Up @@ -622,7 +629,7 @@ def __repr__(self):
d = self._full_mapping({1: BadRepr()})
self.assertRaises(Exc, repr, d)

@support.run_with_limited_c_stack()
@support.skip_if_huge_c_stack()
@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.skip_if_sanitizer("requires deep stack", ub=True)
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -4394,8 +4394,6 @@ def test_fast_save_enter_frozenset(self):
def test_fast_save_enter_set(self):
self.fast_save_enter(lambda i: set([i]))

# TODO: RUSTPYTHON; frozendict support: https://github.com/RustPython/RustPython/pull/8951
@unittest.expectedFailure
def test_fast_save_enter_frozendict(self):
if self.py_version < (3, 15):
self.skipTest('need frozendict')
Expand Down Expand Up @@ -4443,8 +4441,6 @@ def test_deep_nested_struct_set(self):
depth=FAST_NESTING_LIMIT+1,
compare_equal=False)

# TODO: RUSTPYTHON; frozendict support: https://github.com/RustPython/RustPython/pull/8951
@unittest.expectedFailure
def test_deep_nested_struct_frozendict(self):
if self.py_version < (3, 15):
self.skipTest('need frozendict')
Expand Down
Loading
Loading