
Just before I go to bed, here some alternative implementations: Usage: ------ # for argument-less decorators: @decorator def my_deco(func,*func_args,**func_kwargs): pass # of course regular arguments can also be declared (same for all funcs below) @my_deco def func(*func_args,**func_kwargs): pass # for decorators with arguments: # even when there are no default arguments function-call parenthesis are needed @decorator_with_args('foo',bar='baz') def my_deco(func,deco_args,deco_kwargs,*func_args,**func_kwargs): pass @my_deco(*deco_args,**deco_kwargs) def func(*func_args,**func_kwargs): pass # alternative version where the decorator arguments are expanded: # `...` is a placeholder for the arguments of the decorator in regular argument syntax. # This way the decorator arguments can be declared inline and no deco_(kw)args or self.* # is needed. Also decorator arguments are not decoupled from their default values this way. @decorator_with_expanded_args def my_deco(func,...,*func_args,**func_kwargs): pass @my_deco(*deco_args,**deco_kwargs) def func(*func_args,**func_kwargs): pass Implementation: --------------- from types import FunctionType, ClassType from functools import wraps def decorator(deco): @wraps(deco) def _deco(func): @wraps(func) def _f(*args,**kwargs): return deco(func,*args,**kwargs) return _f return _deco def decorator_with_args(*deco_default_args,**deco_default_kwargs): def _deco_deco_deco(deco): @wraps(deco) def _deco_deco(*deco_args,**deco_kwargs): if len(deco_args) < len(deco_default_args): deco_args = deco_args+deco_default_args[len(deco_args):] merged_deco_kwargs = dict(deco_default_kwargs) merged_deco_kwargs.update(deco_kwargs) del deco_kwargs def _deco(func): @wraps(func) def _f(*args,**kwargs): return deco( func,deco_args,merged_deco_kwargs,*args,**kwargs) return _f return _deco return _deco_deco return _deco_deco_deco def decorator_with_expanded_args(deco): if isinstance(deco, FunctionType): co = deco.func_code deco_name = deco.func_name arg_names = list(co.co_varnames[0:co.co_argcount]) elif isinstance(deco, ClassType): co = deco.__init__.func_code deco_name = deco.__name__ arg_names = list(co.co_varnames[1:co.co_argcount]) elif hasattr(deco, '__call__'): co = deco.__call__.func_code deco_name = type(deco).__name__ arg_names = list(co.co_varnames[0:co.co_argcount]) else: raise TypeError('not a decorateable object') if not arg_names: raise TypeError('decorator function needs a func argument') del co del arg_names[0] min_argcount = len(arg_names) if deco.func_defaults: min_argcount -= len(deco.func_defaults) @wraps(deco) def _deco_deco(*args,**kwargs): deco_args = list(args) n = len(deco_args) if n < len(arg_names): i = n - min_argcount for arg in arg_names[n:]: if arg in kwargs: deco_args.append(kwargs.pop(arg)) elif i < 0: raise TypeError( '%s() takes at least %d positional ' + 'arguments (%d given)' % (deco_name, min_argcount, len(deco_args))) else: deco_args.append(deco.func_defaults[i]) i += 1 if kwargs: arg = kwargs.keys()[0] if arg in arg_names: raise TypeError( "%s() got multiple values for keyword argument '%s'" % (deco_name, arg)) else: raise TypeError("%s() got an unexpected keyword argument '%s'" % (deco_name, arg)) deco_args = tuple(deco_args) def _deco(func): @wraps(func) def _f(*args,**kwargs): return deco(func,*(deco_args+args),**kwargs) return _f return _deco return _deco_deco What do you think? -panzi