0% found this document useful (0 votes)
12 views23 pages

Howto Clinic

Uploaded by

ryan suen
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views23 pages

Howto Clinic

Uploaded by

ryan suen
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 23

Argument Clinic

3.11.4

Guido van Rossum and the Python development team

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

Argument Clinic CPython CPython


CPython C
Argument Clinic Argument Clinic CPython

1 Argument Clinic

Argument Clinic CPython


Argument Clinic Argument Clinic
CPython PyObject *args
PyObject *kwargs C
Argument Clinic CPython
Argument Clinic
Argument Clinic
Argument Clinic
Argument Clinic
CPython
Argument Clinic Python
Argument Clinic
Argument Clinic CPython
Argument Clinic
Argument Clinic Argument
Clinic

Argument Clinic CPython Tools/clinic/clinic.py C

$ python3 Tools/clinic/clinic.py foo.c

Argument Clinic C

/*[clinic input]

[clinic start generated code]*/

Argument Clinic Argument


Clinic
Argument Clinic C
Argument Clinic

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() Argument Clinic

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

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

7. C Argument Clinic Argument


Clinic C include statics
)
Python PyModuleDef PyTypeObject

C
PyTypeObject

/*[clinic input]
module _pickle
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
[clinic start generated code]*/

/*[clinic input]
_pickle.Pickler.dump

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

8.

name_of_parameter: converter

name_of_parameter: converter = default_value

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'

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

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'
/

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

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.
/

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

12. Tools/clinic/clinic.py
.c.h

/*[clinic input]
_pickle.Pickler.dump

obj: 'O'
The object to be pickled.
/

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

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"

13. Argument Clinic


PyArg_ParseTuple()
PyArg_ParseTupleAndKeywords() Argument Clinic
PyArg_ParseTuple() PyArg_ParseTupleAndKeywords()

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.
/

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

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;

...

15. PyMethodDef PyMethodDef

)
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

4.2 Argument Clinic C

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

Python keywords file C file_obj


self

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




4.5 Argument Clinic

Argument Clinic

Argument Clinic Python 3.4


Argument Clinic

• PyArg_ParseTuple()

Argument Clinic Python


bool bool()
Argument Clinic Argument Clinic
c_default C
the section on default values
annotation PEP 8 Python

accept Python Python

None NoneType
bitwise Python

converter object C

encoding Python str(Unicode) C char *

subclass_of object Python Python 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.
/

Write a pickled representation of obj to the open file.


[clinic start generated code]*/

unsigned_int
unsigned_ bitwise=True

Argument Clinic
Tools/clinic/clinic.py --converters

4.6 Py_buffer

Py_buffer 's*' 'w*' '*y' 'z*'


PyBuffer_Release() Argument Clinic

4.7

converter`` ``O& subclass_of``


``O! encoding e
subclass_of object() type
PyUnicode_Type
object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')
Argument Clinic e
PyArg_Parse PyArg_ParseTuple()
Argument-Clinic
CPython e

4.8

int float

foo: str = "abc"


bar: int = 123
bat: float = 45.6

Python

yep: bool = True


nope: bool = False
nada: object = None

NULL

12
4.9 NULL

None C
Py_None “NULL“ Python None
C NULL

4.10

foo: Py_ssize_t = sys.maxsize - 1

sys.maxsize Argument Clinic


C

max_widgets

foo: Py_ssize_t = max_widgets

sys.modules sys.maxsize
Python
Argument Clinic C
“c_default“ C

foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1

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
/

[clinic start generated code]*/

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"

NULL NULL Argument


Clinic
PyErr_Occurred() True

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 C Argument Clinic


Python
Python Argument Clinic

/*[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

Argument Clinic self self type


Argument Clinic
self self_converter
self
self self Argument
Clinic self type

/*[clinic input]

_pickle.Pickler.dump

self: self(type="PicklerObject *")


obj: object
/

Write a pickled representation of the given object to the open file.


[clinic start generated code]*/

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
/

Write a pickled representation of the given object to the open file.


[clinic start generated code]*/

4.15

Argument Clinic heap type


PyType_FromModuleAndSpec()
PyType_GetModuleState()
Modules/zlibmodule.c clinic defining_class
/*[clinic input]
zlib.Compress.compress

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'

[python start generated code]*/


/*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/

Argument Clinic ssize_t ssize_t


Py_ssize_t 'O&' ssize_t_converter
ssize_t
C CPython
C CConverter

17
4.17

CReturnConverter
“Tools/clinic/clinic.py“ CReturnConverter

4.18 METH_O METH_NOARGS

METH_O object

/*[clinic input]
meth_o_sample

argument: object
/
[clinic start generated code]*/

METH_NOARGS
self METH_O type

4.19 tp_new tp_init functions

tp_new tp_init __new__ __init__


• __new__ C
• PyMethodDef #define
• __init__ int PyObject *

• __new__ __init__ args`` ``kwargs

4.20 Clinic

Clinic C Clinic
Clinic

Clinic Clinic
Clinic Clinic
Clinic

** field ** Clinic PyMethodDef


#define methoddef_define Clinic 7

18
docstring_prototype
docstring_definition
methoddef_define
impl_prototype
parser_prototype
parser_definition
impl_definition

"<a>_<b>" "<a>" impl


methoddef "<b>" "_prototype"
"_definition"
"methoddef" "_define"
#define
** destination ** Clinic 5
block Clinic
buffer
Clinic
file Clinic Clinic “{basename}.clinic{extension}“
basename extension os.path.splitext()
_pickle.c file _pickle.clinic.c)
** “file“ ** ** * * **
two-pass buffer two-pass
** ** Clinic
suppress
Clinic 5
dump

dump <destination>

buffer two-pass

output output

output <field> <destination>

Clinic **field** **destination** output


everything Clinic ** ** ** **
output

output push
output pop
output preset <preset>

output push output pop

output preset Clinic


block Clinic
parser_prototype docstring_prototype
block
file Clinic #include
typedef`` ``PyTypeObject

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

impl_prototype impl_definition block


docstring_prototype methoddef_define parser_prototype
“two-pass“ buffer
partial-buffer buffer block buffer
buffer
buffer buffer

impl_prototype docstring_definition parser_definition


buffer block
destination

destination <name> <command> [...]

name
new clear
new

destination <name> new <type>

<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}

two-pass two-pass two-pass


clear

destination <name> clear

)
4 set

set line_prefix "string"


set line_suffix "string"

set’ Clinic “line_prefix‘ Clinic line_suffix


Clinic

{block comment start} /* C


{block comment end} */ C
preserve

preserve

Clinic file Clinic Clinic

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 */

PyMethodDef 3 Argument Clinic

21
MODULE_FUNCTIONNAME_METHODDEF

"_METHODDEF"
HAVE_FUNCTIONNAME “MODULE_FUNCTIONNAME_METHODDEF“

Argument Clinic #ifdef Argument Clinic

#ifndef MODULE_FUNCTIONNAME_METHODDEF
#define MODULE_FUNCTIONNAME_METHODDEF
#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */

”block” Argument Clinic


#ifdef
Argument Clinic ”buffer”
Argument Clinic :

Warning in file "Modules/posixmodule.c" on line 12357:


Destination buffer 'buffer' not empty at end of file, emptying.

Argument Clinic dump buffer


PyMethodDef

4.22 Python Argument Clinic

Argument Clinic Python Argument Clinic


Python Argument Clinic Python
Python Python
Python C Python Argument Clinic
:

#/*[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

You might also like