[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