Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Dev

pep 362 - 5th edition

 

 

Python dev RSS feed   Index | Next | Previous | View Threaded


yselivanov.ml at gmail

Jun 19, 2012, 8:27 AM

Post #1 of 21 (1289 views)
Permalink
pep 362 - 5th edition

Hello,

The new revision of PEP 362 has been posted:
http://www.python.org/dev/peps/pep-0362/


Summary:

1. What was 'Signature.__deepcopy__' is now 'Signature.__copy__'.
__copy__ creates a shallow copy of Signature, shallow copying its
Parameters as well.

2. 'Signature.format()' was removed. I think we'll add something
to customize formatting later, in 3.4. Although, Signature still has
its __str__ method.

3. Built-in ('C') functions no longer have mutable '__signature__'
attribute, that patch was reverted. In the "Design Considerations"
section we stated clear that we don't support some callables.

4. Positions of keyword-only parameters now longer affect equality
testing of Signatures, i.e. 'foo(*, a, b)' is equal to 'foo(*, b, a)'
(Thanks to Jim Jewett for pointing that out)


The only question we have now is: when we do equality test between
Signatures, should we account for positional-only, var_positional
and var_keyword arguments names? So that: 'foo(*args)' will
be equal to 'bar(*arguments)', but not to 'spam(*coordinates:int)'
(Again, I think that's a Jim's idea)


Thank you!
--


PEP: 362
Title: Function Signature Object
Version: $Revision$
Last-Modified: $Date$
Author: Brett Cannon <brett [at] python>, Jiwon Seo <seojiwon [at] gmail>,
Yury Selivanov <yselivanov [at] sprymix>, Larry Hastings <larry [at] hastings>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 21-Aug-2006
Python-Version: 3.3
Post-History: 04-Jun-2012


Abstract
========

Python has always supported powerful introspection capabilities,
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
manner, and is spread across a half-dozen deeply nested attributes.

This PEP proposes a new representation for function signatures.
The new representation contains all necessary information about a function
and its parameters, and makes introspection easy and straightforward.

However, this object does not replace the existing function
metadata, which is used by Python itself to execute those
functions. The new metadata object is intended solely to make
function introspection easier for Python programmers.


Signature Object
================

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:

* 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.
* 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``).
* 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.

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
have equal return annotations.

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.


Parameter Object
================

Python's expressive syntax means functions can accept many different
kinds of parameters with many subtle semantic differences. We
propose a rich Parameter object designed to represent any possible
function parameter.

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.

* 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.

Two parameters are equal when they have equal names, kinds, defaults,
and annotations.


BoundArguments Object
=====================

Result of a ``Signature.bind`` call. Holds the mapping of arguments
to the function's parameters.

Has the following public attributes:

* arguments : OrderedDict
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
the 'arguments' attribute.
* kwargs : dict
Dict of keyword arguments values. Dynamically computed from
the 'arguments' attribute.

The ``arguments`` attribute should be used in conjunction with
``Signature.parameters`` for any arguments processing purposes.

``args`` and ``kwargs`` properties can be used to invoke functions:
::

def test(a, *, b):
...

sig = signature(test)
ba = sig.bind(10, b=20)
test(*ba.args, **ba.kwargs)


Implementation
==============

The implementation adds a new function ``signature()`` to the ``inspect``
module. The function is the preferred way of getting a ``Signature`` for
a callable object.

The function implements the following algorithm:

- 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

- If it has a ``__wrapped__`` attribute, return
``signature(object.__wrapped__)``

- 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 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. If, however, the Signature object was
explicitly cached by the user, ``signature()`` returns a new shallow copy
of it on each invocation.

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
----------------------------------------

The first PEP design had a provision for implicit caching of ``Signature``
objects in the ``inspect.signature()`` function. However, this has the
following downsides:

* If the ``Signature`` object is cached then any changes to the function
it describes will not be reflected in it. However, If the caching is
needed, it can be always done manually and explicitly

* It is better to reserve the ``__signature__`` attribute for the cases
when there is a need to explicitly set to a ``Signature`` object that
is different from the actual one


Some functions may not be introspectable
----------------------------------------

Some functions may not be introspectable in certain implementations of
Python. For example, in CPython, builtin functions defined in C provide
no metadata about their arguments. Adding support for them is out of
scope for this PEP.


Examples
========

Visualizing Callable Objects' Signature
---------------------------------------

Let's define some classes and functions:

::

from inspect import signature
from functools import partial, wraps


class FooMeta(type):
def __new__(mcls, name, bases, dct, *, bar:bool=False):
return super().__new__(mcls, name, bases, dct)

def __init__(cls, name, bases, dct, **kwargs):
return super().__init__(name, bases, dct)


class Foo(metaclass=FooMeta):
def __init__(self, spam:int=42):
self.spam = spam

def __call__(self, a, b, *, c) -> tuple:
return a, b, c


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


@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:

::

>>> 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
------------------
::

import inspect
import functools

def checktypes(func):
'''Decorator to verify arguments and return types

Example:

>>> @checktypes
... def test(a:int, b:str) -> int:
... return int(a * b)

>>> test(10, '1')
1111111111

>>> test(10, 1)
Traceback (most recent call last):
...
ValueError: foo: wrong type of 'b' argument, 'str' expected, got 'int'
'''

sig = inspect.signature(func)

types = {}
for param in sig.parameters.values():
# Iterate through function's parameters and build the list of
# arguments types
try:
type_ = param.annotation
except AttributeError:
continue
else:
if not inspect.isclass(type_):
# Not a type, skip it
continue

