Skip to content

Decorator expressions implying deprecated calls typically issue no warning #11292

@finite-state-machine

Description

@finite-state-machine

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

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions