Some might say it's a dangerous abuse of decorators.
Since decorators (generally) run at module load time, any stateful decorator (usually) implies the use of global mutable state, which is (considered by many to be) the root cause of much bad design, convoluted flow, limited reusability and untestability. This is perhaps why most decorators in the standard library are pure (off the top of my head).
This is well beyond the scope of the post but an important and often overlooked point in my opinion.
While you're right it is an easy to make mistake with decorators, I don't think the issue is global mutable state as much as uncontrolled mutable state, especially if used against singleton.
Typically, doing this:
@register
def foo():
….
is bad, but this is much better:
@registry.register
def foo():
…
if registry is a global object that is not a singleton. In that case, you can easily use distinct registries (for testing, etc…) and this is not much of an issue in practice. Another way of doing this kind of things is to post_pone the registration, with the decorator just labelling things:
and then build an explicit list of modules being imported, and look for every instance of WrappedFunc in that module locals(). I use this in my own packaging project where people can define multiple python scripts that hook into different stages of the packaging.
In flask, the app.route decorator mutates the app object. There are no globals necessary. Use of globals to maintain state is an orthogonal issue to use of decorators to update registries.
Well either the app object is global, in which case you've got global mutable state, or you're defining your handler functions later than module load time, which is pretty uncommon practice.
You can use the return value of some function as a decorator, as a way to avoid global state and tie the decorator to a given instance of your routing object. (I don't know flask, but this isn't a limitation of python)
Since decorators (generally) run at module load time, any stateful decorator (usually) implies the use of global mutable state, which is (considered by many to be) the root cause of much bad design, convoluted flow, limited reusability and untestability. This is perhaps why most decorators in the standard library are pure (off the top of my head).
This is well beyond the scope of the post but an important and often overlooked point in my opinion.