Description
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