Skip to content

Design a brand new C API with new PyCAPI_ prefix where all functions respect new guidelines #9

Closed
@vstinner

Description

@vstinner

Many functions of the existing CPython C API don't respect the new guidelines (being discussed here) like have ambiguous return value or return borrowed references. The problem is: how to write a C extension which avoid these issues? How can someone know if a C extensions only use "sane" functions? In the issue capi-workgroup/problems#54, I propose adding an opt-in macro to remove functions known to have issues in the existing API.

Here I propose something different: create a brand new C API with a new PyCAPI_ prefix where all functions respect the new guidelines. For example, PyObject* PyDict_GetItem(PyObject *key, PyObject *key) which has an ambigious return value and returns a borrowed reference will be replaced with int PyCAPI_Dict_GetItem(PyObject*, PyObject *key, PyObject **pvalue).

Goals of such new API:

  • Avoid known issues
  • Be consumed by code generators like Cython and pybind11
  • Consumed by HPy
  • Used by hand written C extensions
  • API should be easier to implement for other Python implementations
  • Smaller than the existing C API: remove functions with known issues, remove functions which are not strictly related
  • Used by CPython itself
  • Slowly replace the existing API
  • (Other goals have to be defined)

Non-goals:

  • Replace HPy: HPy goals are different (stable ABI access CPython versions, PyPy versions, universal ABI working on CPython and PyPy, etc.).
  • Perfect API: we will make new issues and regret, and we will introduce new variants of functions to fix them, one by one.
  • Stable ABI: for now, the stable ABI is only implemented with the existing limited C API
  • (Other non-goals have to be defined)

It would reduce the proliferation of "Ex", "Flags", "2", "Ref", "Object" suffixes of the C API, since we start against counting at 0 (no suffix): see issue capi-workgroup/problems#52 about naming convention for new functions.

I propose that at the beginning, this new C API will be implemented outside de CPython project. The implementation would be only header files using "alias" macros (ex: #define PyCAPI_Dict_GetItem PyDict_GetItem2) and static inline functions. An external project will ease quick experimentation.

Later, once the API is well designed and tested, move the implementation into CPython and both APIs will co-exist for the best and the worst of both world!

Such API doesn't have to be complete, since a C extension can use the old C API for functions which are not supported of the new C API yet.


To accelerate the adoption of this new C API, we should try to support old Python versions. The pythoncapi-compat project is a proof that it's doable with static inline functions. For example, PyImport_AddModuleRef() is implemented for Python 3.12 and older as:

// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1
#if PY_VERSION_HEX < 0x030D00A0
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyImport_AddModuleRef(const char *name)
{
    return Py_XNewRef(PyImport_AddModule(name));
}
#endif

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions