python/cpython
Patterns and conventions
CPython is a 35-year-old C codebase, so it has some of the strongest "house style" in any open-source project. This page collects the patterns you will see again and again. They are enforced by code review more than by tooling.
Coding style
| Surface | Style |
|---|---|
| C public API | PEP 7. 4-space indent, K&R braces, func(void) for no-arg fns. |
| Python | PEP 8. Enforced by ruff. |
| Public Python identifiers | snake_case for functions/methods, CapWords for classes. The stdlib is the canonical example. |
| Private C identifiers | _Py prefix for internal-but-cross-file, _PyName_FunctionName style. |
| Public C identifiers | Py prefix (PyObject_Call, PyDict_New, …). |
| Internal-only headers | Live under Include/internal/, guarded by #define Py_BUILD_CORE 1. |
A .editorconfig at the root of the tree sets indent and line-ending defaults; a .pre-commit-config.yaml wires up ruff, clang-format (limited use), and other checks. Run pre-commit run -a once you have it installed.
Reference counting
The single most important pattern in the codebase. Every C function that returns a PyObject* either returns a new reference (you must Py_DECREF it when done) or returns a borrowed reference (you must not). The convention is documented per-API at https://docs.python.org/3/c-api/intro.html#reference-counts.
The macros live in Include/refcount.h:
PyObject *o = PyTuple_New(2); // new ref
if (o == NULL) goto error;
Py_INCREF(item);
PyTuple_SET_ITEM(o, 0, item); // steals the ref
...
Py_DECREF(o); // give it backThe error-path discipline is the source of most refcount bugs:
PyObject *a = NULL, *b = NULL;
a = PyDict_New();
if (a == NULL) goto error;
b = PyList_New(0);
if (b == NULL) goto error;
... use a, b ...
Py_DECREF(a);
Py_DECREF(b);
return result;
error:
Py_XDECREF(a); // X = NULL-tolerant
Py_XDECREF(b);
return NULL;Two helpers worth knowing:
Py_CLEAR(p)—Py_XDECREF(p); p = NULL;in the right order. Use this intp_clearslots and on object teardown.Py_NewRef(o)—Py_INCREF(o); return o;as a single expression. Common in getters.
In the free-threaded build refcounts use atomic ops; the macros DTRT on either build.
Error handling: "raise then return NULL"
A C function that can fail follows the protocol:
- On failure, set the exception with
PyErr_Set*/PyErr_Formatand returnNULL(forPyObject*) or-1(forint). - On success, return the result and leave
PyErr_Occurred()clear.
The compiler-extension macro Py_RETURN_NONE returns a new ref to Py_None; Py_RETURN_TRUE and Py_RETURN_FALSE return new refs to the singletons. Don't return a borrowed reference to Py_None — this used to be allowed and was a fertile source of crashes.
Argument parsing
Three layers exist; you will see all of them:
- Argument Clinic — the new way. You write a
[clinic input]block at the top of a C function and the parsing wrapper is generated under the correspondingclinic/*.c.h. See Tooling andTools/clinic/. PyArg_ParseTuple/PyArg_ParseTupleAndKeywords— the older way; many older modules still use it directly.METH_FASTCALL— used for performance-critical methods. Receivesargsas aPyObject *const *array andnargsas aPy_ssize_t. New code prefers Clinic's/and*markers to express positional/keyword/flag-only args, and Clinic emitsMETH_FASTCALLautomatically.
When in doubt, copy the structure from a recently-touched stdlib module like Modules/_zstd/ or Modules/_queuemodule.c.
Type definitions and slots
A new built-in type is a static PyTypeObject in C, but the modern way is to register the type with PyType_FromModuleAndSpec and a PyType_Spec (heap types). Per-module heap types respect subinterpreter isolation; static types do not. New stdlib modules must use heap types — see for example Modules/_queuemodule.c.
Common slots:
| Slot | Purpose |
|---|---|
tp_dealloc |
Destructor. Py_XDECREF every owned reference, then tp_free. |
tp_traverse |
Visit every owned PyObject* (used by GC). |
tp_clear |
Drop every reference participating in cycles. Called by GC. |
tp_richcompare |
Implements <, <=, ==, !=, >, >=. |
tp_iter / tp_iternext |
Iterator protocol. |
tp_call |
Make instances callable. |
tp_descr_get / tp_descr_set |
Descriptor protocol (where properties live). |
Module init
Single-phase init (PyInit_modulename) returning a PyObject* is the historical pattern. New modules use multi-phase init with PyModuleDef_Slots and per-module state — see PEP 489 and Modules/_testmultiphase.c. Multi-phase init is required for clean subinterpreter and free-threading support.
Configuration types: PyConfig
Embedders set up the runtime through PyConfig and PyPreConfig (Include/cpython/initconfig.h). The implementation lives in Python/initconfig.c and Python/preconfig.c. Adding a new command-line flag means: add a field to PyConfig, parse it in initconfig.c, document it in Doc/using/cmdline.rst.
Internal vs public API
Three header surfaces:
| Directory | Audience |
|---|---|
Include/ |
Stable, public C API. Py_LIMITED_API macro can hide post-3.x additions. |
Include/cpython/ |
Public CPython-specific extensions (not part of the limited API). |
Include/internal/ |
Interpreter-internal. Only includable when Py_BUILD_CORE is set. |
Anything that touches _PyRuntime, _PyInterpreterState, or named opcodes is internal and lives under Include/internal/. Public API additions are constrained — they are guaranteed forward and ABI-compatible across many releases.
Common idioms in C
Py_UNREACHABLE()— used likeassert(0)for "the type system says this can't happen". Aborts in debug;__builtin_unreachable()in release.PyTuple_Pack(n, a, b, c, ...)— micro-helper for building short tuples.Py_VISIT(p)— used intp_traverse; the GC visit callback.- Critical sections — In free-threaded builds, atomic mutation of an object happens inside
Py_BEGIN_CRITICAL_SECTION(o) ... Py_END_CRITICAL_SECTION(). SeePython/critical_section.c.
Common idioms in Python (in the stdlib)
- No third-party imports. The standard library only depends on the standard library and on extension modules in
Modules/. - Pure-Python and C side parity. Many modules ship both
Lib/foo.pyandModules/_foomodule.c; the Python side is the spec, the C side accelerates it. Always update both. __all__is mandatory for top-level modules.from collections.abc import ...rather thanfrom collections import ....- Avoid eager imports of large modules at startup — files in
Lib/should defer expensive imports (os,sys,reare cheap and OK;email,xml,unittestare not).
Documentation patterns
- Sphinx in
Doc/using reStructuredText. - Cross-references with
:mod:,:func:,:class:,:c:func:,:c:type:. - Every public addition needs an entry in
Doc/whatsnew/3.X.rst. - Deprecations get an entry under
Doc/deprecations/. - Don't write "the X module" in docs; the directive expansion already says it.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.