mirror of
https://github.com/python/cpython.git
synced 2025-11-12 07:02:33 +00:00
Issue #20268: Argument Clinic now supports cloning the parameters
and return converter from existing functions.
This commit is contained in:
parent
e02de8c2ad
commit
4a714d48ad
3 changed files with 92 additions and 0 deletions
|
|
@ -847,6 +847,49 @@ their parameters (if any),
|
||||||
just run ``Tools/clinic/clinic.py --converters`` for the full list.
|
just run ``Tools/clinic/clinic.py --converters`` for the full list.
|
||||||
|
|
||||||
|
|
||||||
|
Cloning existing functions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
If you have a number of functions that look similar, you may be able to
|
||||||
|
use Clinic's "clone" feature. When you clone an existing function,
|
||||||
|
you reuse:
|
||||||
|
|
||||||
|
* its parameters, including
|
||||||
|
|
||||||
|
* their names,
|
||||||
|
|
||||||
|
* their converters, with all parameters,
|
||||||
|
|
||||||
|
* their default values,
|
||||||
|
|
||||||
|
* their per-parameter docstrings,
|
||||||
|
|
||||||
|
* their *kind* (whether they're positional only,
|
||||||
|
positional or keyword, or keyword only), and
|
||||||
|
|
||||||
|
* its return converter.
|
||||||
|
|
||||||
|
The only thing not copied from the original function is its docstring;
|
||||||
|
the syntax allows you to specify a new docstring.
|
||||||
|
|
||||||
|
Here's the syntax for cloning a function::
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
module.class.new_function [as c_basename] = module.class.existing_function
|
||||||
|
|
||||||
|
Docstring for new_function goes here.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
(The functions can be in different modules or classes. I wrote
|
||||||
|
``module.class`` in the sample just to illustrate that you must
|
||||||
|
use the full path to *both* functions.)
|
||||||
|
|
||||||
|
Sorry, there's no syntax for partially-cloning a function, or cloning a function
|
||||||
|
then modifying it. Cloning is an all-or nothing proposition.
|
||||||
|
|
||||||
|
Also, the function you are cloning from must have been previously defined
|
||||||
|
in the current file.
|
||||||
|
|
||||||
Calling Python code
|
Calling Python code
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@ Tests
|
||||||
Tools/Demos
|
Tools/Demos
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
- Issue #20268: Argument Clinic now supports cloning the parameters and
|
||||||
|
return converter of existing functions.
|
||||||
|
|
||||||
- Issue #20228: Argument Clinic now has special support for class special
|
- Issue #20228: Argument Clinic now has special support for class special
|
||||||
methods.
|
methods.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2301,6 +2301,12 @@ class DSLParser:
|
||||||
# modulename.fnname [as c_basename] [-> return annotation]
|
# modulename.fnname [as c_basename] [-> return annotation]
|
||||||
# square brackets denote optional syntax.
|
# square brackets denote optional syntax.
|
||||||
#
|
#
|
||||||
|
# alternatively:
|
||||||
|
# modulename.fnname [as c_basename] = modulename.existing_fn_name
|
||||||
|
# clones the parameters and return converter from that
|
||||||
|
# function. you can't modify them. you must enter a
|
||||||
|
# new docstring.
|
||||||
|
#
|
||||||
# (but we might find a directive first!)
|
# (but we might find a directive first!)
|
||||||
#
|
#
|
||||||
# this line is permitted to start with whitespace.
|
# this line is permitted to start with whitespace.
|
||||||
|
|
@ -2319,6 +2325,45 @@ class DSLParser:
|
||||||
directive(*fields[1:])
|
directive(*fields[1:])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# are we cloning?
|
||||||
|
before, equals, existing = line.rpartition('=')
|
||||||
|
if equals:
|
||||||
|
full_name, _, c_basename = before.partition(' as ')
|
||||||
|
full_name = full_name.strip()
|
||||||
|
c_basename = c_basename.strip()
|
||||||
|
existing = existing.strip()
|
||||||
|
if (is_legal_py_identifier(full_name) and
|
||||||
|
(not c_basename or is_legal_c_identifier(c_basename)) and
|
||||||
|
is_legal_py_identifier(existing)):
|
||||||
|
# we're cloning!
|
||||||
|
fields = [x.strip() for x in existing.split('.')]
|
||||||
|
function_name = fields.pop()
|
||||||
|
module, cls = self.clinic._module_and_class(fields)
|
||||||
|
|
||||||
|
for existing_function in (cls or module).functions:
|
||||||
|
if existing_function.name == function_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
existing_function = None
|
||||||
|
if not existing_function:
|
||||||
|
fail("Couldn't find existing function " + repr(existing) + "!")
|
||||||
|
|
||||||
|
fields = [x.strip() for x in full_name.split('.')]
|
||||||
|
function_name = fields.pop()
|
||||||
|
module, cls = self.clinic._module_and_class(fields)
|
||||||
|
|
||||||
|
if not (existing_function.kind == self.kind and existing_function.coexist == self.coexist):
|
||||||
|
fail("'kind' of function and cloned function don't match! (@classmethod/@staticmethod/@coexist)")
|
||||||
|
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
|
||||||
|
return_converter=existing_function.return_converter, kind=existing_function.kind, coexist=existing_function.coexist)
|
||||||
|
|
||||||
|
self.function.parameters = existing_function.parameters.copy()
|
||||||
|
|
||||||
|
self.block.signatures.append(self.function)
|
||||||
|
(cls or module).functions.append(self.function)
|
||||||
|
self.next(self.state_function_docstring)
|
||||||
|
return
|
||||||
|
|
||||||
line, _, returns = line.partition('->')
|
line, _, returns = line.partition('->')
|
||||||
|
|
||||||
full_name, _, c_basename = line.partition(' as ')
|
full_name, _, c_basename = line.partition(' as ')
|
||||||
|
|
@ -2373,6 +2418,7 @@ class DSLParser:
|
||||||
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
|
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
|
||||||
return_converter=return_converter, kind=self.kind, coexist=self.coexist)
|
return_converter=return_converter, kind=self.kind, coexist=self.coexist)
|
||||||
self.block.signatures.append(self.function)
|
self.block.signatures.append(self.function)
|
||||||
|
(cls or module).functions.append(self.function)
|
||||||
self.next(self.state_parameters_start)
|
self.next(self.state_parameters_start)
|
||||||
|
|
||||||
# Now entering the parameters section. The rules, formally stated:
|
# Now entering the parameters section. The rules, formally stated:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue