microsoft / microsoft/pyright

Decorator expressions implying `deprecated` calls typically issue no warning

Open
#11,292 1 comment 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
15.6k
Forks
1.8k
Avg merge
12h 13m
Merged PRs (30d)
52

Description

**Describe the bug**

Pyright will warn about deprecated decorators, but not when a decorator factory returns a deprecated decorator.

This makes it impossible to deprecate use of a decorator factory over a declaration based on the type of the declaration.

**Code or Screenshots**

Code sample in [pyright playground](https://pyright-play.net/?strict=true&code=GYJw9gtgBA%2BjwFcAuCQFM5QJYQA5hCSgEMA7UsJYpLMUgZwChRIoBjMAG07TZrvoA6YgCM22PASIBhYt1E9m4aEgCeuLKQDmMNAA8kaBrQYT8hKAEFSqgDRQAMlkMg59gCZpc6NtTTv7MAA3NBBOMGJ3KCgAYihcVRAsLQALJAAubC0KdABtdHMkAFksenpNLSKwdwQeAGUwVDY0AF1GdrZOYjKoAFVSDlJ3ZxM5TlUAES8fP3cpjlckAnTGaOiAIk2oAApNeNc%2BLGb7JBTSqAB3Rs4o7zARBVUoETQSKAADAAVwJY5Od4A3FBTucIMQANZoehQChQYbAYChIzNACUqzWm3W6OiAAFPN5eLNtusAHIAeQAhOs0Ws4WhgLAYL5uHBtvQ0JxgPZEANMtY7FAAPQoqAAWgAfFYbJlBLL2oxOt1odI6MN%2BKQxpNpoTDHNeARqMtsVBMb1ytpgWdoZ4FoaQFBgMQ%2BAQnsFQm8AOQPdweh0IAbq7DW7W%2BXXGzHGnFusIRdzGzwMuDMzis9mc7n%2BtiZWTyEQ8XK5JwuOS5D1aMDVD0tFr2El0Vr2YViyX8mVy2lRkIxyKR-EzXXE8lUmlrBOM5Opjlcv28qA5rp5tAFouhEteyJVmtQOukBtCkUSqWqNuCdq0sdJsaT9MzrNzsYKJeywT2flbpuH1vG2nP%2BUxQAApP%2BuRzmSJIACoAEpkg4c6WHUACiUAtIBKGoWh6EYZhWHYehgAIpIwcS0kRxEkaRZHkRRlFEYAQKQEewYwVJaryOs6IBPGQUT0EsSQWqcrzoCgIC7lENoGtx2CkG8hiSK4bGkbRcRBMQSSPlA9ApNcURgpCMJgHCWAIkiAxoECILQr47KXAQ4LQt0UD6LgvC6pklG0e0CDuFEAC8fQDKqIx0JqUwEqG-jzGJBDbGibCeVAPkqkMAUatwWohbM4WLJFaKMDiHlRLE8SJMkaT2RAzjQu8BRSMF-b%2BO8Ox2Q5Tn%2BGiY5kKo8CZlFzbbvWJ7tDiMX5XEsKNEguDIA10JNXwLWMG1MDltUnUDNsMCZCuricKWS0%2Bu%2BB6SjuaD9TlQ3RHExBaMQmj2AkSSpEQaBlUgFVVYQNU6nVU32Xojmze4rX0iQMDeitbBrRtzirtt657Y2B29buJ0EYBwEQfBnxQbBCFITheP4wTeOACSkdFUWT5MU5T0QKZa5zqZpzyvPoTpIOMTFqcQECvHZfEkCIbr2CIk13cVRDuGAUKkB6RAUTTFzKZJohjezfY6iYtPQjQXMuVTutQG5OX9IMaqjClH2hXqtrcVF35rHEgCJhD9f26h8b1IObsyAs8wtFQ9QZqVgPCkEg81A2QlApKEYPdYeR0nTiCUm4FZshul%2BqZSA3UFaNyATUQ2yNb9zUA6HDLh3xICLRW7jR%2BtjhQ1tO3V1W8M9XHUC-jlidJUFqe6hldo26RDtO8XrteNVfd1UCQtECLft04HRgh21FAVyDkS15DxYw96Lf7m3fUd3KKOE2f58XzhgAYpEAA)

```python
from __future__ import annotations
from collections.abc import Callable
from typing_extensions import Any, Literal, deprecated, overload # pyright: ignore[reportMissingModuleSource]

class UnconditionallyDeprecatedDecorator:
""" (in practice, this would probably be a `Protocol`; this makes no difference)
"""
@deprecated("NO!")
def __call__(self, func: Any, /) -> Any: ...

class ConditionallyDeprecatedDecorator:
"""Using this decorator factory over a 'bad' function is deprecated
"""
@overload
def __call__(self, func: Callable[[Literal['good']], None], /) -> Any: ...
@overload
@deprecated("NO!")
def __call__(self, func: Callable[[Literal['bad']], None], /) -> Any: ...

def __call__(self, func: Callable[..., Any], /) -> Any:
...

#──[ CONTROL CASE ]────────────────────────────────────────────────────────────┐
# │
# calling the factory and storing the returned decorator in a temporary │
# variable should make no difference; this case works as expected: │

udd = UnconditionallyDeprecatedDecorator()
cdd = ConditionallyDeprecatedDecorator()

@udd # pyright emits `reportDeprecated` (as expected)
def any_func() -> None: ...

@cdd # no output (as expected)
def a_good_func(_: Literal['good'], /) -> None: ...

@cdd # again, pyright emits `reportDeprecated` (as expected)
def a_bad_func(_: Literal['bad'], /) -> None: ...

#──[ REPRO CASE ]──────────────────────────────────────────────────────────────┤
# │
# this should be exactly the same as the above, but pyright doesn't │
# warn about the deprecation this time: │

@UnconditionallyDeprecatedDecorator()
# ↑ expected `reportDeprecated`; but pyright is silent
def another_func() -> None: ...

@ConditionallyDeprecatedDecorator() # no output (as expected)
def another_good_func(_: Literal['good'], /) -> None: ...

@ConditionallyDeprecatedDecorator()
# ↑ expected `reportDeprecated`; but pyright is silent
def another_bad_func(_: Literal['bad'], /) -> None: ...

#──────────────────────────────────────────────────────────────────────────────┘
```

**VS Code extension or command-line**

Pyright 1.1.408
LSP and `pyright-play.net` give the same results.

Contributor guide

Open the contributing guide

Research direction

Start with the linked pyright playground reproduction and compare the working temporary-variable cases with direct decorator-factory expressions. Trace how Pyright handles decorator calls and the reportDeprecated diagnostic; done means the direct expressions produce the same expected warnings as the equivalent stored decorators, while the conditional good case remains silent.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.