Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #42617

Decorating functions without losing their signatures

From Rotwang <sg552@hotmail.co.uk>
Newsgroups comp.lang.python
Subject Decorating functions without losing their signatures
Date 2013-04-03 02:05 +0100
Organization A noiseless patient Spider
Message-ID <kjfv4f$4g1$1@dont-email.me> (permalink)

Show all headers | View raw


Hi all,

Here's a Python problem I've come up against and my crappy solution. 
Hopefully someone here can suggest something better. I want to decorate 
a bunch of functions with different signatures; for example, I might 
want to add some keyword-only arguments to all functions that return 
instances of a particular class so that the caller can create instances 
with additional attributes. So I do something like this:

import functools

def mydecorator(f):
     @functools.wraps(f)
     def wrapped(*args, attribute = None, **kwargs):
         result = f(*args, **kwargs)
         result.attribute = attribute
         return result
     return wrapped

@mydecorator
def f(x, y = 1, *a, z = 2, **k):
	return something

The problem with this is, when I subsequently type 'f(' in IDLE, the 
signature prompt that appears is not very useful; it looks like this:

(*args, attribute=None, **kwargs)

whereas I'd like it to look like this:

(x, y=1, *a, z=2, attribute=None, **k)


After thinking about it for a while I've come up with the following 
abomination:

import inspect

def sigwrapper(sig):
   if not isinstance(sig, inspect.Signature):
     sig = inspect.signature(sig)
   def wrapper(f):
     ps = 'args = []\n\t\t'
     ks = 'kwargs = {}\n\t\t'
     for p in sig.parameters.values():
       if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD):
         ps = '%sargs.append(%s)\n\t\t' % (ps, p.name)
       elif p.kind == p.VAR_POSITIONAL:
         ps = '%sargs.extend(%s)\n\t\t' % (ps, p.name)
       elif p.kind == p.KEYWORD_ONLY:
         ks = '%skwargs[%r] = %s\n\t\t' % (ks, p.name, p.name)
       elif p.kind == p.VAR_KEYWORD:
         ks = '%skwargs.update(%s)\n\t\t' % (ks, p.name)
     loc = {'wrapped': f}
     defstring = ('def wrapouter(wrapped = wrapped):'
                  '\n\tdef wrapinner%s:'
                  '\n\t\t%s%sreturn wrapped(*args, **kwargs)'
                  '\n\treturn wrapinner' % (sig, ps, ks))
     exec(defstring, f.__globals__, loc)
     return loc['wrapouter']()
   return wrapper

The function sigwrapper() may be passed an inspect.Signature object sig 
(or function, if that function has the right signature) and returns a 
decorator that gives any function the signature sig. I can then replace 
my original decorator with something like

def mydecorator(f):
     sig = inspect.signature(f)
     sig = do_something(sig) # add an additional kw-only argument to sig
     @functools.wraps(f)
     @sigwrapper(sig)
     def wrapped(*args, attribute = None, **kwargs):
         result = f(*args, **kwargs)
         result.attribute = attribute
         return result
     return wrapped

It seems to work, but I don't like it. Does anyone know of a better way 
of doing the same thing?

Back to comp.lang.python | Previous | NextNext in thread | Find similar | Unroll thread


Thread

Decorating functions without losing their signatures Rotwang <sg552@hotmail.co.uk> - 2013-04-03 02:05 +0100
  Re: Decorating functions without losing their signatures Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-04-03 04:15 +0000
    Re: Decorating functions without losing their signatures Rotwang <sg552@hotmail.co.uk> - 2013-04-04 02:53 +0100
  Re: Decorating functions without losing their signatures Jan Riechers <janpeterr@freenet.de> - 2013-04-03 08:06 +0300
  Re: Decorating functions without losing their signatures Michele Simionato <michele.simionato@gmail.com> - 2013-04-03 18:18 -0700
    Re: Decorating functions without losing their signatures Rotwang <sg552@hotmail.co.uk> - 2013-04-04 03:17 +0100
  Re: Decorating functions without losing their signatures Rotwang <sg552@hotmail.co.uk> - 2013-04-04 02:44 +0100

csiph-web