Skip to content

Commit a5e6feb

Browse files
committed
Finish containers_and_refcounts.rst with a number of TODOs.
1 parent 1520658 commit a5e6feb

File tree

4 files changed

+111
-3
lines changed

4 files changed

+111
-3
lines changed

doc/sphinx/source/containers_and_refcounts.rst

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,8 +1416,8 @@ Key Exists
14161416
If the key already exists in the dictionary `PyDict_Pop()`_ returns 1.
14171417
The reference counts are changed as follows:
14181418

1419-
- key: unchanged.
1420-
- value: incremented by one
1419+
- key: decremented by one
1420+
- value: unchanged.
14211421

14221422
``*result`` is equal to the stored value.
14231423

@@ -1446,6 +1446,8 @@ For example:
14461446
* result: 2 as it equals val.
14471447
*/
14481448
1449+
.. code-block:: c
1450+
14491451
For code and tests see:
14501452

14511453
* C, in ``src/cpy/Containers/DebugContainers.c``:
@@ -1508,6 +1510,11 @@ For code and tests see:
15081510
Failure
15091511
^^^^^^^
15101512

1513+
.. todo::
1514+
1515+
Finish Dictionary ``PyDict_Pop()`` Failure
1516+
1517+
15111518
.. index::
15121519
single: Dictionary; Other APIs
15131520

@@ -1520,9 +1527,16 @@ This section describes other dictionary APIs that are simple to describe and hav
15201527

15211528
There are no tests for many of these APIs in the current version of this project.
15221529

1530+
.. todo::
1531+
1532+
Finish Dictionary "Other APIs"
1533+
15231534

15241535
.. index::
15251536
single: Dictionary; PyDict_GetItemWithError()
1537+
pair: Documentation Lacunae; PyDict_GetItemWithError()
1538+
1539+
.. _chapter_containers_and_refcounts.dictionaries.pydict_getitemwitherror:
15261540

15271541
``PyDict_GetItemWithError()``
15281542
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1536,6 +1550,34 @@ The C signature is:
15361550
15371551
PyObject *PyDict_GetItemWithError(PyObject *p, PyObject *key);
15381552
1553+
.. warning::
1554+
1555+
This function is incorrectly documented as it fails to set an exception with a missing key as this code
1556+
demonstrates:
1557+
1558+
.. code-block:: c
1559+
1560+
assert(!PyErr_Occurred());
1561+
PyObject *container = PyDict_New();
1562+
assert(container && Py_REFCNT(container) == 1);
1563+
1564+
PyObject *key = new_unique_string(__FUNCTION__, NULL);
1565+
1566+
assert(!PyErr_Occurred());
1567+
PyObject *get_item = PyDict_GetItemWithError(container, key);
1568+
/* This is the failure point. An exception should have been set with
1569+
* an absent key but it isn't. */
1570+
assert(!PyErr_Occurred());
1571+
1572+
assert(get_item == NULL);
1573+
1574+
Py_DECREF(container);
1575+
Py_DECREF(key);
1576+
1577+
For code and tests see:
1578+
1579+
* C, in ``src/cpy/Containers/DebugContainers.c``:
1580+
* ``dbg_PyDict_GetItemWithError_fails()``
15391581

15401582
.. index::
15411583
single: Dictionary; PyDict_DelItem()
@@ -1554,6 +1596,9 @@ The C function signature is:
15541596
15551597
int PyDict_DelItem(PyObject *p, PyObject *key);
15561598
1599+
.. todo::
1600+
1601+
Complete ``PyDict_DelItem()`` with code examples.
15571602

15581603
.. index::
15591604
single: Dictionary; PyDict_Items()
@@ -1572,6 +1617,9 @@ The C function signature is:
15721617
15731618
Pyobject *PyDict_Items(PyObject *p);
15741619
1620+
.. todo::
1621+
1622+
Complete ``PyDict_Items()`` with code examples.
15751623

15761624
.. index::
15771625
single: Dictionary; PyDict_Keys()
@@ -1590,6 +1638,9 @@ The C function signature is:
15901638
15911639
Pyobject *PyDict_Keys(PyObject *p);
15921640
1641+
.. todo::
1642+
1643+
Complete ``PyDict_Keys()`` with code examples.
15931644

