From 4615e77628f2555a89a551f028a3b86b3cf87690 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 11 Oct 2025 18:44:39 +0200 Subject: [PATCH 1/2] gh-139772: Add PyDict_FromItems() function --- Doc/c-api/dict.rst | 9 +++++++ Doc/whatsnew/3.15.rst | 4 +++ Include/cpython/dictobject.h | 5 ++++ Lib/test/test_capi/test_dict.py | 17 ++++++++++++ ...-10-11-18-44-36.gh-issue-139772.GmNHrN.rst | 3 +++ Modules/_testcapi/dict.c | 26 ++++++++++++++++++- Objects/dictobject.c | 9 +++++++ 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-10-11-18-44-36.gh-issue-139772.GmNHrN.rst diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 0fbe26b56c0a7c..db4ab25b7d7b27 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -36,6 +36,15 @@ Dictionary Objects Return a new empty dictionary, or ``NULL`` on failure. +.. c:function:: PyObject* PyDict_FromItems(PyObject *const *keys, PyObject *const *values, Py_ssize_t length) + + Create a dictionary from *keys* and *values* of *length* items. + + Return a new empty dictionary, or ``NULL`` on failure. + + .. versionadded:: next + + .. c:function:: PyObject* PyDictProxy_New(PyObject *mapping) Return a :class:`types.MappingProxyType` object for a mapping which diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 40286d4fe857e8..6532650a509900 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -852,6 +852,10 @@ New features (Contributed by Victor Stinner in :gh:`129813`.) +* Add :c:func:`PyDict_FromItems(keys, values, length) ` + to create a dictionary from *keys* and *values* of *length* items. + (Contributed by Victor Stinner in :gh:`139772`.) + * Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array. (Contributed by Victor Stinner in :gh:`111489`.) diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index df9ec7050fca1a..97f298ce711da0 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -103,3 +103,8 @@ PyAPI_FUNC(int) PyDict_ClearWatcher(int watcher_id); // Mark given dictionary as "watched" (callback will be called if it is modified) PyAPI_FUNC(int) PyDict_Watch(int watcher_id, PyObject* dict); PyAPI_FUNC(int) PyDict_Unwatch(int watcher_id, PyObject* dict); + +PyAPI_FUNC(PyObject *) PyDict_FromItems( + PyObject *const *keys, + PyObject *const *values, + Py_ssize_t length); diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py index e726e3d813d888..eece621ad7b269 100644 --- a/Lib/test/test_capi/test_dict.py +++ b/Lib/test/test_capi/test_dict.py @@ -545,6 +545,23 @@ def test_dict_popstring(self): # CRASHES dict_popstring({}, NULL) # CRASHES dict_popstring({"a": 1}, NULL) + def test_dict_fromitems(self): + dict_fromitems = _testcapi.dict_fromitems + + d = dict_fromitems((), ()) + self.assertEqual(d, {}) + + d = dict_fromitems(tuple(range(1, 4)), tuple('abc')) + self.assertEqual(d, {1: 'a', 2: 'b', 3: 'c'}) + + # test unicode keys + d = dict_fromitems(tuple('abc'), tuple(range(1, 4))) + self.assertEqual(d, {'a': 1, 'b': 2, 'c': 3}) + + # test "large" dict (1024 items) + d = dict_fromitems(tuple(range(1024)), tuple(map(str, range(1024)))) + self.assertEqual(d, {i: str(i) for i in range(1024)}) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C_API/2025-10-11-18-44-36.gh-issue-139772.GmNHrN.rst b/Misc/NEWS.d/next/C_API/2025-10-11-18-44-36.gh-issue-139772.GmNHrN.rst new file mode 100644 index 00000000000000..5b99d21005564a --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-10-11-18-44-36.gh-issue-139772.GmNHrN.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyDict_FromItems(keys, values, length) ` to +create a dictionary from *keys* and *values* of *length* items. Patch by +Victor Stinner. diff --git a/Modules/_testcapi/dict.c b/Modules/_testcapi/dict.c index b7c73d7332bd4e..08e6c04c9378ad 100644 --- a/Modules/_testcapi/dict.c +++ b/Modules/_testcapi/dict.c @@ -258,6 +258,29 @@ test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) } +static PyObject* +dict_fromitems(PyObject* self, PyObject *args) +{ + PyObject *keys_obj, *values_obj; + if (!PyArg_ParseTuple(args, "O!O!", + &PyTuple_Type, &keys_obj, + &PyTuple_Type, &values_obj)) { + return NULL; + } + + Py_ssize_t length = PyTuple_GET_SIZE(keys_obj); + if (PyTuple_GET_SIZE(values_obj) != length) { + PyErr_SetString(PyExc_ValueError, + "keys and values must have the same length"); + return NULL; + } + + PyObject **keys = &PyTuple_GET_ITEM(keys_obj, 0); + PyObject **values = &PyTuple_GET_ITEM(values_obj, 0); + return PyDict_FromItems(keys, values, length); +} + + static PyMethodDef test_methods[] = { {"dict_containsstring", dict_containsstring, METH_VARARGS}, {"dict_getitemref", dict_getitemref, METH_VARARGS}, @@ -268,7 +291,8 @@ static PyMethodDef test_methods[] = { {"dict_pop_null", dict_pop_null, METH_VARARGS}, {"dict_popstring", dict_popstring, METH_VARARGS}, {"dict_popstring_null", dict_popstring_null, METH_VARARGS}, - {"test_dict_iteration", test_dict_iteration, METH_NOARGS}, + {"test_dict_iteration", test_dict_iteration, METH_NOARGS}, + {"dict_fromitems", dict_fromitems, METH_VARARGS}, {NULL}, }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 24188ffe7132d5..3e4064bce01965 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2263,6 +2263,15 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset, return dict; } + +PyObject * +PyDict_FromItems(PyObject *const *keys, PyObject *const *values, + Py_ssize_t length) +{ + return _PyDict_FromItems(keys, 1, values, 1, length); +} + + /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return From 65bf623ed465d7858c0976877e322ad75f266b9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 12 Oct 2025 10:47:59 +0200 Subject: [PATCH 2/2] Update Doc/c-api/dict.rst Co-authored-by: scoder --- Doc/c-api/dict.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index db4ab25b7d7b27..39241796238c14 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -40,7 +40,7 @@ Dictionary Objects Create a dictionary from *keys* and *values* of *length* items. - Return a new empty dictionary, or ``NULL`` on failure. + Return a new dictionary, or ``NULL`` on failure. .. versionadded:: next