types[param.name] = type_

# If the argument has a type specified, let's check that its
# default value (if present) conforms with the type.
try:
default = param.default
except AttributeError:
continue
else:
if not isinstance(default, type_):
raise ValueError("{func}: wrong type of a default value for {arg!r}". \
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=func.__qualname__, arg=arg_name,
exp=arg_type.__name__, got=type(arg_value).__name__))

@functools.wraps(func)
def wrapper(*args, **kwargs):
# Let's bind the arguments
ba = sig.bind(*args, **kwargs)
for arg_name, arg in ba.arguments.items():
# And iterate through the bound arguments
try:
type_ = types[arg_name]
except KeyError:
continue
else:
# 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.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.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)
else:
# And, finally, if this parameter a regular one:
check_type(sig, arg_name, type_, arg)

result = func(*ba.args, **ba.kwargs)
# The last bit - let's check that the result is correct
try:
return_type = sig.return_annotation
except AttributeError:
# Looks like we don't have any restriction on the return type
pass
else:
if isinstance(return_type, type) and not isinstance(result, return_type):
raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \
format(func=func.__qualname__, exp=return_type.__name__,
got=type(result).__name__))
return result

return wrapper


References
==========

.. [#impl] pep362 branch (https://bitbucket.org/1st1/cpython/overview)
.. [#issue] issue 15008 (http://bugs.python.org/issue15008)


Copyright
=========

This document has been placed in the public domain.

..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
coding: utf-8
End:

_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ethan at stoneleaf

Jun 19, 2012, 8:55 AM

Post #2 of 21 (1259 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

Yury Selivanov wrote:
> Hello,
>
> The new revision of PEP 362 has been posted:
> http://www.python.org/dev/peps/pep-0362/
>
>
> Summary:
>
> 1. What was 'Signature.__deepcopy__' is now 'Signature.__copy__'.
> __copy__ creates a shallow copy of Signature, shallow copying its
> Parameters as well.
>
> 2. 'Signature.format()' was removed. I think we'll add something
> to customize formatting later, in 3.4. Although, Signature still has
> its __str__ method.
>
> 3. Built-in ('C') functions no longer have mutable '__signature__'
> attribute, that patch was reverted. In the "Design Considerations"
> section we stated clear that we don't support some callables.
>
> 4. Positions of keyword-only parameters now longer affect equality
> testing of Signatures, i.e. 'foo(*, a, b)' is equal to 'foo(*, b, a)'
> (Thanks to Jim Jewett for pointing that out)
>
>
> The only question we have now is: when we do equality test between
> Signatures, should we account for positional-only, var_positional
> and var_keyword arguments names? So that: 'foo(*args)' will
> be equal to 'bar(*arguments)', but not to 'spam(*coordinates:int)'
> (Again, I think that's a Jim's idea)

There are obviously cases where the names should be considered (such as
foo(source, dest) and bar(dest, source) ) and cases where it should not
be (spam(text, count) and eggs(txt, cnt) )...

I think the default implementation should be strict (names are
considered) as it is safer to have a false negative than a false positive.

However, rather than force everyone who is willing to cope with the
possible false positives from rewriting a Signature equality routine
that ignores names, perhaps a method can be added to the class that does so?

class Signature:
. . .
def equivalent(self, other):
"compares two Signatures for equality (ignores parameter names)"
. . .

~Ethan~
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 9:38 AM

Post #3 of 21 (1255 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 11:55 AM, Ethan Furman wrote:

> Yury Selivanov wrote:
>> Hello,
>> The new revision of PEP 362 has been posted:
>> http://www.python.org/dev/peps/pep-0362/
>> Summary:
>> 1. What was 'Signature.__deepcopy__' is now 'Signature.__copy__'.
>> __copy__ creates a shallow copy of Signature, shallow copying its
>> Parameters as well.
>> 2. 'Signature.format()' was removed. I think we'll add something
>> to customize formatting later, in 3.4. Although, Signature still has
>> its __str__ method.
>> 3. Built-in ('C') functions no longer have mutable '__signature__'
>> attribute, that patch was reverted. In the "Design Considerations"
>> section we stated clear that we don't support some callables.
>> 4. Positions of keyword-only parameters now longer affect equality
>> testing of Signatures, i.e. 'foo(*, a, b)' is equal to 'foo(*, b, a)'
>> (Thanks to Jim Jewett for pointing that out)
>> The only question we have now is: when we do equality test between
>> Signatures, should we account for positional-only, var_positional
>> and var_keyword arguments names? So that: 'foo(*args)' will
>> be equal to 'bar(*arguments)', but not to 'spam(*coordinates:int)'
>> (Again, I think that's a Jim's idea)
>
> There are obviously cases where the names should be considered (such as foo(source, dest) and bar(dest, source) ) and cases where it should not be (spam(text, count) and eggs(txt, cnt) )...
>
> I think the default implementation should be strict (names are considered) as it is safer to have a false negative than a false positive.

+1

> However, rather than force everyone who is willing to cope with the possible false positives from rewriting a Signature equality routine that ignores names, perhaps a method can be added to the class that does so?
>
> class Signature:
> . . .
> def equivalent(self, other):
> "compares two Signatures for equality (ignores parameter names)"
> . . .

I don't think that comparing signatures will be a common operation,
so maybe we can postpone adding any additional methods for that?

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ethan at stoneleaf

Jun 19, 2012, 10:03 AM

Post #4 of 21 (1254 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

Yury Selivanov wrote:
> On 2012-06-19, at 11:55 AM, Ethan Furman wrote:
>
>> Yury Selivanov wrote:
>>> Hello,
>>> The new revision of PEP 362 has been posted:
>>> http://www.python.org/dev/peps/pep-0362/
>>> Summary:
>>> 1. What was 'Signature.__deepcopy__' is now 'Signature.__copy__'.
>>> __copy__ creates a shallow copy of Signature, shallow copying its
>>> Parameters as well.
>>> 2. 'Signature.format()' was removed. I think we'll add something
>>> to customize formatting later, in 3.4. Although, Signature still has
>>> its __str__ method.
>>> 3. Built-in ('C') functions no longer have mutable '__signature__'
>>> attribute, that patch was reverted. In the "Design Considerations"
>>> section we stated clear that we don't support some callables.
>>> 4. Positions of keyword-only parameters now longer affect equality
>>> testing of Signatures, i.e. 'foo(*, a, b)' is equal to 'foo(*, b, a)'
>>> (Thanks to Jim Jewett for pointing that out)
>>> The only question we have now is: when we do equality test between
>>> Signatures, should we account for positional-only, var_positional
>>> and var_keyword arguments names? So that: 'foo(*args)' will
>>> be equal to 'bar(*arguments)', but not to 'spam(*coordinates:int)'
>>> (Again, I think that's a Jim's idea)
>> There are obviously cases where the names should be considered (such as foo(source, dest) and bar(dest, source) ) and cases where it should not be (spam(text, count) and eggs(txt, cnt) )...
>>
>> I think the default implementation should be strict (names are considered) as it is safer to have a false negative than a false positive.
>
> +1
>
>> However, rather than force everyone who is willing to cope with the possible false positives from rewriting a Signature equality routine that ignores names, perhaps a method can be added to the class that does so?
>>
>> class Signature:
>> . . .
>> def equivalent(self, other):
>> "compares two Signatures for equality (ignores parameter names)"
>> . . .
>
> I don't think that comparing signatures will be a common operation,
> so maybe we can postpone adding any additional methods for that?

Sure, toss it in the list of possible adds for 3.4.

At some point it was suggested that Signature be put in provisionally so
we could modify the API if needed -- are we doing that?

~Ethan~
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 10:53 AM

Post #5 of 21 (1255 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 1:03 PM, Ethan Furman wrote:

> Yury Selivanov wrote:
>> On 2012-06-19, at 11:55 AM, Ethan Furman wrote:
>>> Yury Selivanov wrote:
>>>> Hello,
>>>> The new revision of PEP 362 has been posted:
>>>> http://www.python.org/dev/peps/pep-0362/
>>>> Summary:
>>>> 1. What was 'Signature.__deepcopy__' is now 'Signature.__copy__'.
>>>> __copy__ creates a shallow copy of Signature, shallow copying its
>>>> Parameters as well.
>>>> 2. 'Signature.format()' was removed. I think we'll add something
>>>> to customize formatting later, in 3.4. Although, Signature still has
>>>> its __str__ method.
>>>> 3. Built-in ('C') functions no longer have mutable '__signature__'
>>>> attribute, that patch was reverted. In the "Design Considerations"
>>>> section we stated clear that we don't support some callables.
>>>> 4. Positions of keyword-only parameters now longer affect equality
>>>> testing of Signatures, i.e. 'foo(*, a, b)' is equal to 'foo(*, b, a)'
>>>> (Thanks to Jim Jewett for pointing that out)
>>>> The only question we have now is: when we do equality test between
>>>> Signatures, should we account for positional-only, var_positional
>>>> and var_keyword arguments names? So that: 'foo(*args)' will
>>>> be equal to 'bar(*arguments)', but not to 'spam(*coordinates:int)'
>>>> (Again, I think that's a Jim's idea)
>>> There are obviously cases where the names should be considered (such as foo(source, dest) and bar(dest, source) ) and cases where it should not be (spam(text, count) and eggs(txt, cnt) )...
>>>
>>> I think the default implementation should be strict (names are considered) as it is safer to have a false negative than a false positive.
>> +1
>>> However, rather than force everyone who is willing to cope with the possible false positives from rewriting a Signature equality routine that ignores names, perhaps a method can be added to the class that does so?
>>>
>>> class Signature:
>>> . . .
>>> def equivalent(self, other):
>>> "compares two Signatures for equality (ignores parameter names)"
>>> . . .
>> I don't think that comparing signatures will be a common operation,
>> so maybe we can postpone adding any additional methods for that?
>
> Sure, toss it in the list of possible adds for 3.4.
>
> At some point it was suggested that Signature be put in provisionally so we could modify the API if needed -- are we doing that?

Well, it doesn't have much of an API right now (just few methods)

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 5:39 PM

Post #6 of 21 (1252 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 3:53 AM, Yury Selivanov <yselivanov.ml [at] gmail> wrote:
> On 2012-06-19, at 1:03 PM, Ethan Furman wrote:
>> At some point it was suggested that Signature be put in provisionally so we could modify the API if needed -- are we doing that?
>
> Well, it doesn't have much of an API right now (just few methods)

Right, provisional API status is a fairly blunt instrument that we
should only use if we can't find any other way to allow the standard
library to progress.

In this particular case, we have a superior alternative: distill the
API down to the bare minimum that is needed to provide a more robust,
cross-implementation format for describing callable signatures. We can
then implement that bare minimum API for 3.3 as the foundation for
higher level layered APIs that offer more flexibility, and/or capture
more information about the callables.

Further comments on the PEP and implementation:

1. The PEP should specify the constructor signatures for Signature and
Parameter objects (I'd also prefer it if "kind" was permitted as a
positional argument)

2. The constructor for Parameter objects should require that names for
positional-only parameters start with "<" and end with ">" to ensure
they can always be distinguished from standard parameters in signature
string representations and in BoundArguments.parameters

3. The standard Signature constructor should accept an iterable of
Parameter objects directly (with the return annotation as an optional
keyword-only "annotation" argument) and enforce the following
constraints:
- permitted parameter binding order is strictly (POSITIONAL_ONLY,
POSITIONAL_OR_KEYWORD, VAR_POSITIONAL, KEYWORD_ONLY, VAR_KEYWORD)
- all parameter names must be distinct (otherwise bind() won't work properly)
- if a positional only parameter is not named (param.name is None), it
is given a name based on its position in the parameter list ("<0>",
"<1>", etc)

4. The current Signature constructor should become a "from_function"
class method

With these changes, the following would become straightforward:

>>> def f1(a): pass
>>> str(signature(f1))
(a)
>>> def f2(*args): a, = args
>>> f.__signature__ = Signature([Parameter("<a>",
Parameter.POSITIONAL_ONLY])
>>> str(signature(f2))
(<a>)
>>> def f3(*args): a, = args
>>> f.__signature__ = Signature([Parameter(None, Parameter.POSITIONAL_ONLY])
>>> str(signature(f3))
(<0>)

5. Given the updated constructor signature, we can revisit the
question of immutable signature objects (even just using read-only
properties for public attributes and exposing a dict proxy for the
parameter list). Instead of mutating the parameter list, you would
instead write code like:
new_sig = Signature(old_sig.parameters[1:])

In my opinion, that's a *much* nicer approach than copying an existing
signature object and mutating it.

6. I think "return_annotation" can safely be abbreviated to just
"annotation". The fact it is on the Signature object rather than an
individual parameter is enough to identify it as the return
annotation.

7. The idea of immutable Signature objects does highlight an annoyance
with the "attribute may be missing" style APIs. To actually duplicate
a signature correctly, including its return annotation (and assuming
the attribute is renamed), you would have to do something like:

try:
note = {"annotation": old_sig.annotation}
except AttributeError:
note = {}
new_sig = Signature(old_sig.parameters[1:], **note)

There's an alternative approach to optional attributes that's often
easier to work with: expose them as containers. In this case, since we
want to make them easy to pass as keyword-only arguments, one way to
achieve that would be expose an "optional" attribute on both Signature
and Parameter objects. Then the above would be written as:

new_sig = Signature(old_sig.parameters[1:], **old_sig.optional)

And copying a Parameter would be:
new_param = Parameter("new name", old_param.kind, **old_param.optional)

If we even keep that at all for the initial version of the API, the
direct "default" and "annotation" attributes would just be read-only
properties that accessed the "optional" container (reporting
AttributeError if the corresponding attribute was missing)

8. Not essential, but I suggest moving most of the parameter
formatting details to a Parameter.__str__ method

9. The PEP should explicitly note that we're taking a deliberately
strict approach to the notion of signature and parameter equivalence
by assuming that all parameter names have semantic significance.
Looser checks that ignore the names of positional and variable keyword
parameters can be handled with Signature.bind() or by implementing
custom key or comparison functions.

10. Similar to the discussion of convenience properties on Signature
objects themselves, I now think we should drop the "args" and "kwargs"
properties from the initial version of BoundArguments. Instead, I
propose the following attributes:
- arguments (mapping of parameter names to values supplied as arguments)
- defaults (mapping of unbound parameter names with defaults to
their default values)
- unbound (set of unbound names, always empty for bind(), may have
entries for bind_partial())

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 6:22 PM

Post #7 of 21 (1245 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:

> On Wed, Jun 20, 2012 at 3:53 AM, Yury Selivanov <yselivanov.ml [at] gmail> wrote:
>> On 2012-06-19, at 1:03 PM, Ethan Furman wrote:
>>> At some point it was suggested that Signature be put in provisionally so we could modify the API if needed -- are we doing that?
>>
>> Well, it doesn't have much of an API right now (just few methods)
>
> Right, provisional API status is a fairly blunt instrument that we
> should only use if we can't find any other way to allow the standard
> library to progress.
>
> In this particular case, we have a superior alternative: distill the
> API down to the bare minimum that is needed to provide a more robust,
> cross-implementation format for describing callable signatures. We can
> then implement that bare minimum API for 3.3 as the foundation for
> higher level layered APIs that offer more flexibility, and/or capture
> more information about the callables.
>
> Further comments on the PEP and implementation:
>
> 1. The PEP should specify the constructor signatures for Signature and
> Parameter objects (I'd also prefer it if "kind" was permitted as a
> positional argument)

+1

> 2. The constructor for Parameter objects should require that names for
> positional-only parameters start with "<" and end with ">" to ensure
> they can always be distinguished from standard parameters in signature
> string representations and in BoundArguments.parameters

+1

> 3. The standard Signature constructor should accept an iterable of
> Parameter objects directly (with the return annotation as an optional
> keyword-only "annotation" argument) and enforce the following
> constraints:
> - permitted parameter binding order is strictly (POSITIONAL_ONLY,
> POSITIONAL_OR_KEYWORD, VAR_POSITIONAL, KEYWORD_ONLY, VAR_KEYWORD)
> - all parameter names must be distinct (otherwise bind() won't work properly)
> - if a positional only parameter is not named (param.name is None), it
> is given a name based on its position in the parameter list ("<0>",
> "<1>", etc)

+1

> 4. The current Signature constructor should become a "from_function"
> class method

+1

> With these changes, the following would become straightforward:
>
>>>> def f1(a): pass
>>>> str(signature(f1))
> (a)
>>>> def f2(*args): a, = args
>>>> f.__signature__ = Signature([Parameter("<a>",
> Parameter.POSITIONAL_ONLY])
>>>> str(signature(f2))
> (<a>)
>>>> def f3(*args): a, = args
>>>> f.__signature__ = Signature([Parameter(None, Parameter.POSITIONAL_ONLY])
>>>> str(signature(f3))
> (<0>)
>
> 5. Given the updated constructor signature, we can revisit the
> question of immutable signature objects (even just using read-only
> properties for public attributes and exposing a dict proxy for the
> parameter list). Instead of mutating the parameter list, you would
> instead write code like:
> new_sig = Signature(old_sig.parameters[1:])

I think that mocking immutability here is a better decision than
to implement a truly immutable object in C. Read-only properties
and a dict-proxy for Signature.parameters will work.

> In my opinion, that's a *much* nicer approach than copying an existing
> signature object and mutating it.

+1

> 6. I think "return_annotation" can safely be abbreviated to just
> "annotation". The fact it is on the Signature object rather than an
> individual parameter is enough to identify it as the return
> annotation.

I'm not sure about this one. 'return_annotation' is a very
self-descriptive and clear name.

> 7. The idea of immutable Signature objects does highlight an annoyance
> with the "attribute may be missing" style APIs. To actually duplicate
> a signature correctly, including its return annotation (and assuming
> the attribute is renamed), you would have to do something like:
>
> try:
> note = {"annotation": old_sig.annotation}
> except AttributeError:
> note = {}
> new_sig = Signature(old_sig.parameters[1:], **note)
>
> There's an alternative approach to optional attributes that's often
> easier to work with: expose them as containers. In this case, since we
> want to make them easy to pass as keyword-only arguments, one way to
> achieve that would be expose an "optional" attribute on both Signature
> and Parameter objects. Then the above would be written as:
>
> new_sig = Signature(old_sig.parameters[1:], **old_sig.optional)
>
> And copying a Parameter would be:
> new_param = Parameter("new name", old_param.kind, **old_param.optional)
>
> If we even keep that at all for the initial version of the API, the
> direct "default" and "annotation" attributes would just be read-only
> properties that accessed the "optional" container (reporting
> AttributeError if the corresponding attribute was missing)

+0. I think that 'optional' is a bit unusual attribute for the stdlib,
but it will work if we make Signature immutable.

> 8. Not essential, but I suggest moving most of the parameter
> formatting details to a Parameter.__str__ method

+1

> 9. The PEP should explicitly note that we're taking a deliberately
> strict approach to the notion of signature and parameter equivalence
> by assuming that all parameter names have semantic significance.
> Looser checks that ignore the names of positional and variable keyword
> parameters can be handled with Signature.bind() or by implementing
> custom key or comparison functions.

+1

> 10. Similar to the discussion of convenience properties on Signature
> objects themselves, I now think we should drop the "args" and "kwargs"

Big -1 on this one. Look at the current implementation of those
properties - it's quite complex. One of the major points of new
API is to allow easy modifications of arguments. Without .args &
.kwargs it will be a PITA to call a function.

Imagine, that the "check types" example from the PEP is modified
to coerce arguments to specified types. It won't be possible
to do without .args & .kwargs. I, for instance, use this API to bind,
validate, and coerce arguments for RPC calls. The whole point for me
to work on this PEP was to make these types of functionality easy to
implement.

> properties from the initial version of BoundArguments. Instead, I
> propose the following attributes:
> - arguments (mapping of parameter names to values supplied as arguments)
> - defaults (mapping of unbound parameter names with defaults to
> their default values)

Why would you need 'defaults'? It's very easy to compile that list
manually (and I believe the use cases will be limited)

> - unbound (set of unbound names, always empty for bind(), may have
> entries for bind_partial())

This may be practical. But again - those are easy to deduce from
'BoundArguments.arguments' and 'Signature.parameters'.


In summary - I like everything you've proposed, except comment #10.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov at gmail

Jun 19, 2012, 6:29 PM

Post #8 of 21 (1247 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:

> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>
>> 2. The constructor for Parameter objects should require that names for
>> positional-only parameters start with "<" and end with ">" to ensure
>> they can always be distinguished from standard parameters in signature
>> string representations and in BoundArguments.parameters
>
> +1

Actually, can we just make positional-only parameters to render brackets
in their/Signature's __str__ methods? I think Parameter.kind should be
enough, without adding additional obstacles.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 7:06 PM

Post #9 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 11:29 AM, Yury Selivanov <yselivanov [at] gmail> wrote:
> On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:
>
>> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>>
>>> 2. The constructor for Parameter objects should require that names for
>>> positional-only parameters start with "<" and end with ">" to ensure
>>> they can always be distinguished from standard parameters in signature
>>> string representations and in BoundArguments.parameters
>>
>> +1
>
> Actually, can we just make positional-only parameters to render brackets
> in their/Signature's __str__ methods?  I think Parameter.kind should be
> enough, without adding additional obstacles.

True, the check for name clashes in Signature (and the implied numeric
"names") will cover the BoundArguments.parameters case

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov at gmail

Jun 19, 2012, 7:15 PM

Post #10 of 21 (1249 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 10:06 PM, Nick Coghlan wrote:

> On Wed, Jun 20, 2012 at 11:29 AM, Yury Selivanov <yselivanov [at] gmail> wrote:
>> On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:
>>
>>> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>>>
>>>> 2. The constructor for Parameter objects should require that names for
>>>> positional-only parameters start with "<" and end with ">" to ensure
>>>> they can always be distinguished from standard parameters in signature
>>>> string representations and in BoundArguments.parameters
>>>
>>> +1
>>
>> Actually, can we just make positional-only parameters to render brackets
>> in their/Signature's __str__ methods? I think Parameter.kind should be
>> enough, without adding additional obstacles.
>
> True, the check for name clashes in Signature (and the implied numeric
> "names") will cover the BoundArguments.parameters case

Nick, I also would like to keep Parameter.name being required.
I understand that *currently* we have no parameter names specified
for builtin methods, but we don't have any mechanisms to introspect
them too.

Now, in 3.3 (I hope) we introduce a brand new mechanism, and, probably, in
3.4 we have way to define Signatures for builtins. Why not do it right?
This whole positional-only case is just a weird anachronism of CPython.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 7:16 PM

Post #11 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 11:22 AM, Yury Selivanov
<yselivanov.ml [at] gmail> wrote:
> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>> 6. I think "return_annotation" can safely be abbreviated to just
>> "annotation". The fact it is on the Signature object rather than an
>> individual parameter is enough to identify it as the return
>> annotation.
>
> I'm not sure about this one.  'return_annotation' is a very
> self-descriptive and clear name.

I'm not too worried about that one. If you prefer the longer name,
feel free to keep it.

>> If we even keep that at all for the initial version of the API, the
>> direct "default" and "annotation" attributes would just be read-only
>> properties that accessed the "optional" container (reporting
>> AttributeError if the corresponding attribute was missing)
>
> +0.  I think that 'optional' is a bit unusual attribute for the stdlib,
> but it will work if we make Signature immutable.

The name isn't great, but the mapping is a lot more convenient when
you need to handle the case of attributes potentially being missing.

>> 10. Similar to the discussion of convenience properties on Signature
>> objects themselves, I now think we should drop the "args" and "kwargs"
>
> Big -1 on this one.  Look at the current implementation of those
> properties - it's quite complex.  One of the major points of new
> API is to allow easy modifications of arguments.  Without .args &
> .kwargs it will be a PITA to call a function.
>
> Imagine, that the "check types" example from the PEP is modified
> to coerce arguments to specified types.  It won't be possible
> to do without .args & .kwargs.  I, for instance, use this API to bind,
> validate, and coerce arguments for RPC calls. The whole point for me
> to work on this PEP was to make these types of functionality easy to
> implement.

The one thing that slightly concerns me is that given:

def f(a): pass
s = signature(f)

The following produce the same result:
s.bind(1)
s.bind(a=1)

That said, I guess if a parameter is proclaiming itself to be
KEYWORD_OR_POSITIONAL, then it really shouldn't care which way the
arguments are passed, so a stated preference for binding those as
positional parameters is fine.

>> properties from the initial version of BoundArguments. Instead, I
>> propose the following attributes:
>>  - arguments (mapping of parameter names to values supplied as arguments)
>>  - defaults (mapping of unbound parameter names with defaults to
>> their default values)
>
> Why would you need 'defaults'?  It's very easy to compile that list
> manually (and I believe the use cases will be limited)
>
>>  - unbound (set of unbound names, always empty for bind(), may have
>> entries for bind_partial())
>
> This may be practical.  But again - those are easy to deduce from
> 'BoundArguments.arguments' and 'Signature.parameters'.

OK, leave them out for now. Perhaps add a simple example showing how
to calculate them if you want them? (The most obvious use case I can
see is calculating a new signature after using bind_partial)

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 7:31 PM

Post #12 of 21 (1248 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 12:15 PM, Yury Selivanov <yselivanov [at] gmail> wrote:
> On 2012-06-19, at 10:06 PM, Nick Coghlan wrote:
>> True, the check for name clashes in Signature (and the implied numeric
>> "names") will cover the BoundArguments.parameters case
>
> Nick, I also would like to keep Parameter.name being required.
> I understand that *currently* we have no parameter names specified
> for builtin methods, but we don't have any mechanisms to introspect
> them too.

Sure, so long as the name requirement is enforced at construction time
- the current code will happily accept None as the first argument to
parameter.

> Now, in 3.3 (I hope) we introduce a brand new mechanism, and, probably, in
> 3.4 we have way to define Signatures for builtins.  Why not do it right?
> This whole positional-only case is just a weird anachronism of CPython.

No, it's a characteristic of any FFI - not all target languages will
support keyword arguments. CPython just happens to use such an FFI as
part of its implementation (due to the nature of the PyArg_Parse*
APIs).

There have been serious (albeit failed so far) attempts at coming up
with an acceptable language level syntax for positional only arguments
on python-ideas, since they're a useful concept when you want to avoid
name clashes with arbitrary keyword arguments and you *can*
effectively implement them in Python using a nested function call to
get the correct kind of error:

def _positional_only(a, b, c):
return a, b, c

def f(*args, **kwds): # "a", "b", "c" are supported as values in "kwds"
a, b, c = _positional_only(*args)

With PEP 362, we can at least *represent* the above API cleanly, even
though there's still no dedicated syntax:

>>> def _param(name): return Parameter(name, Parameter.POSITIONAL_ONLY)
>>> s = Signature([_param("a"), _param("b"), _param("c"),
Parameter("kwds", Parameter.VAR_KEYWORD])
>>> str(s)
(<a>, <b>, <c>, **kwds)

If a syntax for positional only parameters is ever defined in the
future, then the Signature __str__ implementation can be updated to
use it at the time.

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 7:36 PM

Post #13 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:
> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>> 7. The idea of immutable Signature objects does highlight an annoyance
>> with the "attribute may be missing" style APIs. To actually duplicate
>> a signature correctly, including its return annotation (and assuming
>> the attribute is renamed), you would have to do something like:
>>
>> try:
>> note = {"annotation": old_sig.annotation}
>> except AttributeError:
>> note = {}
>> new_sig = Signature(old_sig.parameters[1:], **note)

BTW, we don't have slices for OrderedDict. Since the slice object is
not hashable, we can implement it safely. I can create an issue (and draft
implementation), as I think it'd be quite a useful feature.

-
Yury

_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 8:04 PM

Post #14 of 21 (1243 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 10:16 PM, Nick Coghlan wrote:

> On Wed, Jun 20, 2012 at 11:22 AM, Yury Selivanov
> <yselivanov.ml [at] gmail> wrote:
>>> 10. Similar to the discussion of convenience properties on Signature
>>> objects themselves, I now think we should drop the "args" and "kwargs"
>>
>> Big -1 on this one. Look at the current implementation of those
>> properties - it's quite complex. One of the major points of new
>> API is to allow easy modifications of arguments. Without .args &
>> .kwargs it will be a PITA to call a function.
>>
>> Imagine, that the "check types" example from the PEP is modified
>> to coerce arguments to specified types. It won't be possible
>> to do without .args & .kwargs. I, for instance, use this API to bind,
>> validate, and coerce arguments for RPC calls. The whole point for me
>> to work on this PEP was to make these types of functionality easy to
>> implement.
>
> The one thing that slightly concerns me is that given:
>
> def f(a): pass
> s = signature(f)
>
> The following produce the same result:
> s.bind(1)
> s.bind(a=1)
>
> That said, I guess if a parameter is proclaiming itself to be
> KEYWORD_OR_POSITIONAL, then it really shouldn't care which way the
> arguments are passed, so a stated preference for binding those as
> positional parameters is fine.

Right.

>>> properties from the initial version of BoundArguments. Instead, I
>>> propose the following attributes:
>>> - arguments (mapping of parameter names to values supplied as arguments)
>>> - defaults (mapping of unbound parameter names with defaults to
>>> their default values)
>>
>> Why would you need 'defaults'? It's very easy to compile that list
>> manually (and I believe the use cases will be limited)
>>
>>> - unbound (set of unbound names, always empty for bind(), may have
>>> entries for bind_partial())
>>
>> This may be practical. But again - those are easy to deduce from
>> 'BoundArguments.arguments' and 'Signature.parameters'.
>
> OK, leave them out for now. Perhaps add a simple example showing how
> to calculate them if you want them? (The most obvious use case I can
> see is calculating a new signature after using bind_partial)

OK, will add an example to the PEP

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 8:07 PM

Post #15 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

Nick,

I started a new branch to experiment with immutable Signatures.

So far, almost everything works (except a couple unit-tests, that are
modifying now immutable Parameters & Signatures)

https://bitbucket.org/1st1/cpython/changesets/tip/branch(%22pep362-2%22)

I hope tomorrow we get some feedback on this, and if it's positive -
I'll finish off the implementation and update the PEP.

I hope we still can make it to 3.3.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 8:24 PM

Post #16 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 12:36 PM, Yury Selivanov
<yselivanov.ml [at] gmail> wrote:
> On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:
>> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>>> 7. The idea of immutable Signature objects does highlight an annoyance
>>> with the "attribute may be missing" style APIs. To actually duplicate
>>> a signature correctly, including its return annotation (and assuming
>>> the attribute is renamed), you would have to do something like:
>>>
>>>   try:
>>>       note = {"annotation": old_sig.annotation}
>>>   except AttributeError:
>>>       note = {}
>>>   new_sig = Signature(old_sig.parameters[1:], **note)
>
> BTW, we don't have slices for OrderedDict.  Since the slice object is
> not hashable, we can implement it safely. I can create an issue (and draft
> implementation), as I think it'd be quite a useful feature.

No need, my example was just wrong, it should be:

new_sig = Signature(old_sig.parameters.values()[1:])

The constructor accepts an iterable of Parameter objects rather than a mapping.

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 8:28 PM

Post #17 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 11:24 PM, Nick Coghlan wrote:

> On Wed, Jun 20, 2012 at 12:36 PM, Yury Selivanov
> <yselivanov.ml [at] gmail> wrote:
>> On 2012-06-19, at 9:22 PM, Yury Selivanov wrote:
>>> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>>>> 7. The idea of immutable Signature objects does highlight an annoyance
>>>> with the "attribute may be missing" style APIs. To actually duplicate
>>>> a signature correctly, including its return annotation (and assuming
>>>> the attribute is renamed), you would have to do something like:
>>>>
>>>> try:
>>>> note = {"annotation": old_sig.annotation}
>>>> except AttributeError:
>>>> note = {}
>>>> new_sig = Signature(old_sig.parameters[1:], **note)
>>
>> BTW, we don't have slices for OrderedDict. Since the slice object is
>> not hashable, we can implement it safely. I can create an issue (and draft
>> implementation), as I think it'd be quite a useful feature.
>
> No need, my example was just wrong, it should be:
>
> new_sig = Signature(old_sig.parameters.values()[1:])
>
> The constructor accepts an iterable of Parameter objects rather than a mapping.

That's the code I've ended up with:

sig = signature(obj.__func__)
return Signature(OrderedDict(tuple(sig.parameters.items())[1:]),
**sig.optional)

Still looks better than creating implicit & explicit copies ;)

As for slices support in OrderedDict -- it would return values, so
it won't solve the problem anyways.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


yselivanov.ml at gmail

Jun 19, 2012, 8:51 PM

Post #18 of 21 (1251 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On 2012-06-19, at 10:16 PM, Nick Coghlan wrote:
> On Wed, Jun 20, 2012 at 11:22 AM, Yury Selivanov
> <yselivanov.ml [at] gmail> wrote:
>> On 2012-06-19, at 8:39 PM, Nick Coghlan wrote:
>>> If we even keep that at all for the initial version of the API, the
>>> direct "default" and "annotation" attributes would just be read-only
>>> properties that accessed the "optional" container (reporting
>>> AttributeError if the corresponding attribute was missing)
>>
>> +0. I think that 'optional' is a bit unusual attribute for the stdlib,
>> but it will work if we make Signature immutable.
>
> The name isn't great, but the mapping is a lot more convenient when
> you need to handle the case of attributes potentially being missing.


What if instead of 'optional', we have 'base_signature'
(or 'from_signature')?

sig = signature(func)
params = OrderedDict(tuple(sig.parameters.items())[1:])
new_sig = Signature(params, base_signature=sig)

And for Paramater:

param = sig.parameters['foo']

param1 = Parameter('bar', base_parameter=param)
param2 = Parameter('spam', annotation=int, base_parameter=param)
param3 = Parameter(base_parameter=param)
param4 = Parameter(default=42, base_parameter=param)

So 'base_parameter' will be a template from which Parameter's constructor
will copy the missing arguments.

-
Yury
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 9:39 PM

Post #19 of 21 (1246 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 1:28 PM, Yury Selivanov <yselivanov.ml [at] gmail> wrote:
> On 2012-06-19, at 11:24 PM, Nick Coghlan wrote:
>> The constructor accepts an iterable of Parameter objects rather than a mapping.
>
> That's the code I've ended up with:
>
>        sig = signature(obj.__func__)
>        return Signature(OrderedDict(tuple(sig.parameters.items())[1:]),
>                         **sig.optional)

Why require a mapping as the argument? A simple iterable of Parameter
objects seems like a more convenient constructor API to me. The
constructor can take care of building the mapping from that internally
via:

param_map = OrderedDict((param.name, param for param in parameters))

> Still looks better than creating implicit & explicit copies ;)

Indeed :)

>
> As for slices support in OrderedDict -- it would return values, so
> it won't solve the problem anyways.

You wouldn't want to do it anyway - while slices happen to not be
hashable *now*, there's no strong reason behind that.

Cheers,
Nick.

--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


ncoghlan at gmail

Jun 19, 2012, 9:55 PM

Post #20 of 21 (1249 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Wed, Jun 20, 2012 at 1:51 PM, Yury Selivanov <yselivanov.ml [at] gmail> wrote:
> What if instead of 'optional', we have 'base_signature'
> (or 'from_signature')?
>
>    sig = signature(func)
>    params = OrderedDict(tuple(sig.parameters.items())[1:])
>    new_sig = Signature(params, base_signature=sig)
>
> And for Paramater:
>
>    param = sig.parameters['foo']
>
>    param1 = Parameter('bar', base_parameter=param)
>    param2 = Parameter('spam', annotation=int, base_parameter=param)
>    param3 = Parameter(base_parameter=param)
>    param4 = Parameter(default=42, base_parameter=param)
>
> So 'base_parameter' will be a template from which Parameter's constructor
> will copy the missing arguments.

Good thought (and better than my initial idea), but I'd follow the
model of namedtuple._replace and make it a separate instance method:

sig = signature(f)
new_sig = sig.replace(parameters=sig.parameters.values()[1:])

param = sig.parameters['foo']
param1 = param.replace(name='bar')
param2 = param.replace(name='spam', annotation=int)
param3 = param.replace() # namedtuple._replace also allows this edge case
param4 = param.replace(default=42)

Such a copy-and-override method should be the last piece needed to
make immutable Signature objects a viable approach.

Cheers,
Nick.


--
Nick Coghlan   |   ncoghlan [at] gmail   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com


steve at pearwood

Jun 20, 2012, 1:15 AM

Post #21 of 21 (1230 views)
Permalink
Re: pep 362 - 5th edition [In reply to]

On Tue, Jun 19, 2012 at 12:38:38PM -0400, Yury Selivanov wrote:

> > class Signature:
> > . . .
> > def equivalent(self, other):
> > "compares two Signatures for equality (ignores parameter names)"
> > . . .
>
> I don't think that comparing signatures will be a common operation,
> so maybe we can postpone adding any additional methods for that?

I think it may be. Consider callback functions: the caller may wish to
check that the callback function takes the right number of positional
arguments, but without caring whether those arguments are given any
particular name.

Checking for compatible signatures is a little more subtle than just
ignoring names. For example, keyword-only arguments need to always be
compared by names. Also, you might want to ignore optional arguments
with defaults, or at least _private optional arguments.

I think equality should be strict, including names, and we should defer
any decision about less-strict comparisons until 3.4, when we'll have
more solid use-cases for it.

I guess that's a long-winded way of saying +1 :)



--
Steven
_______________________________________________
Python-Dev mailing list
Python-Dev [at] python
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/list-python-dev%40lists.gossamer-threads.com

Python dev RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.