[Python-checkins] peps: The latest changes from Yury Selivanov. I can almost taste the acceptance!

larry.hastings python-checkins at python.org
Thu Jun 21 10:44:33 CEST 2012


http://hg.python.org/peps/rev/1edf1cecae7d
changeset:   4472:1edf1cecae7d
user:        Larry Hastings <larry at hastings.org>
date:        Thu Jun 21 01:44:15 2012 -0700
summary:
  The latest changes from Yury Selivanov.  I can almost taste the acceptance!

files:
  pep-0362.txt |  159 +++++++++++++++++++++++++++++++-------
  1 files changed, 128 insertions(+), 31 deletions(-)


diff --git a/pep-0362.txt b/pep-0362.txt
--- a/pep-0362.txt
+++ b/pep-0362.txt
@@ -42,23 +42,58 @@
 A Signature object has the following public attributes and methods:
 
 * 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
-    attribute is not set.
+    The "return" annotation for the function. If the function
+    has no "return" annotation, this attribute is not set.
+
 * parameters : OrderedDict
     An ordered mapping of parameters' names to the corresponding
-    Parameter objects (keyword-only arguments are in the same order
-    as listed in ``code.co_varnames``).
+    Parameter objects.
+
 * bind(\*args, \*\*kwargs) -> BoundArguments
     Creates a mapping from positional and keyword arguments to
     parameters.  Raises a ``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.)  Raises a ``TypeError`` if the passed arguments do
     not match the signature.
 
+* replace(parameters, \*, return_annotation) -> Signature
+    Creates a new Signature instance based on the instance
+    ``replace`` was invoked on.  It is possible to pass different
+    ``parameters`` and/or ``return_annotation`` to override the
+    corresponding properties of the base signature.  To remove
+    ``return_annotation`` from the copied ``Signature``, pass in
+    ``Signature.empty``.
+
+Signature objects are immutable.  Use ``Signature.replace()`` to
+make a modified copy:
+::
+
+    >>> sig = signature(foo)
+    >>> new_sig = sig.replace(return_annotation="new return annotation")
+    >>> new_sig is not sig
+    True
+    >>> new_sig.return_annotation == sig.return_annotation
+    True
+    >>> new_sig.parameters == sig.parameters
+    True
+
+There are two ways to instantiate a Signature class:
+
+* Signature(parameters, *, return_annotation)
+    Default Signature constructor.  Accepts an optional sequence
+    of ``Parameter`` objects, and an optional ``return_annotation``.
+    Parameters sequence is validated to check that there are no
+    parameters with duplicate names, and that the parameters
+    are in the right order, i.e. positional-only first, then
+    positional-or-keyword, etc.
+* Signature.from_function(function)
+    Returns a Signature object reflecting the signature of the
+    function passed in.
+
 It's possible to test Signatures for equality.  Two signatures are
 equal when their parameters are equal, their positional and
 positional-only parameters appear in the same order, and they
@@ -67,9 +102,14 @@
 Changes to the Signature object, or to any of its data members,
 do not affect the function itself.
 
-Signature also implements ``__str__`` and ``__copy__`` methods.
-The latter creates a shallow copy of Signature, with all Parameter
-objects copied as well.
+Signature also implements ``__str__``:
+::
+
+    >>> str(Signature.from_function((lambda *args: None)))
+    '(*args)'
+
+    >>> str(Signature())
+    '()'
 
 
 Parameter Object
@@ -80,20 +120,22 @@
 propose a rich Parameter object designed to represent any possible
 function parameter.
 
-The structure of the Parameter object is:
+A Parameter object has the following public attributes and methods:
 
 * name : str
-    The name of the parameter as a string.
+    The name of the parameter as a string.  Must be a valid
+    python identifier name (with the exception of ``POSITIONAL_ONLY``
+    parameters, which can have it set to ``None``.)
 
 * default : object
-    The default value for the parameter, if specified.  If the
-    parameter has no default value, this attribute is not set.
+    The default value for the parameter.  If the parameter has no
+    default value, this attribute is not set.
 
 * annotation : object
-    The annotation for the parameter if specified.  If the
-    parameter has no annotation, this attribute is not set.
+    The annotation for the parameter.  If the parameter has no
+    annotation, this attribute is not set.
 
-* kind : str
+* kind
     Describes how argument values are bound to the parameter.
     Possible values:
 
@@ -101,7 +143,7 @@
          as a positional argument.
 
          Python has no explicit syntax for defining positional-only
-         parameters, but many builtin and extension module functions
+         parameters, but many built-in and extension module functions
          (especially those that accept only one or two parameters)
          accept them.
 
@@ -124,9 +166,30 @@
          that aren't bound to any other parameter. This corresponds
          to a "\*\*kwds" parameter in a Python function definition.
 
