PEP 436: fix markup and make it consistent.
This commit is contained in:
parent
3f90114628
commit
298f064289
356
pep-0436.txt
356
pep-0436.txt
|
@ -49,62 +49,62 @@ But over the years the ``PyArg_Parse`` interface has been extended
|
|||
in numerous ways. The modern API is complex, to the point that it
|
||||
is somewhat painful to use. Consider:
|
||||
|
||||
* There are now forty different "format units"; a few are even three
|
||||
characters long. This makes it difficult for the programmer to
|
||||
understand what the format string says--or even perhaps to parse
|
||||
it--without constantly cross-indexing it with the documentation.
|
||||
* There are also six meta-format units that may be buried in the
|
||||
format string. (They are: ``"()|$:;"``.)
|
||||
* The more format units are added, the less likely it is the
|
||||
implementer can pick an easy-to-use mnemonic for the format unit,
|
||||
because the character of choice is probably already in use. In
|
||||
other words, the more format units we have, the more obtuse the
|
||||
format units become.
|
||||
* Several format units are nearly identical to others, having only
|
||||
subtle differences. This makes understanding the exact semantics
|
||||
of the format string even harder, and can make it difficult to
|
||||
figure out exactly which format unit you want.
|
||||
* The docstring is specified as a static C string, making it mildly
|
||||
bothersome to read and edit since it must obey C string quoting rules.
|
||||
* When adding a new parameter to a function using
|
||||
``PyArg_ParseTupleAndKeywords()``, it's necessary to touch six
|
||||
different places in the code: [4]_
|
||||
* There are now forty different "format units"; a few are even three
|
||||
characters long. This makes it difficult for the programmer to
|
||||
understand what the format string says--or even perhaps to parse
|
||||
it--without constantly cross-indexing it with the documentation.
|
||||
* There are also six meta-format units that may be buried in the
|
||||
format string. (They are: ``"()|$:;"``.)
|
||||
* The more format units are added, the less likely it is the
|
||||
implementer can pick an easy-to-use mnemonic for the format unit,
|
||||
because the character of choice is probably already in use. In
|
||||
other words, the more format units we have, the more obtuse the
|
||||
format units become.
|
||||
* Several format units are nearly identical to others, having only
|
||||
subtle differences. This makes understanding the exact semantics
|
||||
of the format string even harder, and can make it difficult to
|
||||
figure out exactly which format unit you want.
|
||||
* The docstring is specified as a static C string, making it mildly
|
||||
bothersome to read and edit since it must obey C string quoting rules.
|
||||
* When adding a new parameter to a function using
|
||||
``PyArg_ParseTupleAndKeywords()``, it's necessary to touch six
|
||||
different places in the code: [4]_
|
||||
|
||||
* Declaring the variable to store the argument.
|
||||
* Passing in a pointer to that variable in the correct spot in
|
||||
``PyArg_ParseTupleAndKeywords()``, also passing in any
|
||||
"length" or "converter" arguments in the correct order.
|
||||
* Adding the name of the argument in the correct spot of the
|
||||
"keywords" array passed in to
|
||||
``PyArg_ParseTupleAndKeywords()``.
|
||||
* Adding the format unit to the correct spot in the format
|
||||
string.
|
||||
* Adding the parameter to the prototype in the docstring.
|
||||
* Documenting the parameter in the docstring.
|
||||
* Declaring the variable to store the argument.
|
||||
* Passing in a pointer to that variable in the correct spot in
|
||||
``PyArg_ParseTupleAndKeywords()``, also passing in any
|
||||
"length" or "converter" arguments in the correct order.
|
||||
* Adding the name of the argument in the correct spot of the
|
||||
"keywords" array passed in to
|
||||
``PyArg_ParseTupleAndKeywords()``.
|
||||
* Adding the format unit to the correct spot in the format
|
||||
string.
|
||||
* Adding the parameter to the prototype in the docstring.
|
||||
* Documenting the parameter in the docstring.
|
||||
|
||||
* There is currently no mechanism for builtin functions to provide
|
||||
their "signature" information (see ``inspect.getfullargspec`` and
|
||||
``inspect.Signature``). Adding this information using a mechanism
|
||||
similar to the existing ``PyArg_Parse`` functions would require
|
||||
repeating ourselves yet again.
|
||||
* There is currently no mechanism for builtin functions to provide
|
||||
their "signature" information (see ``inspect.getfullargspec`` and
|
||||
``inspect.Signature``). Adding this information using a mechanism
|
||||
similar to the existing ``PyArg_Parse`` functions would require
|
||||
repeating ourselves yet again.
|
||||
|
||||
The goal of Argument Clinic is to replace this API with a mechanism
|
||||
inheriting none of these downsides:
|
||||
|
||||
* You need specify each parameter only once.
|
||||
* All information about a parameter is kept together in one place.
|
||||
* For each parameter, you specify a conversion function; Argument
|
||||
Clinic handles the translation from Python value into C value for
|
||||
you.
|
||||
* Argument Clinic also allows for fine-tuning of argument processing
|
||||
behavior with parameterized conversion functions.
|
||||
* Docstrings are written in plain text. Function docstrings are
|
||||
required; per-parameter docstrings are encouraged.
|
||||
* From this, Argument Clinic generates for you all the mundane,
|
||||
repetitious code and data structures CPython needs internally.
|
||||
Once you've specified the interface, the next step is simply to
|
||||
write your implementation using native C types. Every detail of
|
||||
argument parsing is handled for you.
|
||||
* You need specify each parameter only once.
|
||||
* All information about a parameter is kept together in one place.
|
||||
* For each parameter, you specify a conversion function; Argument
|
||||
Clinic handles the translation from Python value into C value for
|
||||
you.
|
||||
* Argument Clinic also allows for fine-tuning of argument processing
|
||||
behavior with parameterized conversion functions.
|
||||
* Docstrings are written in plain text. Function docstrings are
|
||||
required; per-parameter docstrings are encouraged.
|
||||
* From this, Argument Clinic generates for you all the mundane,
|
||||
repetitious code and data structures CPython needs internally.
|
||||
Once you've specified the interface, the next step is simply to
|
||||
write your implementation using native C types. Every detail of
|
||||
argument parsing is handled for you.
|
||||
|
||||
Argument Clinic is implemented as a preprocessor. It draws inspiration
|
||||
for its workflow directly from [Cog]_ by Ned Batchelder. To use Clinic,
|
||||
|
@ -121,11 +121,11 @@ interpreter.
|
|||
|
||||
Future goals of Argument Clinic include:
|
||||
|
||||
* providing signature information for builtins,
|
||||
* enabling alternative implementations of Python to create
|
||||
automated library compatibility tests, and
|
||||
* speeding up argument parsing with improvements to the
|
||||
generated code.
|
||||
* providing signature information for builtins,
|
||||
* enabling alternative implementations of Python to create
|
||||
automated library compatibility tests, and
|
||||
* speeding up argument parsing with improvements to the
|
||||
generated code.
|
||||
|
||||
|
||||
DSL Syntax Summary
|
||||
|
@ -141,117 +141,115 @@ statement, lending it some familiarity to Python core developers.
|
|||
|
||||
::
|
||||
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
| Section | Example |
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
| Clinic DSL start | /*[clinic] |
|
||||
| Module declaration | module module_name |
|
||||
| Class declaration | class module_name.class_name |
|
||||
| Function declaration | module_name.function_name -> return_annotation |
|
||||
| Parameter declaration | name : converter(param=value) |
|
||||
| Parameter docstring | Lorem ipsum dolor sit amet, consectetur |
|
||||
| | adipisicing elit, sed do eiusmod tempor |
|
||||
| Function docstring | Lorem ipsum dolor sit amet, consectetur adipisicing |
|
||||
| | elit, sed do eiusmod tempor incididunt ut labore et |
|
||||
| Clinic DSL end | [clinic]*/ |
|
||||
| Clinic output | ... |
|
||||
| Clinic output end | /*[clinic end output:<checksum>]*/ |
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
| Section | Example |
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
| Clinic DSL start | /*[clinic] |
|
||||
| Module declaration | module module_name |
|
||||
| Class declaration | class module_name.class_name |
|
||||
| Function declaration | module_name.function_name -> return_annotation |
|
||||
| Parameter declaration | name : converter(param=value) |
|
||||
| Parameter docstring | Lorem ipsum dolor sit amet, consectetur |
|
||||
| | adipisicing elit, sed do eiusmod tempor |
|
||||
| Function docstring | Lorem ipsum dolor sit amet, consectetur adipisicing |
|
||||
| | elit, sed do eiusmod tempor incididunt ut labore et |
|
||||
| Clinic DSL end | [clinic]*/ |
|
||||
| Clinic output | ... |
|
||||
| Clinic output end | /*[clinic end output:<checksum>]*/ |
|
||||
+-----------------------+-----------------------------------------------------------------+
|
||||
|
||||
To give some flavor of the proposed DSL syntax, here are some sample Clinic
|
||||
code blocks. This first block reflects the normally preferred style, including
|
||||
blank lines between parameters and per-argument docstrings.
|
||||
It also includes a user-defined converter (``path_t``) created
|
||||
locally
|
||||
locally::
|
||||
|
||||
::
|
||||
/*[clinic]
|
||||
os.stat as os_stat_fn -> stat result
|
||||
|
||||
/*[clinic]
|
||||
os.stat as os_stat_fn -> stat result
|
||||
path: path_t(allow_fd=1)
|
||||
Path to be examined; can be string, bytes, or open-file-descriptor int.
|
||||
|
||||
path: path_t(allow_fd=1)
|
||||
Path to be examined; can be string, bytes, or open-file-descriptor int.
|
||||
*
|
||||
|
||||
*
|
||||
dir_fd: OS_STAT_DIR_FD_CONVERTER = DEFAULT_DIR_FD
|
||||
If not None, it should be a file descriptor open to a directory,
|
||||
and path should be a relative string; path will then be relative to
|
||||
that directory.
|
||||
|
||||
dir_fd: OS_STAT_DIR_FD_CONVERTER = DEFAULT_DIR_FD
|
||||
If not None, it should be a file descriptor open to a directory,
|
||||
and path should be a relative string; path will then be relative to
|
||||
that directory.
|
||||
follow_symlinks: bool = True
|
||||
If False, and the last element of the path is a symbolic link,
|
||||
stat will examine the symbolic link itself instead of the file
|
||||
the link points to.
|
||||
|
||||
follow_symlinks: bool = True
|
||||
If False, and the last element of the path is a symbolic link,
|
||||
stat will examine the symbolic link itself instead of the file
|
||||
the link points to.
|
||||
Perform a stat system call on the given path.
|
||||
|
||||
Perform a stat system call on the given path.
|
||||
{parameters}
|
||||
|
||||
{parameters}
|
||||
dir_fd and follow_symlinks may not be implemented
|
||||
on your platform. If they are unavailable, using them will raise a
|
||||
NotImplementedError.
|
||||
|
||||
dir_fd and follow_symlinks may not be implemented
|
||||
on your platform. If they are unavailable, using them will raise a
|
||||
NotImplementedError.
|
||||
It's an error to use dir_fd or follow_symlinks when specifying path as
|
||||
an open file descriptor.
|
||||
|
||||
It's an error to use dir_fd or follow_symlinks when specifying path as
|
||||
an open file descriptor.
|
||||
|
||||
[clinic]*/
|
||||
[clinic]*/
|
||||
|
||||
This second example shows a minimal Clinic code block, omitting all
|
||||
parameter docstrings and non-significant blank lines::
|
||||
|
||||
/*[clinic]
|
||||
os.access
|
||||
path: path
|
||||
mode: int
|
||||
*
|
||||
dir_fd: OS_ACCESS_DIR_FD_CONVERTER = 1
|
||||
effective_ids: bool = False
|
||||
follow_symlinks: bool = True
|
||||
Use the real uid/gid to test for access to a path.
|
||||
Returns True if granted, False otherwise.
|
||||
/*[clinic]
|
||||
os.access
|
||||
path: path
|
||||
mode: int
|
||||
*
|
||||
dir_fd: OS_ACCESS_DIR_FD_CONVERTER = 1
|
||||
effective_ids: bool = False
|
||||
follow_symlinks: bool = True
|
||||
Use the real uid/gid to test for access to a path.
|
||||
Returns True if granted, False otherwise.
|
||||
|
||||
{parameters}
|
||||
{parameters}
|
||||
|
||||
dir_fd, effective_ids, and follow_symlinks may not be implemented
|
||||
on your platform. If they are unavailable, using them will raise a
|
||||
NotImplementedError.
|
||||
dir_fd, effective_ids, and follow_symlinks may not be implemented
|
||||
on your platform. If they are unavailable, using them will raise a
|
||||
NotImplementedError.
|
||||
|
||||
Note that most operations will use the effective uid/gid, therefore this
|
||||
routine can be used in a suid/sgid environment to test if the invoking user
|
||||
has the specified access to the path.
|
||||
Note that most operations will use the effective uid/gid, therefore this
|
||||
routine can be used in a suid/sgid environment to test if the invoking user
|
||||
has the specified access to the path.
|
||||
|
||||
[clinic]*/
|
||||
[clinic]*/
|
||||
|
||||
This final example shows a Clinic code block handling groups of
|
||||
optional parameters, including parameters on the left::
|
||||
|
||||
/*[clinic]
|
||||
curses.window.addch
|
||||
/*[clinic]
|
||||
curses.window.addch
|
||||
|
||||
[
|
||||
y: int
|
||||
Y-coordinate.
|
||||
[
|
||||
y: int
|
||||
Y-coordinate.
|
||||
|
||||
x: int
|
||||
X-coordinate.
|
||||
]
|
||||
x: int
|
||||
X-coordinate.
|
||||
]
|
||||
|
||||
ch: char
|
||||
Character to add.
|
||||
ch: char
|
||||
Character to add.
|
||||
|
||||
[
|
||||
attr: long
|
||||
Attributes for the character.
|
||||
]
|
||||
[
|
||||
attr: long
|
||||
Attributes for the character.
|
||||
]
|
||||
|
||||
/
|
||||
/
|
||||
|
||||
Paint character ch at (y, x) with attributes attr,
|
||||
overwriting any character previously painter at that location.
|
||||
By default, the character position and attributes are the
|
||||
current settings for the window object.
|
||||
[clinic]*/
|
||||
Paint character ch at (y, x) with attributes attr,
|
||||
overwriting any character previously painter at that location.
|
||||
By default, the character position and attributes are the
|
||||
current settings for the window object.
|
||||
[clinic]*/
|
||||
|
||||
|
||||
General Behavior Of the Argument Clinic DSL
|
||||
|
@ -275,17 +273,13 @@ Module and Class Declarations
|
|||
-----------------------------
|
||||
|
||||
When a C file implements a module or class, this should be declared to
|
||||
Clinic. The syntax is simple:
|
||||
Clinic. The syntax is simple::
|
||||
|
||||
::
|
||||
module module_name
|
||||
|
||||
module module_name
|
||||
or ::
|
||||
|
||||
or
|
||||
|
||||
::
|
||||
|
||||
class module_name.class_name
|
||||
class module_name.class_name
|
||||
|
||||
(Note that these are not actually special syntax; they are implemented
|
||||
as `Directives`_.)
|
||||
|
@ -297,11 +291,9 @@ from the top-level module. Nested modules and classes are supported.
|
|||
Function Declaration
|
||||
--------------------
|
||||
|
||||
The full form of the function declaration is as follows:
|
||||
The full form of the function declaration is as follows::
|
||||
|
||||
::
|
||||
|
||||
dotted.name [ as legal_c_id ] [ -> return_annotation ]
|
||||
dotted.name [ as legal_c_id ] [ -> return_annotation ]
|
||||
|
||||
The dotted name should be the full name of the function, starting
|
||||
with the highest-level package (e.g. "os.stat" or "curses.window.addch").
|
||||
|
@ -323,11 +315,9 @@ a *return converter*.
|
|||
Parameter Declaration
|
||||
---------------------
|
||||
|
||||
The full form of the parameter declaration line as as follows:
|
||||
The full form of the parameter declaration line as as follows::
|
||||
|
||||
::
|
||||
|
||||
name: converter [ (parameter=value [, parameter2=value2]) ] [ = default]
|
||||
name: converter [ (parameter=value [, parameter2=value2]) ] [ = default]
|
||||
|
||||
The "name" must be a legal C identifier. Whitespace is permitted between
|
||||
the name and the colon (though this is not the preferred style). Whitespace
|
||||
|
@ -377,11 +367,9 @@ For convenience's sake in converting existing code to Argument Clinic,
|
|||
Clinic provides a set of legacy converters that match ``PyArg_ParseTuple``
|
||||
format units. They are specified as a C string containing the format
|
||||
unit. For example, to specify a parameter "foo" as taking a Python
|
||||
"int" and emitting a C int, you could specify:
|
||||
"int" and emitting a C int, you could specify::
|
||||
|
||||
::
|
||||
|
||||
foo : "i"
|
||||
foo : "i"
|
||||
|
||||
(To more closely resemble a C string, these must always use double quotes.)
|
||||
|
||||
|
@ -554,11 +542,9 @@ Argument Clinic also permits "directives" in Clinic code blocks.
|
|||
Directives are similar to *pragmas* in C; they are statements
|
||||
that modify Argument Clinic's behavior.
|
||||
|
||||
The format of a directive is as follows:
|
||||
The format of a directive is as follows::
|
||||
|
||||
::
|
||||
|
||||
directive_name [argument [second_argument [ ... ]]]
|
||||
directive_name [argument [second_argument [ ... ]]]
|
||||
|
||||
Directives only take positional arguments.
|
||||
|
||||
|
@ -566,7 +552,7 @@ A Clinic code block must contain either one or more directives,
|
|||
or a function declaration. It may contain both, in which
|
||||
case all directives must come before the function declaration.
|
||||
|
||||
Internally directives map directly to Python callables.
|
||||
Internally directives map directly to Python callables.
|
||||
The directive's arguments are passed directly to the callable
|
||||
as positional arguments of type ``str()``.
|
||||
|
||||
|
@ -581,18 +567,16 @@ Python Code
|
|||
|
||||
Argument Clinic also permits embedding Python code inside C files,
|
||||
which is executed in-place when Argument Clinic processes the file.
|
||||
Embedded code looks like this:
|
||||
Embedded code looks like this::
|
||||
|
||||
::
|
||||
/*[python]
|
||||
|
||||
/*[python]
|
||||
# this is python code!
|
||||
print("/" + "* Hello world! *" + "/")
|
||||
|
||||
# this is python code!
|
||||
print("/" + "* Hello world! *" + "/")
|
||||
|
||||
[python]*/
|
||||
/* Hello world! */
|
||||
/*[python end:da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
|
||||
[python]*/
|
||||
/* Hello world! */
|
||||
/*[python end:da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
|
||||
|
||||
The ``"/* Hello world! */"`` line above was generated by running the Python
|
||||
code in the preceding comment.
|
||||
|
@ -610,14 +594,14 @@ after the section of Clinic code. For "python" sections, the output
|
|||
is everything printed using ``builtins.print``. For "clinic"
|
||||
sections, the output is valid C code, including:
|
||||
|
||||
* a ``#define`` providing the correct ``methoddef`` structure for the
|
||||
function
|
||||
* a prototype for the "impl" function -- this is what you'll write
|
||||
to implement this function
|
||||
* a function that handles all argument processing, which calls your
|
||||
"impl" function
|
||||
* the definition line of the "impl" function
|
||||
* and a comment indicating the end of output.
|
||||
* a ``#define`` providing the correct ``methoddef`` structure for the
|
||||
function
|
||||
* a prototype for the "impl" function -- this is what you'll write
|
||||
to implement this function
|
||||
* a function that handles all argument processing, which calls your
|
||||
"impl" function
|
||||
* the definition line of the "impl" function
|
||||
* and a comment indicating the end of output.
|
||||
|
||||
The intention is that you write the body of your impl function immediately
|
||||
after the output -- as in, you write a left-curly-brace immediately after
|
||||
|
@ -738,15 +722,15 @@ Notes / TBD
|
|||
|
||||
::
|
||||
|
||||
/*[clinic]
|
||||
... prototype and parameters (including parameter docstrings) go here
|
||||
[clinic]*/
|
||||
... some output ...
|
||||
/*[clinic docstring start]*/
|
||||
... hand-edited function docstring goes here <-- you edit this by hand!
|
||||
/*[clinic docstring end]*/
|
||||
... more output
|
||||
/*[clinic output end]*/
|
||||
/*[clinic]
|
||||
... prototype and parameters (including parameter docstrings) go here
|
||||
[clinic]*/
|
||||
... some output ...
|
||||
/*[clinic docstring start]*/
|
||||
... hand-edited function docstring goes here <-- you edit this by hand!
|
||||
/*[clinic docstring end]*/
|
||||
... more output
|
||||
/*[clinic output end]*/
|
||||
|
||||
I tried it this way and don't like it -- I think it's clumsy. I
|
||||
prefer that everything you write goes in one place, rather than
|
||||
|
@ -756,7 +740,7 @@ Notes / TBD
|
|||
* Argument Clinic does not support automatic tuple unpacking
|
||||
(the "``(OOO)``" style format string for ``PyArg_ParseTuple()``.)
|
||||
|
||||
* Argument Clinic removes some dynamism / flexibility. With
|
||||
* Argument Clinic removes some dynamism / flexibility. With
|
||||
``PyArg_ParseTuple()`` one could theoretically pass in different
|
||||
encodings at runtime for the "``es``"/"``et``" format units.
|
||||
AFAICT CPython doesn't do this itself, however it's possible
|
||||
|
@ -773,7 +757,7 @@ shamelessly rip off his clever design for Cog--"my favorite tool
|
|||
that I've never gotten to use". Thanks also to everyone who provided
|
||||
feedback on the [bugtracker issue] and on python-dev. Special thanks
|
||||
to Nick Coglan and Guido van Rossum for a rousing two-hour in-person
|
||||
deep dive on the topic at PyCon US 2013.
|
||||
deep dive on the topic at PyCon US 2013.
|
||||
|
||||
|
||||
References
|
||||
|
|
Loading…
Reference in New Issue