diff --git a/pep-0436.txt b/pep-0436.txt index aa858f218..39a7e56ca 100644 --- a/pep-0436.txt +++ b/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:]*/ | - +-----------------------+-----------------------------------------------------------------+ + +-----------------------+-----------------------------------------------------------------+ + | 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:]*/ | + +-----------------------+-----------------------------------------------------------------+ 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