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
|
||||
work with Argument Clinic. Note that for code you plan to
|
||||
check in to CPython, you really should take the conversion farther,
|
||||
using some of the advanced concepts you'll see later on in
|
||||
the document (like "return converters" and "self converters").
|
||||
using some of the :ref:`advanced concepts <clinic-howtos>`
|
||||
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.
|
||||
|
||||
Let's dive in!
|
||||
|
||||
0. Make sure you're working with a freshly updated checkout
|
||||
First, make sure you're working with a freshly updated checkout
|
||||
of the CPython trunk.
|
||||
|
||||
1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
|
||||
Next, find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
|
||||
or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted
|
||||
to work with Argument Clinic yet.
|
||||
For my example I'm using
|
||||
For this tutorial, we'll be 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:
|
||||
If the call to the :c:func:`!PyArg_Parse*` function uses any of the
|
||||
following format units...:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
|
@ -194,10 +194,9 @@ Let's dive in!
|
|||
et
|
||||
et#
|
||||
|
||||
or if it has multiple calls to :c:func:`PyArg_ParseTuple`,
|
||||
you should choose a different function. Argument Clinic *does*
|
||||
support all of these scenarios. But these are advanced
|
||||
topics—let's do something simpler for your first function.
|
||||
... or if it has multiple calls to :c:func:`PyArg_ParseTuple`,
|
||||
you should choose a different function.
|
||||
(See :ref:`clinic-howto-advanced-converters` for those scenarios.)
|
||||
|
||||
Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple`
|
||||
or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different
|
||||
|
@ -206,45 +205,48 @@ Let's dive in!
|
|||
isn't suitable for conversion to Argument Clinic. Argument Clinic
|
||||
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 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.
|
||||
When you're done you should have just the text, based at the left
|
||||
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.)
|
||||
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::
|
||||
Example docstring summary line::
|
||||
|
||||
/*[clinic input]
|
||||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
5. If your docstring doesn't have a "summary" line, Argument Clinic will
|
||||
complain. So let's make sure it has one. The "summary" line should
|
||||
If your docstring doesn't have a "summary" line, Argument Clinic will
|
||||
complain, so let's make sure it has one. The "summary" line should
|
||||
be a paragraph consisting of a single 80-column line
|
||||
at the beginning of the docstring.
|
||||
(See :pep:`257` regarding docstring conventions.)
|
||||
|
||||
(Our example docstring consists solely of a summary line, so the sample
|
||||
code doesn't have to change for this step.)
|
||||
Our example docstring consists solely of a summary line, so the sample
|
||||
code doesn't have to change for this step.
|
||||
|
||||
6. Above the docstring, enter the name of the function, followed
|
||||
Now, above the docstring, enter the name of the function, followed
|
||||
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,
|
||||
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.
|
||||
|
||||
Sample::
|
||||
In our example, :mod:`!_pickle` is the module, :py:class:`!Pickler` is the class,
|
||||
and :py:meth:`!dump` is the method, so the name becomes
|
||||
:py:meth:`!_pickle.Pickler.dump`::
|
||||
|
||||
/*[clinic input]
|
||||
_pickle.Pickler.dump
|
||||
|
@ -252,13 +254,13 @@ Let's dive in!
|
|||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
7. If this is the first time that module or class has been used with Argument
|
||||
If this is the first time that module or class has been used with Argument
|
||||
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.)
|
||||
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
|
||||
seen by Python. Check the name defined in the :c:type:`PyModuleDef`
|
||||
|
@ -266,9 +268,7 @@ Let's dive in!
|
|||
|
||||
When you declare a class, you must also specify two aspects of its type
|
||||
in C: the type declaration you'd use for a pointer to an instance of
|
||||
this class, and a pointer to the :c:type:`!PyTypeObject` for this class.
|
||||
|
||||
Sample::
|
||||
this class, and a pointer to the :c:type:`!PyTypeObject` for this class::
|
||||
|
||||
/*[clinic input]
|
||||
module _pickle
|
||||
|
@ -281,13 +281,9 @@ Let's dive in!
|
|||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
|
||||
|
||||
|
||||
8. Declare each of the parameters to the function. Each parameter
|
||||
Declare each of the parameters to the function. Each parameter
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
@ -302,29 +298,28 @@ Let's dive in!
|
|||
name_of_parameter: converter = default_value
|
||||
|
||||
Argument Clinic's support for "default values" is quite sophisticated;
|
||||
please see :ref:`the section below on default values <default_values>`
|
||||
for more information.
|
||||
see :ref:`clinic-howto-default-values` for more information.
|
||||
|
||||
Add a blank line below the parameters.
|
||||
Next, add a blank line below the parameters.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
For each parameter, copy the "format unit" for that
|
||||
parameter from the :c:func:`PyArg_Parse` format argument and
|
||||
specify *that* as its converter, as a quoted
|
||||
string. ("format unit" is the formal name for the one-to-three
|
||||
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`.)
|
||||
is and how to convert it.
|
||||
For more on format units please see :ref:`arg-parsing`.
|
||||
|
||||
For multicharacter format units like ``z#``, use the
|
||||
entire two-or-three character string.
|
||||
For multicharacter format units like ``z#``,
|
||||
use the entire two-or-three character string.
|
||||
|
||||
Sample::
|
||||
|
||||
|
@ -341,31 +336,26 @@ Let's dive in!
|
|||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
9. If your function has ``|`` in the format 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.
|
||||
If your function has ``|`` in the format 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
(:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.)
|
||||
:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.
|
||||
|
||||
|
||||
10. If the existing C function calls :c:func:`PyArg_ParseTuple`
|
||||
Next, if the existing C function calls :c:func:`PyArg_ParseTuple`
|
||||
(as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its
|
||||
arguments are positional-only.
|
||||
|
||||
To mark all parameters as positional-only in Argument Clinic,
|
||||
add a ``/`` on a line by itself after the last parameter,
|
||||
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.
|
||||
|
||||
Currently this is all-or-nothing; either all parameters are
|
||||
positional-only, or none of them are. (In the future Argument
|
||||
Clinic may relax this restriction.)
|
||||
|
||||
Sample::
|
||||
|
||||
/*[clinic input]
|
||||
|
@ -382,16 +372,17 @@ Let's dive in!
|
|||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
11. It's helpful to write a per-parameter docstring for each parameter.
|
||||
But per-parameter docstrings are optional; you can skip this step
|
||||
if you prefer.
|
||||
It can be helpful to write a per-parameter docstring for each parameter.
|
||||
Since per-parameter docstrings are optional,
|
||||
you can skip this step if you prefer.
|
||||
|
||||
Here's how to add a per-parameter docstring. The first line
|
||||
of the per-parameter docstring 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.
|
||||
Nevertheless, here's how to add a per-parameter docstring.
|
||||
The first line of the per-parameter docstring
|
||||
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.
|
||||
|
||||
Sample::
|
||||
|
||||
|
@ -410,10 +401,10 @@ Let's dive in!
|
|||
Write a pickled representation of obj to the open file.
|
||||
[clinic start generated code]*/
|
||||
|
||||
12. Save and close the file, then run ``Tools/clinic/clinic.py`` on
|
||||
it. With luck everything worked---your block now has output, and
|
||||
a :file:`.c.h` file has been generated! Reopen the file in your
|
||||
text editor to see::
|
||||
Save and close the file, then run ``Tools/clinic/clinic.py`` on it.
|
||||
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::
|
||||
|
||||
/*[clinic input]
|
||||
_pickle.Pickler.dump
|
||||
|
@ -429,9 +420,10 @@ Let's dive in!
|
|||
_pickle_Pickler_dump(PicklerObject *self, PyObject *obj)
|
||||
/*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/
|
||||
|
||||
Obviously, if Argument Clinic didn't produce any output, it's because
|
||||
it found an error in your input. Keep fixing your errors and retrying
|
||||
until Argument Clinic processes your file without complaint.
|
||||
Obviously, if Argument Clinic didn't produce any output,
|
||||
it's because it found an error in your input.
|
||||
Keep fixing your errors and retrying until Argument Clinic processes your file
|
||||
without complaint.
|
||||
|
||||
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,
|
||||
|
@ -439,7 +431,7 @@ Let's dive in!
|
|||
|
||||
#include "clinic/_pickle.c.h"
|
||||
|
||||
13. Double-check that the argument-parsing code Argument Clinic generated
|
||||
Double-check that the argument-parsing code Argument Clinic generated
|
||||
looks basically the same as the existing code.
|
||||
|
||||
First, ensure both places use the same argument-parsing function.
|
||||
|
@ -450,22 +442,22 @@ Let's dive in!
|
|||
|
||||
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, up to the colon
|
||||
or semi-colon.
|
||||
as the hand-written one in the existing function,
|
||||
up to the colon or semi-colon.
|
||||
|
||||
(Argument Clinic always generates its format strings
|
||||
with a ``:`` followed by the name of the function. If the
|
||||
existing code's format string ends with ``;``, to provide
|
||||
usage help, this change is harmless—don't worry about it.)
|
||||
Argument Clinic always generates its format strings
|
||||
with a ``:`` followed by the name of the function.
|
||||
If the existing code's format string ends with ``;``,
|
||||
to provide usage help, this change is harmless --- don't worry about it.
|
||||
|
||||
Third, for parameters whose format units require two arguments
|
||||
(like a length variable, or an encoding string, or a pointer
|
||||
to a conversion function), ensure that the second argument is
|
||||
Third, for parameters whose format units require two arguments,
|
||||
like a length variable, an encoding string, or a pointer
|
||||
to a conversion function, ensure that the second argument is
|
||||
*exactly* the same between the two invocations.
|
||||
|
||||
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::
|
||||
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::
|
||||
|
||||
#define __PICKLE_PICKLER_DUMP_METHODDEF \
|
||||
{"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},
|
||||
|
@ -477,8 +469,7 @@ Let's dive in!
|
|||
adjust your Argument Clinic function specification and rerun
|
||||
``Tools/clinic/clinic.py`` until they *are* the same.
|
||||
|
||||
|
||||
14. Notice that the last line of its output is the declaration
|
||||
Notice that the last line of its output is the declaration
|
||||
of your "impl" function. This is where the builtin's implementation goes.
|
||||
Delete the existing prototype of the function you're modifying, but leave
|
||||
the opening curly brace. Now delete its argument parsing code and the
|
||||
|
@ -486,17 +477,17 @@ Let's dive in!
|
|||
Notice how the Python arguments are now arguments to this impl function;
|
||||
if the implementation used different names for these variables, fix it.
|
||||
|
||||
Let's reiterate, just because it's kind of weird. Your code should now
|
||||
look like this::
|
||||
Let's reiterate, just because it's kind of weird.
|
||||
Your code should now look like this::
|
||||
|
||||
static return_type
|
||||
your_function_impl(...)
|
||||
/*[clinic end generated code: checksum=...]*/
|
||||
/*[clinic end generated code: input=..., output=...]*/
|
||||
{
|
||||
...
|
||||
|
||||
Argument Clinic generated the checksum line and the function prototype just
|
||||
above it. You should write the opening (and closing) curly braces for the
|
||||
above it. You should write the opening and closing curly braces for the
|
||||
function, and the implementation inside.
|
||||
|
||||
Sample::
|
||||
|
@ -535,19 +526,20 @@ Let's dive in!
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (_Pickler_ClearBuffer(self) < 0)
|
||||
if (_Pickler_ClearBuffer(self) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
15. 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
|
||||
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
|
||||
is at module scope, this will probably be very near the end of the file;
|
||||
if the builtin is a class method, this will probably be below but relatively
|
||||
near to the implementation.)
|
||||
near to the implementation.
|
||||
|
||||
Note that the body of the macro contains a trailing comma. So when you
|
||||
Note that the body of the macro contains a trailing comma; when you
|
||||
replace the existing static :c:type:`!PyMethodDef` structure with the macro,
|
||||
*don't* add a comma to the end.
|
||||
|
||||
|
@ -559,20 +551,17 @@ Let's dive in!
|
|||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
||||
16. Argument Clinic may generate new instances of ``_Py_ID``. For example::
|
||||
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.
|
||||
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.
|
||||
|
||||
Well, except for one difference: :py:func:`inspect.signature` run on your function
|
||||
and there should be no externally visible change to Python's behavior,
|
||||
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!
|
||||
|
@ -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).
|
||||
|
||||
|
||||
.. _clinic-howto-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``.
|
||||
|
||||
|
||||
.. _clinic-howto-default-values:
|
||||
.. _default_values:
|
||||
|
||||
How to assign default values to parameter
|
||||
|
@ -1053,6 +1045,8 @@ you're not permitted to use:
|
|||
* Tuple/list/set/dict literals.
|
||||
|
||||
|
||||
.. _clinic-howto-return-converters:
|
||||
|
||||
How to use return converters
|
||||
----------------------------
|
||||
|
||||
|
@ -1195,6 +1189,8 @@ variable to the C code::
|
|||
/*[python checksum:...]*/
|
||||
|
||||
|
||||
.. _clinic-howto-self-converter:
|
||||
|
||||
How to use the "self converter"
|
||||
-------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue