Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4a714d4

Browse files
committed
Issue #20268: Argument Clinic now supports cloning the parameters
and return converter from existing functions.
1 parent e02de8c commit 4a714d4

3 files changed

Lines changed: 92 additions & 0 deletions

File tree

Doc/howto/clinic.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,49 @@ their parameters (if any),
847847
just run ``Tools/clinic/clinic.py --converters`` for the full list.
848848

849849

850+
Cloning existing functions
851+
--------------------------
852+
853+
If you have a number of functions that look similar, you may be able to
854+
use Clinic's "clone" feature. When you clone an existing function,
855+
you reuse:
856+
857+
* its parameters, including
858+
859+
* their names,
860+
861+
* their converters, with all parameters,
862+
863+
* their default values,
864+
865+
* their per-parameter docstrings,
866+
867+
* their *kind* (whether they're positional only,
868+
positional or keyword, or keyword only), and
869+
870+
* its return converter.
871+
872+
The only thing not copied from the original function is its docstring;
873+
the syntax allows you to specify a new docstring.
874+
875+
Here's the syntax for cloning a function::
876+
877+
/*[clinic input]
878+
module.class.new_function [as c_basename] = module.class.existing_function
879+
880+
Docstring for new_function goes here.
881+
[clinic start generated code]*/
882+
883+
(The functions can be in different modules or classes. I wrote
884+
``module.class`` in the sample just to illustrate that you must
885+
use the full path to *both* functions.)
886+
887+
Sorry, there's no syntax for partially-cloning a function, or cloning a function
888+
then modifying it. Cloning is an all-or nothing proposition.
889+
890+
Also, the function you are cloning from must have been previously defined
891+
in the current file.
892+
850893
Calling Python code
851894
-------------------
852895

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ Tests
8686
Tools/Demos
8787
-----------
8888

89+
- Issue #20268: Argument Clinic now supports cloning the parameters and
90+
return converter of existing functions.
91+
8992
- Issue #20228: Argument Clinic now has special support for class special
9093
methods.
9194

Tools/clinic/clinic.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,6 +2301,12 @@ def state_modulename_name(self, line):
23012301
# modulename.fnname [as c_basename] [-> return annotation]
23022302
# square brackets denote optional syntax.
23032303
#
2304+
# alternatively:
2305+
# modulename.fnname [as c_basename] = modulename.existing_fn_name
2306+
# clones the parameters and return converter from that
2307+
# function. you can't modify them. you must enter a
2308+
# new docstring.
2309+
#
23042310
# (but we might find a directive first!)
23052311
#
23062312
# this line is permitted to start with whitespace.
@@ -2319,6 +2325,45 @@ def state_modulename_name(self, line):
23192325
directive(*fields[1:])
23202326
return
23212327

2328+
# are we cloning?
2329+
before, equals, existing = line.rpartition('=')
2330+
if equals:
2331+
full_name, _, c_basename = before.partition(' as ')
2332+
full_name = full_name.strip()
2333+
c_basename = c_basename.strip()
2334+
existing = existing.strip()
2335+
if (is_legal_py_identifier(full_name) and
2336+
(not c_basename or is_legal_c_identifier(c_basename)) and
2337+
is_legal_py_identifier(existing)):
2338+
# we're cloning!
2339+
fields = [x.strip() for x in existing.split('.')]
2340+
function_name = fields.pop()
2341+
module, cls = self.clinic._module_and_class(fields)
2342+
2343+
for existing_function in (cls or module).functions:
2344+
if existing_function.name == function_name:
2345+
break
2346+
else:
2347+
existing_function = None
2348+
if not existing_function:
2349+
fail("Couldn't find existing function " + repr(existing) + "!")
2350+
2351+
fields = [x.strip() for x in full_name.split('.')]
2352+
function_name = fields.pop()
2353+
module, cls = self.clinic._module_and_class(fields)
2354+
2355+
if not (existing_function.kind == self.kind and existing_function.coexist == self.coexist):
2356+
fail("'kind' of function and cloned function don't match! (@classmethod/@staticmethod/@coexist)")
2357+
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
2358+
return_converter=existing_function.return_converter, kind=existing_function.kind, coexist=existing_function.coexist)
2359+
2360+
self.function.parameters = existing_function.parameters.copy()
2361+
2362+
self.block.signatures.append(self.function)
2363+
(cls or module).functions.append(self.function)
2364+
self.next(self.state_function_docstring)
2365+
return
2366+
23222367
line, _, returns = line.partition('->')
23232368

23242369
full_name, _, c_basename = line.partition(' as ')
@@ -2373,6 +2418,7 @@ def state_modulename_name(self, line):
23732418
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
23742419
return_converter=return_converter, kind=self.kind, coexist=self.coexist)
23752420
self.block.signatures.append(self.function)
2421+
(cls or module).functions.append(self.function)
23762422
self.next(self.state_parameters_start)
23772423

23782424
# Now entering the parameters section. The rules, formally stated:

0 commit comments

Comments
 (0)