[Python-checkins] peps: Update to PEP 362 from Yury.

brett.cannon python-checkins at python.org
Thu Jun 14 04:15:07 CEST 2012


http://hg.python.org/peps/rev/882f0a5ba45e
changeset:   4459:882f0a5ba45e
user:        Brett Cannon <brett at python.org>
date:        Wed Jun 13 22:15:01 2012 -0400
summary:
  Update to PEP 362 from Yury.

files:
  pep-0362.txt |  140 ++++++++++++++++++++------------------
  1 files changed, 75 insertions(+), 65 deletions(-)


diff --git a/pep-0362.txt b/pep-0362.txt
--- a/pep-0362.txt
+++ b/pep-0362.txt
@@ -16,7 +16,7 @@
 ========
 
 Python has always supported powerful introspection capabilities,
-including introspecting functions and methods.  (For the rest of
+including introspecting functions and methods (for the rest of
 this PEP, "function" refers to both functions and methods).  By
 examining a function object you can fully reconstruct the function's
 signature.  Unfortunately this information is stored in an inconvenient
@@ -35,16 +35,12 @@
 Signature Object
 ================
 
-A Signature object represents the overall signature of a function.
-It stores a `Parameter object`_ for each parameter accepted by the
-function, as well as information specific to the function itself.
+A Signature object represents the call signature of a function and
+its return annotation.  For each parameter accepted by the function
+it stores a `Parameter object`_ in its ``parameters`` collection.
 
 A Signature object has the following public attributes and methods:
 
-* name : str
-    Name of the function.
-* qualname : str
-    Fully qualified name of the function.
 * return_annotation : object
     The annotation for the return type of the function if specified.
     If the function has no annotation for its return type, this
@@ -55,8 +51,22 @@
     as listed in ``code.co_varnames``).
 * bind(\*args, \*\*kwargs) -> BoundArguments
     Creates a mapping from positional and keyword arguments to
-    parameters.  Raises a ``BindError`` if the passed arguments
-    do not match the signature.
+    parameters.  Raises a ``BindError`` (subclass of ``TypeError``)
+    if the passed arguments do not match the signature.
+* bind_partial(\*args, \*\*kwargs) -> BoundArguments
+    Works the same way as ``bind()``, but allows the omission
+    of some required arguments (mimics ``functools.partial``
+    behavior.)
+* format(...) -> str
+    Formats the Signature object to a string.  Optional arguments allow
+    for custom render functions for parameter names,
+    annotations and default values, along with custom separators.
+
+Signature implements the ``__str__`` method, which fallbacks to the
+``Signature.format()`` call.
+
+It's possible to test Signatures for equality.  Two signatures
+are equal when they have equal parameters and return annotations.
 
 Changes to the Signature object, or to any of its data members,
 do not affect the function itself.
@@ -75,7 +85,7 @@
 * name : str
     The name of the parameter as a string.
 * default : object
-    The default value for the parameter if specified.  If the
+    The default value for the parameter, if specified.  If the
     parameter has no default value, this attribute is not set.
 * annotation : object
     The annotation for the parameter if specified.  If the
@@ -97,11 +107,7 @@
     all conditions where ``is_implemented`` may be False be
     thoroughly documented.
 
-Parameter objects support testing for equality.  Two Parameter
-objects are equal, when all their properties are equal.  Those
-who need to test if one signature has the same parameters as
-another, can do a direct comparison of ``Signature.parameters``
-collections: ``signature(foo).parameters == signature(bar).parameters``.
+Two parameters are equal when all their attributes are equal.
 
 
 BoundArguments Object
@@ -113,7 +119,7 @@
 Has the following public attributes:
 
 * arguments : OrderedDict
-    An ordered mutable mapping of parameters' names to arguments' values.
+    An ordered, mutable mapping of parameters' names to arguments' values.
     Does not contain arguments' default values.
 * args : tuple
     Tuple of positional arguments values.  Dynamically computed from
@@ -125,7 +131,7 @@
 The ``arguments`` attribute should be used in conjunction with
 ``Signature.parameters`` for any arguments processing purposes.
 
-``args`` and ``kwargs`` properties should be used to invoke functions:
+``args`` and ``kwargs`` properties can be used to invoke functions:
 ::
 
     def test(a, *, b):
@@ -148,7 +154,7 @@
     - If the object is not callable - raise a TypeError
 
     - If the object has a ``__signature__`` attribute and if it
-      is not ``None`` - return it
+      is not ``None`` - return a deepcopy of it
 
         - If it is ``None`` and the object is an instance of
           ``BuiltinFunction``, raise a ``ValueError``
@@ -160,29 +166,43 @@
 
         - Or else construct a new ``Signature`` object and return it
 
-    - if the object is a method or a classmethod, construct and return
+    - If the object is a method or a classmethod, construct and return
       a new ``Signature`` object, with its first parameter (usually
       ``self`` or ``cls``) removed
 
