Preserving the argspec of a function after generating a closure

Discussion in 'Python' started by Victor Ng, Mar 11, 2005.

  1. Victor Ng

    Victor Ng Guest

    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

    --
    "Never attribute to malice that which can be adequately explained by
    stupidity." - Hanlon's Razor
    Victor Ng, Mar 11, 2005
    #1
    1. Advertising

  2. On Fri, 11 Mar 2005 11:55:51 -0500, Victor Ng <> 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
    Bengt Richter, Mar 11, 2005
    #2
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. TTroy
    Replies:
    16
    Views:
    780
    Peter Nilsson
    Jan 31, 2005
  2. Replies:
    4
    Views:
    506
  3. Lee Farrant
    Replies:
    2
    Views:
    161
    Bob Barrows
    Dec 24, 2003
  4. Julian Mehnle
    Replies:
    0
    Views:
    229
    Julian Mehnle
    Jul 17, 2003
  5. joe
    Replies:
    0
    Views:
    205
Loading...

Share This Page