summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/howto/clinic.rst900
-rw-r--r--Doc/howto/index.rst1
-rw-r--r--Misc/NEWS2
-rw-r--r--Modules/zlibmodule.c7
-rwxr-xr-xTools/clinic/clinic.py54
5 files changed, 954 insertions, 10 deletions
diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst
new file mode 100644
index 0000000..afbeb41
--- /dev/null
+++ b/Doc/howto/clinic.rst
@@ -0,0 +1,900 @@
+======================
+Argument Clinic How-To
+======================
+
+:author: Larry Hastings
+
+
+.. topic:: Abstract
+
+ Argument Clinic is a preprocessor for CPython C files.
+ Its purpose is to automate all the boilerplate involved
+ with writing argument parsing code for "builtins".
+ This document shows you how to convert your first C
+ function to work with Argument Clinic, and then introduces
+ some advanced topics on Argument Clinic usage.
+
+ Argument Clinic is currently considered an internal
+ tool for the CPython code tree. Its use is not supported
+ for files outside the CPython code tree, and no guarantees
+ are made regarding backwards compatibility for future
+ versions. In other words: if you maintain an external C
+ extension for CPython, you're welcome to experiment with
+ Argument Clinic in your own code. But the version of Argument
+ Clinic that ships with CPython 3.5 *could* be totally
+ incompatible and break all your code.
+
+========================
+Basic Concepts And Usage
+========================
+
+Argument Clinic ships with CPython. You can find it in ``Tools/clinic/clinic.py``.
+If you run that script, specifying a C file as an argument::
+
+ % python3 Tools/clinic/clinic.py foo.c
+
+Argument Clinic will scan over the file looking for lines that
+look exactly like this::
+
+ /*[clinic]
+
+When it finds one, it reads everything up to a line that looks
+like this::
+
+ [clinic]*/
+
+Everything in between these two lines is input for Argument Clinic.
+All of these lines, including the beginning and ending comment
+lines, are collectively called an Argument Clinic "input block",
+or "block" for short.
+
+When Argument Clinic parses one of these blocks, it
+generates output. This output is rewritten into the C file
+immediately after the block, followed by a comment containing a checksum.
+The resulting Argument Clinic block looks like this::
+
+ /*[clinic]
+ ... clinic input goes here ...
+ [clinic]*/
+ ... clinic output goes here ...
+ /*[clinic checksum:...]*/
+
+If you run Argument Clinic on the same file a second time, Argument Clinic
+will discard the old output and write out the new output with a fresh checksum
+line. However, if the input hasn't changed, the output won't change either.
+
+You should never modify the output portion of an Argument Clinic block. Instead,
+change the input until it produces the output you want. (That's the purpose of the
+checksum--to detect and warn you in case someone accidentally modifies the output.)
+
+For the sake of clarity, here's the terminology we'll use with Argument Clinic:
+
+* The first line of the comment (``/*[clinic]``) is the *start line*.
+* The last line of the initial comment (``[clinic]*/``) is the *end line*.
+* The last line (``/*[clinic checksum:...]*/``) is the *checksum line*.
+* In between the start line and the end line is the *input*.
+* In between the end line and the checksum line is the *output*.
+* All the text collectively, from the start line to the checksum line inclusively,
+ is the *block*. (A block that hasn't been successfully processed by Argument
+ Clinic yet doesn't have output or a checksum line, but it's still considered
+ a block.)
+
+
+==============================
+Converting Your First Function
+==============================
+
+The best way to get a sense of how Argument Clinic works is to
+convert a function to work with it. Let's dive in!
+
+0. Make sure you're working with a freshly updated trunk.
+
+1. Find a Python builtin that calls either ``PyArg_ParseTuple()``
+ or ``PyArg_ParseTupleAndKeywords()``, and hasn't been converted yet.
+ For my example I'm using ``pickle.Pickler.dump()``.
+
+2. If the call to the ``PyArg_Parse`` function uses any of the
+ following format units::
+
+ O&
+ O!
+ es
+ es#
+ et
+ et#
+
+ or if it has multiple calls to ``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.
+
+3. Add the following boilerplate above the function, creating our block::
+
+ /*[clinic]
+ [clinic]*/
+
+4. 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.)
+
+ Sample::
+
+ /*[clinic]
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+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
+ be a paragraph consisting of a single 80-column line
+ at the beginning of the docstring.
+
+ (Our docstring consists solely of the 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
+ 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.
+
+ Sample::
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+7. 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.)
+
+ Sample::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+
+8. 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::
+
+ name_of_parameter: converter
+
+ If the parameter has a default value, add that after the
+ converter::
+
+ name_of_parameter: converter = default_value
+
+ 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
+ Clinic easier.
+
+ For each parameter, copy the "format unit" for that
+ parameter from the ``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
+ 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 multicharacter format units like ``z#``, use the
+ entire two-or-three character string.
+
+ Sample::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: 'O'
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+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 it
+ takes keyword-only arguments, specify ``*`` on a line by
+ itself before the first keyword-only argument, indented the
+ same as the parameter lines.
+
+ (``pickle.Pickler.dump`` has neither, so our sample is unchanged.)
+
+
+10. If the existing C function uses ``PyArg_ParseTuple()``
+ (instead of ``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,
+ indented the same as the parameter lines.
+
+ Sample::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: 'O'
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+11. It's helpful to write a per-parameter docstring, indented
+ another level past the parameter declaration. But per-parameter
+ docstrings are optional; you can skip this step if you prefer.
+
+ Here's how per-parameter docstrings work. The first line
+ of the per-parameter docstring must be indented further than the
+ parameter definition. This left margin 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 as you like,
+ across multiple lines if you wish.
+
+ Sample::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+12. Save and close the file, then run ``Tools/clinic/clinic.py`` on it.
+ With luck everything worked and your block now has output! Reopen
+ the file in your text editor to see::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+ /*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+ PyDoc_STRVAR(pickle_Pickler_dump__doc__,
+ "Write a pickled representation of obj to the open file.\n"
+ "\n"
+ ...
+ static PyObject *
+ pickle_Pickler_dump_impl(PyObject *self, PyObject *obj)
+ /*[clinic checksum: 3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
+
+13. 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.
+ The existing code must call either
+ ``PyArg_ParseTuple()`` or ``PyArg_ParseTupleAndKeywords()``;
+ ensure that the code generated by Argument Clinic calls the
+ same function.
+
+ Second, the format string passed in to ``PyArg_ParseTuple()`` or
+ ``PyArg_ParseTupleAndKeywords()`` should be *exactly* the same
+ as the hand-written one in the existing function.
+
+ Well, there's one way that Argument Clinic's output is permitted
+ to be different. Argument Clinic always generates a format string
+ ending with ``:`` followed by the name of the function. If the
+ format string originally ended with ``;`` (to specify usage help),
+ this is harmless--don't worry about this difference.
+
+ Apart from that, if either of these things differ in *any way*,
+ fix your input to Argument Clinic and rerun ``Tools/clinic/clinic.py``
+ until they are the same.
+
+
+14. 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
+ 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.
+ The result should be a function that handles just the implementation
+ of the Python function without any argument-parsing code.
+
+ Sample::
+
+ /*[clinic]
+ module pickle
+ class pickle.Pickler
+ [clinic]*/
+ /*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+ PyDoc_STRVAR(pickle_Pickler_dump__doc__,
+ "Write a pickled representation of obj to the open file.\n"
+ "\n"
+ ...
+ static PyObject *
+ pickle_Pickler_dump_impl(PyObject *self, PyObject *obj)
+ /*[clinic 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. Compile and 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: ``inspect.signature()`` run on your function
+ should now provide a valid signature!
+
+ Congratulations, you've ported your first function to work with Argument Clinic!
+
+===============
+Advanced Topics
+===============
+
+
+Renaming the C functions generated by Argument Clinic
+-----------------------------------------------------
+
+Argument Clinic automatically names the functions it generates for you.
+Occasionally this may cause a problem, if the generated name collides with
+the name of an existing C function. There's an easy solution: you can explicitly
+specify the base name to use for the C functions. Just add the keyword ``"as"``
+to your function declaration line, followed by the function name you wish to use.
+Argument Clinic will use the function name you use for the base (generated) function,
+and then add ``"_impl"`` to the end for the name of the impl function.
+
+For example, if we wanted to rename the C function names generated for
+``pickle.Pickler.dump``, it'd look like this::
+
+ /*[clinic]
+ pickle.Pickler.dump as pickler_dumper
+
+ ...
+
+The base function would now be named ``pickler_dumper()``,
+and the impl function would be named ``pickler_dumper_impl()``.
+
+
+Optional Groups
+---------------
+
+Some legacy functions have a tricky approach to parsing their arguments:
+they count the number of positional arguments, then use a ``switch`` statement
+to call one of several different ``PyArg_ParseTuple()`` calls depending on
+how many positional arguments there are. (These functions cannot accept
+keyword-only arguments.) This approach was used to simulate optional
+arguments back before ``PyArg_ParseTupleAndKeywords()`` was created.
+
+Functions using this approach can often be converted to
+use ``PyArg_ParseTupleAndKeywords()``, optional arguments, and default values.
+But it's not always possible, because some of these legacy functions have
+behaviors ``PyArg_ParseTupleAndKeywords()`` can't directly support.
+The most obvious example is the builtin function ``range()``, which has
+an optional argument on the *left* side of its required argument!
+Another example is ``curses.window.addch()``, which has a group of two
+arguments that must always be specified together. (The arguments are
+called ``x`` and ``y``; if you call the function passing in ``x``,
+you must also pass in ``y``--and if you don't pass in ``x`` you may not
+pass in ``y`` either.)
+
+For the sake of backwards compatibility, Argument Clinic supports this
+alternate approach to parsing, using what are called *optional groups*.
+Optional groups are groups of arguments that can only be specified together.
+They can be to the left or the right of the required arguments. They
+can *only* be used with positional-only parameters.
+
+To specify an optional group, add a ``[`` on a line by itself before
+the parameters you wish to be
+in a group together, and a ``]`` on a line by itself after the
+parameters. As an example, here's how ``curses.window.addch``
+uses optional groups to make the first two parameters and the last
+parameter optional::
+
+ /*[clinic]
+
+ curses.window.addch
+
+ [
+ x: int
+ X-coordinate.
+ y: int
+ Y-coordinate.
+ ]
+
+ ch: object
+ Character to add.
+
+ [
+ attr: long
+ Attributes for the character.
+ ]
+ /
+
+ ...
+
+
+Notes:
+
+* For every optional group, one additional parameter will be passed into the
+ impl function representing the group. The parameter will be an int, and it will
+ be named ``group_{direction}_{number}``,
+ where ``{direction}`` is either ``right`` or ``left`` depending on whether the group
+ is before or after the required parameters, and ``{number}`` is a monotonically
+ increasing number (starting at 1) indicating how far away the group is from
+ the required parameters. When the impl is called, this parameter will be set
+ to zero if this group was unused, and set to non-zero if this group was used.
+ (By used or unused, I mean whether or not the parameters received arguments
+ in this invocation.)
+
+* If there are no required arguments, the optional groups will behave
+ as if they are to the right of the required arguments.
+
+* In the case of ambiguity, the argument parsing code
+ favors parameters on the left (before the required parameters).
+
+* Optional groups are *only* intended for legacy code. Please do not
+ use optional groups for new code.
+
+
+Using real Argument Clinic converters, instead of "legacy converters"
+---------------------------------------------------------------------
+
+To save time, and to minimize how much you need to learn
+to achieve your first port to Argument Clinic, the walkthrough above tells
+you to use the "legacy converters". "Legacy converters" are a convenience,
+designed explicitly to make porting existing code to Argument Clinic
+easier. And to be clear, their use is entirely acceptable when porting
+code for Python 3.4.
+
+However, in the long term we probably want all our blocks to
+use Argument Clinic's real syntax for converters. Why? A couple
+reasons:
+
+* The proper converters are far easier to read and clearer in their intent.
+* There are some format units that are unsupported as "legacy converters",
+ because they require arguments, and the legacy converter syntax doesn't
+ support specifying arguments.
+* In the future we may have a new argument parsing library that isn't
+ restricted to what ``PyArg_ParseTuple()`` supports.
+
+So if you want
+to go that extra effort, you should consider using normal
+converters instead of the legacy converters.
+
+In a nutshell, the syntax for Argument Clinic (non-legacy) converters
+looks like a Python function call. However, if there are no explicit
+arguments to the function (all functions take their default values),
+you may omit the parentheses. Thus ``bool`` and ``bool()`` are exactly
+the same. All parameters to Argument Clinic converters are keyword-only.
+
+All Argument Clinic converters accept the following arguments:
+
+``doc_default``
+ If the parameter takes a default value, normally this value is also
+ provided in the ``inspect.Signature`` metadata, and displayed in the
+ docstring. ``doc_default`` lets you override the value used in these
+ two places: pass in a string representing the Python value you wish
+ to use in these two contexts.
+
+``required``
+ If a parameter takes a default value, Argument Clinic infers that the
+ parameter is optional. However, you may want a parameter to take a
+ default value in C, but not behave in Python as if the parameter is
+ optional. Passing in ``required=True`` to a converter tells Argument
+ Clinic that this parameter is not optional, even if it has a default
+ value.
+
+``annotation``
+ The annotation value for this parameter. Not currently supported,
+ because PEP 8 mandates that the Python library may not use
+ annotations.
+
+Below is a table showing the mapping of legacy converters into real
+Argument Clinic converters. On the left is the legacy converter,
+on the right is the text you'd replace it with.
+
+========= =================================================================================
+``'B'`` ``byte(bitwise=True)``
+``'b'`` ``byte``
+``'c'`` ``char``
+``'C'`` ``int(types='str')``
+``'d'`` ``double``
+``'D'`` ``Py_complex``
+``'es#'`` ``str(encoding='name_of_encoding', length=True, zeroes=True)``
+``'es'`` ``str(encoding='name_of_encoding')``
+``'et#'`` ``str(encoding='name_of_encoding', types='bytes bytearray str', length=True)``
+``'et'`` ``str(encoding='name_of_encoding', types='bytes bytearray str')``
+``'f'`` ``float``
+``'h'`` ``short``
+``'H'`` ``unsigned_short``
+``'i'`` ``int``
+``'I'`` ``unsigned_int``
+``'K'`` ``unsigned_PY_LONG_LONG``
+``'L'`` ``PY_LONG_LONG``
+``'n'`` ``Py_ssize_t``
+``'O!'`` ``object(type='name_of_Python_type')``
+``'O&'`` ``object(converter='name_of_c_function')``
+``'O'`` ``object``
+``'p'`` ``bool``
+``'s#'`` ``str(length=True)``
+``'S'`` ``PyBytesObject``
+``'s'`` ``str``
+``'s*'`` ``Py_buffer(types='str bytes bytearray buffer')``
+``'u#'`` ``Py_UNICODE(length=True)``
+``'u'`` ``Py_UNICODE``
+``'U'`` ``unicode``
+``'w*'`` ``Py_buffer(types='bytearray rwbuffer')``
+``'y#'`` ``str(type='bytes', length=True)``
+``'Y'`` ``PyByteArrayObject``
+``'y'`` ``str(type='bytes')``
+``'y*'`` ``Py_buffer``
+``'Z#'`` ``Py_UNICODE(nullable=True, length=True)``
+``'z#'`` ``str(nullable=True, length=True)``
+``'Z'`` ``Py_UNICODE(nullable=True)``
+``'z'`` ``str(nullable=True)``
+``'z*'`` ``Py_buffer(types='str bytes bytearray buffer', nullable=True)``
+========= =================================================================================
+
+As an example, here's our sample ``pickle.Pickler.dump`` using the proper
+converter::
+
+ /*[clinic]
+ pickle.Pickler.dump
+
+ obj: object
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic]*/
+
+Argument Clinic will show you all the converters it has
+available. For each converter it'll show you all the parameters
+it accepts, along with the default value for each parameter.
+Just run ``Tools/clinic/clinic.py --converters`` to see the full list.
+
+
+Advanced converters
+-------------------
+
+Remeber those format units you skipped for your first
+time because they were advanced? Here's how to handle those too.
+
+The trick is, all those format units take arguments--either
+conversion functions, or types, or strings specifying an encoding.
+(But "legacy converters" don't support arguments. That's why we
+skipped them for your first function.) The argument you specified
+to the format unit is now an argument to the converter; this
+argument is either ``converter`` (for ``O&``), ``type`` (for ``O!``),
+or ``encoding`` (for all the format units that start with ``e``).
+
+Note that ``object()`` must explicitly support each Python type you specify
+for the ``type`` argument. Currently it only supports ``str``. It should be
+easy to add more, just edit ``Tools/clinic/clinic.py``, search for ``O!`` in
+the text, and add more entries to the dict mapping types to strings just above it.
+
+Note also that this approach takes away some possible flexibility for the format
+units starting with ``e``. It used to be possible to decide at runtime what
+encoding string to pass in to ``PyArg_ParseTuple()``. But now this string must
+be hard-coded at compile-time. This limitation is deliberate; it made supporting
+this format unit much easier, and may allow for future compile-time optimizations.
+This restriction does not seem unreasonable; CPython itself always passes in static
+hard-coded strings when using format units starting with ``e``.
+
+
+Using a return converter
+------------------------
+
+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 converting 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. 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.
+
+(If you use both ``"as"`` *and* a return converter for your function,
+the ``"as"`` should come before the return converter.)
+
+There's one additional complication when using return converters: how do you
+indicate an error has occured? Normally, a function returns a valid (non-``NULL``)
+pointer for success, and ``NULL`` for failure. But if you use an integer return converter,
+all integers are valid. How can Argument Clinic detect an error? Its solution: each return
+converter implicitly looks for a special value that indicates an error. If you return
+that value, and an error has been set (``PyErr_Occurred()`` returns a true
+value), then the generated code will propogate the error. Otherwise it will
+encode the value you return like normal.
+
+Currently Argument Clinic supports only a few return converters::
+
+ int
+ long
+ Py_ssize_t
+ DecodeFSDefault
+
+None of these take parameters. For the first three, return -1 to indicate
+error. For ``DecodeFSDefault``, the return type is ``char *``; return a NULL
+pointer to indicate an error.
+
+Calling Python code
+-------------------
+
+The rest of the advanced topics require you to write Python code
+which lives inside your C file and modifies Argument Clinic at
+runtime. This is simple; you simply define a Python block.
+
+A Python block uses different delimiter lines than an Argument
+Clinic function block. It looks like this::
+
+ /*[python]
+ # python code goes here
+ [python]*/
+
+All the code inside the Python block is executed at the
+time it's parsed. All text written to stdout inside the block
+is redirected into the "output" after the block.
+
+As an example, here's a Python block that adds a static integer
+variable to the C code::
+
+ /*[python]
+ print('static int __ignored_unused_variable__ = 0;')
+ [python]*/
+ static int __ignored_unused_variable__ = 0;
+ /*[python checksum:...]*/
+
+
+Using a "self converter"
+------------------------
+
+Argument Clinic automatically adds a "self" parameter for you
+using a default converter. However, you can override
+Argument Clinic's converter and specify one yourself.
+Just add your own ``self`` parameter as the first parameter in a
+block, and ensure that its converter is an instance of
+``self_converter`` or a subclass thereof.
+
+What's the point? This lets you automatically cast ``self``
+from ``PyObject *`` to a custom type.
+
+How do you specify the custom type you want to cast ``self`` to?
+If you only have one or two functions with the same type for ``self``,
+you can directly use Argument Clinic's existing ``self`` converter,
+passing in the type you want to use as the ``type`` parameter::
+
+ /*[clinic]
+
+ _pickle.Pickler.dump
+
+ self: self(type="PicklerObject *")
+ obj: object
+ /
+
+ Write a pickled representation of the given object to the open file.
+ [clinic]*/
+
+On the other hand, if you have a lot of functions that will use the same
+type for ``self``, it's best to create your own converter, subclassing
+``self_converter`` but overwriting the ``type`` member::
+
+ /*[clinic]
+ class PicklerObject_converter(self_converter):
+ type = "PicklerObject *"
+ [clinic]*/
+
+ /*[clinic]
+
+ _pickle.Pickler.dump
+
+ self: PicklerObject
+ obj: object
+ /
+
+ Write a pickled representation of the given object to the open file.
+ [clinic]*/
+
+
+
+Writing a custom converter
+--------------------------
+
+As we hinted at in the previous section... you can write your own converters!
+A converter is simply a Python class that inherits from ``CConverter``.
+The main purpose of a custom converter is if you have a parameter using
+the ``O&`` format unit--parsing this parameter means calling
+a ``PyArg_ParseTuple()`` "converter function".
+
+Your converter class should be named ``*something*_converter``.
+If the name follows this convention, then your converter class
+will be automatically registered with Argument Clinic; its name
+will be the name of your class with the ``_converter`` suffix
+stripped off. (This is done automatically for you with a metaclass.)
+
+You shouldn't subclass ``CConverter.__init__``. Instead, you should
+write a ``converter_init()`` function. ``converter_init()``
+always accepts a ``self`` parameter; after that, all additional
+parameters *must* be keyword-only. Any arguments passed in to
+the converter in Argument Clinic will be passed along to your
+``converter_init()``.
+
+There are some additional members of ``CConverter`` you may wish
+to specify in your subclass. Here's the current list:
+
+``type``
+ The C type to use for this variable.
+ ``type`` should be a Python string specifying the type, e.g. ``int``.
+ If this is a pointer type, the type string should end with ``' *'``.
+
+``default``
+ The Python default value for this parameter, as a Python value.
+ Or the magic value ``unspecified`` if there is no default.
+
+``doc_default``
+ ``default`` as it should appear in the documentation,
+ as a string.
+ Or ``None`` if there is no default.
+ This string, when run through ``eval()``, should produce
+ a Python value.
+
+``py_default``
+ ``default`` as it should appear in Python code,
+ as a string.
+ Or ``None`` if there is no default.
+
+``c_default``
+ ``default`` as it should appear in C code,
+ as a string.
+ Or ``None`` if there is no default.
+
+``c_ignored_default``
+ The default value used to initialize the C variable when
+ there is no default, but not specifying a default may
+ result in an "uninitialized variable" warning. This is
+ easily happen when using option groups--although
+ properly-written code won't actually use the variable,
+ 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 be a string.
+
+``converter``
+ The name of the C converter function, as a string.
+
+``impl_by_reference``
+ A boolean value. If true,
+ Argument Clinic will add a ``&`` in front of the name of
+ the variable when passing it into the impl function.
+
+``parse_by_reference``
+ A boolean value. If true,
+ Argument Clinic will add a ``&`` in front of the name of
+ the variable when passing it into ``PyArg_ParseTuple()``.
+
+
+Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``::
+
+ /*[python]
+
+ class uint_converter(CConverter):
+ type = 'unsigned int'
+ converter = 'uint_converter'
+
+ [python]*/
+ /*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+This block adds a ``uint`` converter to Argument Clinic. Parameters
+declared as ``uint`` will be declared as type ``unsigned int``, and will
+be parsed by calling the ``uint_converter`` converter function in C.
+``uint`` variables automatically support default values.
+
+More sophisticated custom converters can insert custom C code to
+handle initialization and cleanup.
+You can see more examples of custom converters in the CPython
+source tree; grep the C files for the string ``CConverter``.
+
+Writing a custom return converter
+---------------------------------
+
+Writing a custom return converter is much like writing
+a custom converter. Except it's much simpler, because return
+converters are themselves much simpler.
+
+Return converters must subclass ``CReturnConverter``.
+There are no examples yet of custom return converters,
+because they are not widely used yet. If you wish to
+write your own return converter, please read ``Tools/clinic/clinic.py``,
+specifically the implementation of ``CReturnConverter`` and
+all its subclasses.
+
+
+Using Argument Clinic in Python files
+-------------------------------------
+
+It's actually possible to use Argument Clinic to preprocess Python files.
+There's no point to using Argument Clinic blocks, of course, as the output
+wouldn't make any sense to the Python interpreter. But using Argument Clinic
+to run Python blocks lets you use Python as a Python preprocessor!
+
+Since Python comments are different from C comments, Argument Clinic
+blocks embedded in Python files look slightly different. They look like this::
+
+ #/*[python]
+ #print("def foo(): pass")
+ #[python]*/
+ def foo(): pass
+ #/*[python checksum:...]*/
diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst
index 81a4f8b..2c9d699 100644
--- a/Doc/howto/index.rst
+++ b/Doc/howto/index.rst
@@ -28,4 +28,5 @@ Currently, the HOWTOs are:
webservers.rst
argparse.rst
ipaddress.rst
+ clinic.rst
diff --git a/Misc/NEWS b/Misc/NEWS
index e8f86df..7b7b30d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -343,6 +343,8 @@ Documentation
Tools/Demos
-----------
+- Issue #19659: Added documentation for Argument Clinic.
+
- Issue #19976: Argument Clinic METH_NOARGS functions now always
take two parameters.
diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c
index 72137db..d197d88 100644
--- a/Modules/zlibmodule.c
+++ b/Modules/zlibmodule.c
@@ -312,9 +312,6 @@ class uint_converter(CConverter):
type = 'unsigned int'
converter = 'uint_converter'
-class compobject_converter(self_converter):
- type = "compobject *"
-
[python]*/
/*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
@@ -750,7 +747,7 @@ save_unconsumed_input(compobject *self, int err)
zlib.Decompress.decompress
- self: compobject
+ self: self(type="compobject *")
data: Py_buffer
The binary data to decompress.
@@ -1032,7 +1029,7 @@ PyZlib_flush(compobject *self, PyObject *args)
/*[clinic]
zlib.Compress.copy
- self: compobject
+ self: self(type="compobject *")
Return a copy of the compression object.
[clinic]*/
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 7dd6215..5480add 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -1296,11 +1296,13 @@ class CConverter(metaclass=CConverterAutoRegister):
must be keyword-only.
"""
+ # The C type to use for this variable.
+ # 'type' should be a Python string specifying the type, e.g. "int".
+ # If this is a pointer type, the type string should end with ' *'.
type = None
- format_unit = 'O&'
# The Python default value for this parameter, as a Python value.
- # Or "unspecified" if there is no default.
+ # Or the magic value "unspecified" if there is no default.
default = unspecified
# "default" as it should appear in the documentation, as a string.
@@ -1330,9 +1332,32 @@ class CConverter(metaclass=CConverterAutoRegister):
# (If this is not None, format_unit must be 'O&'.)
converter = None
- encoding = None
+ # Should Argument Clinic add a '&' before the name of
+ # the variable when passing it into the _impl function?
impl_by_reference = False
+
+ # Should Argument Clinic add a '&' before the name of
+ # the variable when passing it into PyArg_ParseTuple (AndKeywords)?
parse_by_reference = True
+
+ #############################################################
+ #############################################################
+ ## You shouldn't need to read anything below this point to ##
+ ## write your own converter functions. ##
+ #############################################################
+ #############################################################
+
+ # The "format unit" to specify for this variable when
+ # parsing arguments using PyArg_ParseTuple (AndKeywords).
+ # Custom converters should always use the default value of 'O&'.
+ format_unit = 'O&'
+
+ # What encoding do we want for this variable? Only used
+ # by format units starting with 'e'.
+ encoding = None
+
+ # Do we want an adjacent '_length' variable for this variable?
+ # Only used by format units ending with '#'.
length = False
def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False, annotation=unspecified, **kwargs):
@@ -1751,7 +1776,7 @@ class self_converter(CConverter):
this is the default converter used for "self".
"""
type = "PyObject *"
- def converter_init(self):
+ def converter_init(self, *, type=None):
f = self.function
if f.kind == CALLABLE:
if f.cls:
@@ -1766,6 +1791,9 @@ class self_converter(CConverter):
self.name = "cls"
self.type = "PyTypeObject *"
+ if type:
+ self.type = type
+
def render(self, parameter, data):
fail("render() should never be called on self_converter instances")
@@ -1787,7 +1815,13 @@ class CReturnConverterAutoRegister(type):
class CReturnConverter(metaclass=CReturnConverterAutoRegister):
+ # The C type to use for this variable.
+ # 'type' should be a Python string specifying the type, e.g. "int".
+ # If this is a pointer type, the type string should end with ' *'.
type = 'PyObject *'
+
+ # The Python default value for this parameter, as a Python value.
+ # Or the magic value "unspecified" if there is no default.
default = None
def __init__(self, *, doc_default=None, **kwargs):
@@ -1826,6 +1860,16 @@ class CReturnConverter(metaclass=CReturnConverterAutoRegister):
add_c_return_converter(CReturnConverter, 'object')
+class NoneType_return_converter(CReturnConverter):
+ def render(self, function, data):
+ self.declare(data)
+ data.return_conversion.append('''
+if (_return_value != Py_None)
+ goto exit;
+return_value = Py_None;
+Py_INCREF(Py_None);
+'''.strip())
+
class int_return_converter(CReturnConverter):
type = 'int'
@@ -2680,7 +2724,7 @@ def main(argv):
# print(" ", short_name + "".join(parameters))
print()
- print("All converters also accept (doc_default=None, required=False).")
+ print("All converters also accept (doc_default=None, required=False, annotation=None).")
print("All return converters also accept (doc_default=None).")
sys.exit(0)