-    - If the object is a class return ``signature(object.__init__)``
+    - If the object is a staticmethod, construct and return
+      a new ``Signature`` object
 
     - If the object is an instance of ``functools.partial``, construct
       a new ``Signature`` from its ``partial.func`` attribute, and
       account for already bound ``partial.args`` and ``partial.kwargs``
 
+    - If the object is a class or metaclass:
+
+        - If the object's type has a ``__call__`` method defined in
+          its MRO, return a Signature for it
+
+        - If the object has a ``__new__`` method defined in its class,
+          return a Signature object for it
+
+        - If the object has a ``__init__`` method defined in its class,
+          return a Signature object for it
+
     - Return ``signature(object.__call__)``
 
 Note, that the ``Signature`` object is created in a lazy manner, and
-is not automatically cached.
+is not automatically cached.  If, however, the Signature object was
+explicitly cached by the user, ``signature()`` returns a new deepcopy
+of it on each invocation.
 
-An implementation for Python 3.3 can be found here: [#impl]_.
-A python issue was also created: [#issue]_.
+An implementation for Python 3.3 can be found at [#impl]_.
+The python issue tracking the patch is [#issue]_.
 
 
 Design Considerations
 =====================
 
-No Implicit Caching of Signature Objects
+No implicit caching of Signature objects
 ----------------------------------------
 
 The first PEP design had a provision for implicit caching of ``Signature``
@@ -201,60 +221,49 @@
 Examples
 ========
 
-Function Signature Renderer
----------------------------
+Visualizing Callable Objects' Signature
+---------------------------------------
 ::
 
-    def render_signature(signature):
-        '''Renders function definition by its signature.
+    from inspect import signature
+    from functools import partial, wraps
 
-        Example:
 
-            >>> def test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam':
-            ...    pass
+    class FooMeta(type):
+        def __new__(mcls, name, bases, dct, *, bar:bool=False):
+            return super().__new__(mcls, name, bases, dct)
 
-            >>> render_signature(inspect.signature(test))
-            test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam'
-        '''
+        def __init__(cls, name, bases, dct, **kwargs):
+            return super().__init__(name, bases, dct)
 
-        result = []
-        render_kw_only_separator = True
-        for param in signature.parameters.values():
-            formatted = param.name
 
-            # Add annotation and default value
-            if hasattr(param, 'annotation'):
-                formatted = '{}:{!r}'.format(formatted, param.annotation)
-            if hasattr(param, 'default'):
-                formatted = '{}={!r}'.format(formatted, param.default)
+    class Foo(metaclass=FooMeta):
+        def __init__(self, spam:int=42):
+            self.spam = spam
 
-            # Handle *args and **kwargs -like parameters
-            if param.is_args:
-                formatted = '*' + formatted
-            elif param.is_kwargs:
-                formatted = '**' + formatted
+        def __call__(self, a, b, *, c) -> tuple:
+            return a, b, c
 
-            if param.is_args:
-                # OK, we have an '*args'-like parameter, so we won't need
-                # a '*' to separate keyword-only arguments
-                render_kw_only_separator = False
-            elif param.is_keyword_only and render_kw_only_separator:
-                # We have a keyword-only parameter to render and we haven't
-                # rendered an '*args'-like parameter before, so add a '*'
-                # separator to the parameters list ("foo(arg1, *, arg2)" case)
-                result.append('*')
-                # This condition should be only triggered once, so
-                # reset the flag
-                render_kw_only_separator = False
 
-            result.append(formatted)
+    print('FooMeta >', str(signature(FooMeta)))
+    print('Foo >', str(signature(Foo)))
+    print('Foo.__call__ >', str(signature(Foo.__call__)))
+    print('Foo().__call__ >', str(signature(Foo().__call__)))
+    print('partial(Foo().__call__, 1, c=3) >',
+          str(signature(partial(Foo().__call__, 1, c=3))))
+    print('partial(partial(Foo().__call__, 1, c=3), 2, c=20) >',
+          str(signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20))))
 
-        rendered = '{}({})'.format(signature.name, ', '.join(result))
 
-        if hasattr(signature, 'return_annotation'):
-            rendered += ' -> {!r}'.format(signature.return_annotation)
+The script will output:
+::
 
-        return rendered
+    FooMeta > (name, bases, dct, *, bar:bool=False)
+    Foo > (spam:int=42)
+    Foo.__call__ > (self, a, b, *, c) -> tuple
+    Foo().__call__ > (a, b, *, c) -> tuple
+    partial(Foo().__call__, 1, c=3) > (b, *, c=3) -> tuple
+    partial(partial(Foo().__call__, 1, c=3), 2, c=20) > (*, c=20) -> tuple
 
 
 Annotation Checker

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


More information about the Python-checkins mailing list