Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5131071

Browse files
committed
gh-106004: Add PyDict_GetItemRef() function
* Add PyDict_GetItemRef() and PyDict_GetItemStringRef() functions. * Add unit tests on the PyDict C API in test_capi.
1 parent ee52158 commit 5131071

File tree

6 files changed

+285
-14
lines changed

6 files changed

+285
-14
lines changed

Doc/c-api/dict.rst

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,26 @@ Dictionary Objects
9393
Return ``0`` on success or ``-1`` on failure.
9494
9595
96+
.. c:function:: int PyDict_GetItemRef(PyObject *p, PyObject *key, PyObject **value)
97+
98+
Return a new :term:`strong reference` to the object from dictionary *p*
99+
which has a key *key*:
100+
101+
* If the key is present, set *\*pvalue* to a new :term:`strong reference`
102+
to the value and return ``0``.
103+
* If the key is missing, set *\*pvalue* to ``NULL`` and return ``0``.
104+
* On error, raise an exception and return ``-1``.
105+
106+
.. versionadded:: 3.13
107+
108+
See also the :c:func:`PyObject_GetItem` function.
109+
110+
96111
.. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key)
97112
98-
Return the object from dictionary *p* which has a key *key*. Return ``NULL``
99-
if the key *key* is not present, but *without* setting an exception.
113+
Return a :term:`borrowed reference` to the object from dictionary *p* which
114+
has a key *key*. Return ``NULL`` if the key *key* is missing *without*
115+
setting an exception.
100116
101117
Note that exceptions which occur while calling :meth:`__hash__` and
102118
:meth:`__eq__` methods will get suppressed.
@@ -126,6 +142,14 @@ Dictionary Objects
126142
To get error reporting use :c:func:`PyDict_GetItemWithError()` instead.
127143
128144
145+
.. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **pvalue)
146+
147+
Similar than :c:func:`PyDict_GetItemRef`, but *key* is specified as a
148+
:c:expr:`const char*`, rather than a :c:expr:`PyObject*`.
149+
150+
.. versionadded:: 3.13
151+
152+
129153
.. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *defaultobj)
130154
131155
This is the same as the Python-level :meth:`dict.setdefault`. If present, it

Doc/whatsnew/3.13.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,13 @@ New Features
440440
``NULL`` if the referent is no longer live.
441441
(Contributed by Victor Stinner in :gh:`105927`.)
442442

443+
* Add :c:func:`PyDict_GetItemRef` and :c:func:`PyDict_GetItemStringRef`
444+
functions: similar than :c:func:`PyDict_GetItemWithError` but return a
445+
:term:`strong reference` instead of a :term:`borrowed reference`. Moreover,
446+
these functions return -1 on error and so checking ``PyErr_Occurred()`` is
447+
not needed.
448+
(Contributed by Victor Stinner in :gh:`106004`.)
449+
443450
Porting to Python 3.13
444451
----------------------
445452

Include/dictobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ PyAPI_DATA(PyTypeObject) PyDict_Type;
2121
PyAPI_FUNC(PyObject *) PyDict_New(void);
2222
PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
2323
PyAPI_FUNC(PyObject *) PyDict_GetItemWithError(PyObject *mp, PyObject *key);
24+
PyAPI_FUNC(int) PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **pvalue);
2425
PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item);
2526
PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key);
2627
PyAPI_FUNC(void) PyDict_Clear(PyObject *mp);
@@ -55,6 +56,7 @@ PyAPI_FUNC(int) PyDict_MergeFromSeq2(PyObject *d,
5556
int override);
5657

5758
PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key);
59+
PyAPI_FUNC(int) PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **pvalue);
5860
PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item);
5961
PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key);
6062
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add :c:func:`PyDict_GetItemRef` and :c:func:`PyDict_GetItemStringRef`
2+
functions: similar than :c:func:`PyDict_GetItemWithError` but return a
3+
:term:`strong reference` instead of a :term:`borrowed reference`. Patch by
4+
Victor Stinner.

Modules/_testcapimodule.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,6 +3466,195 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
34663466
}
34673467

34683468

