mirror of https://github.com/python/cpython
Docs: Remove the numbered steps from the Argument Clinic tutorial (#107203)
Instead, order the tutorial as one body of prose, making it easier to align the tutorial according to Diátaxis principles.
This commit is contained in:
parent
5aa6964a5c
commit
592395577c
|
@ -167,23 +167,23 @@ convert a function to work with it. Here, then, are the bare
|
||||||
minimum steps you'd need to follow to convert a function to
|
minimum steps you'd need to follow to convert a function to
|
||||||
work with Argument Clinic. Note that for code you plan to
|
work with Argument Clinic. Note that for code you plan to
|
||||||
check in to CPython, you really should take the conversion farther,
|
check in to CPython, you really should take the conversion farther,
|
||||||
using some of the advanced concepts you'll see later on in
|
using some of the :ref:`advanced concepts <clinic-howtos>`
|
||||||
the document (like "return converters" and "self converters").
|
you'll see later on in the document,
|
||||||
|
like :ref:`clinic-howto-return-converters`
|
||||||
|
and :ref:`clinic-howto-self-converter`.
|
||||||
But we'll keep it simple for this walkthrough so you can learn.
|
But we'll keep it simple for this walkthrough so you can learn.
|
||||||
|
|
||||||
Let's dive in!
|
First, make sure you're working with a freshly updated checkout
|
||||||
|
of the CPython trunk.
|
||||||
|
|
||||||
0. Make sure you're working with a freshly updated checkout
|
Next, find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
|
||||||
of the CPython trunk.
|
or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted
|
||||||
|
to work with Argument Clinic yet.
|
||||||
|
For this tutorial, we'll be using
|
||||||
|
:py:meth:`_pickle.Pickler.dump <pickle.Pickler.dump>`.
|
||||||
|
|
||||||
1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
|
If the call to the :c:func:`!PyArg_Parse*` function uses any of the
|
||||||
or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted
|
following format units...:
|
||||||
to work with Argument Clinic yet.
|
|
||||||
For my example I'm using
|
|
||||||
:py:meth:`_pickle.Pickler.dump <pickle.Pickler.dump>`.
|
|
||||||
|
|
||||||
2. If the call to the :c:func:`!PyArg_Parse*` function uses any of the
|
|
||||||
following format units:
|
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
|
@ -194,388 +194,377 @@ Let's dive in!
|
||||||
et
|
et
|
||||||
et#
|
et#
|
||||||
|
|
||||||
or if it has multiple calls to :c:func:`PyArg_ParseTuple`,
|
... or if it has multiple calls to :c:func:`PyArg_ParseTuple`,
|
||||||
you should choose a different function. Argument Clinic *does*
|
you should choose a different function.
|
||||||
support all of these scenarios. But these are advanced
|
(See :ref:`clinic-howto-advanced-converters` for those scenarios.)
|
||||||
topics—let's do something simpler for your first function.
|
|
||||||
|
|
||||||
Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple`
|
Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple`
|
||||||
or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different
|
or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different
|
||||||
types for the same argument, or if the function uses something besides
|
types for the same argument, or if the function uses something besides
|
||||||
:c:func:`!PyArg_Parse*` functions to parse its arguments, it probably
|
:c:func:`!PyArg_Parse*` functions to parse its arguments, it probably
|
||||||
isn't suitable for conversion to Argument Clinic. Argument Clinic
|
isn't suitable for conversion to Argument Clinic. Argument Clinic
|
||||||
doesn't support generic functions or polymorphic parameters.
|
doesn't support generic functions or polymorphic parameters.
|
||||||
|
|
||||||
3. Add the following boilerplate above the function, creating our block::
|
Next, add the following boilerplate above the function,
|
||||||
|
creating our input block::
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
4. Cut the docstring and paste it in between the ``[clinic]`` lines,
|
Cut the docstring and paste it in between the ``[clinic]`` lines,
|
||||||
removing all the junk that makes it a properly quoted C string.
|
removing all the junk that makes it a properly quoted C string.
|
||||||
When you're done you should have just the text, based at the left
|
When you're done you should have just the text, based at the left
|
||||||
margin, with no line wider than 80 characters.
|
margin, with no line wider than 80 characters.
|
||||||
(Argument Clinic will preserve indents inside the docstring.)
|
Argument Clinic will preserve indents inside the docstring.
|
||||||
|
|
||||||
|
If the old docstring had a first line that looked like a function
|
||||||
|
signature, throw that line away; The docstring doesn't need it anymore ---
|
||||||
|
when you use :py:func:`help` on your builtin in the future,
|
||||||
|
the first line will be built automatically based on the function's signature.
|
||||||
|
|
||||||
If the old docstring had a first line that looked like a function
|
Example docstring summary line::
|
||||||
signature, throw that line away. (The docstring doesn't need it
|
|
||||||
anymore—when you use :py:func:`help` on your builtin in the future,
|
|
||||||
the first line will be built automatically based on the function's
|
|
||||||
signature.)
|
|
||||||
|
|
||||||
Sample::
|
/*[clinic input]
|
||||||
|
Write a pickled representation of obj to the open file.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
/*[clinic input]
|
If your docstring doesn't have a "summary" line, Argument Clinic will
|
||||||
Write a pickled representation of obj to the open file.
|
complain, so let's make sure it has one. The "summary" line should
|
||||||
[clinic start generated code]*/
|
be a paragraph consisting of a single 80-column line
|
||||||
|
at the beginning of the docstring.
|
||||||
|
(See :pep:`257` regarding docstring conventions.)
|
||||||
|
|
||||||
5. If your docstring doesn't have a "summary" line, Argument Clinic will
|
Our example docstring consists solely of a summary line, so the sample
|
||||||
complain. So let's make sure it has one. The "summary" line should
|
code doesn't have to change for this step.
|
||||||
be a paragraph consisting of a single 80-column line
|
|
||||||
at the beginning of the docstring.
|
|
||||||
|
|
||||||
(Our example docstring consists solely of a summary line, so the sample
|
Now, above the docstring, enter the name of the function, followed
|
||||||
code doesn't have to change for this step.)
|
by a blank line. This should be the Python name of the function,
|
||||||
|
and should be the full dotted path to the function ---
|
||||||
|
it should start with the name of the module,
|
||||||
|
include any sub-modules, and if the function is a method on
|
||||||
|
a class it should include the class name too.
|
||||||
|
|
||||||
6. Above the docstring, enter the name of the function, followed
|
In our example, :mod:`!_pickle` is the module, :py:class:`!Pickler` is the class,
|
||||||
by a blank line. This should be the Python name of the function,
|
and :py:meth:`!dump` is the method, so the name becomes
|
||||||
and should be the full dotted path
|
:py:meth:`!_pickle.Pickler.dump`::
|
||||||
to the function—it should start with the name of the module,
|
|
||||||
include any sub-modules, and if the function is a method on
|
|
||||||
a class it should include the class name too.
|
|
||||||
|
|
||||||
Sample::
|
/*[clinic input]
|
||||||
|
_pickle.Pickler.dump
|
||||||
|
|
||||||
/*[clinic input]
|
Write a pickled representation of obj to the open file.
|
||||||
_pickle.Pickler.dump
|
[clinic start generated code]*/
|
||||||
|
|
||||||
Write a pickled representation of obj to the open file.
|
If this is the first time that module or class has been used with Argument
|
||||||
[clinic start generated code]*/
|
Clinic in this C file,
|
||||||
|
you must declare the module and/or class. Proper Argument Clinic hygiene
|
||||||
|
prefers declaring these in a separate block somewhere near the
|
||||||
|
top of the C file, in the same way that include files and statics go at
|
||||||
|
the top.
|
||||||
|
In our sample code we'll just show the two blocks next to each other.
|
||||||
|
|
||||||
7. If this is the first time that module or class has been used with Argument
|
The name of the class and module should be the same as the one
|
||||||
Clinic in this C file,
|
seen by Python. Check the name defined in the :c:type:`PyModuleDef`
|
||||||
you must declare the module and/or class. Proper Argument Clinic hygiene
|
or :c:type:`PyTypeObject` as appropriate.
|
||||||
prefers declaring these in a separate block somewhere near the
|
|
||||||
top of the C file, in the same way that include files and statics go at
|
|
||||||
the top. (In our sample code we'll just show the two blocks next to
|
|
||||||
each other.)
|
|
||||||
|
|
||||||
The name of the class and module should be the same as the one
|
When you declare a class, you must also specify two aspects of its type
|
||||||
seen by Python. Check the name defined in the :c:type:`PyModuleDef`
|
in C: the type declaration you'd use for a pointer to an instance of
|
||||||
or :c:type:`PyTypeObject` as appropriate.
|
this class, and a pointer to the :c:type:`!PyTypeObject` for this class::
|
||||||
|
|
||||||
When you declare a class, you must also specify two aspects of its type
|
/*[clinic input]
|
||||||
in C: the type declaration you'd use for a pointer to an instance of
|
module _pickle
|
||||||
this class, and a pointer to the :c:type:`!PyTypeObject` for this class.
|
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
Sample::
|
/*[clinic input]
|
||||||
|
_pickle.Pickler.dump
|
||||||
|
|
||||||
/*[clinic input]
|
Write a pickled representation of obj to the open file.
|
||||||
module _pickle
|
[clinic start generated code]*/
|
||||||
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
/*[clinic input]
|
Declare each of the parameters to the function. Each parameter
|
||||||
_pickle.Pickler.dump
|
should get its own line. All the parameter lines should be
|
||||||
|
indented from the function name and the docstring.
|
||||||
|
The general form of these parameter lines is as follows:
|
||||||
|
|
||||||
Write a pickled representation of obj to the open file.
|
.. code-block:: none
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
|
name_of_parameter: converter
|
||||||
|
|
||||||
|
If the parameter has a default value, add that after the
|
||||||
|
converter:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
8. Declare each of the parameters to the function. Each parameter
|
name_of_parameter: converter = default_value
|
||||||
should get its own line. All the parameter lines should be
|
|
||||||
indented from the function name and the docstring.
|
|
||||||
|
|
||||||
The general form of these parameter lines is as follows:
|
Argument Clinic's support for "default values" is quite sophisticated;
|
||||||
|
see :ref:`clinic-howto-default-values` for more information.
|
||||||
|
|
||||||
.. code-block:: none
|
Next, add a blank line below the parameters.
|
||||||
|
|
||||||
name_of_parameter: converter
|
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.
|
||||||
|
|
||||||
If the parameter has a default value, add that after the
|
For each parameter, copy the "format unit" for that
|
||||||
converter:
|
parameter from the :c:func:`PyArg_Parse` format argument and
|
||||||
|
specify *that* as its converter, as a quoted string.
|
||||||
|
The "format unit" is the formal name for the one-to-three
|
||||||
|
character substring of the *format* parameter that tells
|
||||||
|
the argument parsing function what the type of the variable
|
||||||
|
is and how to convert it.
|
||||||
|
For more on format units please see :ref:`arg-parsing`.
|
||||||
|
|
||||||
.. code-block:: none
|
For multicharacter format units like ``z#``,
|
||||||
|
use the entire two-or-three character string.
|
||||||
|
|
||||||
name_of_parameter: converter = default_value
|
Sample::
|
||||||
|
|
||||||
Argument Clinic's support for "default values" is quite sophisticated;
|
/*[clinic input]
|
||||||
please see :ref:`the section below on default values <default_values>`
|
module _pickle
|
||||||
for more information.
|
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
Add a blank line below the parameters.
|
/*[clinic input]
|
||||||
|
_pickle.Pickler.dump
|
||||||
|
|
||||||
What's a "converter"? It establishes both the type
|
obj: 'O'
|
||||||
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.
|
|
||||||
|
|
||||||
For each parameter, copy the "format unit" for that
|
Write a pickled representation of obj to the open file.
|
||||||
parameter from the :c:func:`PyArg_Parse` format argument and
|
[clinic start generated code]*/
|
||||||
specify *that* as its converter, as a quoted
|
|
||||||
string. ("format unit" is the formal name for the one-to-three
|
|
||||||
character substring of the *format* parameter that tells
|
|
||||||
the argument parsing function what the type of the variable
|
|
||||||
is and how to convert it. For more on format units please
|
|
||||||
see :ref:`arg-parsing`.)
|
|
||||||
|
|
||||||
For multicharacter format units like ``z#``, use the
|
If your function has ``|`` in the format string,
|
||||||
entire two-or-three character string.
|
meaning some parameters have default values, you can ignore it.
|
||||||
|
Argument Clinic infers which parameters are optional
|
||||||
|
based on whether or not they have default values.
|
||||||
|
|
||||||
Sample::
|
If your function has ``$`` in the format string,
|
||||||
|
meaning it takes keyword-only arguments,
|
||||||
|
specify ``*`` on a line by itself before the first keyword-only argument,
|
||||||
|
indented the same as the parameter lines.
|
||||||
|
|
||||||
/*[clinic input]
|
:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.
|
||||||
module _pickle
|
|
||||||
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
/*[clinic input]
|
Next, if the existing C function calls :c:func:`PyArg_ParseTuple`
|
||||||
_pickle.Pickler.dump
|
(as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its
|
||||||
|
arguments are positional-only.
|
||||||
|
|
||||||
obj: 'O'
|
To mark parameters as positional-only in Argument Clinic,
|
||||||
|
add a ``/`` on a line by itself after the last positional-only parameter,
|
||||||
|
indented the same as the parameter lines.
|
||||||
|
|
||||||
Write a pickled representation of obj to the open file.
|
Sample::
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
9. If your function has ``|`` in the format string, meaning some
|
/*[clinic input]
|
||||||
parameters have default values, you can ignore it. Argument
|
module _pickle
|
||||||
Clinic infers which parameters are optional based on whether
|
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
||||||
or not they have default values.
|
[clinic start generated code]*/
|
||||||
|
|
||||||
If your function has ``$`` in the format string, meaning it
|
/*[clinic input]
|
||||||
takes keyword-only arguments, specify ``*`` on a line by
|
_pickle.Pickler.dump
|
||||||
itself before the first keyword-only argument, indented the
|
|
||||||
same as the parameter lines.
|
|
||||||
|
|
||||||
(:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.)
|
obj: 'O'
|
||||||
|
/
|
||||||
|
|
||||||
|
Write a pickled representation of obj to the open file.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
10. If the existing C function calls :c:func:`PyArg_ParseTuple`
|
It can be helpful to write a per-parameter docstring for each parameter.
|
||||||
(as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its
|
Since per-parameter docstrings are optional,
|
||||||
arguments are positional-only.
|
you can skip this step if you prefer.
|
||||||
|
|
||||||
To mark all parameters as positional-only in Argument Clinic,
|
Nevertheless, here's how to add a per-parameter docstring.
|
||||||
add a ``/`` on a line by itself after the last parameter,
|
The first line of the per-parameter docstring
|
||||||
indented the same as the parameter lines.
|
must be indented further than the parameter definition.
|
||||||
|
The left margin of this first line establishes
|
||||||
|
the left margin for the whole per-parameter docstring;
|
||||||
|
all the text you write will be outdented by this amount.
|
||||||
|
You can write as much text as you like, across multiple lines if you wish.
|
||||||
|
|
||||||
Currently this is all-or-nothing; either all parameters are
|
Sample::
|
||||||
positional-only, or none of them are. (In the future Argument
|
|
||||||
Clinic may relax this restriction.)
|
|
||||||
|
|
||||||
Sample::
|
/*[clinic input]
|
||||||
|
module _pickle
|
||||||
|
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module _pickle
|
_pickle.Pickler.dump
|
||||||
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
/*[clinic input]
|
obj: 'O'
|
||||||
_pickle.Pickler.dump
|
The object to be pickled.
|
||||||
|
/
|
||||||
|
|
||||||
obj: 'O'
|
Write a pickled representation of obj to the open file.
|
||||||
/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
Write a pickled representation of obj to the open file.
|
Save and close the file, then run ``Tools/clinic/clinic.py`` on it.
|
||||||
[clinic start generated code]*/
|
With luck everything worked---your block now has output,
|
||||||
|
and a :file:`.c.h` file has been generated!
|
||||||
|
Reload the file in your text editor to see the generated code::
|
||||||
|
|
||||||
11. It's helpful to write a per-parameter docstring for each parameter.
|
/*[clinic input]
|
||||||
But per-parameter docstrings are optional; you can skip this step
|
_pickle.Pickler.dump
|
||||||
if you prefer.
|
|
||||||
|
|
||||||
Here's how to add a per-parameter docstring. The first line
|
obj: 'O'
|
||||||
of the per-parameter docstring must be indented further than the
|
The object to be pickled.
|
||||||
parameter definition. The left margin of this first line establishes
|
/
|
||||||
the left margin for the whole per-parameter docstring; all the text
|
|
||||||
you write will be outdented by this amount. You can write as much
|
|
||||||
text as you like, across multiple lines if you wish.
|
|
||||||
|
|
||||||
Sample::
|
Write a pickled representation of obj to the open file.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
/*[clinic input]
|
static PyObject *
|
||||||
module _pickle
|
_pickle_Pickler_dump(PicklerObject *self, PyObject *obj)
|
||||||
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
/*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/
|
||||||
[clinic start generated code]*/
|
|
||||||
|
Obviously, if Argument Clinic didn't produce any output,
|
||||||
/*[clinic input]
|
it's because it found an error in your input.
|
||||||
_pickle.Pickler.dump
|
Keep fixing your errors and retrying until Argument Clinic processes your file
|
||||||
|
without complaint.
|
||||||
obj: 'O'
|
|
||||||
The object to be pickled.
|
For readability, most of the glue code has been generated to a :file:`.c.h`
|
||||||
/
|
file. You'll need to include that in your original :file:`.c` file,
|
||||||
|
typically right after the clinic module block::
|
||||||
Write a pickled representation of obj to the open file.
|
|
||||||
[clinic start generated code]*/
|
#include "clinic/_pickle.c.h"
|
||||||
|
|
||||||
12. Save and close the file, then run ``Tools/clinic/clinic.py`` on
|
Double-check that the argument-parsing code Argument Clinic generated
|
||||||
it. With luck everything worked---your block now has output, and
|
looks basically the same as the existing code.
|
||||||
a :file:`.c.h` file has been generated! Reopen the file in your
|
|
||||||
text editor to see::
|
First, ensure both places use the same argument-parsing function.
|
||||||
|
The existing code must call either
|
||||||
/*[clinic input]
|
:c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`;
|
||||||
_pickle.Pickler.dump
|
ensure that the code generated by Argument Clinic calls the
|
||||||
|
*exact* same function.
|
||||||
obj: 'O'
|
|
||||||
The object to be pickled.
|
Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or
|
||||||
/
|
:c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same
|
||||||
|
as the hand-written one in the existing function,
|
||||||
Write a pickled representation of obj to the open file.
|
up to the colon or semi-colon.
|
||||||
[clinic start generated code]*/
|
|
||||||
|
Argument Clinic always generates its format strings
|
||||||
static PyObject *
|
with a ``:`` followed by the name of the function.
|
||||||
_pickle_Pickler_dump(PicklerObject *self, PyObject *obj)
|
If the existing code's format string ends with ``;``,
|
||||||
/*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/
|
to provide usage help, this change is harmless --- don't worry about it.
|
||||||
|
|
||||||
Obviously, if Argument Clinic didn't produce any output, it's because
|
Third, for parameters whose format units require two arguments,
|
||||||
it found an error in your input. Keep fixing your errors and retrying
|
like a length variable, an encoding string, or a pointer
|
||||||
until Argument Clinic processes your file without complaint.
|
to a conversion function, ensure that the second argument is
|
||||||
|
*exactly* the same between the two invocations.
|
||||||
For readability, most of the glue code has been generated to a :file:`.c.h`
|
|
||||||
file. You'll need to include that in your original :file:`.c` file,
|
Fourth, inside the output portion of the block,
|
||||||
typically right after the clinic module block::
|
you'll find a preprocessor macro defining the appropriate static
|
||||||
|
:c:type:`PyMethodDef` structure for this builtin::
|
||||||
#include "clinic/_pickle.c.h"
|
|
||||||
|
#define __PICKLE_PICKLER_DUMP_METHODDEF \
|
||||||
13. Double-check that the argument-parsing code Argument Clinic generated
|
{"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},
|
||||||
looks basically the same as the existing code.
|
|
||||||
|
This static structure should be *exactly* the same as the existing static
|
||||||
First, ensure both places use the same argument-parsing function.
|
:c:type:`!PyMethodDef` structure for this builtin.
|
||||||
The existing code must call either
|
|
||||||
:c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`;
|
If any of these items differ in *any way*,
|
||||||
ensure that the code generated by Argument Clinic calls the
|
adjust your Argument Clinic function specification and rerun
|
||||||
*exact* same function.
|
``Tools/clinic/clinic.py`` until they *are* the same.
|
||||||
|
|
||||||
Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or
|
Notice that the last line of its output is the declaration
|
||||||
:c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same
|
of your "impl" function. This is where the builtin's implementation goes.
|
||||||
as the hand-written one in the existing function, up to the colon
|
Delete the existing prototype of the function you're modifying, but leave
|
||||||
or semi-colon.
|
the opening curly brace. Now delete its argument parsing code and the
|
||||||
|
declarations of all the variables it dumps the arguments into.
|
||||||
(Argument Clinic always generates its format strings
|
Notice how the Python arguments are now arguments to this impl function;
|
||||||
with a ``:`` followed by the name of the function. If the
|
if the implementation used different names for these variables, fix it.
|
||||||
existing code's format string ends with ``;``, to provide
|
|
||||||
usage help, this change is harmless—don't worry about it.)
|
Let's reiterate, just because it's kind of weird.
|
||||||
|
Your code should now look like this::
|
||||||
Third, for parameters whose format units require two arguments
|
|
||||||
(like a length variable, or an encoding string, or a pointer
|
static return_type
|
||||||
to a conversion function), ensure that the second argument is
|
your_function_impl(...)
|
||||||
*exactly* the same between the two invocations.
|
/*[clinic end generated code: input=..., output=...]*/
|
||||||
|
{
|
||||||
Fourth, inside the output portion of the block you'll find a preprocessor
|
...
|
||||||
macro defining the appropriate static :c:type:`PyMethodDef` structure for
|
|
||||||
this builtin::
|
Argument Clinic generated the checksum line and the function prototype just
|
||||||
|
above it. You should write the opening and closing curly braces for the
|
||||||
#define __PICKLE_PICKLER_DUMP_METHODDEF \
|
function, and the implementation inside.
|
||||||
{"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},
|
|
||||||
|
Sample::
|
||||||
This static structure should be *exactly* the same as the existing static
|
|
||||||
:c:type:`!PyMethodDef` structure for this builtin.
|
/*[clinic input]
|
||||||
|
module _pickle
|
||||||
If any of these items differ in *any way*,
|
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
||||||
adjust your Argument Clinic function specification and rerun
|
[clinic start generated code]*/
|
||||||
``Tools/clinic/clinic.py`` until they *are* the same.
|
/*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
14. Notice that the last line of its output is the declaration
|
_pickle.Pickler.dump
|
||||||
of your "impl" function. This is where the builtin's implementation goes.
|
|
||||||
Delete the existing prototype of the function you're modifying, but leave
|
obj: 'O'
|
||||||
the opening curly brace. Now delete its argument parsing code and the
|
The object to be pickled.
|
||||||
declarations of all the variables it dumps the arguments into.
|
/
|
||||||
Notice how the Python arguments are now arguments to this impl function;
|
|
||||||
if the implementation used different names for these variables, fix it.
|
Write a pickled representation of obj to the open file.
|
||||||
|
[clinic start generated code]*/
|
||||||
Let's reiterate, just because it's kind of weird. Your code should now
|
|
||||||
look like this::
|
PyDoc_STRVAR(__pickle_Pickler_dump__doc__,
|
||||||
|
"Write a pickled representation of obj to the open file.\n"
|
||||||
static return_type
|
"\n"
|
||||||
your_function_impl(...)
|
...
|
||||||
/*[clinic end generated code: checksum=...]*/
|
static PyObject *
|
||||||
{
|
_pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
|
||||||
...
|
/*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
|
||||||
|
{
|
||||||
Argument Clinic generated the checksum line and the function prototype just
|
/* Check whether the Pickler was initialized correctly (issue3664).
|
||||||
above it. You should write the opening (and closing) curly braces for the
|
Developers often forget to call __init__() in their subclasses, which
|
||||||
function, and the implementation inside.
|
would trigger a segfault without this check. */
|
||||||
|
if (self->write == NULL) {
|
||||||
Sample::
|
PyErr_Format(PicklingError,
|
||||||
|
"Pickler.__init__() was not called by %s.__init__()",
|
||||||
/*[clinic input]
|
Py_TYPE(self)->tp_name);
|
||||||
module _pickle
|
return NULL;
|
||||||
class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
|
}
|
||||||
[clinic start generated code]*/
|
|
||||||
/*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
|
if (_Pickler_ClearBuffer(self) < 0) {
|
||||||
|
return NULL;
|
||||||
/*[clinic input]
|
}
|
||||||
_pickle.Pickler.dump
|
|
||||||
|
...
|
||||||
obj: 'O'
|
|
||||||
The object to be pickled.
|
Remember the macro with the :c:type:`PyMethodDef` structure for this function?
|
||||||
/
|
Find the existing :c:type:`!PyMethodDef` structure for this
|
||||||
|
function and replace it with a reference to the macro. If the builtin
|
||||||
Write a pickled representation of obj to the open file.
|
is at module scope, this will probably be very near the end of the file;
|
||||||
[clinic start generated code]*/
|
if the builtin is a class method, this will probably be below but relatively
|
||||||
|
near to the implementation.
|
||||||
PyDoc_STRVAR(__pickle_Pickler_dump__doc__,
|
|
||||||
"Write a pickled representation of obj to the open file.\n"
|
Note that the body of the macro contains a trailing comma; when you
|
||||||
"\n"
|
replace the existing static :c:type:`!PyMethodDef` structure with the macro,
|
||||||
...
|
*don't* add a comma to the end.
|
||||||
static PyObject *
|
|
||||||
_pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
|
Sample::
|
||||||
/*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
|
|
||||||
{
|
static struct PyMethodDef Pickler_methods[] = {
|
||||||
/* Check whether the Pickler was initialized correctly (issue3664).
|
__PICKLE_PICKLER_DUMP_METHODDEF
|
||||||
Developers often forget to call __init__() in their subclasses, which
|
__PICKLE_PICKLER_CLEAR_MEMO_METHODDEF
|
||||||
would trigger a segfault without this check. */
|
{NULL, NULL} /* sentinel */
|
||||||
if (self->write == NULL) {
|
};
|
||||||
PyErr_Format(PicklingError,
|
|
||||||
"Pickler.__init__() was not called by %s.__init__()",
|
Argument Clinic may generate new instances of ``_Py_ID``. For example::
|
||||||
Py_TYPE(self)->tp_name);
|
|
||||||
return NULL;
|
&_Py_ID(new_unique_py_id)
|
||||||
}
|
|
||||||
|
If it does, you'll have to run ``make regen-global-objects``
|
||||||
if (_Pickler_ClearBuffer(self) < 0)
|
to regenerate the list of precompiled identifiers at this point.
|
||||||
return NULL;
|
|
||||||
|
Finally, 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,
|
||||||
15. Remember the macro with the :c:type:`PyMethodDef` structure for this
|
except for one difference: :py:func:`inspect.signature` run on your function
|
||||||
function? Find the existing :c:type:`!PyMethodDef` structure for this
|
should now provide a valid signature!
|
||||||
function and replace it with a reference to the macro. (If the builtin
|
|
||||||
is at module scope, this will probably be very near the end of the file;
|
Congratulations, you've ported your first function to work with Argument Clinic!
|
||||||
if the builtin is a class method, this will probably be below but relatively
|
|
||||||
near to the implementation.)
|
|
||||||
|
|
||||||
Note that the body of the macro contains a trailing comma. So when you
|
|
||||||
replace the existing static :c:type:`!PyMethodDef` structure with the macro,
|
|
||||||
*don't* add a comma to the end.
|
|
||||||
|
|
||||||
Sample::
|
|
||||||
|
|
||||||
static struct PyMethodDef Pickler_methods[] = {
|
|
||||||
__PICKLE_PICKLER_DUMP_METHODDEF
|
|
||||||
__PICKLE_PICKLER_CLEAR_MEMO_METHODDEF
|
|
||||||
{NULL, NULL} /* sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
16. Argument Clinic may generate new instances of ``_Py_ID``. For example::
|
|
||||||
|
|
||||||
&_Py_ID(new_unique_py_id)
|
|
||||||
|
|
||||||
If it does, you'll have to run ``make regen-global-objects``
|
|
||||||
to regenerate the list of precompiled identifiers at this point.
|
|
||||||
|
|
||||||
|
|
||||||
17. 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.
|
|
||||||
|
|
||||||
Well, except for one difference: :py:func:`inspect.signature` run on your function
|
|
||||||
should now provide a valid signature!
|
|
||||||
|
|
||||||
Congratulations, you've ported your first function to work with Argument Clinic!
|
|
||||||
|
|
||||||
|
|
||||||
.. _clinic-howtos:
|
.. _clinic-howtos:
|
||||||
|
@ -913,6 +902,8 @@ you *must* not call :c:func:`PyBuffer_Release` on the provided buffer.
|
||||||
Argument Clinic generates code that does it for you (in the parsing function).
|
Argument Clinic generates code that does it for you (in the parsing function).
|
||||||
|
|
||||||
|
|
||||||
|
.. _clinic-howto-advanced-converters:
|
||||||
|
|
||||||
How to use advanced converters
|
How to use advanced converters
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
@ -943,6 +934,7 @@ This restriction doesn't seem unreasonable; CPython itself always passes in stat
|
||||||
hard-coded encoding strings for parameters whose format units start with ``e``.
|
hard-coded encoding strings for parameters whose format units start with ``e``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _clinic-howto-default-values:
|
||||||
.. _default_values:
|
.. _default_values:
|
||||||
|
|
||||||
How to assign default values to parameter
|
How to assign default values to parameter
|
||||||
|
@ -1053,6 +1045,8 @@ you're not permitted to use:
|
||||||
* Tuple/list/set/dict literals.
|
* Tuple/list/set/dict literals.
|
||||||
|
|
||||||
|
|
||||||
|
.. _clinic-howto-return-converters:
|
||||||
|
|
||||||
How to use return converters
|
How to use return converters
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -1195,6 +1189,8 @@ variable to the C code::
|
||||||
/*[python checksum:...]*/
|
/*[python checksum:...]*/
|
||||||
|
|
||||||
|
|
||||||
|
.. _clinic-howto-self-converter:
|
||||||
|
|
||||||
How to use the "self converter"
|
How to use the "self converter"
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue