Here's another variation, which is fairly concise and doesn't use functools:
def decorator(*args, **kwargs):
def inner_decorator(fn, foo=23, bar=42, abc=None):
'''Always passed , the function to decorate.
# Do whatever decorating is required.
...
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
Depending on whether inner_decorator can be called with only one parameter, one can then do @decorator, @decorator(), @decorator(24) etc.
This can be generalised to a 'decorator decorator':
def make_inner_decorator(inner_decorator):
def decorator(*args, **kwargs):
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
return decorator
@make_inner_decorator
def my_decorator(fn, a=34, b='foo'):
...
@my_decorator
def foo(): ...
@my_decorator()
def foo(): ...
@my_decorator(42)
def foo(): ...