PEP 702: Marking deprecations using the type system

As I mentioned in typing-sig before, I still strongly disagree with this decorator emitting runtime warnings. I can see that many people do want this functionality, but I don’t think a decorator from typing is a way to do it.

Instead, I’d love to see a deprecated decorator from warnings module instead, which handles the runtime behavior. This decorator could then also get the object marked as deprecated typing wise, but typing.deprecated should only affect type-checkers, without any runtime consequences. I simply don’t think that typing is a good place for something with runtime behavior like this.

Also, a decorator without runtime behavior would then allow the users to still create their own decorators, that handle deprecation warnings differently, as some people may want to for example use dates instead of library versions, or include some other information without having to do so in the literal string in that decorator, additionally, it would allow for different ways to show the deprecation warning itself i.e. with a print statement/log message.

Of course, custom decorators could also be made possible by just including a no_runtime argument for the decorator, and that does mostly address custom decorators, however it could be pretty annoying having to specify that arg every time, and it still goes against my general point, of not handling runtime behavior in the typing module.


That said, I actually think there’s an even better solution than just 2 decorators, one in typing, other in warnings, and that is to instead have this PEP add something like typing.Deprecated[X] instead. I’m aware that deprecating constants/variables is currently a rejected idea, however this approach doesn’t just allow that, what it would actually give us is a complete system, that would allow anyone to create custom deprecation decorators, which carry along the typing information, and can handle runtime behavior in any way users would like (there could still be a default implementation in warnings).

This kind of implementation could for example look like this:

T = TypeVar("T", bound=Callable)

def deprecate(fun: T) -> Deprecated[T]:
    def wrapper(*a, **kw) :
        print("Using a deprecated function!")
        return fun(*a, **kw)
    return cast(Deprecated[T], wrapper)

This is generally much more versatile than just the 2 implementations (warnings + typing), and I think many projects could actually benefit from this. There are already some popular libraries that implement deprecation decorators that raise warnings (like Deprecated · PyPI), and we should give these libraries a way for their custom decorators to carry along the typing information about deprecation.

Not only that, this would indeed also add support for deprecating constants easily, with syntax like: MY_CONST: Deprecated[int] = 5. Which I have seen some other people ask about here too, and while currently rejected due to the statistics suggesting that there aren’t many cases of constants getting deprecated in stdlib, even just because of the prior benefits I mentioned this could be worth it, with deprecations of constants being an extra bonus of this.

I would however still suggest doing some more research into how many projects could actually benefit from constant deprecations, outside of just the stdlib. As for me, as an author of a few typed libraries, I know I would benefit from it in various places, and I’m sure many others would too.

3 Likes