]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-141510: No longer accept frozendict in PyDict_Copy() (#145542)
authorVictor Stinner <vstinner@python.org>
Thu, 5 Mar 2026 14:26:54 +0000 (15:26 +0100)
committerGitHub <noreply@github.com>
Thu, 5 Mar 2026 14:26:54 +0000 (15:26 +0100)
Rename _PyDict_Copy() to anydict_copy().

Replace PyObject_IsInstance(op, &PyFrozenDict_Type) with
PyFrozenDict_Check().

Doc/c-api/dict.rst
Lib/test/test_capi/test_dict.py
Objects/clinic/dictobject.c.h
Objects/dictobject.c

index 734462bc0051af552b5efa0d3d3926a0539dce99..371761573e97de5f314fe7eb67c7b4c175a348d8 100644 (file)
@@ -82,10 +82,6 @@ Dictionary objects
 
    Return a new dictionary that contains the same key-value pairs as *p*.
 
-   .. versionchanged:: next
-      If *p* is a subclass of :class:`frozendict`, the result will be a
-      :class:`frozendict` instance instead of a :class:`dict` instance.
-
 .. c:function:: int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val)
 
    Insert *val* into the dictionary *p* with a key of *key*.  *key* must be
index 561e1ea4d5284643799e24489da58bab02a1d4a8..5bdf74ef73ab5444d1c43484de15410d91806e0f 100644 (file)
@@ -97,21 +97,13 @@ class CAPITest(unittest.TestCase):
     def test_dict_copy(self):
         # Test PyDict_Copy()
         copy = _testlimitedcapi.dict_copy
-        for dict_type in ANYDICT_TYPES:
+        for dict_type in DICT_TYPES:
             dct = dict_type({1: 2})
             dct_copy = copy(dct)
-            if dict_type == frozendict:
-                expected_type = frozendict
-                self.assertIs(dct_copy, dct)
-            else:
-                if issubclass(dict_type, frozendict):
-                    expected_type = frozendict
-                else:
-                    expected_type = dict
-                self.assertIs(type(dct_copy), expected_type)
-                self.assertEqual(dct_copy, dct)
+            self.assertIs(type(dct_copy), dict)
+            self.assertEqual(dct_copy, dct)
 
-        for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES:
+        for test_type in NOT_DICT_TYPES + OTHER_TYPES:
             self.assertRaises(SystemError, copy, test_type())
         self.assertRaises(SystemError, copy, NULL)
 
index abf6b38449fcb0db572da6cc466ce7d609704898..15b8705d9c78e3c14629837a05ff7e8b961ce118 100644 (file)
@@ -323,4 +323,22 @@ dict_values(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
     return dict_values_impl((PyDictObject *)self);
 }
-/*[clinic end generated code: output=9007b74432217017 input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(frozendict_copy__doc__,
+"copy($self, /)\n"
+"--\n"
+"\n"
+"Return a shallow copy of the frozendict.");
+
+#define FROZENDICT_COPY_METHODDEF    \
+    {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, frozendict_copy__doc__},
+
+static PyObject *
+frozendict_copy_impl(PyFrozenDictObject *self);
+
+static PyObject *
+frozendict_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return frozendict_copy_impl((PyFrozenDictObject *)self);
+}
+/*[clinic end generated code: output=f4c88a3464928ae3 input=a9049054013a1b77]*/
index d86ab2634a9b136c23fb78014272120cdaf9297e..61fde37f8d4fff726fbb62e618d44a95d6756ce8 100644 (file)
@@ -146,8 +146,9 @@ static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override);
 
 /*[clinic input]
 class dict "PyDictObject *" "&PyDict_Type"
+class frozendict "PyFrozenDictObject *" "&PyFrozenDict_Type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5dfa93bac68e7c54]*/
 
 
 /*
@@ -2406,7 +2407,7 @@ dict_unhashable_type(PyObject *op, PyObject *key)
     }
 
     const char *errmsg;
-    if (PyObject_IsInstance(op, (PyObject*)&PyFrozenDict_Type)) {
+    if (PyFrozenDict_Check(op)) {
         errmsg = "cannot use '%T' as a frozendict key (%S)";
     }
     else {
@@ -4384,35 +4385,37 @@ copy_lock_held(PyObject *o, int as_frozendict)
     return NULL;
 }
 
-// Similar to PyDict_Copy(), but copy also frozendict.
-static PyObject *
-_PyDict_Copy(PyObject *o)
+PyObject *
+PyDict_Copy(PyObject *o)
 {
-    assert(PyAnyDict_Check(o));
+    if (o == NULL || !PyDict_Check(o)) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
 
     PyObject *res;
     Py_BEGIN_CRITICAL_SECTION(o);
-    res = copy_lock_held(o, PyFrozenDict_Check(o));
+    res = copy_lock_held(o, 0);
     Py_END_CRITICAL_SECTION();
     return res;
 }
 
-PyObject *
-PyDict_Copy(PyObject *o)
+// Similar to PyDict_Copy(), but return a frozendict if the argument
+// is a frozendict.
+static PyObject *
+anydict_copy(PyObject *o)
 {
-    if (o == NULL || !PyAnyDict_Check(o)) {
-        PyErr_BadInternalCall();
-        return NULL;
-    }
-
-    if (PyFrozenDict_CheckExact(o)) {
-        return Py_NewRef(o);
-    }
+    assert(PyAnyDict_Check(o));
 
-    return _PyDict_Copy(o);
+    PyObject *res;
+    Py_BEGIN_CRITICAL_SECTION(o);
+    res = copy_lock_held(o, PyFrozenDict_Check(o));
+    Py_END_CRITICAL_SECTION();
+    return res;
 }
 
-// Similar to PyDict_Copy(), but return a dict if the argument is a frozendict.
+// Similar to PyDict_Copy(), but accept also frozendict:
+// convert frozendict to a new dict.
 PyObject*
 _PyDict_CopyAsDict(PyObject *o)
 {
@@ -4969,7 +4972,7 @@ dict_or(PyObject *self, PyObject *other)
     if (!PyAnyDict_Check(self) || !PyAnyDict_Check(other)) {
         Py_RETURN_NOTIMPLEMENTED;
     }
-    PyObject *new = _PyDict_Copy(self);
+    PyObject *new = anydict_copy(self);
     if (new == NULL) {
         return NULL;
     }
@@ -8057,7 +8060,7 @@ static PyMethodDef frozendict_methods[] = {
     DICT_ITEMS_METHODDEF
     DICT_VALUES_METHODDEF
     DICT_FROMKEYS_METHODDEF
-    DICT_COPY_METHODDEF
+    FROZENDICT_COPY_METHODDEF
     DICT___REVERSED___METHODDEF
     {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
     {"__getnewargs__", frozendict_getnewargs, METH_NOARGS},
@@ -8182,6 +8185,25 @@ PyFrozenDict_New(PyObject *iterable)
     }
 }
 
+/*[clinic input]
+frozendict.copy
+
+Return a shallow copy of the frozendict.
+[clinic start generated code]*/
+
+static PyObject *
+frozendict_copy_impl(PyFrozenDictObject *self)
+/*[clinic end generated code: output=e580fd91d9fc2cf7 input=35f6abeaa08fd4bc]*/
+{
+    assert(PyFrozenDict_Check(self));
+
+    if (PyFrozenDict_CheckExact(self)) {
+        return Py_NewRef(self);
+    }
+
+    return anydict_copy((PyObject*)self);
+}
+
 
 PyTypeObject PyFrozenDict_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)