Howto Clinic
Howto Clinic
3.11.4
24, 2023
Python Software Foundation
Email: [email protected]
Contents
1 Argument Clinic 2
2 2
3 3
4 8
4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Argument Clinic C . . . . . . . . . . . . . . . . . . . . . . . 8
4.3 PyArg_UnpackTuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.5 Argument Clinic . . . . . . . . . . . . . . . . . . 10
4.6 Py_buffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.9 NULL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.13 Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.14 self . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.17 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.18 METH_O METH_NOARGS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.19 tp_new tp_init functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.20 Clinic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.21 #ifdef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.22 Python Argument Clinic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
23
Larry Hastings
1
Argument Clinic CPython C
C Argument Clinic Argument Clinic
1 Argument Clinic
Argument Clinic C
/*[clinic input]
2
/*[clinic input]
... clinic input goes here ...
[clinic start generated code]*/
... clinic output goes here ...
/*[clinic end generated code: checksum=...]*/
Argument Clinic
Argument Clinic
Argument Clinic
Argument Clinic
• /*[clinic input]
• [clinic start generated code]*/
• /*[clinic end generated code: checksum=...]*/
•
•
• Argument Clinic
Argument Clinic
CPython
0. CPython
1. PyArg_ParseTuple() PyArg_ParseTupleAndKeywords()
Argument Clinic Python _pickle.Pickler.dump()
2. PyArg_Parse
O&
O!
es
es#
et
et#
PyArg_ParseTuple() PyArg_ParseTupleAndKeywords()
PyArg_Parse
Argument Clinic Argument Clinic
3.
/*[clinic input]
[clinic start generated code]*/
3
4. [clinic] C
80 Clinic
(
help()
/*[clinic input]
Write a pickled representation of obj to the open file.
[clinic start generated code]*/
5. Argument Clinic
80
6. Python
/*[clinic input]
_pickle.Pickler.dump
C
PyTypeObject
/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/
/*[clinic input]
_pickle.Pickler.dump
8.
name_of_parameter: converter
Argument Clinic
4
What’s a ”converter”? It establishes both the type of the variable used in C, and the method to convert the
Python value into a C value at runtime. For now you’re going to use what’s called a ”legacy converter” a
convenience syntax intended to make porting old code into Argument Clinic easier.
“PyArg_Parse()“
format 1-3
arg-parsing
z# 2-3
/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/
/*[clinic input]
_pickle.Pickler.dump
obj: 'O'
9. | Argument Clinic
$
*
_pickle.Pickler.dump
10. C PyArg_ParseTuple() ( PyArg_ParseTupleAndKeywords())
Argument Clinic /
Argument Clinic
/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/
/*[clinic input]
_pickle.Pickler.dump
obj: 'O'
/
11.
5
/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/
/*[clinic input]
_pickle.Pickler.dump
obj: 'O'
The object to be pickled.
/
12. Tools/clinic/clinic.py
.c.h
/*[clinic input]
_pickle.Pickler.dump
obj: 'O'
The object to be pickled.
/
static PyObject *
_pickle_Pickler_dump(PicklerObject *self, PyObject *obj)
/*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/
Argument Clinic
Argument Clinic
.c.h .c
clinic
#include "clinic/_pickle.c.h"
Argument Clinic : ;
PyMethodDef
#define __PICKLE_PICKLER_DUMP_METHODDEF \
{"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__
,→doc__},
PyMethodDef
Argument Clinic Tools/clinic/
clinic.py
6
14.
Python
static return_type
your_function_impl(...)
/*[clinic end generated code: checksum=...]*/
{
...
Argument Clinic
/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/
/*[clinic end generated code:␣
,→checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic input]
_pickle.Pickler.dump
obj: 'O'
The object to be pickled.
/
PyDoc_STRVAR(__pickle_Pickler_dump__doc__,
"Write a pickled representation of obj to the open file.\n"
"\n"
...
static PyObject *
_pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
/*[clinic end generated code:␣
,→checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
{
/* Check whether the Pickler was initialized correctly (issue3664).
Developers often forget to call __init__() in their subclasses, which
would trigger a segfault without this check. */
if (self->write == NULL) {
PyErr_Format(PicklingError,
"Pickler.__init__() was not called by %s.__init__()",
Py_TYPE(self)->tp_name);
return NULL;
}
if (_Pickler_ClearBuffer(self) < 0)
return NULL;
...
)
PyMethodDef
7
static struct PyMethodDef Pickler_methods[] = {
__PICKLE_PICKLER_DUMP_METHODDEF
__PICKLE_PICKLER_CLEAR_MEMO_METHODDEF
{NULL, NULL} /* sentinel */
};
16. Compile, then run the relevant portions of the regression-test suite. This change should not introduce any new
compile-time warnings or errors, and there should be no externally visible change to Python’s behavior.
inspect.signature()
Argument Clinic
Argument Clinic
4.1
•
•
• True False None
• sys.maxsize
CONSTANT - 1
Argument Clinic C
C "as"
Argument Clinic "_impl"
pickle.Pickler.dump C
/*[clinic input]
pickle.Pickler.dump as pickler_dumper
...
pickler_dumper() “pickler_dumper_impl()“
Python C Argument Clinic
Python C "as"
/*[clinic input]
pickle.Pickler.dump
obj: object
file as file_obj: object
protocol: object = NULL
( )
8
( )
*
fix_imports: bool = True
4.3 PyArg_UnpackTuple
PyArg_UnpackTuple()
object type
/
PyArg_ParseTuple()
4.4
switch
PyArg_ParseTuple()
PyArg_ParseTupleAndKeywords()
PyArg_ParseTupleAndKeywords()
PyArg_ParseTupleAndKeywords()
range()
curses.window.addch() x y
x “y“ x y
Argument Clinic CPython
Argument Clinic
: PyArg_ParseTuple()
Argument Clinic Python
Python
[ “]“
curses.window.addch
/*[clinic input]
curses.window.addch
[
x: int
X-coordinate.
y: int
Y-coordinate.
]
ch: object
Character to add.
[
attr: long
Attributes for the character.
]
/
( )
9
( )
...
• int
group_{direction}_{number} {direction} right
left {number} 1
•
•
•
•
Argument Clinic
• PyArg_ParseTuple()
None NoneType
bitwise Python
converter object C
10
type object self C "PyObject
*"
zeroes True NUL '\\0'
<parameter_name>_length
PyArg_ParseTuple
bitwise=True unsigned_short
Argument Clinic
Argument Clinic
'B' unsigned_char(bitwise=True)
'b' unsigned_char
'c' char
'C' int(accept={str})
'd' double
'D' Py_complex
'es' str(encoding='name_of_encoding')
'es#' str(encoding='name_of_encoding', zeroes=True)
'et' str(encoding='name_of_encoding', accept={bytes, bytearray, str})
'et#' str(encoding='name_of_encoding', accept={bytes, bytearray, str}, zeroes=True)
'f' float
'h' short
'H' unsigned_short(bitwise=True)
'i' int
'I' unsigned_int(bitwise=True)
'k' unsigned_long(bitwise=True)
'K' unsigned_long_long(bitwise=True)
'l' long
'L' long long
'n' Py_ssize_t
'O' object
'O!' object(subclass_of='&PySomething_Type')
'O&' object(converter='name_of_c_function')
'p' bool
'S' PyBytesObject
's' str
's#' str(zeroes=True)
's*' Py_buffer(accept={buffer, str})
'U' unicode
'u' Py_UNICODE
'u#' Py_UNICODE(zeroes=True)
'w*' Py_buffer(accept={rwbuffer})
'Y' PyByteArrayObject
'y' str(accept={bytes})
'y#' str(accept={robuffer}, zeroes=True)
'y*' Py_buffer
'Z' Py_UNICODE(accept={str, NoneType})
'Z#' Py_UNICODE(accept={str, NoneType}, zeroes=True)
'z' str(accept={str, NoneType})
'z#' str(accept={str, NoneType}, zeroes=True)
'z*' Py_buffer(accept={buffer, str, NoneType})
pickle.Pickler.dump
11
/*[clinic input]
pickle.Pickler.dump
obj: object
The object to be pickled.
/
unsigned_int
unsigned_ bitwise=True
Argument Clinic
Tools/clinic/clinic.py --converters
4.6 Py_buffer
4.7
4.8
int float
Python
NULL
12
4.9 NULL
None C
Py_None “NULL“ Python None
C NULL
4.10
max_widgets
sys.modules sys.maxsize
Python
Argument Clinic C
“c_default“ C
Argument Clinic
C Python
•
• if 3 if foo else 5
• *[1, 2, 3]
•
•
4.11
By default, the impl function Argument Clinic generates for you returns PyObject *. But your C function often
computes some C type, then converts it into the PyObject * at the last moment. Argument Clinic handles con-
verting your inputs from Python types into native C types why not have it convert your return value from a native
C type into a Python type too?
That’s what a ”return converter” does. It changes your impl function to return some C type, then adds code to the
generated (non-impl) function to handle converting that value into the appropriate PyObject *.
The syntax for return converters is similar to that of parameter converters. You specify the return converter like it
was a return annotation on the function itself, using -> notation.
For example:
13
/*[clinic input]
add -> int
a: int
b: int
/
Return converters behave much the same as parameter converters; they take arguments, the arguments are all
keyword-only, and if you’re not changing any of the default arguments you can omit the parentheses.
"as" "as"
Argument Clinic
bool
double
float
int
long
Py_ssize_t
size_t
unsigned int
unsigned long
None of these take parameters. For all of these, return -1 to indicate error.
NoneType Py_None NULL
Py_None
Tools/clinic/clinic.py --converters Argument Clinic
4.12
Clinic
•
–
–
–
–
–
•
/*[clinic input]
module.class.new_function [as c_basename] = module.class.existing_function
( )
14
( )
Docstring for new_function goes here.
[clinic start generated code]*/
module.class
Sorry, there’s no syntax for partially cloning a function, or cloning a function then modifying it. Cloning is an all-or
nothing proposition.
4.13 Python
/*[python input]
# python code goes here
[python start generated code]*/
Python stdout
Python C
/*[python input]
print('static int __ignored_unused_variable__ = 0;')
[python start generated code]*/
static int __ignored_unused_variable__ = 0;
/*[python checksum:...]*/
4.14 self
/*[clinic input]
_pickle.Pickler.dump
self self_converter
type
15
/*[python input]
class PicklerObject_converter(self_converter):
type = "PicklerObject *"
[python start generated code]*/
/*[clinic input]
_pickle.Pickler.dump
self: PicklerObject
obj: object
/
4.15
cls: defining_class
data: Py_buffer
Binary data to be compressed.
/
Argument Clinic
/*[clinic start generated code]*/
static PyObject *
zlib_Compress_compress_impl(compobject *self, PyTypeObject *cls,
Py_buffer *data)
/*[clinic end generated code: output=6731b3f0ff357ca6 input=04d00f65ab01d260]*/
PyType_GetModuleState(cls)
zlibstate *state = PyType_GetModuleState(cls);
self self
PyTypeObject * __text_signature__
defining_class __init__ __new__ METH_METHOD
It is not possible to use defining_class with slot methods. In order to fetch the module state from such methods,
use PyType_GetModuleByDef() to look up the module and then PyModule_GetState() to fetch the
module state. Example from the setattro slot method in Modules/_threadmodule.c:
static int
local_setattro(localobject *self, PyObject *name, PyObject *v)
{
PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
thread_module_state *state = get_thread_state(module);
...
}
PEP 573
16
4.16
“CConverter“ Python
O& PyArg_ParseTuple()
*something*_converter
Argument Clinic _converter
CConverter.__init__ converter_init()
converter_init() self
Argument Clinic converter_init()
CConverter
type C type int Python
' *'
default Python unspecified
py_default Python default None
c_default C default None
c_ignored_default The default value used to initialize the C variable when there is no default, but not spec-
ifying a default may result in an ”uninitialized variable” warning. This can easily happen when using option
groups although properly written code will never actually use this value, the variable does get passed in to the
impl, and the C compiler will complain about the ”use” of the uninitialized value. This value should always be
a non-empty string.
converter C
impl_by_reference True Argument Clinic impl
&
parse_by_reference Argument Clinic PyArg_ParseTuple()
&
Modules/zlibmodule.c
/*[python input]
class ssize_t_converter(CConverter):
type = 'Py_ssize_t'
converter = 'ssize_t_converter'
17
4.17
CReturnConverter
“Tools/clinic/clinic.py“ CReturnConverter
METH_O object
/*[clinic input]
meth_o_sample
argument: object
/
[clinic start generated code]*/
METH_NOARGS
self METH_O type
4.20 Clinic
Clinic C Clinic
Clinic
Clinic Clinic
Clinic Clinic
Clinic
18
docstring_prototype
docstring_definition
methoddef_define
impl_prototype
parser_prototype
parser_definition
impl_definition
dump <destination>
buffer two-pass
output output
output push
output pop
output preset <preset>
19
parser_prototype docstring_prototype impl_definition
block file
"{dirname}/clinic/{basename}.h"
buffer Clinic Python
PyMethodDef
buffer file
parser_prototype impl_prototype docstring_prototype
impl_definition block file
two-pass buffer “two-pass“
buffer buffer
two-pass buffer buffer
name
new clear
new
<name> “<type>“
5
suppress
block Clinic
buffer buffer
file
destination <name> new <type> <file_template>
3
{path}
{dirname}
{basename}
{basename_root} .
{basename_extension} .
20
{basename} {basename_root} {base-
name_extension} {basename_root}{basename_extension} {basename}
)
4 set
preserve
4.21 #ifdef
#ifdef HAVE_FUNCTIONNAME
static module_functionname(...)
{
...
}
#endif /* HAVE_FUNCTIONNAME */
PyMethodDef
#ifdef HAVE_FUNCTIONNAME
{'functionname', ... },
#endif /* HAVE_FUNCTIONNAME */
impl #ifdef
#ifdef HAVE_FUNCTIONNAME
/*[clinic input]
module.functionname
...
[clinic start generated code]*/
static module_functionname(...)
{
...
}
#endif /* HAVE_FUNCTIONNAME */
21
MODULE_FUNCTIONNAME_METHODDEF
"_METHODDEF"
HAVE_FUNCTIONNAME “MODULE_FUNCTIONNAME_METHODDEF“
#ifndef MODULE_FUNCTIONNAME_METHODDEF
#define MODULE_FUNCTIONNAME_METHODDEF
#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */
#/*[python input]
#print("def foo(): pass")
#[python start generated code]*/
def foo(): pass
#/*[python checksum:...]*/
22
P
Python
PEP 8, 10
PEP 573, 16
23