15941645
.. index::
15951646
single: Dictionary; PyDict_Values()
@@ -1608,6 +1659,9 @@ The C function signature is:
16081659
16091660
Pyobject *PyDict_Values(PyObject *p);
16101661
1662+
.. todo::
1663+
1664+
Complete ``PyDict_Values()`` with code examples.
16111665

16121666
.. index::
16131667
single: Dictionary; PyDict_Next()
@@ -1623,6 +1677,10 @@ The C function signature is:
16231677
16241678
int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue);
16251679
1680+
.. todo::
1681+
1682+
Complete ``PyDict_Next()`` with code examples.
1683+
16261684
.. index::
16271685
single: Dictionary; Py_BuildValue()
16281686

@@ -1633,7 +1691,6 @@ The C function signature is:
16331691
``Py_BuildValue("{OO}", key, value);`` will increment the refcount of the key and value and this can,
16341692
potentially, leak.
16351693

1636-
16371694
.. Links, mostly to the Python documentation:
16381695
16391696
.. Setters
@@ -1898,6 +1955,12 @@ Dictionaries
18981955
See :ref:`chapter_containers_and_refcounts.dictionaries.setitem.failure`.
18991956
- `PyDict_SetDefault()`_ has generally graceful behaviour on failure.
19001957
See :ref:`chapter_containers_and_refcounts.dictionaries.setdefault`.
1958+
- `PyDict_GetItemWithError()`_ is incorrectly implemented or documented.
1959+
See :ref:`chapter_containers_and_refcounts.dictionaries.pydict_getitemwitherror`.
1960+
1961+
.. todo::
1962+
1963+
Complete the summary of dictionary APIs and their documentation lacunae.
19011964

19021965
Sets
19031966
--------------

src/cpy/Containers/DebugContainers.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,48 @@ void dbg_PyDict_GetItemRef(void) {
21902190

21912191
#endif // #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13
21922192

2193+
/**
2194+
* This tests PyDict_GetItemWithError which contrary to the Python documentation
2195+
* does *not* set an exception if the key exists.
2196+
*/
2197+
void dbg_PyDict_GetItemWithError_fails(void) {
2198+
printf("%s():\n", __FUNCTION__);
2199+
if (PyErr_Occurred()) {
2200+
fprintf(stderr, "%s(): On entry PyErr_Print() %s#%d:\n", __FUNCTION__, __FILE_NAME__, __LINE__);
2201+
PyErr_Print();
2202+
return;
2203+
}
2204+
assert(!PyErr_Occurred());
2205+
Py_ssize_t ref_count;
2206+
PyObject *get_item;
2207+
2208+
PyObject *container = PyDict_New();
2209+
assert(container);
2210+
2211+
ref_count = Py_REFCNT(container);
2212+
assert(ref_count == 1);
2213+
2214+
2215+
PyObject *key = new_unique_string(__FUNCTION__, NULL);
2216+
ref_count = Py_REFCNT(key);
2217+
assert(ref_count == 1);
2218+
2219+
// No Key in the dictionary, exception set.
2220+
assert(!PyErr_Occurred());
2221+
get_item = PyDict_GetItemWithError(container, key);
2222+
assert(get_item == NULL);
2223+
/* This is the failure point. An exception should have been set with an absent key but it isn't. */
2224+
assert(!PyErr_Occurred());
2225+
2226+
Py_DECREF(container);
2227+
ref_count = Py_REFCNT(key);
2228+
assert(ref_count == 1);
2229+
2230+
Py_DECREF(key);
2231+
2232+
assert(!PyErr_Occurred());
2233+
}
2234+
21932235
#pragma mark - Dictionaries - deleters
21942236

21952237
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13

src/cpy/Containers/DebugContainers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void dbg_PyDict_GetItem(void);
7070
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13
7171
void dbg_PyDict_GetItemRef(void);
7272
#endif // #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13
73+
void dbg_PyDict_GetItemWithError_fails(void);
7374

7475
#pragma mark - Dictionaries - deleters
7576

src/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ void dbg_PyDict(void) {
193193
dbg_PyDict_GetItemRef();
194194
#endif // #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13
195195

196+
dbg_PyDict_GetItemWithError_fails();
197+
196198
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13
197199
dbg_PyDict_Pop_key_present();
198200
dbg_PyDict_Pop_key_absent();

0 commit comments

Comments
 (0)