Skip to content
Open
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
9 changes: 9 additions & 0 deletions Doc/c-api/dict.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Return a new empty dictionary, or ``NULL`` on failure.
Return a new dictionary, or ``NULL`` on failure.


.. versionadded:: next


.. c:function:: PyObject* PyDictProxy_New(PyObject *mapping)

Return a :class:`types.MappingProxyType` object for a mapping which
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ New features

(Contributed by Victor Stinner in :gh:`129813`.)

* Add :c:func:`PyDict_FromItems(keys, values, length) <PyDict_FromItems>`
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`.)

Expand Down
5 changes: 5 additions & 0 deletions Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
17 changes: 17 additions & 0 deletions Lib/test/test_capi/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add :c:func:`PyDict_FromItems(keys, values, length) <PyDict_FromItems>` to
create a dictionary from *keys* and *values* of *length* items. Patch by
Victor Stinner.
26 changes: 25 additions & 1 deletion Modules/_testcapi/dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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},
};

Expand Down
9 changes: 9 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading