You don't have to copy the function signature. Instead, accept arbitrary positional and keyword arguments and pass those on:
def proxy_function(*args, **kw):
return original_function(*args, **kw)
Here the *args
and **kw
syntax in the proxy_function
signature are given a tuple and dictionary, respectively, of arguments passed into the function:
>>> def foo(*args, **kw):
... print args
... print kw
...
>>> foo('spam', 'ham', monty='python')
('spam', 'ham')
{'monty': 'python'}
Similarly, the *args
and **kw
syntax in the original_function()
call takes a sequence or a mapping, respectively, to apply their contents as separate arguments to the function being called:
>>> def bar(baz, fourtytwo=42):
... print baz
... print fourtytwo
...
>>> args = ('hello world!',)
>>> kwargs = {'fourtytwo': 'the answer'}
>>> bar(*args, **kwargs)
hello world!
the answer
Combined, the two serve as an arbitrary-argument-pass-through for proxy functions.
Creating a full facade on the other hand is a little more involved:
import inspect
_default = object()
def build_facade(func):
"""Build a facade function, matching the signature of `func`.
Note that defaults are replaced by _default, and _proxy will reconstruct
these to preserve mutable defaults.
"""
name = func.__name__
docstring = func.__doc__
spec = inspect.getargspec(func)
args, defaults = spec[0], spec[3]
boundmethod = getattr(func, '__self__', None)
arglen = len(args)
if defaults is not None:
defaults = zip(args[arglen - len(defaults):], defaults)
arglen -= len(defaults)
def _proxy(*args, **kw):
if boundmethod:
args = args[1:] # assume we are bound too and don't pass on self
# Reconstruct keyword arguments
if defaults is not None:
args, kwparams = args[:arglen], args[arglen:]
for positional, (key, default) in zip(kwparams, defaults):
if positional is _default:
kw[key] = default
else:
kw[key] = positional
return func(*args, **kw)
args = inspect.formatargspec(formatvalue=lambda v: '=_default', *spec)
callargs = inspect.formatargspec(formatvalue=lambda v: '', *spec)
facade = 'def {}{}:\n """{}"""\n return _proxy{}'.format(
name, args, docstring, callargs)
facade_globs = {'_proxy': _proxy, '_default': _default}
exec facade in facade_globs
return facade_globs[name]
This produces a whole new function object with the same argument names, and handles defaults by referencing the original proxied function defaults rather than copy them to the facade; this ensures that even mutable defaults continue to work.
The facade builder handles bound methods too; in that case self
is removed from the call before passing on, to ensure that the target method is not getting an extra self
argument (which would be the wrong type anyway).
Handling unbound methods is out of scope here; provide your own _proxy
function in that case that can instantiate the proxied class and pass on arguments without the self
or provide a new value for self
; you cannot pass in self
unaltered.
Demo:
>>> def foobar(bar, baz, default=[]):
... print bar, baz, default
...
>>> build_facade(foobar)
<function foobar at 0x10258df50>
>>> build_facade(foobar)('spam', 'eggs')
spam eggs []
>>> inspect.getargspec(build_facade(foobar))
ArgSpec(args=['bar', 'baz', 'default'], varargs=None, keywords=None, defaults=(<object object at 0x102593cd0>,))
>>> class Foo(object):
... def bar(self, spam): print spam
...
>>> foo = Foo()
>>> class FooProxy(object):
... bar = build_facade(foo.bar)
...
>>> FooProxy().bar('hello!')
hello!
>>> inspect.getargspec(FooProxy.bar)
ArgSpec(args=['self', 'spam'], varargs=None, keywords=None, defaults=None)