+* replace(\*, name, kind, default, annotation) -> Parameter
+    Creates a new Parameter instance based on the instance
+    ``replaced`` was invoked on.  To override a Parameter
+    attribute, pass the corresponding argument.  To remove
+    an attribute from a ``Parameter``, pass ``Parameter.empty``.
+
+
 Two parameters are equal when they have equal names, kinds, defaults,
 and annotations.
 
+Parameter objects are immutable.  Instead of modifying a Parameter object,
+you can use ``Parameter.replace()`` to create a modified copy like so:
+::
+
+    >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)
+    >>> str(param)
+    'foo=42'
+
+    >>> str(param.replace())
+    'foo=42'
+
+    >>> str(param.replace(default=Parameter.empty, annotation='spam'))
+    "foo:'spam'"
+
 
 BoundArguments Object
 =====================
@@ -138,7 +201,8 @@
 
 * arguments : OrderedDict
     An ordered, mutable mapping of parameters' names to arguments' values.
-    Does not contain arguments' default values.
+    Contains only explicitly bound arguments.  Arguments for
+    which ``bind()`` relied on a default value are skipped.
 * args : tuple
     Tuple of positional arguments values.  Dynamically computed from
     the 'arguments' attribute.
@@ -159,6 +223,23 @@
     ba = sig.bind(10, b=20)
     test(*ba.args, **ba.kwargs)
 
+Arguments which could be passed as part of either ``*args`` or ``**kwargs``
+will be included only in the ``BoundArguments.args`` attribute.  Consider the
+following example:
+::
+
+    def test(a=1, b=2, c=3):
+        pass
+
+    sig = signature(test)
+    ba = sig.bind(a=10, c=13)
+
+    >>> ba.args
+    (10,)
+
+    >>> ba.kwargs:
+    {'c': 13}
+
 
 Implementation
 ==============
@@ -172,7 +253,7 @@
     - If the object is not callable - raise a TypeError
 
     - If the object has a ``__signature__`` attribute and if it
-      is not ``None`` - return a shallow copy of it
+      is not ``None`` - return it
 
     - If it has a ``__wrapped__`` attribute, return
       ``signature(object.__wrapped__)``
@@ -180,12 +261,9 @@
     - If the object is a an instance of ``FunctionType`` construct
       and return a new ``Signature`` for it
 
-    - 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 staticmethod, construct and return
-      a new ``Signature`` object
+    - If the object is a method, construct and return a new ``Signature``
+      object, with its first parameter (usually ``self`` or ``cls``)
+      removed
 
     - If the object is an instance of ``functools.partial``, construct
       a new ``Signature`` from its ``partial.func`` attribute, and
@@ -196,15 +274,15 @@
         - 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,
+        - If the object has a ``__new__`` method defined in its MRO,
           return a Signature object for it
 
-        - If the object has a ``__init__`` method defined in its class,
+        - If the object has a ``__init__`` method defined in its MRO,
           return a Signature object for it
 
     - Return ``signature(object.__call__)``
 
-Note, that the ``Signature`` object is created in a lazy manner, and
+Note that the ``Signature`` object is created in a lazy manner, and
 is not automatically cached.  If, however, the Signature object was
 explicitly cached by the user, ``signature()`` returns a new shallow copy
 of it on each invocation.
@@ -236,11 +314,21 @@
 ----------------------------------------
 
 Some functions may not be introspectable in certain implementations of
-Python.  For example, in CPython, builtin functions defined in C provide
+Python.  For example, in CPython, built-in functions defined in C provide
 no metadata about their arguments.  Adding support for them is out of
 scope for this PEP.
 
 
+Signature and Parameter equivalence
+-----------------------------------
+
+We assume that parameter names have semantic significance--two
+signatures are equal only when their corresponding parameters have
+the exact same names.  Users who want looser equivalence tests, perhaps
+ignoring names of VAR_KEYWORD or VAR_POSITIONAL parameters, will
+need to implement those themselves.
+
+
 Examples
 ========
 
@@ -270,6 +358,10 @@
         def __call__(self, a, b, *, c) -> tuple:
             return a, b, c
 
+        @classmethod
+        def spam(cls, a):
+            return a
+
 
     def shared_vars(*shared_args):
         """Decorator factory that defines shared variables that are
@@ -280,10 +372,12 @@
             def wrapper(*args, **kwds):
                 full_args = shared_args + args
                 return f(*full_args, **kwds)
+
             # Override signature
-            sig = wrapper.__signature__ = signature(f)
-            for __ in shared_args:
-                sig.parameters.popitem(last=False)
+            sig = signature(f)
+            sig = sig.replace(tuple(sig.parameters.values())[1:])
+            wrapper.__signature__ = sig
+
             return wrapper
         return decorator
 
@@ -313,6 +407,9 @@
     >>> format_signature(Foo().__call__)
     '(a, b, *, c) -> tuple'
 
+    >>> format_signature(Foo.spam)
+    '(a)'
+
     >>> format_signature(partial(Foo().__call__, 1, c=3))
     '(b, *, c=3) -> tuple'
 

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


More information about the Python-checkins mailing list