Open-Source Wikis

/

CPython

/

How to contribute

/

Patterns and conventions

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 back

The 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 in tp_clear slots 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:

  1. On failure, set the exception with PyErr_Set*/PyErr_Format and return NULL (for PyObject*) or -1 (for int).
  2. 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:

  1. 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 corresponding clinic/*.c.h. See Tooling and Tools/clinic/.
  2. PyArg_ParseTuple / PyArg_ParseTupleAndKeywords — the older way; many older modules still use it directly.
  3. METH_FASTCALL — used for performance-critical methods. Receives args as a PyObject *const * array and nargs as a Py_ssize_t. New code prefers Clinic's / and * markers to express positional/keyword/flag-only args, and Clinic emits METH_FASTCALL automatically.

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 like assert(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 in tp_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(). See Python/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.py and Modules/_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 than from collections import ....
  • Avoid eager imports of large modules at startup — files in Lib/ should defer expensive imports (os, sys, re are cheap and OK; email, xml, unittest are 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.

Patterns and conventions – CPython wiki | Factory