[issue42152] Use PyDict_Contains() and PyDict_SetDefault() instead of PyDict_GetItemWithError()

Serhiy Storchaka report at bugs.python.org
Mon Oct 26 03:39:38 EDT 2020


New submission from Serhiy Storchaka <storchaka+cpython at gmail.com>:

It was common to use the code PyDict_GetItem(dict, key) == NULL to check whether the key is in the dict. PyDict_GetItem() returns borrowed reference, so no clean up is needed. And if we need to check only existence of the key, we do not need to store a value.

But PyDict_GetItem() which suppresses all internal exceptions is considered not safe, so PyDict_GetItemWithError() should be used. The code looks like:

    if (PyDict_GetItemWithError(dict, key) == NULL) {
        if (PyErr_Occurred()) {
            goto error;
        }
        // does not have the key
    }
    else {
        // has the key
    }

It can be written with using PyDict_Contains():

    int r = PyDict_Contains(dict, key);
    if (r < 0) {
        goto error;
    }
    if (r == 0) {
        // does not have the key
    }
    else {
        // has the key
    }

Advantages:

* It is more clear expression of what we do.
* It is more simple and efficient in PyPy, because it has to keep borrowed references and track their lifetime.
* It may be more efficient in CPython, because calling PyErr_Occurred() has small but non-zero cost.
* PyErr_Occurred() would not be fooled by exception raised before. So you can use this code even if an exception is set.

Disadvantages:

* You need to use an int variable.

In some cases, when this check is used in combinations with PyDict_SetItem(), the code can be replaced with PyDict_SetDefault(), which is bot more terse and efficient. And when it is used in combinations with PyDict_DelItem(), the code can be replaced with _PyDict_Pop().

The proposed patch makes the code using PyDict_Contains() and PyDict_SetDefault() if appropriate. All use cases for _PyDict_Pop() were already covered by previous changes.

----------
components: +Interpreter Core
nosy: +methane, vstinner
title: Use PyDict_Contains() and PyDict_SetDefault() instead of PyDict_GetItemIdWithError -> Use PyDict_Contains() and PyDict_SetDefault() instead of PyDict_GetItemWithError()
type:  -> enhancement
versions: +Python 3.10

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue42152>
_______________________________________


More information about the Python-bugs-list mailing list