[Python-checkins] cpython: Issue #20268: Argument Clinic now supports cloning the parameters

larry.hastings python-checkins at python.org
Wed Jan 15 07:23:10 CET 2014


http://hg.python.org/cpython/rev/565773aabafb
changeset:   88486:565773aabafb
user:        Larry Hastings <larry at hastings.org>
date:        Tue Jan 14 22:22:41 2014 -0800
summary:
  Issue #20268: Argument Clinic now supports cloning the parameters
and return converter from existing functions.

files:
  Doc/howto/clinic.rst   |  43 ++++++++++++++++++++++++++++
  Misc/NEWS              |   3 +
  Tools/clinic/clinic.py |  46 ++++++++++++++++++++++++++++++
  3 files changed, 92 insertions(+), 0 deletions(-)


diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst
--- a/Doc/howto/clinic.rst
+++ b/Doc/howto/clinic.rst
@@ -847,6 +847,49 @@
 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
 -------------------
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -86,6 +86,9 @@
 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
   methods.
 
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -2301,6 +2301,12 @@
         #     modulename.fnname [as c_basename] [-> return annotation]
         # 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!)
         #
         # this line is permitted to start with whitespace.
@@ -2319,6 +2325,45 @@
             directive(*fields[1:])
             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('->')
 
         full_name, _, c_basename = line.partition(' as ')
@@ -2373,6 +2418,7 @@
         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)
         self.block.signatures.append(self.function)
+        (cls or module).functions.append(self.function)
         self.next(self.state_parameters_start)
 
     # Now entering the parameters section.  The rules, formally stated:

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list