GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — C API: Add PyDict_GetItemRef() function · Issue #106004 · python/cpython

C API: Add PyDict_GetItemRef() function #106004

New issue
Copy link
New issue
Copy link
Closed
Closed
C API: Add PyDict_GetItemRef() function#106004
Copy link
Labels
topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@vstinner
vstinner
opened on Jun 23, 2023
Issue body actions

The PyDict C API has a bad history. PyDict_GetItem() ignores all exception: error on hash(), error on "key == key2", KeyboardInterrupt, etc. PyDict_GetItemWithError() was added to fix this design. Moreover, Python 3.9 and older allowed to call PyDict_GetItem() with the GIL released.

PyDict_GetItem() returns a borrowed reference which is usually safe since the dictionary still contains a strong reference to the request value. But in general, borrowed references are error prone and can likely lead to complex race conditions causing crashes:

  • "Functions must not return borrowed references" says https://devguide.python.org/developer-workflow/c-api/index.html
  • Unclear lifetimes of borrowed references capi-workgroup/problems#5

While PyDict_GetItem() calls can be quite easily replaced with PyObject_GetItem() which has a better API (return a new stong reference), developers usually prefer to still use the specialized PyDict API for best performance: avoid the minor overhead of type dispatching, Py_TYPE(obj)->tp_as_mapping->mp_subscript.

I propose adding PyDict_GetItemRef() and PyDict_GetItemStringRef() functions to the limited C API (version 3.13): replacements for PyDict_GetItem(), PyDict_GetItemWithError() and PyDict_GetItemString().

API:

int PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **pvalue);
int PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **pvalue);

PyDict_GetItemWithError() has another API issue: when it returns NULL, it can mean two things. It returns NULL if the key is missing, but it also returns NULL on error. The caller has to check PyErr_Occurred() to distinguish the two cases (to write correct code). See capi-workgroup/problems#1 Proposed API avoids this by returning an int: return -1 on error, or return 0 otherwise (present or missing key). Checking PyErr_Occurred() is no longer needed.

By the way, the public C API has no PyDict_GetItemStringWithError() function: using PyDict_GetItemWithError() with a char* key is not convenient. The _PyDict_GetItemStringWithError() function exists but it's a private C API.

Linked PRs

  • gh-106004: Add PyDict_GetItemRef() function #106005
  • gh-106004: PyDict_GetItemRef() should only be in the limited API 3.13 #107229

