+ if not help:
+ raise TypeError("'help' should not be empty")
+ def make_wrapper(f):
+ log.debug('handler(): Decorating %s()', f.__name__)
+ # Here comes the tricky part:
+ # We need to make our wrapped function to accept any number of
+ # positional and keyword arguments, but checking for the correct
+ # arguments and raising an exception in case the arguments doesn't
+ # match.
+ # So we create a dummy function, with the same signature as the
+ # wrapped one, so we can check later (at "dispatch-time") if the
+ # real function call will be successful. If the dummy function don't
+ # raise a TypeError, the arguments are just fine.
+ env = dict()
+ argspec = inspect.getargspec(f)
+ signature = inspect.formatargspec(*argspec)
+ # The dummy function
+ exec "def f%s: pass" % signature in env
+ signature_check = env['f']
+ # The wrapper to check the signature at "dispatch-time"
+ def wrapper(*args, **kwargs):
+ # First we check if the arguments passed are OK.
+ try:
+ signature_check(*args, **kwargs)
+ except TypeError, e:
+ # If not, we raise an appropriate error.
+ raise WrongArgumentsError(f, e)
+ # If they are fine, we call the real function
+ return f(*args, **kwargs)
+ # Some flag to mark our handlers for simple checks
+ wrapper._dispatcher_handler = True
+ # The help string we asked for in the first place =)
+ wrapper.handler_help = help
+ # We store the original signature for better help generation
+ wrapper.handler_argspec = argspec
+ # And some makeup, to make our wrapper look like the original function
+ wrapper.__name__ = f.__name__
+ wrapper.__dict__.update(f.__dict__)
+ # We add a hint in the documentation
+ wrapper.__doc__ = "Pymin handler with signature: %s%s" \
+ % (wrapper.__name__, signature)
+ if f.__doc__ is not None:
+ wrapper.__doc__ += "\n\n" + f.__doc__
+ wrapper.__module__ = f.__module__
+ return wrapper
+ return make_wrapper