3469+
static PyObject *
3470+
test_dict_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
3471+
{
3472+
assert(!PyErr_Occurred());
3473+
3474+
PyObject *dict= NULL, *key = NULL, *missing_key = NULL, *value = NULL;
3475+
PyObject *invalid_key = NULL;
3476+
3477+
// test PyDict_New()
3478+
dict = PyDict_New();
3479+
if (dict == NULL) {
3480+
goto error;
3481+
}
3482+
3483+
key = PyUnicode_FromString("key");
3484+
if (key == NULL) {
3485+
goto error;
3486+
}
3487+
3488+
missing_key = PyUnicode_FromString("missing_key");
3489+
if (missing_key == NULL) {
3490+
goto error;
3491+
}
3492+
3493+
value = PyUnicode_FromString("value");
3494+
if (value == NULL) {
3495+
goto error;
3496+
}
3497+
3498+
// test PyDict_SetItem()
3499+
Py_ssize_t key_refcnt = Py_REFCNT(key);
3500+
Py_ssize_t value_refcnt = Py_REFCNT(value);
3501+
if (PyDict_SetItem(dict, key, value) < 0) {
3502+
goto error;
3503+
}
3504+
assert(Py_REFCNT(key) == (key_refcnt + 1));
3505+
assert(Py_REFCNT(value) == (value_refcnt + 1));
3506+
3507+
// test PyDict_SetItemString()
3508+
if (PyDict_SetItemString(dict, "key", value) < 0) {
3509+
goto error;
3510+
}
3511+
assert(Py_REFCNT(key) == (key_refcnt + 1));
3512+
assert(Py_REFCNT(value) == (value_refcnt + 1));
3513+
3514+
// test PyDict_Size()
3515+
assert(PyDict_Size(dict) == 1);
3516+
3517+
// test PyDict_Contains(), key is present
3518+
assert(PyDict_Contains(dict, key) == 1);
3519+
3520+
// test PyDict_GetItem(), key is present
3521+
assert(PyDict_GetItem(dict, key) == value);
3522+
3523+
// test PyDict_GetItemString(), key is present
3524+
assert(PyDict_GetItemString(dict, "key") == value);
3525+
3526+
// test PyDict_GetItemWithError(), key is present
3527+
assert(PyDict_GetItemWithError(dict, key) == value);
3528+
assert(!PyErr_Occurred());
3529+
3530+
// test PyDict_GetItemRef(), key is present
3531+
PyObject *get_value = Py_Ellipsis; // marker value
3532+
if (PyDict_GetItemRef(dict, key, &get_value) < 0) {
3533+
goto error;
3534+
}
3535+
assert(get_value == value);
3536+
Py_DECREF(get_value);
3537+
3538+
// test PyDict_GetItemStringRef(), key is present
3539+
get_value = Py_Ellipsis; // marker value
3540+
if (PyDict_GetItemStringRef(dict, "key", &get_value) < 0) {
3541+
goto error;
3542+
}
3543+
assert(get_value == value);
3544+
Py_DECREF(get_value);
3545+
3546+
// test PyDict_Contains(), missing key
3547+
assert(PyDict_Contains(dict, missing_key) == 0);
3548+
3549+
// test PyDict_GetItem(), missing key
3550+
assert(PyDict_GetItem(dict, missing_key) == NULL);
3551+
assert(!PyErr_Occurred());
3552+
3553+
// test PyDict_GetItemString(), missing key
3554+
assert(PyDict_GetItemString(dict, "missing_key") == NULL);
3555+
assert(!PyErr_Occurred());
3556+
3557+
// test PyDict_GetItemWithError(), missing key
3558+
assert(PyDict_GetItem(dict, missing_key) == NULL);
3559+
assert(!PyErr_Occurred());
3560+
3561+
// test PyDict_GetItemRef(), missing key
3562+
get_value = Py_Ellipsis; // marker value
3563+
assert(PyDict_GetItemRef(dict, missing_key, &get_value) == 0);
3564+
assert(!PyErr_Occurred());
3565+
assert(get_value == NULL);
3566+
3567+
// test PyDict_GetItemStringRef(), missing key
3568+
get_value = Py_Ellipsis; // marker value
3569+
assert(PyDict_GetItemStringRef(dict, "missing_key", &get_value) == 0);
3570+
assert(!PyErr_Occurred());
3571+
assert(get_value == NULL);
3572+
3573+
// test PyDict_GetItem(), invalid dict
3574+
PyObject *invalid_dict = key; // borrowed reference
3575+
assert(PyDict_GetItem(invalid_dict, key) == NULL);
3576+
assert(!PyErr_Occurred());
3577+
3578+
// test PyDict_GetItemWithError(), invalid dict
3579+
assert(PyDict_GetItemWithError(invalid_dict, key) == NULL);
3580+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
3581+
PyErr_Clear();
3582+
3583+
// test PyDict_GetItemRef(), invalid dict
3584+
get_value = Py_Ellipsis; // marker value
3585+
assert(PyDict_GetItemRef(invalid_dict, key, &get_value) == -1);
3586+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
3587+
PyErr_Clear();
3588+
assert(get_value == NULL);
3589+
3590+
// test PyDict_GetItemStringRef(), invalid dict
3591+
get_value = Py_Ellipsis; // marker value
3592+
assert(PyDict_GetItemStringRef(invalid_dict, "key", &get_value) == -1);
3593+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
3594+
PyErr_Clear();
3595+
assert(get_value == NULL);
3596+
3597+
invalid_key = PyList_New(0);
3598+
if (invalid_key == NULL) {
3599+
goto error;
3600+
}
3601+
3602+
// test PyDict_Contains(), invalid key
3603+
assert(PyDict_Contains(dict, invalid_key) == -1);
3604+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3605+
PyErr_Clear();
3606+
3607+
// test PyDict_GetItem(), invalid key
3608+
assert(PyDict_GetItem(dict, invalid_key) == NULL);
3609+
assert(!PyErr_Occurred());
3610+
3611+
// test PyDict_GetItemWithError(), invalid key
3612+
assert(PyDict_GetItemWithError(dict, invalid_key) == NULL);
3613+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3614+
PyErr_Clear();
3615+
3616+
// test PyDict_GetItemRef(), invalid key
3617+
get_value = Py_Ellipsis; // marker value
3618+
assert(PyDict_GetItemRef(dict, invalid_key, &get_value) == -1);
3619+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3620+
PyErr_Clear();
3621+
assert(get_value == NULL);
3622+
3623+
// test PyDict_DelItem(), key is present
3624+
assert(PyDict_DelItem(dict, key) == 0);
3625+
assert(PyDict_Size(dict) == 0);
3626+
3627+
// test PyDict_DelItem(), missing key
3628+
assert(PyDict_DelItem(dict, missing_key) == -1);
3629+
assert(PyErr_ExceptionMatches(PyExc_KeyError));
3630+
PyErr_Clear();
3631+
3632+
// test PyDict_DelItem(), invalid key
3633+
assert(PyDict_DelItem(dict, invalid_key) == -1);
3634+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3635+
PyErr_Clear();
3636+
3637+
// test PyDict_Clear()
3638+
PyDict_Clear(dict);
3639+
3640+
Py_DECREF(dict);
3641+
Py_DECREF(key);
3642+
Py_DECREF(missing_key);
3643+
Py_DECREF(value);
3644+
Py_DECREF(invalid_key);
3645+
3646+
Py_RETURN_NONE;
3647+
3648+
error:
3649+
Py_XDECREF(dict);
3650+
Py_XDECREF(key);
3651+
Py_XDECREF(missing_key);
3652+
Py_XDECREF(value);
3653+
Py_XDECREF(invalid_key);
3654+
return NULL;
3655+
}
3656+
3657+
34693658
static PyMethodDef TestMethods[] = {
34703659
{"set_errno", set_errno, METH_VARARGS},
34713660
{"test_config", test_config, METH_NOARGS},
@@ -3611,6 +3800,7 @@ static PyMethodDef TestMethods[] = {
36113800
{"test_atexit", test_atexit, METH_NOARGS},
36123801
{"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
36133802
{"test_weakref_capi", test_weakref_capi, METH_NOARGS},
3803+
{"test_dict_capi", test_dict_capi, METH_NOARGS},
36143804
{NULL, NULL} /* sentinel */
36153805
};
36163806

0 commit comments

Comments
 (0)