Update from Yury.
This commit is contained in:
parent
e22d1112c4
commit
5f54b726b5
164
pep-0362.txt
164
pep-0362.txt
|
@ -51,12 +51,13 @@ A Signature object has the following public attributes and methods:
|
|||
as listed in ``code.co_varnames``).
|
||||
* bind(\*args, \*\*kwargs) -> BoundArguments
|
||||
Creates a mapping from positional and keyword arguments to
|
||||
parameters. Raises a ``BindError`` (subclass of ``TypeError``)
|
||||
if the passed arguments do not match the signature.
|
||||
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.)
|
||||
behavior.) Raises a ``TypeError`` if the passed arguments do
|
||||
not match the signature.
|
||||
* format(...) -> str
|
||||
Formats the Signature object to a string. Optional arguments allow
|
||||
for custom render functions for parameter names,
|
||||
|
@ -84,27 +85,53 @@ The structure of the Parameter object is:
|
|||
|
||||
* name : str
|
||||
The name of the parameter as a string.
|
||||
|
||||
* default : object
|
||||
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
|
||||
parameter has no annotation, this attribute is not set.
|
||||
* is_keyword_only : bool
|
||||
True if the parameter is keyword-only, else False.
|
||||
* is_args : bool
|
||||
True if the parameter accepts variable number of arguments
|
||||
(``*args``-like), else False.
|
||||
* is_kwargs : bool
|
||||
True if the parameter accepts variable number of keyword
|
||||
arguments (``**kwargs``-like), else False.
|
||||
* is_implemented : bool
|
||||
|
||||
* kind : str
|
||||
Describes how argument values are bound to the parameter.
|
||||
Possible values:
|
||||
|
||||
* ``Parameter.POSITIONAL_ONLY`` - value must be supplied
|
||||
as a positional argument.
|
||||
|
||||
Python has no explicit syntax for defining positional-only
|
||||
parameters, but many builtin and extension module functions
|
||||
(especially those that accept only one or two parameters)
|
||||
accept them.
|
||||
|
||||
* ``Parameter.POSITIONAL_OR_KEYWORD`` - value may be
|
||||
supplied as either a keyword or positional argument
|
||||
(this is the standard binding behaviour for functions
|
||||
implemented in Python.)
|
||||
|
||||
* ``Parameter.KEYWORD_ONLY`` - value must be supplied
|
||||
as a keyword argument. Keyword only parameters are those
|
||||
which appear after a "*" or "\*args" entry in a Python
|
||||
function definition.
|
||||
|
||||
* ``Parameter.VAR_POSITIONAL`` - a tuple of positional
|
||||
arguments that aren't bound to any other parameter.
|
||||
This corresponds to a "\*args" parameter in a Python
|
||||
function definition.
|
||||
|
||||
* ``Parameter.VAR_KEYWORD`` - a dict of keyword arguments
|
||||
that aren't bound to any other parameter. This corresponds
|
||||
to a "\*\*kwds" parameter in a Python function definition.
|
||||
|
||||
* implemented : bool
|
||||
True if the parameter is implemented for use. Some platforms
|
||||
implement functions but can't support specific parameters
|
||||
(e.g. "mode" for ``os.mkdir``). Passing in an unimplemented
|
||||
parameter may result in the parameter being ignored,
|
||||
or in NotImplementedError being raised. It is intended that
|
||||
all conditions where ``is_implemented`` may be False be
|
||||
all conditions where ``implemented`` may be False be
|
||||
thoroughly documented.
|
||||
|
||||
Two parameters are equal when all their attributes are equal.
|
||||
|
@ -159,12 +186,11 @@ The function implements the following algorithm:
|
|||
- If it is ``None`` and the object is an instance of
|
||||
``BuiltinFunction``, raise a ``ValueError``
|
||||
|
||||
- If the object is a an instance of ``FunctionType``:
|
||||
- If it has a ``__wrapped__`` attribute, return
|
||||
``signature(object.__wrapped__)``
|
||||
|
||||
- If it has a ``__wrapped__`` attribute, return
|
||||
``signature(object.__wrapped__)``
|
||||
|
||||
- Or else construct a new ``Signature`` object and return it
|
||||
- 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
|
||||
|
@ -223,6 +249,9 @@ Examples
|
|||
|
||||
Visualizing Callable Objects' Signature
|
||||
---------------------------------------
|
||||
|
||||
Let's define some classes and functions:
|
||||
|
||||
::
|
||||
|
||||
from inspect import signature
|
||||
|
@ -245,25 +274,62 @@ Visualizing Callable Objects' Signature
|
|||
return a, b, c
|
||||
|
||||
|
||||
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))))
|
||||
def shared_vars(*shared_args):
|
||||
"""Decorator factory that defines shared variables that are
|
||||
passed to every invocation of the function"""
|
||||
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
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)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
The script will output:
|
||||
@shared_vars({})
|
||||
def example(_state, a, b, c):
|
||||
return _state, a, b, c
|
||||
|
||||
|
||||
def format_signature(obj):
|
||||
return str(signature(obj))
|
||||
|
||||
|
||||
Now, in the python REPL:
|
||||
|
||||
::
|
||||
|
||||
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
|
||||
>>> format_signature(FooMeta)
|
||||
'(name, bases, dct, *, bar:bool=False)'
|
||||
|
||||
>>> format_signature(Foo)
|
||||
'(spam:int=42)'
|
||||
|
||||
>>> format_signature(Foo.__call__)
|
||||
'(self, a, b, *, c) -> tuple'
|
||||
|
||||
>>> format_signature(Foo().__call__)
|
||||
'(a, b, *, c) -> tuple'
|
||||
|
||||
>>> format_signature(partial(Foo().__call__, 1, c=3))
|
||||
'(b, *, c=3) -> tuple'
|
||||
|
||||
>>> format_signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20))
|
||||
'(*, c=20) -> tuple'
|
||||
|
||||
>>> format_signature(example)
|
||||
'(a, b, c)'
|
||||
|
||||
>>> format_signature(partial(example, 1, 2))
|
||||
'(c)'
|
||||
|
||||
>>> format_signature(partial(partial(example, 1, b=2), c=3))
|
||||
'(b=2, c=3)'
|
||||
|
||||
|
||||
Annotation Checker
|
||||
|
@ -317,14 +383,14 @@ Annotation Checker
|
|||
else:
|
||||
if not isinstance(default, type_):
|
||||
raise ValueError("{func}: wrong type of a default value for {arg!r}". \
|
||||
format(func=sig.qualname, arg=param.name))
|
||||
format(func=func.__qualname__, arg=param.name))
|
||||
|
||||
def check_type(sig, arg_name, arg_type, arg_value):
|
||||
# Internal function that encapsulates arguments type checking
|
||||
if not isinstance(arg_value, arg_type):
|
||||
raise ValueError("{func}: wrong type of {arg!r} argument, " \
|
||||
"{exp!r} expected, got {got!r}". \
|
||||
format(func=sig.qualname, arg=arg_name,
|
||||
format(func=func.__qualname__, arg=arg_name,
|
||||
exp=arg_type.__name__, got=type(arg_value).__name__))
|
||||
|
||||
@functools.wraps(func)
|
||||
|
@ -341,12 +407,12 @@ Annotation Checker
|
|||
# OK, we have a type for the argument, lets get the corresponding
|
||||
# parameter description from the signature object
|
||||
param = sig.parameters[arg_name]
|
||||
if param.is_args:
|
||||
if param.kind == param.VAR_POSITIONAL:
|
||||
# If this parameter is a variable-argument parameter,
|
||||
# then we need to check each of its values
|
||||
for value in arg:
|
||||
check_type(sig, arg_name, type_, value)
|
||||
elif param.is_kwargs:
|
||||
elif param.kind == param.VAR_KEYWORD:
|
||||
# If this parameter is a variable-keyword-argument parameter:
|
||||
for subname, value in arg.items():
|
||||
check_type(sig, arg_name + ':' + subname, type_, value)
|
||||
|
@ -364,13 +430,35 @@ Annotation Checker
|
|||
else:
|
||||
if isinstance(return_type, type) and not isinstance(result, return_type):
|
||||
raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \
|
||||
format(func=sig.qualname, exp=return_type.__name__,
|
||||
format(func=func.__qualname__, exp=return_type.__name__,
|
||||
got=type(result).__name__))
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
Render Function Signature to HTML
|
||||
---------------------------------
|
||||
|
||||
::
|
||||
|
||||
import inspect
|
||||
|
||||
def format_to_html(func):
|
||||
sig = inspect.signature(func)
|
||||
|
||||
html = sig.format(token_params_separator='<span class="t-comma">,</span>',
|
||||
token_colon='<span class="t-colon">:</span>',
|
||||
token_eq='<span class="t-eq">=</span>',
|
||||
token_return_annotation='<span class="t-ra">-></span>',
|
||||
token_left_paren='<span class="t-lp">(</span>',
|
||||
token_right_paren='<span class="t-lp">)</span>',
|
||||
token_kwonly_separator='<span class="t-ast">*</span>',
|
||||
format_name=lambda name: '<span class="name">'+name+'</span>')
|
||||
|
||||
return '<span class="py-func">{}</span>'.format(html)
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
|
|
Loading…
Reference in New Issue