On Fri, 11 Mar 2005 11:55:51 -0500, Victor Ng <cr*********@gmail.com> wrote:
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)
from ut.presets import presets, curry
@curry(y=123)
... 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 wrap(func, y):
... def getf(f): return func
... @curry(y=y)
... @getf
... def foo(): pass
... foo.func_name = func.func_name
... return foo
... def bar(x, y): return x+y
... wbar = wrap(bar, 1000)
wbar
<function bar at 0x02F1AAE4> bar(123)
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