Activity

  1. vstinner
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Jun 23, 2023
  2. vstinner commented on Jun 23, 2023

    @vstinner
    vstinner
    on Jun 23, 2023
    MemberAuthor
    More actions

    Example of PyDict_GetItemRef() usage of the function, replacing PyDict_GetItemWithError():

    @@ -5146,13 +5191,12 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
             return 0;
         key = PyTuple_GET_ITEM(obj, 0);
         value = PyTuple_GET_ITEM(obj, 1);
    -    found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key);
    +    if (PyDict_GetItemRef((PyObject *)dv->dv_dict, key, &found) < 0) {
    +        return -1;
    +    }
         if (found == NULL) {
    -        if (PyErr_Occurred())
    -            return -1;
             return 0;
         }
    -    Py_INCREF(found);
         result = PyObject_RichCompareBool(found, value, Py_EQ);
         Py_DECREF(found);
         return result;

    PyErr_Occurred() and Py_INCREF() calls can be removed.

    Reacted by Gregory P. Smith
  3. vstinner
    added a commit that references this issue on Jun 23, 2023

    pythongh-106004: Add PyDict_GetItemRef() function

    cdf3009
  4. vstinner commented on Jun 23, 2023

    @vstinner
    vstinner
    on Jun 23, 2023
    MemberAuthor
    More actions

    See also my previous attempt in 2020: issue #86460 "Add new C functions with more regular reference counting like PyTuple_GetItemRef()".

  5. vstinner
    added a commit that references this issue on Jun 23, 2023

    pythongh-106004: Add PyDict_GetItemRef() function

    5131071
  6. AlexWaygood
    added
    topic-C-API
    on Jun 23, 2023
  7. vstinner
    added a commit that references this issue on Jun 23, 2023

    pythongh-106004: Add PyDict_GetItemRef() function

    bc8655c
  8. gpshead commented on Jun 29, 2023

    @gpshead
    gpshead
    on Jun 29, 2023
    Member
    More actions

    I'm in favor of this because I don't think we should have public APIs that (a) require a value check + PyErr_Occurred() call pattern - a frequent source of lurking bugs - or (b) return borrowed references. Yes I know we already have them, that's missing the point. The point is that with these in place, we can promote their use over the others because these are better in all respects.

    One possible long term future would have us deprecate the messy borrowing APIs. Having these already in place sooner rather than later smooths that potential transition.

    There is no foreseeable future in which we'd go the other way towards all-borrowing-only APIs.

    Reacted by Erlend E. Aasland
  9. vstinner commented on Jun 29, 2023

    @vstinner
    vstinner
    on Jun 29, 2023
    MemberAuthor
    More actions

    There is no foreseeable future in which we'd go the other way towards all-borrowing-only APIs.

    FYI I'm tracking the list of C API functions returning borrowed references at: https://pythoncapi.readthedocs.io/bad_api.html#functions

    I'm trying to provide a replacement for each function, and if possible an efficient replacement. For example, PyDict_GetItem() can be replaced with PyObject_GetItem(), but it's less efficient (that's what I explained in my first message).

  10. vstinner
    added a commit that references this issue on Jun 29, 2023

    pythongh-106004: Add PyDict_GetItemRef() function

    3ffbae6
  11. serhiy-storchaka commented on Jul 1, 2023

    @serhiy-storchaka
    serhiy-storchaka
    on Jul 1, 2023
    Member
    More actions

    The only problem is that functions with so similar names have completely different interface. It is pretty confusing. Would not be better to name it PyDict_LookupItem or like? It may be worth to add also PyMapping_LookupItem for convenience. BTW, I think it is time to make _PyObject_LookupAttr public (if we agree about the name).

    Reacted by Petr Viktorin
  12. vstinner commented on Jul 1, 2023

    @vstinner
    vstinner
    on Jul 1, 2023
    MemberAuthor
    More actions

    I replied on the PR, since the discussion is ongoing there.

  13. vstinner
    added 4 commits that reference this issue on Jul 12, 2023

    pythongh-106004: Add PyDict_GetItemRef() function

    e9bacfa

    pythongh-106004: Add PyDict_GetItemRef() function

    a60ab95

    pythongh-106004: Add PyDict_GetItemRef() function

    01388dc

    gh-106004: Add PyDict_GetItemRef() function (#106005)

    41ca164
  14. carljm
    added a commit that references this issue on Jul 21, 2023

    Merge branch 'main' into superfix

    b9fab74
  15. vstinner
    closed this as completedon Jul 21, 2023
  16. serhiy-storchaka
    added 3 commits that reference this issue on Jul 25, 2023

    pythongh-106004: PyDict_GetItemRef() should only be in the limited AP…

    b0233b5

    pythongh-106004: PyDict_GetItemRef() should only be in the limited AP…

    4210c15

    pythongh-106004: PyDict_GetItemRef() should only be in the limited AP…

    6730eec
  17. serhiy-storchaka commented on Jul 25, 2023

    @serhiy-storchaka
    serhiy-storchaka
    on Jul 25, 2023
    Member
    More actions

    Tests will be added in #107179.

  18. serhiy-storchaka
    added a commit that references this issue on Jul 25, 2023

    gh-106004: PyDict_GetItemRef() should only be in the limited API 3.13 (…

    2b1a81e
  19. jtcave
    added a commit that references this issue on Jul 27, 2023

    pythongh-106004: PyDict_GetItemRef() should only be in the limited AP…

    af34f7d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.