PEP: 9999 Title: Guidelines for Python's C API Author: Petr Viktorin <encukou@gmail.com>,
Guido van Rossum <guido@python.org>, Victor Stinner <vstinner@python.org>, Steve Dower <steve.dower@python.org>, Erlend Egeberg Aasland <erlend@python.org>, Serhiy Storchaka, Michael Droettboom,
Discussions-To: https://discuss.python.org/c/c-api/30 Status: Draft Type: Process Requires: 731 Created: 20-Nov-2023 Post-History: XXX
.. pep-banner:: This is a **incomplete draft**, published for initial discussion. It is likely to be amended substantially.
TODO: Write the abstract last :)
These guidelines represent the consensus of the C API Working Group, established in PEP 731 to oversee and coordinate the development and maintenance of the CPython C API.
It is a living document. It can be changed with the approval of either all members of the C API Working Group, or the Steering Council.
Our goal is that the C API is:
- Useful: It should provide access to all relevant functionality.
- Ergonomic: It should be easy to use.
- Fair: It should serve the purposes of a variety of stakeholders.
- Stable: It should avoid requiring users to update their code more than necessary.
- Evolvable: It should give Python implementers enough freedom to change implementation details.
- Maintainable: It should be easy to maintain and extend.
We do not expect you, fellow CPython contributor, to read and remember all of the guidelines. If you have any doubts, or if you find that the guidelines are unclear, incomplete or contradictory, ask the C API Working Group for help and clarifications.
In several cases, you should seek approval from the C API Working Group before changing public C API. Most notably:
- when the guidelines tell you to do so,
- when adding an exception to the rules,
- when adding to the limited API,
- when adding something that is likely to establish precedent,
- when adding many APIs at once, or
- when the guidelines are unclear or don't make sense.
Approvals allow the working group to coordinate designs from multiple people, and to collect feedback needed to refine these guidelines.
Request an approval by opening an issue in the decisions repository.
Where possible, exceptions to these guidelines should be added as alternatives to API that follows the guidelines. Such exceptions should be clearly identifiable from their names.
If you want to add an exception that is not mentioned in the guidelines, please request approval from the C API Working Group.
These guidelines only apply to API that is:
- Newly added: existing API does not necessarily follow these guidelines; this PEP is not the place for plans to replace, deprecate or remove it.
- Public: meant for users; that is, not in the private or internal tiers as defined in the next section.
Note that we have a style guide, PEP 7, which applies to all C code in CPython, including the API.
CPython has several tiers of C API. The more stable the tier is, the stricter the rules for it are:
- For internal API (only accessible with
Py_BUILD_CORE
, or not declared in a public header at all), you are free to ignore these guidelines entirely. - Private API (not intended for users) should be internal if possible.
Otherwise it must use the
_Py
prefix (see :ref:`naming`). The rest of these guidelines don't apply to it. - In unstable API (prefixed with
PyUnstable_
), it is OK to ignore these guidelines if there is a reason to do so -- usually for performance. Please document the reason in a source comment. - In the general public API, these guidelines apply in full force. The C API working group can grant exceptions.
- For the limited API, always seek explicit approval from the C API Working Group.
All public API should be available after including :file:`Python.h`.
To allow selecting an alternate API, such as a subset,
use feature flags/macros that users define before including the header
(for example Py_LIMITED_API
).
Adding such a feature macro needs approval from the C API Working Group.
[TODO: PEP 7 should be updated to link here once this PEP goes live.]
All newly added public names must be prefixed with Py
.
Names that users should not use directly, but need to be visible to the
compiler/linker, should be prefixed with _Py
.
(Such names are not considered public API, that is, they should not appear in
third-party source code.)
This applies to all names in a global namespace: functions, macros, variables, typedefs, structs, enums, etc.; not to parameters or struct fields.
The Py_
prefix is reserved for global service routines like
Py_FatalError
; specific groups of APIs use a longer prefix,
for example PyUnicode_
for string functions.
Use an existing prefix when applicable. If you want to add a new prefix,
contact the C API Working Group.
The Py
prefix is in mixed case, even in macro names.
For example: PyUnicode_AS_STRING
.
(Several existing macros use the upper-case PY
; if you need this prefix
for consistency, please get approval from the C API Working Group.)
Unstable API is prefixed with PyUnstable_
instead of Py
,
for example PyUnstable_Long_IsCompact
or (hypothetically)
PyUnstable_String_GET_SIZE
.
If an API's interface or behavior changes in a backwards-incompatible way, add new API with a new name. You can deprecate and remove the old version following Python's :pep:`backwards compatibility policy <387>`.
After API has been removed, do not reuse the old name, since existing documentation and tutorials will continue to refer to the old behavior.
[TODO: PEP 7 should be updated to link here once this PEP goes live.]
Public C API must be compatible with:
- C11, with optional features needed by CPython:
- IEEE 754 floating point
- Atomics (
!__STDC_NO_ATOMICS__
, or MSVC)
- C99
- C89 with several select C99 features:
<stdint.h>
and<inttypes.h>
static inline
functions- designated initializers
- intermingled declarations
- line comments (
//
)
- C++03
It is OK to use other features -- compiler-specific ones, optional standard ones, or platform-specific ones -- if:
- the behavior of correct user code is the same as with a standard compiler,
- the feature is detected using appropriate preprocessor checks, and
- their use does not produce warnings on any supported compiler, including earlier versions of the one it is specific to.
For example, compiler-specific code is often used to improve performance or compiler diagnostics.
It is also OK to use these other features for platform-specific API,
which needs to be documented as such and have a feature test macro
(for example, Py_HAVE_C_COMPLEX
).
Note
The existing API uses a few C11 features which are commonly available as compiler extensions to C99. In particular, we do use an unnamed union. New API should not use these features.
All function declarations and definitions must use full prototypes, that is, the types of all arguments must be specified.
While the C API is defined in terms of the C language, it supports building
wrappers for languages other than C, such as Rust, Java, assembly, or
Python with ctypes
.
These wrappers cannot realisically use a full-featured C parser.
To make the public API easier to describe and wrap, it should
avoid some of C's features:
- Avoid
static inline
functions and macros. All functions must be exported as actual library symbols. - Avoid variadic functions.
- Avoid C-specific types like
long long
,enum
or bit fields (see :ref:`types`).
Once you add API that conforms to this “portable subset”, you can add additional C/C++-specific API. Usually, the additional API will be either more performant, or easier to use from C. For example:
- A function may be shadowed by a
static inline
function or macro with the same behavior. Typically, this allows better performance for C/C++. See shadowing example. - A function that uses C-specific types, such as
PyLong_AsLongLong
, is OK if equivalent functions are provided for :ref:`the preferred types <types>`. - A variadic function is OK if there's a non-variadic function with equivalent functionality. Usually, the equivalent take an array or pre-constructed Python object: for example, PyObject_Vectorcall is the equivalent for PyObject_CallFunction; PySys_AuditTuple for PySys_Audit; PyErr_SetObject for PyErr_Format.
Macros can be used in the following cases:
- Feature flags (e.g.
HAVE_FORK
,Py_LIMITED_API
) - Simple constants (e.g.
Py_TPFLAGS_BASETYPE
,PY_VERSION_HEX
). - Shortcuts for functionality that can be accomplished trivially,
but perhaps tediously, without macros (e.g.
Py_VISIT
,Py_BEGIN_ALLOW_THREADS
,Py_RETURN_RICHCOMPARE
). In this case, the macro-less equivalent should be clear from documentation: consider adding the macro's expansion to the docs. Non-C wrappers are expected re-implement these macros. - Features that aren't needed in non-C languages (e.g.
Py_MAX
,Py_STRINGIFY
). - Macros used to define the API (e.g.
PyAPI_FUNC
,Py_ALWAYS_INLINE
,Py_OBJECT_H
). - As an implementation detail (for example, when shadowing a function).
As always, new exceptions can be added with approval from the C API working group.
Note
For background and discussions, see:
Avoid types with compiler-/platform-specific sizes, such as long
or unsigned short
.
Instead, use:
int32_t
and other C99+ fixed width integer typesPy_ssize_t
,intptr_t
,ptrdiff_t
for values of the appropriate platform-specific typesdouble
(IEEE 754binary64
)
As an exception, use int
for small ranges (typically, as a replacement
for enum).
If the 16-bit limit is relevant, and for all unsigned values, prefer
explicit fixed width types over int
.
As another exception, use char*
for UTF-8/ASCII strings (and char
for bytes of such text). For byte-strings, prefer uint8_t*
or void*
.
For memory sizes and byte counts, use the signed Py_ssize_t
,
not the unsigned size_t
.
Avoid enum
, which have compiler-/platform-dependent size.
(CPython cannot yet use C23's fixed underlying enum types.)
Instead, use int
with defined constants.
Avoid bitfields, which have compiler-/platform-dependent memory layout. Instead, use fixed width integer types and bitmask constants.
To be clear: it is fine to use enum
and bitfields within CPython sources,
provided they do not need to be used in public headers.
Use PyObject*
for all Python objects.
Avoid using concrete types (e.g. PyDictObject*
).
Public API should type-check all objects passed to it.
When it gets an object of an unexpected type, public API should fail with
TypeError
rather than crash.
As an exception, with approval from the C API Working Group you can use concrete types,
such as PyTypeObject*
, PyCodeObject*
& PyFrameObject*
,
for consistency with existing API.
These objects should be type-checked as if they were PyObject*
.
The return value of a function must indicate whether an exception was set.
It must not be necessary to use PyErr_Occurred
to disambiguate.
(Recall that these guidelines apply to new API; existing API does not
necessarily follow this.)
Generally, API functions can return one of:
- An integral value, where
-1
is returned if and only if an exception was set, and other values signal an absence of exception. - A pointer, where
NULL
is returned if and only if an exception was set. - A few special cases:
- Functions that never return, or always set an exception, should use the :c:expr:`void` return type.
- Functions used when the runtime might not be initialized
should either:
- return
PyStatus
, or - return
-1
/NULL
to signal failure, but have an alternate way of reporting error details.
- return
In cases where -1
or NULL
is a valid result, use an
:ref:`output argument <output argument>` to provide that result.
See return schemes for concrete examples.
Some functions cannot fail, and callers cannot check for exceptions:
- Deallocators and reference sinks like
PyMem_Free
andPy_DECREF
, which usevoid
as the return type.
Other functions cannot fail, and users may optionally skip error checking:
- Refcounting operations, like
Py_NewRef
. - Operations on native types that cannot have exceptional cases
(e.g. overflow), like
Py_HashPointer
. - Subtype-checking functions, like
PyTuple_Check
andPyTuple_CheckExact
, which return either0
or1
. (This does not extend to subclass checking, likePyObject_IsSubclass
, which can call Python code.)
Even in these cases, -1
and NULL
are reserved for errors;
infallible functions must never return these values.
(This allows auto-generated API wrappers to avoid unnecessary special cases.)
As always, other exceptions can be added here with approval from the C API working group.
Mind that infallibility is very often an implementation detail that should not be exposed in the API. That is, some functions' current CPython implementations cannot fail, but we may want the function to fail (or warn) in the future. Users should only skip error checking if the function's documentation explicitly allows it.
Output arguments are pointers to memory that a function fills in. Use these when a result from a function cannot be returned as the return value, for example:
-1
orNULL
is a valid (non-exceptional) result: for example, inPyDict_GetItemRef
.- There are multiple results: for example, in
PyUnicode_AsUTF8AndSize
.
Guidelines for output arguments:
- Functions must always fill in the output arguments. If an error
occurs or the result is not available, the output should typically be set
to
NULL
or zero. - When it might be useful for users to call a function but ignore an output,
allow passing
NULL
as the output argument. - Ownership of a
PyObject*
result is transferred to the caller, as with return values. [TODO: Link to guidelines about ownership & borrowing, when those are added]
Since existing API does not necessarily follow these guidelines, all of the above points should be explicitly mentioned in the documentation of each function they're relevant to.
To provide a static inline
equivalent to an exported function,
write something like:
Header:
static inline returntype
_Py_Foo_impl(ARGS)
{
...
}
PyAPI_FUNC(returntype) Py_Foo (ARGS);
#define Py_Foo _Py_Foo_impl
Code:
// at the end (after all calls to Py_Foo):
#undef Py_Foo
returntype
Py_Foo(ARGS)
{
return _Py_Foo_impl(ARGS);
}
Here are common schemes of how to encode return values.
- Success or failure: return
int
0
for success-1
, with an exception set, for failure
- Yes or no: return
int
1
fortrue
0
forfalse
-1
, with an exception set, for failure
- Lookup (“getattr”, “getitem” or “setdefault” style) functions: return
int
; the lookup result is passed via an :ref:`output argument <output argument>`):1
for “found” (result is set)0
for “not found” (result is set toNULL
or other zero/empty value)-1
, with an exception set, for failure (result is set toNULL
or other zero/empty value)
- Enumeration-style: return
int
-1
, with an exception set, for failure- Zero and positive numbers for valid results
- Hashes: return
Py_hash_t
-1
, with an exception set, for failure- All other numbers for result.
- Objects: return
PyObject*
NULL
, with an exception set, for failure- Valid pointer for result