[Python-Dev] Updated PEP 362 (Function Signature Object)

Nick Coghlan ncoghlan at gmail.com
Thu Jun 7 02:12:06 CEST 2012


On Thu, Jun 7, 2012 at 8:38 AM, Steven D'Aprano <steve at pearwood.info> wrote:
> Brett Cannon wrote:
>> This is also Python, the language that assumes everyone is an consenting
>> adult.
>
>
> Exactly, which is why I'm not asking for __signature__ to be immutable. Who
> knows, despite Larry's skepticism (and mine!), perhaps there is a use-case
> for __signature__ being modified that we haven't thought of yet.
> But that's not really the point. It may be that nobody will be stupid enough
> to mangle __signature__, and inspect.getfullargspec becomes redundant.

I've presented use cases for doing this already. Please stop calling me stupid.

It will make sense to lie in __signature__ any time there are
constraints on a callable object that aren't accurately reflected in
its Python level signature. The simplest example I can think of is a
decorator that passes extra arguments in to the underlying function on
every call. For example, here's a more elegant alternative to the
default argument hack that relies on manipulating __signature__ to
avoid breaking introspection:

    def shared_vars(*shared_args):
        """Decorator factory that defines shared variables that are
passed to every invocation of the function"""
        def decorator(f):
            @functools.wraps(f) # Sets wrapper.__signature__ to Signature(f)
            def wrapper(*args, **kwds):
                full_args = shared_args + args
                return f(*full_args, **kwds)
            # When using this decorator, the public signature isn't
the same as that
            # provided by the underlying function, as the first few positional
            # arguments are provided by the decorator
            sig = wrapper.__signature__
            for __ in shared_args:
                sig.popitem()

    @shared_vars({})
    def example(_state, arg1, arg2, arg3):
        # _state is for private communication between "shared_vars"
and the function
        # callers can't set it, and never see it (unless they dig into
example.__wrapped__)

This has always been possible, but it's been a bad idea because of the
way it breaks pydoc (including help(example)) and other automatic
documentation tools. With a writable __signature__ attribute it
becomes possible to have our cake and eat it too.

>> If people really want to keep getullargspec() around then I want to at
>> least add a note to the function that signature objects exist as an
>> alternative (but not vice-versa).
>
> +1

Also +1, since inspect.getfullargspec() and inspect.signature()
operate at different levels in order to answer different questions.
The former asks "what is the *actual* signature", while the latter
provides a way to ask "what is the *effective* signature".

That's why I see the PEP as more than just a way to more easily
introspect function signatures: the ability to set a __signature__
attribute and have the inspect module pay attention to it means it
becomes possible to cleanly advertise the signature of callables that
aren't actual functions, and *also* possible to derive a new signature
from an existing one, *without needing to care about the details of
that existing signature* (as in the example above, it's only necessary
to know how the signature will *change*).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list