diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 371761573e97de..43f08af322d4a3 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -542,6 +542,10 @@ Frozen dictionary objects Create an empty dictionary if *iterable* is ``NULL``. +.. c:function:: PyObject* PyAnyDict_AsNewDict(PyObject *p) + + Create a new dictionary from a :class:`dict` or a :class:`frozendict`. + Ordered dictionaries ^^^^^^^^^^^^^^^^^^^^ diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 5e7811416aba63..fe84fa149d5233 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -106,3 +106,6 @@ PyAPI_FUNC(int) PyDict_Unwatch(int watcher_id, PyObject* dict); // Create a frozendict. Create an empty dictionary if iterable is NULL. PyAPI_FUNC(PyObject*) PyFrozenDict_New(PyObject *iterable); + +// Create a new dictionary from a dict or a frozendict. +PyAPI_FUNC(PyObject*) PyAnyDict_AsNewDict(PyObject *o); diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 6d7d68eda84c5a..08166b10468367 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -160,9 +160,6 @@ extern void _PyDict_Clear_LockHeld(PyObject *op); PyAPI_FUNC(void) _PyDict_EnsureSharedOnRead(PyDictObject *mp); #endif -// Export for '_elementtree' shared extension -PyAPI_FUNC(PyObject*) _PyDict_CopyAsDict(PyObject *op); - #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py index 5bdf74ef73ab54..80c038db352203 100644 --- a/Lib/test/test_capi/test_dict.py +++ b/Lib/test/test_capi/test_dict.py @@ -624,6 +624,19 @@ def test_frozendict_new(self): self.assertEqual(dct, frozendict()) self.assertIs(type(dct), frozendict) + def test_anydict_asnewdict(self): + # Test PyAnyDict_AsNewDict() + anydict_asnewdict = _testcapi.anydict_asnewdict + for dict_type in ANYDICT_TYPES: + dct = dict_type({1: 2}) + dct_copy = anydict_asnewdict(dct) + self.assertIs(type(dct_copy), dict) + self.assertEqual(dct_copy, dct) + + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, anydict_asnewdict, test_type()) + self.assertRaises(SystemError, anydict_asnewdict, NULL) + if __name__ == "__main__": unittest.main() diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index e0bc69c5fe22f8..00cc1acb22f15f 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -16,7 +16,6 @@ #endif #include "Python.h" -#include "pycore_dict.h" // _PyDict_CopyAsDict() #include "pycore_pyhash.h" // _Py_HashSecret #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() @@ -390,7 +389,7 @@ get_attrib_from_keywords(PyObject *kwds) Py_DECREF(attrib); return NULL; } - Py_SETREF(attrib, _PyDict_CopyAsDict(attrib)); + Py_SETREF(attrib, PyAnyDict_AsNewDict(attrib)); } else { attrib = PyDict_New(); @@ -429,7 +428,7 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds) if (attrib) { /* attrib passed as positional arg */ - attrib = _PyDict_CopyAsDict(attrib); + attrib = PyAnyDict_AsNewDict(attrib); if (!attrib) return -1; if (kwds) { diff --git a/Modules/_testcapi/dict.c b/Modules/_testcapi/dict.c index 172591b03182ab..491f21299c6613 100644 --- a/Modules/_testcapi/dict.c +++ b/Modules/_testcapi/dict.c @@ -295,6 +295,14 @@ frozendict_new(PyObject *self, PyObject *obj) } +static PyObject* +anydict_asnewdict(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyAnyDict_AsNewDict(obj); +} + + static PyMethodDef test_methods[] = { {"dict_containsstring", dict_containsstring, METH_VARARGS}, {"dict_getitemref", dict_getitemref, METH_VARARGS}, @@ -311,6 +319,7 @@ static PyMethodDef test_methods[] = { {"anydict_check", anydict_check, METH_O}, {"anydict_checkexact", anydict_checkexact, METH_O}, {"frozendict_new", frozendict_new, METH_O}, + {"anydict_asnewdict", anydict_asnewdict, METH_O}, {NULL}, }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 61fde37f8d4fff..32f6b68f7fc01f 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4414,12 +4414,13 @@ anydict_copy(PyObject *o) return res; } -// Similar to PyDict_Copy(), but accept also frozendict: -// convert frozendict to a new dict. PyObject* -_PyDict_CopyAsDict(PyObject *o) +PyAnyDict_AsNewDict(PyObject *o) { - assert(PyAnyDict_Check(o)); + if (o == NULL || !PyAnyDict_Check(o)) { + PyErr_BadInternalCall(); + return NULL; + } PyObject *res; Py_BEGIN_CRITICAL_SECTION(o); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1fdd3cbdaaa639..9ceaba5efc8e8d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4850,7 +4850,7 @@ type_new_get_slots(type_new_ctx *ctx, PyObject *dict) static PyTypeObject* type_new_init(type_new_ctx *ctx) { - PyObject *dict = _PyDict_CopyAsDict(ctx->orig_dict); + PyObject *dict = PyAnyDict_AsNewDict(ctx->orig_dict); if (dict == NULL) { goto error; }