Since there are things like @typing.final and @typing.deprecated, do you think that it would be useful to have a “marker” @typing.decorator that marks higher-order functions/classes as intended to be used only as decorators?
from __main__ import main # let's assume __main__.main() wasn't decorated with @autorun
autorun(main)
wouldn’t.
In this particular case, it’s critical to ensure autorun() has a decorator nature, because otherwise f.__module__ == "__main__" might be a false positive, as in the last snippet.
functools.wraps could be marked with @typing.decorator in the typeshed, to make people rather use functools.update_wrapper in no-decorator scenarios.
From the technical perspective, the implementation of the typing.decorator marker would be a simple identity function, similarly to @typing.final. The marker would affect static type checkers which then have to validate the AST to ensure that a given function is used only as a decorator.
Specifically, if I marked a function with @typing.decorator
then only the following usages would be allowed (only from the @typing.decorator rule, we leave the details to return types type safety):
@some_decorator_func
def spam() -> ...: ...
@some_decorator_func(...)
def eggs() -> ...: ...
@some_decorator_func
class Ham:
...
@some_decorator_func(...)
class Cheese:
...
Similarly in classes,
@typing.decorator
class SomeDecoratorClass:
...
would only allow
@SomeDecoratorClass
def spam() -> ...: ...
@SomeDecoratorClass(...)
def eggs() -> ...: ...
@SomeDecoratorClass
class Ham:
...
@SomeDecoratorClass(...)
class Cheese:
...
Please note that nesting decorators is also implicitly OK here, since what really only matters here is whether the @typing.decorator-marked callable is used as a decorator.
This seems more like a linting issue than a typing issue. That said, I don’t know how you’d signal to a linter that a function is meant to be a decorator without using the type system, so maybe that’s enough to justify it anyway.
Given so many limitations that @typing.decorator introduces (must be an innermost decorator, most probably can’t be picked dynamically e.g. with @decorate if __debug__ else identity (which would be better off without the decorator syntax)), I’m not sure there isn’t too much probability of @typing.decorator being misused. Maybe there are some trade-offs that could be done to add more flexibility to that idea, or maybe the idea is overly specific and has too few use cases.