Skip to content

[C API] Make Py_TYPE() opaque in limited C API 3.14 #120600

Closed

Description

@vstinner

In the limited C API 3.14 and newer, I propose to change Py_TYPE() and Py_SET_TYPE() implementation to opaque function calls to hide implementation details. I made a similar change for Py_REFCNT() and Py_SET_REFCNT() in Python 3.12.

The problem is that with Free Threading (PEP 703), the implementation of these functions become less trivial than just getting/setting an object member:

static inline PyTypeObject* Py_TYPE(PyObject *ob) {
    return (PyTypeObject *)_Py_atomic_load_ptr_relaxed(&ob->ob_type);
}

static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
    _Py_atomic_store_ptr(&ob->ob_type, type);
}

_Py_atomic_load_ptr_relaxed() and _Py_atomic_store_ptr() must now be called. But I would prefer to not "leak" such implementation detail into the limited C API.

cc @colesbury @Fidget-Spinner

Linked PRs

Activity

  1. added 2 commits that reference this issue on Jun 16, 2024
  2. encukou commented on Jun 17, 2024

    @encukou
    Member

    I'd prefer if Py_TYPE was a public exported function. If it remains a macro, non-C languages will still need to access ob_type directly (or start using private API).

    As for Py_SET_TYPE, you need knowledge of the internals to use it correctly. I support changing it to call an opaque function, but unlike Py_TYPE I'd prefer not exposing any more than it is (so: keep it as a macro).
    The proper way to change a type is currently setting the __class__ attribute. We could add a new API function, like PyObject_SetType, that does the required checks and updates. And then deprecate Py_SET_TYPE.

    Please add tests for the old implementation (getting/setting ob_type directly), to ensure existing extensions aren't unintentionally broken.

  3. vstinner commented on Jun 17, 2024

    @vstinner
    MemberAuthor

    Please add tests for the old implementation (getting/setting ob_type directly), to ensure existing extensions aren't unintentionally broken.

    _testcapi has test_set_type_size() which tests Py_TYPE() and Py_SET_TYPE():

        // Ensure that following tests don't modify the object,
        // to ensure that Py_DECREF() will not crash.
        assert(Py_TYPE(obj) == &PyList_Type);
        assert(Py_SIZE(obj) == 0);
    
        // bpo-39573: Test Py_SET_TYPE() and Py_SET_SIZE() functions.
        Py_SET_TYPE(obj, &PyList_Type);
        Py_SET_SIZE(obj, 0);
  4. added a commit that references this issue on Jun 17, 2024
  5. vstinner commented on Jun 17, 2024

    @vstinner
    MemberAuthor

    I modified my PR to only reuse Py_TYPE() name (instead of adding a _Py_TYPE() function) and to leave Py_SET_TYPE() unchanged.

  6. encukou commented on Jun 17, 2024

    @encukou
    Member

    Sounds good, thanks!

    I don't think it'll solve thread safety -- Py_TYPE returns a borrowed reference, and something could invalidate that reference while you're using it. But for the use cases where Py_TYPE is already safe, it's nice to have functions rather than macros in the limited API.

  7. vstinner commented on Jun 17, 2024

    @vstinner
    MemberAuthor

    I don't think it'll solve thread safety -- Py_TYPE returns a borrowed reference

    PyObject_Type(obj) already exists to get a strong reference.

    I also wrote PEP 737 "C API to format a type fully qualified name" to avoid a common usage of Py_TYPE() borrowed reference. (The old usage can lead to a crash, see the PEP!)

  8. vstinner commented on Jun 17, 2024

    @vstinner
    MemberAuthor
  9. added a commit that references this issue on Jun 18, 2024
  10. added a commit that references this issue on Jun 19, 2024
  11. nineteendo commented on Jun 19, 2024

    @nineteendo
    Contributor

    Is there something left to do here?

  12. vstinner commented on Jun 19, 2024

    @vstinner
    MemberAuthor

    Fixed by change 16f8e22.

  13. encukou commented on Jun 20, 2024

    @encukou
    Member

    There's still Py_SET_TYPE, which IMO could use a private opaque function.
    Should that be done with a new issue?

  14. vstinner commented on Jun 20, 2024

    @vstinner
    MemberAuthor

    There's still Py_SET_TYPE, which IMO could use a private opaque function. Should that be done with a new issue?

    I suggest to open a new issue. If I understood correctly, Py_SET_TYPE() should not exist in the limited C API, since static types cannot be defined with the limited C API.

  15. added a commit that references this issue on Jun 30, 2024
  16. added a commit that references this issue on Jul 11, 2024
  17. added a commit that references this issue on Jul 17, 2024
  18. vstinner commented on Sep 16, 2024

    @vstinner
    MemberAuthor

    I created an issue for Py_REFCNT(): #124127

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions