Preserving the argspec of a function after generating a closure

V

Victor Ng

Is there a way to preserve the argspec of a function after wrapping it
in a closure?

I'm looking for a general way to say "wrap function F in a closure",
such that inspect.getargspec on the closure would return the same
(args, varargs, varkw, defaults) tuple ass the enclosed function.

The typical code I'm using is something like this:

def wrapFunc(func):
def tmpWrapper(*args, **kwargs):
return func(*args, **kwargs)
tmpWrapper.func_name = func.func_name
return wrapFunc

This preserves the function name - how do I do more?

vic
 
B

Bengt Richter

Is there a way to preserve the argspec of a function after wrapping it
in a closure?

I'm looking for a general way to say "wrap function F in a closure",
such that inspect.getargspec on the closure would return the same
(args, varargs, varkw, defaults) tuple ass the enclosed function.

The typical code I'm using is something like this:

def wrapFunc(func):
def tmpWrapper(*args, **kwargs):
return func(*args, **kwargs)
tmpWrapper.func_name = func.func_name
return wrapFunc

This preserves the function name - how do I do more?
Probably you could use the inspect.getargspec(func) info to
build a wrapper with the identical signature that would call func
passing everything through 1:1, but that would seem kind of useless, unless
you want to substitute some closure values into the func call and get
a currying effect. In that case, your new signature should probably be changed
to reflect the real effective signature of the curried function.

I recently posted a byte-code-munging decorator hack that accomplishes some of that. E.g.,
(I keep presets.py in my ut package, where I accumulate miscellaneous experimental untility stuff)
... def foo(x, y): return x*y
...
>>> import inspect
>>> inspect.getargspec(foo) (['x'], None, None, None)
>>> import dis
>>> dis.dis(foo)
1 0 LOAD_CONST 1 (123)
3 STORE_FAST 1 (y)

3 6 LOAD_FAST 0 (x)
9 LOAD_FAST 1 (y)
12 BINARY_MULTIPLY
13 RETURN_VALUE

And without the currying decorator:
>>> def foo(x, y): return x*y ...
>>> inspect.getargspec(foo) (['x', 'y'], None, None, None)
>>> dis.dis(foo)
1 0 LOAD_FAST 0 (x)
3 LOAD_FAST 1 (y)
6 BINARY_MULTIPLY
7 RETURN_VALUE


Basically, the curry decorator here injects a local assignment and adjusts the line map
and signature and a few other tweaks, depending.

This doesn't wrap the function though, it modifies it.
I haven't tried decorating indirectly... Hm...
Faking decoration of foo with getf to substitute the func passed ...
... def getf(f): return func
... @curry(y=y)
... @getf
... def foo(): pass
... foo.func_name = func.func_name
... return foo
... Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: bar() takes exactly 2 arguments (1 given)

oops, that should have been wbar ...
>>> wbar(123) 1123
>>> inspect.getargspec(wbar) (['x'], None, None, None)
>>> import dis
>>> dis.dis(wbar)
1 0 LOAD_CONST 1 (1000)
3 STORE_FAST 1 (y)

3 6 LOAD_FAST 0 (x)
9 LOAD_FAST 1 (y)
12 BINARY_ADD
13 RETURN_VALUE
>>> inspect.getargspec(bar) (['x', 'y'], None, None, None)
>>> dis.dis(bar)
1 0 LOAD_FAST 0 (x)
3 LOAD_FAST 1 (y)
6 BINARY_ADD
7 RETURN_VALUE

I don't know where this is leading. What was your goal again?

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top