I'm seeing a lot of documentation on StackOverflow and elsewhere about how to write Python decorators. They typically recommend using functools.wraps
and (potentially multiple) inner functions. Especially complex is if I want a decorator which can either be called with or without brackets, i.e. @foo
or @foo(bar)
.
For example, this StackOverflow question and its various answers give a lot of insight into how to do this. However, they all appear rather surprisingly complicated (either extra conditional logic or deeper nesting of functions) for something which seems conceptually simple. My biggest concern is that, in all the examples given, >50% of the code is unrelated to that particular decorator's behavior and is shared boilerplate among all decorators written using that pattern.
The real-world examples I was looking at were the Fabric project's decorators.py and some of the Django project's various decorator.py instances. Seems a bit strange to me that there is a lot of boilerplate code unrelated to the actual intent.
I understand why I would want to use functools.wraps
for code maintainability reasons, but this seems overly complex. Is there some way to DRY and/or encapsulate this code? It seems like the only part that I really care about from the perspective of user code is the actual body of the generated inner function. How could I go about writing the remaining boilerplate once and reusing it forever?
Thanks in advance!
The decorator, wrapt, and decorators packages on PyPI abstract out some of that boilerplate code beyond what
functools.wraps
provides.If you want to use both
@foo
and@foo(args)
, you will necessarily incur extra complexity in your code, sincefoo(func)
andfoo(args)(func)
are completely different ways to wrap a function. I agree with Pete that@foo()
is clearer in that case.