Skip to main content

clinic/: Argument Clinic Generated Stubs

Python/clinic/ holds the C stubs that the Tools/clinic/clinic.py preprocessor generates from DSL blocks embedded in CPython source files. Each .c.h file is a 1-to-1 companion for a module file. The DSL block lives between /*[clinic input] and /*[clinic start generated code]*/ markers; the output block follows immediately.

Map

File patternSymbol patternRole
*.c.h (all modules)<module>_<func>_implThe hand-written implementation function; the generated wrapper calls it after parsing args
*.c.h<module>_<func>The public PyObject * wrapper with the PyMethodDef-compatible signature
*.c.h<module>_<func>_methoddefStatic PyMethodDef entry; flags include METH_FASTCALL, METH_KEYWORDS, or METH_O depending on the DSL signature
builtinmodule.c.hbuiltin_*Stubs for len, isinstance, print, repr, and the other ~30 builtins
abstract.c.h_PyObject_*Internal abstract-object helpers exposed through the clinic machinery
typeobject.c.hobject_*object.__init__, object.__init_subclass__, object.__class_getitem__
_io/*.c.h_io_*io.FileIO, io.BufferedReader, and related classes

Reading

DSL format and type codes

A typical clinic block looks like this (from Modules/_bisectmodule.c):

/*[clinic input]
bisect.bisect_left

a: object
x: object
lo: Py_ssize_t = 0
hi: object = None
*
key: object = None

[clinic start generated code]*/

The type codes map directly to argument parsing:

DSL typeGenerated parser callC type
objectraw PyObject *, no conversionPyObject *
int_PyLong_AsIntint
Py_ssize_tPyLong_AsSsize_tPy_ssize_t
strPyUnicode_FSConverterconst char *
boolPyObject_IsTrueint (0/1)
defining_classinjects PyTypeObject * as first extra argPyTypeObject *

impl_prefix naming and _PyArg_CheckPositional

The generated wrapper calls _PyArg_CheckPositional before dispatching to the _impl function. For a METH_FASTCALL signature:

/* Python/clinic/builtinmodule.c.h:47 builtin_len */
static PyObject *
builtin_len(PyObject *module, PyObject *arg)
/*[clinic end generated code: output=... input=...]*/
{
return builtin_len_impl(module, arg);
}

For variadic keyword signatures the generator emits a _PyArg_ParseStackAndKeywords call guarded by a nargs check:

/* Python/clinic/_bisectmodule.c.h:12 bisect_bisect_left */
static PyObject *
bisect_bisect_left(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObject *kwnames)
{
...
if (!_PyArg_CheckPositional("bisect_left", nargs, 2, 4)) {
goto exit;
}
...
return_value = bisect_bisect_left_impl(module, a, x, lo, hi, key);
exit:
return return_value;
}

3.14 changes

3.14 introduced defining_class as a first-class DSL type for heap-type methods. The generated wrapper now receives PyTypeObject *cls and passes it through so the impl can call PyType_GetSlot without going through the instance. The methoddef entry gains METH_METHOD | METH_FASTCALL | METH_KEYWORDS flags for these stubs.

/* Python/clinic/typeobject.c.h:1 object___init_subclass___impl */
static PyObject *
object___init_subclass__(PyObject *self, PyTypeObject *cls,
PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)

gopy notes

  • gopy does not run clinic.py; Go function signatures are written by hand following the same _impl convention to keep diffs readable against CPython.
  • The METH_FASTCALL | METH_KEYWORDS pattern maps to Go functions receiving (args []Object, kwargs StringDict).
  • defining_class injection is not yet supported. Methods that need the type pointer receive it via the receiver type's Type() method instead.
  • Argument validation equivalent to _PyArg_CheckPositional lives in vm/eval_call.go.