diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 100250928d2064..93540395e13618 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -44,6 +44,8 @@ extern int _PyDict_HasOnlyStringKeys(PyObject *mp); extern void _PyDict_MaybeUntrack(PyObject *mp); +// Export for '_copy' shared extension +PyAPI_FUNC(PyObject*) _PyDict_NewPresized(Py_ssize_t minused); // Export for '_ctypes' shared extension PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *); diff --git a/Lib/copy.py b/Lib/copy.py index a79976d3a658f0..1bb137a5bf29ae 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -116,9 +116,11 @@ def _copy_immutable(x): del d, t -def deepcopy(x, memo=None, _nil=[]): +def _deepcopy_fallback(x, memo=None, _nil=[]): """Deep copy operation on arbitrary Python objects. + This is the fallback from the C accelerator, and the main implementation if the C accelerator is not available. + See the module's __doc__ string for more info. """ @@ -171,6 +173,13 @@ def deepcopy(x, memo=None, _nil=[]): _keep_alive(x, memo) # Make sure x lives at least as long as d return y +try: + from _copy import deepcopy +except ImportError: + # the fallback is for projects like PyPy that reuse the stdlib + deepcopy = _deepcopy_fallback + deepcopy.__name__ = 'deepcopy' + _atomic_types = {types.NoneType, types.EllipsisType, types.NotImplementedType, int, float, bool, complex, bytes, str, types.CodeType, type, range, types.BuiltinFunctionType, types.FunctionType, weakref.ref, property} diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index b103bf2450bde0..64592d671270b5 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -294,7 +294,7 @@ def test_getitem_with_error(self): r'Current thread .* \(most recent call first\):\n' r' File .*, line 6 in \n' r'\n' - r'Extension modules: _testcapi \(total: 1\)\n') + r'Extension modules: .* \(total: 1\)\n') else: # Python built with NDEBUG macro defined: # test _Py_CheckFunctionResult() instead. diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 3dec64cc9a2414..942371af6749f4 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -1,31 +1,38 @@ """Unit tests for the copy module.""" -import copy +from test.support.import_helper import import_fresh_module + +py_copy = import_fresh_module( + 'copy', blocked=['_copy'] +) +c_copy = import_fresh_module( + 'copy', fresh=['_copy'] +) + import copyreg import weakref import abc from operator import le, lt, ge, gt, eq, ne, attrgetter import unittest +import unittest.mock from test import support order_comparisons = le, lt, ge, gt equality_comparisons = eq, ne comparisons = order_comparisons + equality_comparisons -class TestCopy(unittest.TestCase): +class TestCopy: - # Attempt full line coverage of copy.py from top to bottom + copy_module = None def test_exceptions(self): - self.assertIs(copy.Error, copy.error) - self.assertTrue(issubclass(copy.Error, Exception)) - - # The copy() method + self.assertIs(self.copy_module.Error, self.copy_module.error) + self.assertTrue(issubclass(self.copy_module.Error, Exception)) def test_copy_basic(self): x = 42 - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(x, y) def test_copy_copy(self): @@ -35,7 +42,7 @@ def __init__(self, foo): def __copy__(self): return C(self.foo) x = C(42) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y.__class__, x.__class__) self.assertEqual(y.foo, x.foo) @@ -48,9 +55,9 @@ def __new__(cls, foo): def pickle_C(obj): return (C, (obj.foo,)) x = C(42) - self.assertRaises(TypeError, copy.copy, x) + self.assertRaises(TypeError, self.copy_module.copy, x) copyreg.pickle(C, pickle_C, C) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIsNot(x, y) self.assertEqual(type(y), C) self.assertEqual(y.foo, x.foo) @@ -64,7 +71,7 @@ def __reduce__(self): self.fail("shouldn't call this") c = [] x = C() - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIs(y, x) self.assertEqual(c, [1]) @@ -75,7 +82,7 @@ def __reduce__(self): return "" c = [] x = C() - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIs(y, x) self.assertEqual(c, [1]) @@ -86,7 +93,7 @@ def __getattribute__(self, name): raise AttributeError(name) return object.__getattribute__(self, name) x = C() - self.assertRaises(copy.Error, copy.copy, x) + self.assertRaises(self.copy_module.Error, self.copy_module.copy, x) # Type-specific _copy_xxx() methods @@ -103,59 +110,59 @@ class WithMetaclass(metaclass=abc.ABCMeta): b"world", bytes(range(256)), range(10), slice(1, 10, 2), NewStyle, max, WithMetaclass, property()] for x in tests: - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) def test_copy_list(self): x = [1, 2, 3] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) x = [] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) def test_copy_tuple(self): x = (1, 2, 3) - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) x = () - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) x = (1, 2, 3, []) - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) def test_copy_dict(self): x = {"foo": 1, "bar": 2} - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) x = {} - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) def test_copy_set(self): x = {1, 2, 3} - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) x = set() - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) def test_copy_frozenset(self): x = frozenset({1, 2, 3}) - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) x = frozenset() - self.assertIs(copy.copy(x), x) + self.assertIs(self.copy_module.copy(x), x) def test_copy_bytearray(self): x = bytearray(b'abc') - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) x = bytearray() - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) self.assertIsNot(y, x) @@ -166,7 +173,7 @@ def __init__(self, foo): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) def test_copy_inst_copy(self): class C: @@ -177,7 +184,7 @@ def __copy__(self): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) def test_copy_inst_getinitargs(self): class C: @@ -188,7 +195,7 @@ def __getinitargs__(self): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) def test_copy_inst_getnewargs(self): class C(int): @@ -201,7 +208,7 @@ def __getnewargs__(self): def __eq__(self, other): return self.foo == other.foo x = C(42) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIsInstance(y, C) self.assertEqual(y, x) self.assertIsNot(y, x) @@ -218,7 +225,7 @@ def __getnewargs_ex__(self): def __eq__(self, other): return self.foo == other.foo x = C(foo=42) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIsInstance(y, C) self.assertEqual(y, x) self.assertIsNot(y, x) @@ -233,7 +240,7 @@ def __getstate__(self): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) def test_copy_inst_setstate(self): class C: @@ -244,7 +251,7 @@ def __setstate__(self, state): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) def test_copy_inst_getstate_setstate(self): class C: @@ -257,16 +264,18 @@ def __setstate__(self, state): def __eq__(self, other): return self.foo == other.foo x = C(42) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) # State with boolean value is false (issue #25718) x = C(0.0) - self.assertEqual(copy.copy(x), x) + self.assertEqual(self.copy_module.copy(x), x) - # The deepcopy() method +class TestDeepcopy: + + copy_module = None def test_deepcopy_basic(self): x = 42 - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) def test_deepcopy_memo(self): @@ -274,7 +283,7 @@ def test_deepcopy_memo(self): # This tests only repetitions of objects. x = [] x = [x, x] - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y[0], x[0]) @@ -289,7 +298,7 @@ class Meta(type): pass class C(metaclass=Meta): pass - self.assertEqual(copy.deepcopy(C), C) + self.assertEqual(self.copy_module.deepcopy(C), C) def test_deepcopy_deepcopy(self): class C(object): @@ -298,7 +307,7 @@ def __init__(self, foo): def __deepcopy__(self, memo=None): return C(self.foo) x = C(42) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y.__class__, x.__class__) self.assertEqual(y.foo, x.foo) @@ -311,9 +320,9 @@ def __new__(cls, foo): def pickle_C(obj): return (C, (obj.foo,)) x = C(42) - self.assertRaises(TypeError, copy.deepcopy, x) + self.assertRaises(TypeError, self.copy_module.deepcopy, x) copyreg.pickle(C, pickle_C, C) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIsNot(x, y) self.assertEqual(type(y), C) self.assertEqual(y.foo, x.foo) @@ -327,7 +336,7 @@ def __reduce__(self): self.fail("shouldn't call this") c = [] x = C() - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(y, x) self.assertEqual(c, [1]) @@ -338,7 +347,7 @@ def __reduce__(self): return "" c = [] x = C() - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(y, x) self.assertEqual(c, [1]) @@ -349,7 +358,7 @@ def __getattribute__(self, name): raise AttributeError(name) return object.__getattribute__(self, name) x = C() - self.assertRaises(copy.Error, copy.deepcopy, x) + self.assertRaises(Exception, self.copy_module.deepcopy, x) # Type-specific _deepcopy_xxx() methods @@ -362,11 +371,11 @@ def f(): b"bytes", "hello", "hello\u1234", f.__code__, NewStyle, range(10), max, property()] for x in tests: - self.assertIs(copy.deepcopy(x), x) + self.assertIs(self.copy_module.deepcopy(x), x) def test_deepcopy_list(self): x = [[1, 2], 3] - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(x, y) self.assertIsNot(x[0], y[0]) @@ -374,7 +383,7 @@ def test_deepcopy_list(self): def test_deepcopy_reflexive_list(self): x = [] x.append(x) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) for op in comparisons: self.assertRaises(RecursionError, op, y, x) self.assertIsNot(y, x) @@ -383,25 +392,25 @@ def test_deepcopy_reflexive_list(self): def test_deepcopy_empty_tuple(self): x = () - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(x, y) def test_deepcopy_tuple(self): x = ([1, 2], 3) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(x, y) self.assertIsNot(x[0], y[0]) def test_deepcopy_tuple_of_immutables(self): x = ((1, 2), 3) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(x, y) def test_deepcopy_reflexive_tuple(self): x = ([],) x[0].append(x) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) for op in comparisons: self.assertRaises(RecursionError, op, y, x) self.assertIsNot(y, x) @@ -410,7 +419,7 @@ def test_deepcopy_reflexive_tuple(self): def test_deepcopy_dict(self): x = {"foo": [1, 2], "bar": 3} - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(x, y) self.assertIsNot(x["foo"], y["foo"]) @@ -418,7 +427,7 @@ def test_deepcopy_dict(self): def test_deepcopy_reflexive_dict(self): x = {} x['foo'] = x - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) for op in order_comparisons: self.assertRaises(TypeError, op, y, x) for op in equality_comparisons: @@ -430,20 +439,20 @@ def test_deepcopy_reflexive_dict(self): def test_deepcopy_keepalive(self): memo = {} x = [] - y = copy.deepcopy(x, memo) + y = self.copy_module.deepcopy(x, memo) self.assertIs(memo[id(memo)][0], x) def test_deepcopy_dont_memo_immutable(self): memo = {} x = [1, 2, 3, 4] - y = copy.deepcopy(x, memo) + y = self.copy_module.deepcopy(x, memo) self.assertEqual(y, x) # There's the entry for the new list, and the keep alive. self.assertEqual(len(memo), 2) memo = {} x = [(1, 2)] - y = copy.deepcopy(x, memo) + y = self.copy_module.deepcopy(x, memo) self.assertEqual(y, x) # Tuples with immutable contents are immutable for deepcopy. self.assertEqual(len(memo), 2) @@ -455,20 +464,23 @@ def __init__(self, foo): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y.foo, x.foo) def test_deepcopy_inst_deepcopy(self): + copy_module = self.copy_module + import copy + copy_module = copy class C: def __init__(self, foo): self.foo = foo def __deepcopy__(self, memo): - return C(copy.deepcopy(self.foo, memo)) + return C(copy_module.deepcopy(self.foo, memo)) def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) @@ -482,7 +494,7 @@ def __getinitargs__(self): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) @@ -498,7 +510,7 @@ def __getnewargs__(self): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIsInstance(y, C) self.assertEqual(y, x) self.assertIsNot(y, x) @@ -516,7 +528,7 @@ def __getnewargs_ex__(self): def __eq__(self, other): return self.foo == other.foo x = C(foo=[42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIsInstance(y, C) self.assertEqual(y, x) self.assertIsNot(y, x) @@ -532,7 +544,7 @@ def __getstate__(self): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) @@ -546,7 +558,7 @@ def __setstate__(self, state): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) @@ -562,13 +574,13 @@ def __setstate__(self, state): def __eq__(self, other): return self.foo == other.foo x = C([42]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) # State with boolean value is false (issue #25718) x = C([]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y, x) self.assertIsNot(y.foo, x.foo) @@ -578,7 +590,7 @@ class C: pass x = C() x.foo = x - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIsNot(y, x) self.assertIs(y.foo, y) @@ -589,9 +601,9 @@ class C(object): def __reduce__(self): return "" x = C() - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIs(y, x) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(y, x) def test_reconstruct_nostate(self): @@ -600,9 +612,9 @@ def __reduce__(self): return (C, ()) x = C() x.foo = 42 - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIs(y.__class__, x.__class__) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIs(y.__class__, x.__class__) def test_reconstruct_state(self): @@ -613,9 +625,9 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ x = C() x.foo = [42] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y.foo, x.foo) @@ -629,9 +641,9 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ x = C() x.foo = [42] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(y, x) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(y, x) self.assertIsNot(y.foo, x.foo) @@ -640,7 +652,7 @@ class C(object): pass x = C() x.foo = x - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertIsNot(y, x) self.assertIs(y.foo, y) @@ -654,11 +666,11 @@ def __eq__(self, other): return (list(self) == list(other) and self.__dict__ == other.__dict__) x = C([[1, 2], 3]) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(x, y) self.assertIsNot(x, y) self.assertIs(x[0], y[0]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(x, y) self.assertIsNot(x, y) self.assertIsNot(x[0], y[0]) @@ -671,11 +683,11 @@ def __eq__(self, other): return (dict(self) == dict(other) and self.__dict__ == other.__dict__) x = C([("foo", [1, 2]), ("bar", 3)]) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(x, y) self.assertIsNot(x, y) self.assertIs(x["foo"], y["foo"]) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(x, y) self.assertIsNot(x, y) self.assertIsNot(x["foo"], y["foo"]) @@ -688,9 +700,9 @@ def __reduce__(self): return C, (), self.__dict__, None, None, state_setter x = C() with self.assertRaises(TypeError): - copy.copy(x) + self.copy_module.copy(x) with self.assertRaises(TypeError): - copy.deepcopy(x) + self.copy_module.deepcopy(x) def test_reduce_6tuple_none(self): class C: @@ -698,16 +710,16 @@ def __reduce__(self): return C, (), self.__dict__, None, None, None x = C() with self.assertRaises(TypeError): - copy.copy(x) + self.copy_module.copy(x) with self.assertRaises(TypeError): - copy.deepcopy(x) + self.copy_module.deepcopy(x) def test_copy_slots(self): class C(object): __slots__ = ["foo"] x = C() x.foo = [42] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertIs(x.foo, y.foo) def test_deepcopy_slots(self): @@ -715,7 +727,7 @@ class C(object): __slots__ = ["foo"] x = C() x.foo = [42] - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(x.foo, y.foo) self.assertIsNot(x.foo, y.foo) @@ -731,7 +743,7 @@ def __setitem__(self, key, item): if key not in self._keys: self._keys.append(key) x = C(d={'foo':0}) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(x, y) self.assertEqual(x._keys, y._keys) self.assertIsNot(x, y) @@ -744,7 +756,7 @@ class C(list): pass x = C([[1, 2], 3]) x.foo = [4, 5] - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(list(x), list(y)) self.assertEqual(x.foo, y.foo) self.assertIs(x[0], y[0]) @@ -755,7 +767,7 @@ class C(list): pass x = C([[1, 2], 3]) x.foo = [4, 5] - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(list(x), list(y)) self.assertEqual(x.foo, y.foo) self.assertIsNot(x[0], y[0]) @@ -766,7 +778,7 @@ class C(tuple): pass x = C([1, 2, 3]) self.assertEqual(tuple(x), (1, 2, 3)) - y = copy.copy(x) + y = self.copy_module.copy(x) self.assertEqual(tuple(y), (1, 2, 3)) def test_deepcopy_tuple_subclass(self): @@ -774,7 +786,7 @@ class C(tuple): pass x = C([[1, 2], 3]) self.assertEqual(tuple(x), ([1, 2], 3)) - y = copy.deepcopy(x) + y = self.copy_module.deepcopy(x) self.assertEqual(tuple(y), ([1, 2], 3)) self.assertIsNot(x, y) self.assertIsNot(x[0], y[0]) @@ -783,21 +795,21 @@ def test_getstate_exc(self): class EvilState(object): def __getstate__(self): raise ValueError("ain't got no stickin' state") - self.assertRaises(ValueError, copy.copy, EvilState()) + self.assertRaises(ValueError, self.copy_module.copy, EvilState()) def test_copy_function(self): - self.assertEqual(copy.copy(global_foo), global_foo) + self.assertEqual(self.copy_module.copy(global_foo), global_foo) def foo(x, y): return x+y - self.assertEqual(copy.copy(foo), foo) + self.assertEqual(self.copy_module.copy(foo), foo) bar = lambda: None - self.assertEqual(copy.copy(bar), bar) + self.assertEqual(self.copy_module.copy(bar), bar) def test_deepcopy_function(self): - self.assertEqual(copy.deepcopy(global_foo), global_foo) + self.assertEqual(self.copy_module.deepcopy(global_foo), global_foo) def foo(x, y): return x+y - self.assertEqual(copy.deepcopy(foo), foo) + self.assertEqual(self.copy_module.deepcopy(foo), foo) bar = lambda: None - self.assertEqual(copy.deepcopy(bar), bar) + self.assertEqual(self.copy_module.deepcopy(bar), bar) def _check_weakref(self, _copy): class C(object): @@ -811,10 +823,10 @@ class C(object): self.assertIs(y, x) def test_copy_weakref(self): - self._check_weakref(copy.copy) + self._check_weakref(self.copy_module.copy) def test_deepcopy_weakref(self): - self._check_weakref(copy.deepcopy) + self._check_weakref(self.copy_module.deepcopy) def _check_copy_weakdict(self, _dicttype): class C(object): @@ -823,7 +835,7 @@ class C(object): u = _dicttype() u[a] = b u[c] = d - v = copy.copy(u) + v = self.copy_module.copy(u) self.assertIsNot(v, u) self.assertEqual(v, u) self.assertEqual(v[a], b) @@ -852,7 +864,7 @@ def __init__(self, i): u[a] = b u[c] = d # Keys aren't copied, values are - v = copy.deepcopy(u) + v = self.copy_module.deepcopy(u) self.assertNotEqual(v, u) self.assertEqual(len(v), 2) self.assertIsNot(v[a], b) @@ -872,7 +884,7 @@ def __init__(self, i): u[a] = b u[c] = d # Keys are copied, values aren't - v = copy.deepcopy(u) + v = self.copy_module.deepcopy(u) self.assertNotEqual(v, u) self.assertEqual(len(v), 2) (x, y), (z, t) = sorted(v.items(), key=lambda pair: pair[0].i) @@ -893,23 +905,23 @@ def m(self): pass f = Foo() f.b = f.m - g = copy.deepcopy(f) + g = self.copy_module.deepcopy(f) self.assertEqual(g.m, g.b) self.assertIs(g.b.__self__, g) g.b() -class TestReplace(unittest.TestCase): +class TestReplace: def test_unsupported(self): - self.assertRaises(TypeError, copy.replace, 1) - self.assertRaises(TypeError, copy.replace, []) - self.assertRaises(TypeError, copy.replace, {}) + self.assertRaises(TypeError, self.copy_module.replace, 1) + self.assertRaises(TypeError, self.copy_module.replace, []) + self.assertRaises(TypeError, self.copy_module.replace, {}) def f(): pass - self.assertRaises(TypeError, copy.replace, f) + self.assertRaises(TypeError, self.copy_module.replace, f) class A: pass - self.assertRaises(TypeError, copy.replace, A) - self.assertRaises(TypeError, copy.replace, A()) + self.assertRaises(TypeError, self.copy_module.replace, A) + self.assertRaises(TypeError, self.copy_module.replace, A()) def test_replace_method(self): class A: @@ -929,10 +941,10 @@ def __replace__(self, **changes): attrs = attrgetter('x', 'y', 'z') a = A(11, 22) - self.assertEqual(attrs(copy.replace(a)), (11, 22, 33)) - self.assertEqual(attrs(copy.replace(a, x=1)), (1, 22, 23)) - self.assertEqual(attrs(copy.replace(a, y=2)), (11, 2, 13)) - self.assertEqual(attrs(copy.replace(a, x=1, y=2)), (1, 2, 3)) + self.assertEqual(attrs(self.copy_module.replace(a)), (11, 22, 33)) + self.assertEqual(attrs(self.copy_module.replace(a, x=1)), (1, 22, 23)) + self.assertEqual(attrs(self.copy_module.replace(a, y=2)), (11, 2, 13)) + self.assertEqual(attrs(self.copy_module.replace(a, x=1, y=2)), (1, 2, 3)) def test_namedtuple(self): from collections import namedtuple @@ -947,13 +959,13 @@ class PointFromClass(NamedTuple): with self.subTest(Point=Point): p = Point(11, 22) self.assertIsInstance(p, Point) - self.assertEqual(copy.replace(p), (11, 22)) - self.assertIsInstance(copy.replace(p), Point) - self.assertEqual(copy.replace(p, x=1), (1, 22)) - self.assertEqual(copy.replace(p, y=2), (11, 2)) - self.assertEqual(copy.replace(p, x=1, y=2), (1, 2)) + self.assertEqual(self.copy_module.replace(p), (11, 22)) + self.assertIsInstance(self.copy_module.replace(p), Point) + self.assertEqual(self.copy_module.replace(p, x=1), (1, 22)) + self.assertEqual(self.copy_module.replace(p, y=2), (11, 2)) + self.assertEqual(self.copy_module.replace(p, x=1, y=2), (1, 2)) with self.assertRaisesRegex(TypeError, 'unexpected field name'): - copy.replace(p, x=1, error=2) + self.copy_module.replace(p, x=1, error=2) def test_dataclass(self): from dataclasses import dataclass @@ -964,12 +976,12 @@ class C: attrs = attrgetter('x', 'y') c = C(11, 22) - self.assertEqual(attrs(copy.replace(c)), (11, 22)) - self.assertEqual(attrs(copy.replace(c, x=1)), (1, 22)) - self.assertEqual(attrs(copy.replace(c, y=2)), (11, 2)) - self.assertEqual(attrs(copy.replace(c, x=1, y=2)), (1, 2)) + self.assertEqual(attrs(self.copy_module.replace(c)), (11, 22)) + self.assertEqual(attrs(self.copy_module.replace(c, x=1)), (1, 22)) + self.assertEqual(attrs(self.copy_module.replace(c, y=2)), (11, 2)) + self.assertEqual(attrs(self.copy_module.replace(c, x=1, y=2)), (1, 2)) with self.assertRaisesRegex(TypeError, 'unexpected keyword argument'): - copy.replace(c, x=1, error=2) + self.copy_module.replace(c, x=1, error=2) class MiscTestCase(unittest.TestCase): @@ -979,5 +991,35 @@ def test__all__(self): def global_foo(x, y): return x+y +class TestCopyPy(TestCopy, unittest.TestCase): + copy_module = py_copy + + +class TestReplacePy(TestReplace, unittest.TestCase): + copy_module = py_copy + + +class TestDeepcopyPy(TestDeepcopy, unittest.TestCase): + copy_module = py_copy + + +@unittest.skipUnless(c_copy, 'requires _copy') +class TestDeepcopyC(TestDeepcopy, unittest.TestCase): + copy_module = c_copy + + def test_deepcopy_standard_types_no_fallback(self): + with unittest.mock.patch.object(self.copy_module, '_deepcopy_fallback') as _deepcopy_fallback_mock: + _=self.copy_module.deepcopy({'str': 's', 'int': 0, 'list': [1,(1,2)]}) + _deepcopy_fallback_mock.assert_not_called() + + class C: + pass + + with unittest.mock.patch.object(self.copy_module, '_deepcopy_fallback') as _deepcopy_fallback_mock: + _=self.copy_module.deepcopy(C()) + _deepcopy_fallback_mock.assert_called() + + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-16-20-21-13.gh-issue-72793.qc-BP-.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-16-20-21-13.gh-issue-72793.qc-BP-.rst new file mode 100644 index 00000000000000..99e2cf55fd5da4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-16-20-21-13.gh-issue-72793.qc-BP-.rst @@ -0,0 +1,2 @@ +Improve the performance of :func:`copy.deepcopy` by implementing part of the +function in C. Patch by Rasmus Villemoes and Pieter Eendebak. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 9121a8c5dc69e0..7bfe42546e6001 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -32,6 +32,7 @@ @MODULE__ASYNCIO_TRUE@_asyncio _asynciomodule.c @MODULE__BISECT_TRUE@_bisect _bisectmodule.c @MODULE__CONTEXTVARS_TRUE@_contextvars _contextvarsmodule.c +@MODULE__COPY_TRUE@_copy _copy.c @MODULE__CSV_TRUE@_csv _csv.c @MODULE__HEAPQ_TRUE@_heapq _heapqmodule.c @MODULE__JSON_TRUE@_json _json.c diff --git a/Modules/_copy.c b/Modules/_copy.c new file mode 100644 index 00000000000000..00430b16cbf372 --- /dev/null +++ b/Modules/_copy.c @@ -0,0 +1,465 @@ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + +#include "Python.h" +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_object.h" // _PyNone_Type, _PyNotImplemented_Type +#include "pycore_dict.h" // _PyDict_NewPresized +#include "clinic/_copy.c.h" + +/*[clinic input] +module _copy +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b34c1b75f49dbfff]*/ + +static inline PyObject * +object_id(PyObject* obj) +{ + return PyLong_FromVoidPtr(obj); +} + +typedef struct { + PyObject *python_copy_module; + PyObject *str_deepcopy_fallback; +} copy_module_state; + +static inline copy_module_state* +get_copy_module_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (copy_module_state *)state; +} + +static int +memo_keepalive(PyObject* x, PyObject* memo) +{ + PyObject *memoid = object_id(memo); + if (memoid == NULL) { + return -1; + } + /* try: memo[id(memo)].append(x) */ + PyObject *list = PyDict_GetItem(memo, memoid); + if (list != NULL) { + Py_DECREF(memoid); + return PyList_Append(list, x); + } + + /* except KeyError: memo[id(memo)] = [x] */ + list = PyList_New(1); + if (list == NULL) { + Py_DECREF(memoid); + return -1; + } + Py_INCREF(x); + PyList_SET_ITEM(list, 0, x); + int ret = PyDict_SetItem(memo, memoid, list); + Py_DECREF(memoid); + Py_DECREF(list); + return ret; +} + +/* Forward declaration. */ +static PyObject* do_deepcopy(PyObject *module, PyObject* x, PyObject* memo); + +static PyObject* +do_deepcopy_fallback(PyObject* module, PyObject* x, PyObject* memo) +{ + copy_module_state *state = get_copy_module_state(module); + PyObject *args[] = {state->python_copy_module, x, memo}; + + return PyObject_VectorcallMethod(state->str_deepcopy_fallback, + args, 3, NULL); +} + +static PyObject* +deepcopy_list(PyObject* module, PyObject* x, PyObject* memo, PyObject* id_x, + Py_hash_t hash_id_x) +{ + assert(PyList_CheckExact(x)); + Py_ssize_t size = PyList_GET_SIZE(x); + + /* + * Make a copy of x, then replace each element with its deepcopy. This + * avoids building the new list with repeated PyList_Append calls, and + * also avoids problems that could occur if some user-defined __deepcopy__ + * mutates the source list. However, this doesn't eliminate all possible + * problems, since Python code can still get its hands on y via the memo, + * so we're still careful to check 'i < PyList_GET_SIZE(y)' before + * getting/setting in the loop below. + */ + PyObject *y = PyList_GetSlice(x, 0, size); + if (y == NULL) { + return NULL; + } + assert(PyList_CheckExact(y)); + + if (_PyDict_SetItem_KnownHash(memo, id_x, y, hash_id_x) < 0) { + Py_DECREF(y); + return NULL; + } + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(y); ++i) { + PyObject *elem = PyList_GET_ITEM(y, i); + Py_INCREF(elem); + Py_SETREF(elem, do_deepcopy(module, elem, memo)); + if (elem == NULL) { + Py_DECREF(y); + return NULL; + } + /* + * This really should not happen, but let's just return what's left in + * the list. + */ + if (i >= PyList_GET_SIZE(y)) { + Py_DECREF(elem); + break; + } + PyList_SetItem(y, i, elem); + } + return y; +} + +/* + * Helpers exploiting ma_version_tag. dict_iter_next returns 0 if the dict is + * exhausted, 1 if the next key/value pair was succesfully fetched, -1 if + * mutation is detected. + */ +struct dict_iter { + PyObject* d; + uint64_t tag; + Py_ssize_t pos; +}; + +static void +dict_iter_init(struct dict_iter* di, PyObject* x) +{ + di->d = x; + di->tag = ((PyDictObject*)x)->ma_version_tag; + di->pos = 0; +} + +static int +dict_iter_next(struct dict_iter* di, PyObject** key, PyObject** val) +{ + int ret = PyDict_Next(di->d, &di->pos, key, val); + if (((PyDictObject*)di->d)->ma_version_tag != di->tag) { + PyErr_SetString(PyExc_RuntimeError, "dict mutated during iteration"); + return -1; + } + return ret; +} + +static PyObject* +deepcopy_dict(PyObject* module, PyObject* x, PyObject* memo, PyObject* id_x, + Py_hash_t hash_id_x) +{ + PyObject* y, * key, * val; + Py_ssize_t size; + int ret; + struct dict_iter di; + + assert(PyDict_CheckExact(x)); + size = PyDict_Size(x); + + y = _PyDict_NewPresized(size); + if (y == NULL) { + return NULL; + } + if (_PyDict_SetItem_KnownHash(memo, id_x, y, hash_id_x) < 0) { + Py_DECREF(y); + return NULL; + } + + dict_iter_init(&di, x); + while ((ret = dict_iter_next(&di, &key, &val)) > 0) { + Py_INCREF(key); + Py_INCREF(val); + + Py_SETREF(key, do_deepcopy(module, key, memo)); + if (key == NULL) { + Py_DECREF(val); + break; + } + Py_SETREF(val, do_deepcopy(module, val, memo)); + if (val == NULL) { + Py_DECREF(key); + break; + } + + ret = PyDict_SetItem(y, key, val); + Py_DECREF(key); + Py_DECREF(val); + if (ret < 0) { + /* Shouldn't happen - y is presized */ + break; + } + } + /* + * We're only ok if the iteration ended with ret == 0; otherwise we've + * either detected mutation, or encountered an error during deepcopying or + * setting a key/value pair in y. + */ + if (ret != 0) { + Py_DECREF(y); + return NULL; + } + return y; +} + +static PyObject* +deepcopy_tuple(PyObject* module, PyObject* x, PyObject* memo, PyObject* id_x, + Py_hash_t hash_id_x) +{ + PyObject* y, * z; + int all_identical = 1; /* are all members their own deepcopy? */ + + assert(PyTuple_CheckExact(x)); + Py_ssize_t size = PyTuple_GET_SIZE(x); + + y = PyTuple_New(size); + if (y == NULL) { + return NULL; + } + /* We cannot add y to the memo just yet, since Python code would then be + * able to observe a tuple with values changing. We do, however, have an + * advantage over the Python implementation in that we can actually build + * the tuple directly instead of using an intermediate list object. + */ + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *elem = PyTuple_GET_ITEM(x, i); + PyObject *copy = do_deepcopy(module, elem, memo); + if (copy == NULL) { + Py_DECREF(y); + return NULL; + } + if (copy != elem) { + all_identical = 0; + } + PyTuple_SET_ITEM(y, i, copy); + } + + /* + * Tuples whose elements are all their own deepcopies don't + * get memoized. Adding to the memo costs time and memory, and + * we assume that pathological cases like [(1, 2, 3, 4)]*10000 + * are rare. + */ + if (all_identical) { + Py_INCREF(x); + Py_SETREF(y, x); + } + /* Did we do a copy of the same tuple deeper down? */ + else if ((z = _PyDict_GetItem_KnownHash(memo, id_x, hash_id_x)) != NULL) { + Py_INCREF(z); + Py_SETREF(y, z); + } + /* Make sure any of our callers up the stack return this copy. */ + else if (_PyDict_SetItem_KnownHash(memo, id_x, y, hash_id_x) < 0) { + Py_CLEAR(y); + } + return y; +} + + + +/* + * Using the private _PyNone_Type and _PyNotImplemented_Type avoids + * special-casing those in do_deepcopy. + */ +static const PyTypeObject* const atomic_type[] = { + &_PyNone_Type, /* type(None) */ + &PyUnicode_Type, /* str */ + &PyBytes_Type, /* bytes */ + &PyLong_Type, /* int */ + &PyBool_Type, /* bool */ + &PyFloat_Type, /* float */ + &PyComplex_Type, /* complex */ + &PyType_Type, /* type */ + &PyEllipsis_Type, /* type(Ellipsis) */ + &_PyNotImplemented_Type, /* type(NotImplemented) */ +}; + +typedef PyObject* (deepcopy_dispatcher_handler)(PyObject *module, PyObject* x, + PyObject* memo, PyObject* id_x, Py_hash_t hash_id_x); + +struct deepcopy_dispatcher { + PyTypeObject* type; + deepcopy_dispatcher_handler *handler; +}; + +static const struct deepcopy_dispatcher deepcopy_dispatch[] = { + {&PyList_Type, deepcopy_list}, + {&PyDict_Type, deepcopy_dict}, + {&PyTuple_Type, deepcopy_tuple}, +}; + +static PyObject* +do_deepcopy(PyObject *module, PyObject* x, PyObject* memo) +{ + unsigned i; + PyObject* y, * id_x; + const struct deepcopy_dispatcher* dd; + Py_hash_t hash_id_x; + + assert(PyDict_CheckExact(memo)); + + PyTypeObject *type_x = Py_TYPE(x); + + /* + * No need to have a separate dispatch function for this. Also, the + * array would have to be quite a lot larger before a smarter data + * structure is worthwhile. + */ + for (i = 0; i < Py_ARRAY_LENGTH(atomic_type); ++i) { + if (type_x == atomic_type[i]) { + return Py_NewRef(x); + } + } + + /* Have we already done a deepcopy of x? */ + id_x = object_id(x); + if (id_x == NULL) { + return NULL; + } + hash_id_x = PyObject_Hash(id_x); + + y = _PyDict_GetItem_KnownHash(memo, id_x, hash_id_x); + if (y != NULL) { + Py_DECREF(id_x); + return Py_NewRef(y); + } + /* + * Hold on to id_x and its hash a little longer - the dispatch handlers + * will all need it. + */ + for (i = 0; i < Py_ARRAY_LENGTH(deepcopy_dispatch); ++i) { + dd = &deepcopy_dispatch[i]; + if (type_x != dd->type) { + continue; + } + y = dd->handler(module, x, memo, id_x, hash_id_x); + Py_DECREF(id_x); + if (y == NULL) { + return NULL; + } + if (x != y && memo_keepalive(x, memo) < 0) { + Py_DECREF(y); + return NULL; + } + return y; + } + + Py_DECREF(id_x); + + return do_deepcopy_fallback(module, x, memo); +} + +/* + * This is the Python entry point. Hopefully we can stay in the C code + * most of the time, but we will occasionally call the Python code to + * handle the stuff that's very inconvenient to write in C, and that + * will then call back to us. + */ + +/*[clinic input] + _copy.deepcopy + + x: object + memo: object = None + / + + Create a deep copy of x + + See the documentation for the copy module for details. + +[clinic start generated code]*/ + +static PyObject * +_copy_deepcopy_impl(PyObject *module, PyObject *x, PyObject *memo) +/*[clinic end generated code: output=825a9c8dd4bfc002 input=519bbb0201ae2a5c]*/ +{ + PyObject* result; + + if (memo == Py_None) { + memo = PyDict_New(); + if (memo == NULL) { + return NULL; + } + } + else { + if (!PyDict_CheckExact(memo)) { + PyErr_SetString(PyExc_TypeError, "memo must be a dict"); + return NULL; + } + Py_INCREF(memo); + } + + result = do_deepcopy(module, x, memo); + + Py_DECREF(memo); + return result; +} + +static PyMethodDef copy_functions[] = { + {"deepcopy", _PyCFunction_CAST(_copy_deepcopy), + METH_FASTCALL, _copy_deepcopy__doc__}, + {NULL, NULL} +}; + +static int +copy_clear(PyObject *module) +{ + copy_module_state *state = get_copy_module_state(module); + Py_CLEAR(state->str_deepcopy_fallback); + Py_CLEAR(state->python_copy_module); + return 0; +} + +static void +copy_free(void *module) +{ + copy_clear((PyObject *)module); +} + +static int copy_module_exec(PyObject *module) +{ + copy_module_state *state = get_copy_module_state(module); + state->str_deepcopy_fallback = + PyUnicode_InternFromString("_deepcopy_fallback"); + if (state->str_deepcopy_fallback == NULL) { + return -1; + } + + PyObject *copymodule = PyImport_ImportModule("copy"); + if (copymodule == NULL) { + return -1; + } + state->python_copy_module = copymodule; + + return 0; +} + +static PyModuleDef_Slot copy_slots[] = { + {Py_mod_exec, copy_module_exec}, + {0, NULL} +}; + +static struct PyModuleDef copy_moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "_copy", + .m_doc = PyDoc_STR("C implementation of deepcopy"), + .m_size = sizeof(copy_module_state), + .m_methods = copy_functions, + .m_slots = copy_slots, + .m_clear = copy_clear, + .m_free = copy_free, +}; + + +PyMODINIT_FUNC +PyInit__copy(void) +{ + return PyModuleDef_Init(©_moduledef); +} diff --git a/Modules/clinic/_copy.c.h b/Modules/clinic/_copy.c.h new file mode 100644 index 00000000000000..cf104a464b5260 --- /dev/null +++ b/Modules/clinic/_copy.c.h @@ -0,0 +1,42 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(_copy_deepcopy__doc__, +"deepcopy($module, x, memo=None, /)\n" +"--\n" +"\n" +"Create a deep copy of x\n" +"\n" +"See the documentation for the copy module for details."); + +#define _COPY_DEEPCOPY_METHODDEF \ + {"deepcopy", _PyCFunction_CAST(_copy_deepcopy), METH_FASTCALL, _copy_deepcopy__doc__}, + +static PyObject * +_copy_deepcopy_impl(PyObject *module, PyObject *x, PyObject *memo); + +static PyObject * +_copy_deepcopy(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *x; + PyObject *memo = Py_None; + + if (!_PyArg_CheckPositional("deepcopy", nargs, 1, 2)) { + goto exit; + } + x = args[0]; + if (nargs < 2) { + goto skip_optional; + } + memo = args[1]; +skip_optional: + return_value = _copy_deepcopy_impl(module, x, memo); + +exit: + return return_value; +} +/*[clinic end generated code: output=641369a748545ff8 input=a9049054013a1b77]*/ diff --git a/PC/config.c b/PC/config.c index b744f711b0d636..29b38a7db5796b 100644 --- a/PC/config.c +++ b/PC/config.c @@ -76,6 +76,7 @@ extern PyObject* PyInit__string(void); extern PyObject* PyInit__stat(void); extern PyObject* PyInit__opcode(void); extern PyObject* PyInit__contextvars(void); +extern PyObject* PyInit__copy(void); extern PyObject* PyInit__tokenize(void); /* tools/freeze/makeconfig.py marker for additional "extern" */ @@ -176,6 +177,7 @@ struct _inittab _PyImport_Inittab[] = { {"_stat", PyInit__stat}, {"_opcode", PyInit__opcode}, + {"_copy", PyInit__copy}, {"_contextvars", PyInit__contextvars}, /* Sentinel */ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index dbb18ba96d6e50..d7cf18c1ba230e 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -424,6 +424,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 075910915fb912..3924c18f888e21 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1529,6 +1529,9 @@ Modules + + Modules + Modules\zlib diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 4d595d98445a05..ca0279034b17c0 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -24,6 +24,7 @@ static const char* _Py_stdlib_module_names[] = { "_compat_pickle", "_compression", "_contextvars", +"_copy", "_csv", "_ctypes", "_curses", diff --git a/configure b/configure index 2c58af3eace84a..99fe717ff10433 100755 --- a/configure +++ b/configure @@ -808,6 +808,8 @@ MODULE__HEAPQ_FALSE MODULE__HEAPQ_TRUE MODULE__CSV_FALSE MODULE__CSV_TRUE +MODULE__COPY_FALSE +MODULE__COPY_TRUE MODULE__CONTEXTVARS_FALSE MODULE__CONTEXTVARS_TRUE MODULE__BISECT_FALSE @@ -29187,6 +29189,28 @@ then : +fi + + + if test "$py_cv_module__copy" != "n/a" +then : + py_cv_module__copy=yes +fi + if test "$py_cv_module__copy" = yes; then + MODULE__COPY_TRUE= + MODULE__COPY_FALSE='#' +else + MODULE__COPY_TRUE='#' + MODULE__COPY_FALSE= +fi + + as_fn_append MODULE_BLOCK "MODULE__COPY_STATE=$py_cv_module__copy$as_nl" + if test "x$py_cv_module__copy" = xyes +then : + + + + fi @@ -31896,6 +31920,10 @@ if test -z "${MODULE__CONTEXTVARS_TRUE}" && test -z "${MODULE__CONTEXTVARS_FALSE as_fn_error $? "conditional \"MODULE__CONTEXTVARS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__COPY_TRUE}" && test -z "${MODULE__COPY_FALSE}"; then + as_fn_error $? "conditional \"MODULE__COPY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE__CSV_TRUE}" && test -z "${MODULE__CSV_FALSE}"; then as_fn_error $? "conditional \"MODULE__CSV\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/configure.ac b/configure.ac index 3c1dc1cc5018d4..69430afc37bf20 100644 --- a/configure.ac +++ b/configure.ac @@ -7715,6 +7715,7 @@ PY_STDLIB_MOD_SIMPLE([array]) PY_STDLIB_MOD_SIMPLE([_asyncio]) PY_STDLIB_MOD_SIMPLE([_bisect]) PY_STDLIB_MOD_SIMPLE([_contextvars]) +PY_STDLIB_MOD_SIMPLE([_copy]) PY_STDLIB_MOD_SIMPLE([_csv]) PY_STDLIB_MOD_SIMPLE([_heapq]) PY_STDLIB_MOD_SIMPLE([_json])