Kaydet (Commit) 6d2ea213 authored tarafından Larry Hastings's avatar Larry Hastings

Argument Clinic: fixed test suite, improved howto.

üst 5ea97506
...@@ -14,21 +14,20 @@ Argument Clinic How-To ...@@ -14,21 +14,20 @@ Argument Clinic How-To
function to work with Argument Clinic, and then introduces function to work with Argument Clinic, and then introduces
some advanced topics on Argument Clinic usage. some advanced topics on Argument Clinic usage.
Argument Clinic is currently considered an internal Currently Argument Clinic is considered internal-only
tool for the CPython code tree. Its use is not supported for CPython. Its use is not supported for files outside
for files outside the CPython code tree, and no guarantees CPython, and no guarantees are made regarding backwards
are made regarding backwards compatibility for future compatibility for future versions. In other words: if you
versions. In other words: if you maintain an external C maintain an external C extension for CPython, you're welcome
extension for CPython, you're welcome to experiment with to experiment with Argument Clinic in your own code. But the
Argument Clinic in your own code. But the version of Argument version of Argument Clinic that ships with CPython 3.5 *could*
Clinic that ships with CPython 3.5 *could* be totally be totally incompatible and break all your code.
incompatible and break all your code.
======================== ========================
Basic Concepts And Usage Basic Concepts And Usage
======================== ========================
Argument Clinic ships with CPython. You can find it in ``Tools/clinic/clinic.py``. Argument Clinic ships with CPython; you'll find it in ``Tools/clinic/clinic.py``.
If you run that script, specifying a C file as an argument:: If you run that script, specifying a C file as an argument::
% python3 Tools/clinic/clinic.py foo.c % python3 Tools/clinic/clinic.py foo.c
...@@ -45,13 +44,12 @@ like this:: ...@@ -45,13 +44,12 @@ like this::
Everything in between these two lines is input for Argument Clinic. Everything in between these two lines is input for Argument Clinic.
All of these lines, including the beginning and ending comment All of these lines, including the beginning and ending comment
lines, are collectively called an Argument Clinic "input block", lines, are collectively called an Argument Clinic "block".
or "block" for short.
When Argument Clinic parses one of these blocks, it When Argument Clinic parses one of these blocks, it
generates output. This output is rewritten into the C file generates output. This output is rewritten into the C file
immediately after the block, followed by a comment containing a checksum. immediately after the block, followed by a comment containing a checksum.
The resulting Argument Clinic block looks like this:: The Argument Clinic block now looks like this::
/*[clinic] /*[clinic]
... clinic input goes here ... ... clinic input goes here ...
...@@ -65,7 +63,8 @@ line. However, if the input hasn't changed, the output won't change either. ...@@ -65,7 +63,8 @@ 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, 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 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.) checksum--to detect if someone changed the output, as these edits would be lost
the next time Argument Clinic writes out fresh output.)
For the sake of clarity, here's the terminology we'll use with Argument Clinic: For the sake of clarity, here's the terminology we'll use with Argument Clinic:
...@@ -87,10 +86,12 @@ Converting Your First Function ...@@ -87,10 +86,12 @@ Converting Your First Function
The best way to get a sense of how Argument Clinic works is to 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! convert a function to work with it. Let's dive in!
0. Make sure you're working with a freshly updated trunk. 0. Make sure you're working with a freshly updated checkout
of the CPython trunk.
1. Find a Python builtin that calls either ``PyArg_ParseTuple()`` 1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
or ``PyArg_ParseTupleAndKeywords()``, and hasn't been converted yet. or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted
to work with Argument Clinic yet.
For my example I'm using ``pickle.Pickler.dump()``. For my example I'm using ``pickle.Pickler.dump()``.
2. If the call to the ``PyArg_Parse`` function uses any of the 2. If the call to the ``PyArg_Parse`` function uses any of the
...@@ -103,7 +104,7 @@ convert a function to work with it. Let's dive in! ...@@ -103,7 +104,7 @@ convert a function to work with it. Let's dive in!
et et
et# et#
or if it has multiple calls to ``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. Argument Clinic *does*
support all of these scenarios. But these are advanced support all of these scenarios. But these are advanced
topics--let's do something simpler for your first function. topics--let's do something simpler for your first function.
...@@ -130,7 +131,7 @@ convert a function to work with it. Let's dive in! ...@@ -130,7 +131,7 @@ convert a function to work with it. Let's dive in!
be a paragraph consisting of a single 80-column line be a paragraph consisting of a single 80-column line
at the beginning of the docstring. at the beginning of the docstring.
(Our docstring consists solely of the summary line, so the sample (Our example docstring consists solely of a summary line, so the sample
code doesn't have to change for this step.) code doesn't have to change for this step.)
6. Above the docstring, enter the name of the function, followed 6. Above the docstring, enter the name of the function, followed
...@@ -198,7 +199,8 @@ convert a function to work with it. Let's dive in! ...@@ -198,7 +199,8 @@ convert a function to work with it. Let's dive in!
string. ("format unit" is the formal name for the one-to-three string. ("format unit" is the formal name for the one-to-three
character substring of the ``format`` parameter that tells character substring of the ``format`` parameter that tells
the argument parsing function what the type of the variable the argument parsing function what the type of the variable
is and how to convert it.) is and how to convert it. For more on format units please
see :ref:`arg-parsing`.)
For multicharacter format units like ``z#``, use the For multicharacter format units like ``z#``, use the
entire two-or-three character string. entire two-or-three character string.
...@@ -231,14 +233,18 @@ convert a function to work with it. Let's dive in! ...@@ -231,14 +233,18 @@ convert a function to work with it. Let's dive in!
(``pickle.Pickler.dump`` has neither, so our sample is unchanged.) (``pickle.Pickler.dump`` has neither, so our sample is unchanged.)
10. If the existing C function uses ``PyArg_ParseTuple()`` 10. If the existing C function calls :c:func:`PyArg_ParseTuple`
(instead of ``PyArg_ParseTupleAndKeywords()``), then all its (as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its
arguments are positional-only. arguments are positional-only.
To mark all parameters as positional-only in Argument Clinic, To mark all parameters as positional-only in Argument Clinic,
add a ``/`` on a line by itself after the last parameter, add a ``/`` on a line by itself after the last parameter,
indented the same as the parameter lines. 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:: Sample::
/*[clinic] /*[clinic]
...@@ -255,16 +261,16 @@ convert a function to work with it. Let's dive in! ...@@ -255,16 +261,16 @@ convert a function to work with it. Let's dive in!
Write a pickled representation of obj to the open file. Write a pickled representation of obj to the open file.
[clinic]*/ [clinic]*/
11. It's helpful to write a per-parameter docstring, indented 11. It's helpful to write a per-parameter docstring for each parameter.
another level past the parameter declaration. But per-parameter But per-parameter docstrings are optional; you can skip this step
docstrings are optional; you can skip this step if you prefer. if you prefer.
Here's how per-parameter docstrings work. The first line Here's how to add a per-parameter docstring. The first line
of the per-parameter docstring must be indented further than the of the per-parameter docstring must be indented further than the
parameter definition. This left margin establishes the left margin parameter definition. The left margin of this first line establishes
for the whole per-parameter docstring; all the text you write will the left margin for the whole per-parameter docstring; all the text
be outdented by this amount. You can write as much as you like, you write will be outdented by this amount. You can write as much
across multiple lines if you wish. text as you like, across multiple lines if you wish.
Sample:: Sample::
...@@ -311,28 +317,47 @@ convert a function to work with it. Let's dive in! ...@@ -311,28 +317,47 @@ convert a function to work with it. Let's dive in!
pickle_Pickler_dump_impl(PyObject *self, PyObject *obj) pickle_Pickler_dump_impl(PyObject *self, PyObject *obj)
/*[clinic checksum: 3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/ /*[clinic checksum: 3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
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.
13. Double-check that the argument-parsing code Argument Clinic generated 13. Double-check that the argument-parsing code Argument Clinic generated
looks basically the same as the existing code. looks basically the same as the existing code.
First, ensure both places use the same argument-parsing function. First, ensure both places use the same argument-parsing function.
The existing code must call either The existing code must call either
``PyArg_ParseTuple()`` or ``PyArg_ParseTupleAndKeywords()``; :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`;
ensure that the code generated by Argument Clinic calls the ensure that the code generated by Argument Clinic calls the
same function. *exact* same function.
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.
(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
*exactly* the same between the two invocations.
Second, the format string passed in to ``PyArg_ParseTuple()`` or Fourth, inside the output portion of the block you'll find a preprocessor
``PyArg_ParseTupleAndKeywords()`` should be *exactly* the same macro defining the appropriate static :c:type:`PyMethodDef` structure for
as the hand-written one in the existing function. this builtin::
Well, there's one way that Argument Clinic's output is permitted #define _PICKLE_PICKLER_DUMP_METHODDEF \
to be different. Argument Clinic always generates a format string {"dump", (PyCFunction)_pickle_Pickler_dump, METH_O, _pickle_Pickler_dump__doc__},
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*, This static structure should be *exactly* the same as the existing static
fix your input to Argument Clinic and rerun ``Tools/clinic/clinic.py`` :c:type:`PyMethodDef` structure for this builtin.
until they are the same.
If any of these items differ in *any way*,
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 14. Notice that the last line of its output is the declaration
...@@ -342,8 +367,19 @@ convert a function to work with it. Let's dive in! ...@@ -342,8 +367,19 @@ convert a function to work with it. Let's dive in!
declarations of all the variables it dumps the arguments into. declarations of all the variables it dumps the arguments into.
Notice how the Python arguments are now arguments to this impl function; Notice how the Python arguments are now arguments to this impl function;
if the implementation used different names for these variables, fix it. 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. Let's reiterate, just because it's kind of weird. Your code should now
look like this::
static return_type
your_function_impl(...)
/*[clinic checksum: ...]*/
{
...
Argument Clinic generated the checksum line and the function prototype just
above it. You should write the opening (and closing) curly braces for the
function, and the implementation inside.
Sample:: Sample::
...@@ -386,7 +422,27 @@ convert a function to work with it. Let's dive in! ...@@ -386,7 +422,27 @@ convert a function to work with it. Let's dive in!
... ...
15. Compile and run the relevant portions of the regression-test suite. 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
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.)
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. Compile, then run the relevant portions of the regression-test suite.
This change should not introduce any new compile-time warnings or errors, This change should not introduce any new compile-time warnings or errors,
and there should be no externally-visible change to Python's behavior. and there should be no externally-visible change to Python's behavior.
...@@ -405,11 +461,11 @@ Renaming the C functions generated by Argument Clinic ...@@ -405,11 +461,11 @@ Renaming the C functions generated by Argument Clinic
Argument Clinic automatically names the functions it generates for you. Argument Clinic automatically names the functions it generates for you.
Occasionally this may cause a problem, if the generated name collides with 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 the name of an existing C function. There's an easy solution: override the names
specify the base name to use for the C functions. Just add the keyword ``"as"`` used for the C functions. Just add the keyword ``"as"``
to your function declaration line, followed by the function name you wish to use. 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, Argument Clinic will use that function name for the base (generated) function,
and then add ``"_impl"`` to the end for the name of the impl function. then add ``"_impl"`` to the end and use that for the name of the impl function.
For example, if we wanted to rename the C function names generated for For example, if we wanted to rename the C function names generated for
``pickle.Pickler.dump``, it'd look like this:: ``pickle.Pickler.dump``, it'd look like this::
...@@ -420,7 +476,7 @@ For example, if we wanted to rename the C function names generated for ...@@ -420,7 +476,7 @@ For example, if we wanted to rename the C function names generated for
... ...
The base function would now be named ``pickler_dumper()``, The base function would now be named ``pickler_dumper()``,
and the impl function would be named ``pickler_dumper_impl()``. and the impl function would now be named ``pickler_dumper_impl()``.
Optional Groups Optional Groups
...@@ -428,15 +484,15 @@ Optional Groups ...@@ -428,15 +484,15 @@ Optional Groups
Some legacy functions have a tricky approach to parsing their arguments: Some legacy functions have a tricky approach to parsing their arguments:
they count the number of positional arguments, then use a ``switch`` statement they count the number of positional arguments, then use a ``switch`` statement
to call one of several different ``PyArg_ParseTuple()`` calls depending on to call one of several different :c:func:`PyArg_ParseTuple` calls depending on
how many positional arguments there are. (These functions cannot accept how many positional arguments there are. (These functions cannot accept
keyword-only arguments.) This approach was used to simulate optional keyword-only arguments.) This approach was used to simulate optional
arguments back before ``PyArg_ParseTupleAndKeywords()`` was created. arguments back before :c:func:`PyArg_ParseTupleAndKeywords` was created.
Functions using this approach can often be converted to While functions using this approach can often be converted to
use ``PyArg_ParseTupleAndKeywords()``, optional arguments, and default values. use :c:func:`PyArg_ParseTupleAndKeywords`, optional arguments, and default values,
But it's not always possible, because some of these legacy functions have it's not always possible. Some of these legacy functions have
behaviors ``PyArg_ParseTupleAndKeywords()`` can't directly support. behaviors :c:func:`PyArg_ParseTupleAndKeywords` doesn't directly support.
The most obvious example is the builtin function ``range()``, which has The most obvious example is the builtin function ``range()``, which has
an optional argument on the *left* side of its required argument! an optional argument on the *left* side of its required argument!
Another example is ``curses.window.addch()``, which has a group of two Another example is ``curses.window.addch()``, which has a group of two
...@@ -445,16 +501,17 @@ called ``x`` and ``y``; if you call the function passing in ``x``, ...@@ -445,16 +501,17 @@ 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 you must also pass in ``y``--and if you don't pass in ``x`` you may not
pass in ``y`` either.) pass in ``y`` either.)
For the sake of backwards compatibility, Argument Clinic supports this In any case, the goal of Argument Clinic is to support argument parsing
alternate approach to parsing, using what are called *optional groups*. for all existing CPython builtins without changing their semantics.
Optional groups are groups of arguments that can only be specified together. Therefore Argument Clinic supports
this alternate approach to parsing, using what are called *optional groups*.
Optional groups are groups of arguments that must all be passed in together.
They can be to the left or the right of the required arguments. They They can be to the left or the right of the required arguments. They
can *only* be used with positional-only parameters. can *only* be used with positional-only parameters.
To specify an optional group, add a ``[`` on a line by itself before To specify an optional group, add a ``[`` on a line by itself before
the parameters you wish to be the parameters you wish to group together, and a ``]`` on a line by itself
in a group together, and a ``]`` on a line by itself after the after these parameters. As an example, here's how ``curses.window.addch``
parameters. As an example, here's how ``curses.window.addch``
uses optional groups to make the first two parameters and the last uses optional groups to make the first two parameters and the last
parameter optional:: parameter optional::
...@@ -484,8 +541,8 @@ parameter optional:: ...@@ -484,8 +541,8 @@ parameter optional::
Notes: Notes:
* For every optional group, one additional parameter will be passed into the * 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 impl function representing the group. The parameter will be an int named
be named ``group_{direction}_{number}``, ``group_{direction}_{number}``,
where ``{direction}`` is either ``right`` or ``left`` depending on whether the group where ``{direction}`` is either ``right`` or ``left`` depending on whether the group
is before or after the required parameters, and ``{number}`` is a monotonically 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 increasing number (starting at 1) indicating how far away the group is from
...@@ -495,11 +552,13 @@ Notes: ...@@ -495,11 +552,13 @@ Notes:
in this invocation.) in this invocation.)
* If there are no required arguments, the optional groups will behave * If there are no required arguments, the optional groups will behave
as if they are to the right of the required arguments. as if they're to the right of the required arguments.
* In the case of ambiguity, the argument parsing code * In the case of ambiguity, the argument parsing code
favors parameters on the left (before the required parameters). favors parameters on the left (before the required parameters).
* Optional groups can only contain positional-only parameters.
* Optional groups are *only* intended for legacy code. Please do not * Optional groups are *only* intended for legacy code. Please do not
use optional groups for new code. use optional groups for new code.
...@@ -509,7 +568,7 @@ Using real Argument Clinic converters, instead of "legacy converters" ...@@ -509,7 +568,7 @@ Using real Argument Clinic converters, instead of "legacy converters"
To save time, and to minimize how much you need to learn To save time, and to minimize how much you need to learn
to achieve your first port to Argument Clinic, the walkthrough above tells to achieve your first port to Argument Clinic, the walkthrough above tells
you to use the "legacy converters". "Legacy converters" are a convenience, you to use "legacy converters". "Legacy converters" are a convenience,
designed explicitly to make porting existing code to Argument Clinic designed explicitly to make porting existing code to Argument Clinic
easier. And to be clear, their use is entirely acceptable when porting easier. And to be clear, their use is entirely acceptable when porting
code for Python 3.4. code for Python 3.4.
...@@ -523,18 +582,19 @@ reasons: ...@@ -523,18 +582,19 @@ reasons:
because they require arguments, and the legacy converter syntax doesn't because they require arguments, and the legacy converter syntax doesn't
support specifying arguments. support specifying arguments.
* In the future we may have a new argument parsing library that isn't * In the future we may have a new argument parsing library that isn't
restricted to what ``PyArg_ParseTuple()`` supports. restricted to what :c:func:`PyArg_ParseTuple` supports; this flexibility
won't be available to parameters using legacy converters.
So if you want Therefore, if you don't mind a little extra effort, you should consider
to go that extra effort, you should consider using normal using normal converters instead of legacy converters.
converters instead of the legacy converters.
In a nutshell, the syntax for Argument Clinic (non-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 looks like a Python function call. However, if there are no explicit
arguments to the function (all functions take their default values), arguments to the function (all functions take their default values),
you may omit the parentheses. Thus ``bool`` and ``bool()`` are exactly you may omit the parentheses. Thus ``bool`` and ``bool()`` are exactly
the same. All parameters to Argument Clinic converters are keyword-only. the same converters.
All arguments to Argument Clinic converters are keyword-only.
All Argument Clinic converters accept the following arguments: All Argument Clinic converters accept the following arguments:
``doc_default`` ``doc_default``
...@@ -643,11 +703,11 @@ the text, and add more entries to the dict mapping types to strings just above i ...@@ -643,11 +703,11 @@ the text, and add more entries to the dict mapping types to strings just above i
Note also that this approach takes away some possible flexibility for the format 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 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 encoding string to pass in to :c:func:`PyArg_ParseTuple`. But now this string must
be hard-coded at compile-time. This limitation is deliberate; it made supporting 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 format unit much easier, and may allow for future compile-time optimizations.
This restriction does not seem unreasonable; CPython itself always passes in static This restriction does not seem unreasonable; CPython itself always passes in static
hard-coded strings when using format units starting with ``e``. hard-coded encoding strings for parameters whose format units start with ``e``.
Using a return converter Using a return converter
...@@ -692,12 +752,17 @@ None of these take parameters. For the first three, return -1 to indicate ...@@ -692,12 +752,17 @@ None of these take parameters. For the first three, return -1 to indicate
error. For ``DecodeFSDefault``, the return type is ``char *``; return a NULL error. For ``DecodeFSDefault``, the return type is ``char *``; return a NULL
pointer to indicate an error. pointer to indicate an error.
To see all the return converters Argument Clinic supports, along with
their parameters (if any),
just run ``Tools/clinic/clinic.py --converters`` for the full list.
Calling Python code Calling Python code
------------------- -------------------
The rest of the advanced topics require you to write 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 which lives inside your C file and modifies Argument Clinic's
runtime. This is simple; you simply define a Python block. runtime state. This is simple: you simply define a Python block.
A Python block uses different delimiter lines than an Argument A Python block uses different delimiter lines than an Argument
Clinic function block. It looks like this:: Clinic function block. It looks like this::
...@@ -778,13 +843,13 @@ As we hinted at in the previous section... you can write your own converters! ...@@ -778,13 +843,13 @@ 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``. 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 main purpose of a custom converter is if you have a parameter using
the ``O&`` format unit--parsing this parameter means calling the ``O&`` format unit--parsing this parameter means calling
a ``PyArg_ParseTuple()`` "converter function". a :c:func:`PyArg_ParseTuple` "converter function".
Your converter class should be named ``*something*_converter``. Your converter class should be named ``*something*_converter``.
If the name follows this convention, then your converter class If the name follows this convention, then your converter class
will be automatically registered with Argument Clinic; its name will be automatically registered with Argument Clinic; its name
will be the name of your class with the ``_converter`` suffix will be the name of your class with the ``_converter`` suffix
stripped off. (This is done automatically for you with a metaclass.) stripped off. (This is accomplished with a metaclass.)
You shouldn't subclass ``CConverter.__init__``. Instead, you should You shouldn't subclass ``CConverter.__init__``. Instead, you should
write a ``converter_init()`` function. ``converter_init()`` write a ``converter_init()`` function. ``converter_init()``
...@@ -825,12 +890,13 @@ to specify in your subclass. Here's the current list: ...@@ -825,12 +890,13 @@ to specify in your subclass. Here's the current list:
``c_ignored_default`` ``c_ignored_default``
The default value used to initialize the C variable when The default value used to initialize the C variable when
there is no default, but not specifying a default may there is no default, but not specifying a default may
result in an "uninitialized variable" warning. This is result in an "uninitialized variable" warning. This can
easily happen when using option groups--although easily happen when using option groups--although
properly-written code won't actually use the variable, properly-written code will never actually use this value,
the variable does get passed in to the _impl, and the the variable does get passed in to the impl, and the
C compiler will complain about the "use" of the uninitialized C compiler will complain about the "use" of the
value. This value should be a string. uninitialized value. This value should always be a
non-empty string.
``converter`` ``converter``
The name of the C converter function, as a string. The name of the C converter function, as a string.
...@@ -843,7 +909,7 @@ to specify in your subclass. Here's the current list: ...@@ -843,7 +909,7 @@ to specify in your subclass. Here's the current list:
``parse_by_reference`` ``parse_by_reference``
A boolean value. If true, A boolean value. If true,
Argument Clinic will add a ``&`` in front of the name of Argument Clinic will add a ``&`` in front of the name of
the variable when passing it into ``PyArg_ParseTuple()``. the variable when passing it into :c:func:`PyArg_ParseTuple`.
Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``:: Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``::
...@@ -857,9 +923,10 @@ Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c`` ...@@ -857,9 +923,10 @@ Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``
[python]*/ [python]*/
/*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ /*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
This block adds a ``uint`` converter to Argument Clinic. Parameters This block adds a converter to Argument Clinic named ``uint``. Parameters
declared as ``uint`` will be declared as type ``unsigned int``, and will declared as ``uint`` will be declared as type ``unsigned int``, and will
be parsed by calling the ``uint_converter`` converter function in C. be parsed by the ``'O&'`` format unit, which will call the ``uint_converter``
converter function.
``uint`` variables automatically support default values. ``uint`` variables automatically support default values.
More sophisticated custom converters can insert custom C code to More sophisticated custom converters can insert custom C code to
...@@ -871,7 +938,7 @@ Writing a custom return converter ...@@ -871,7 +938,7 @@ Writing a custom return converter
--------------------------------- ---------------------------------
Writing a custom return converter is much like writing Writing a custom return converter is much like writing
a custom converter. Except it's much simpler, because return a custom converter. Except it's somewhat simpler, because return
converters are themselves much simpler. converters are themselves much simpler.
Return converters must subclass ``CReturnConverter``. Return converters must subclass ``CReturnConverter``.
......
...@@ -997,7 +997,8 @@ class BlockPrinter: ...@@ -997,7 +997,8 @@ class BlockPrinter:
# "languages" maps the name of the language ("C", "Python"). # "languages" maps the name of the language ("C", "Python").
# "extensions" maps the file extension ("c", "py"). # "extensions" maps the file extension ("c", "py").
languages = { 'C': CLanguage, 'Python': PythonLanguage } languages = { 'C': CLanguage, 'Python': PythonLanguage }
extensions = { 'c': CLanguage, 'h': CLanguage, 'py': PythonLanguage } extensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
extensions['py'] = PythonLanguage
# maps strings to callables. # maps strings to callables.
...@@ -2430,9 +2431,6 @@ class DSLParser: ...@@ -2430,9 +2431,6 @@ class DSLParser:
# the final stanza of the DSL is the docstring. # the final stanza of the DSL is the docstring.
def state_function_docstring(self, line): def state_function_docstring(self, line):
if not self.function.self_converter:
self.function.self_converter = self_converter("self", self.function)
if self.group: if self.group:
fail("Function " + self.function.name + " has a ] without a matching [.") fail("Function " + self.function.name + " has a ] without a matching [.")
...@@ -2604,6 +2602,9 @@ class DSLParser: ...@@ -2604,6 +2602,9 @@ class DSLParser:
if not self.function: if not self.function:
return return
if not self.function.self_converter:
self.function.self_converter = self_converter("self", self.function)
if self.keyword_only: if self.keyword_only:
values = self.function.parameters.values() values = self.function.parameters.values()
if not values: if not values:
......
...@@ -296,9 +296,9 @@ os.stat as os_stat_fn ...@@ -296,9 +296,9 @@ os.stat as os_stat_fn
Perform a stat system call on the given path.""") Perform a stat system call on the given path.""")
self.assertEqual(""" self.assertEqual("""
stat(path)
Perform a stat system call on the given path. Perform a stat system call on the given path.
os.stat(path)
path path
Path to be examined Path to be examined
""".strip(), function.docstring) """.strip(), function.docstring)
...@@ -316,9 +316,9 @@ This is the documentation for foo. ...@@ -316,9 +316,9 @@ This is the documentation for foo.
Okay, we're done here. Okay, we're done here.
""") """)
self.assertEqual(""" self.assertEqual("""
bar(x, y)
This is the documentation for foo. This is the documentation for foo.
foo.bar(x, y)
x x
Documentation for x. Documentation for x.
...@@ -356,7 +356,7 @@ This/used to break Clinic! ...@@ -356,7 +356,7 @@ This/used to break Clinic!
def test_left_group(self): def test_left_group(self):
function = self.parse_function(""" function = self.parse_function("""
module curses module curses
curses.window.addch curses.addch
[ [
y: int y: int
Y-coordinate. Y-coordinate.
...@@ -380,7 +380,9 @@ curses.window.addch ...@@ -380,7 +380,9 @@ curses.window.addch
self.assertEqual(p.group, group) self.assertEqual(p.group, group)
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.assertEqual(function.docstring.strip(), """ self.assertEqual(function.docstring.strip(), """
curses.window.addch([y, x,] ch, [attr]) addch([y, x,] ch, [attr])
y y
Y-coordinate. Y-coordinate.
x x
...@@ -394,7 +396,7 @@ curses.window.addch([y, x,] ch, [attr]) ...@@ -394,7 +396,7 @@ curses.window.addch([y, x,] ch, [attr])
def test_nested_groups(self): def test_nested_groups(self):
function = self.parse_function(""" function = self.parse_function("""
module curses module curses
curses.window.imaginary curses.imaginary
[ [
[ [
y1: int y1: int
...@@ -439,7 +441,9 @@ curses.window.imaginary ...@@ -439,7 +441,9 @@ curses.window.imaginary
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.assertEqual(function.docstring.strip(), """ self.assertEqual(function.docstring.strip(), """
curses.window.imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, attr6]]) imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, attr6]])
y1 y1
Y-coordinate. Y-coordinate.
y2 y2
...@@ -557,7 +561,7 @@ foo.bar ...@@ -557,7 +561,7 @@ foo.bar
Docstring Docstring
""") """)
self.assertEqual("Docstring\n\nfoo.bar()", function.docstring) self.assertEqual("bar()\nDocstring", function.docstring)
self.assertEqual(0, len(function.parameters)) self.assertEqual(0, len(function.parameters))
def test_illegal_module_line(self): def test_illegal_module_line(self):
...@@ -652,9 +656,9 @@ foo.bar ...@@ -652,9 +656,9 @@ foo.bar
Not at column 0! Not at column 0!
""") """)
self.assertEqual(""" self.assertEqual("""
bar(x, *, y)
Not at column 0! Not at column 0!
foo.bar(x, *, y)
x x
Nested docstring here, goeth. Nested docstring here, goeth.
""".strip(), function.docstring) """.strip(), function.docstring)
...@@ -666,7 +670,7 @@ os.stat ...@@ -666,7 +670,7 @@ os.stat
path: str path: str
This/used to break Clinic! This/used to break Clinic!
""") """)
self.assertEqual("This/used to break Clinic!\n\nos.stat(path)", function.docstring) self.assertEqual("stat(path)\nThis/used to break Clinic!", function.docstring)
def test_directive(self): def test_directive(self):
c = FakeClinic() c = FakeClinic()
...@@ -692,7 +696,7 @@ This/used to break Clinic! ...@@ -692,7 +696,7 @@ This/used to break Clinic!
def parse_function(self, text): def parse_function(self, text):
block = self.parse(text) block = self.parse(text)
s = block.signatures s = block.signatures
assert len(s) == 2 self.assertEqual(len(s), 2)
assert isinstance(s[0], clinic.Module) assert isinstance(s[0], clinic.Module)
assert isinstance(s[1], clinic.Function) assert isinstance(s[1], clinic.Function)
return s[1] return s[1]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment