From 3e0684b4517f5183c4cf57e92da0f1093a94a576 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:22:39 +0100 Subject: [PATCH] Support `@deprecated()` on overloaded `__get__` for generic descriptors --- mypy/checker.py | 11 +++++++++-- test-data/unit/check-deprecated.test | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b2c4f2263262e..e1602206d6235 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7714,7 +7714,12 @@ def warn_deprecated(self, node: Node | None, context: Context) -> None: warn(deprecated, context, code=codes.DEPRECATED) def warn_deprecated_overload_item( - self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None + self, + node: Node | None, + context: Context, + *, + target: Type, + selftype: Instance | None = None, ) -> None: """Warn if the overload item corresponding to the given callable is deprecated.""" target = get_proper_type(target) @@ -7724,7 +7729,9 @@ def warn_deprecated_overload_item( candidate := item.func.type, CallableType ): if selftype is not None: - candidate = bind_self(candidate, selftype) + candidate = bind_self( + expand_type_by_instance(candidate, selftype), selftype + ) if candidate == target: self.warn_deprecated(item.func, context) diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 362d8725f1831..b2adaa2520cf7 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -506,9 +506,11 @@ C().g = "x" # E: function __main__.C.g is deprecated: use g2 instead \ [case testDeprecatedDescriptor] # flags: --enable-error-code=deprecated -from typing import Any, Optional, Union +from typing import Any, Generic, Optional, TypeVar, Union from typing_extensions import deprecated, overload +T = TypeVar("T") + @deprecated("use E1 instead") class D1: def __get__(self, obj: Optional[C], objtype: Any) -> Union[D1, int]: ... @@ -536,10 +538,18 @@ class D3: def __set__(self, obj: C, value: str) -> None: ... def __set__(self, obj: C, value: Union[int, str]) -> None: ... +class D4(Generic[T]): + @overload + def __get__(self, obj: None, objtype: Any) -> T: ... + @overload + @deprecated("deprecated instance access") + def __get__(self, obj: C, objtype: Any) -> T: ... + class C: d1 = D1() # E: class __main__.D1 is deprecated: use E1 instead d2 = D2() d3 = D3() + d4 = D4[int]() c: C C.d1 @@ -554,6 +564,9 @@ C.d3 # E: overload def (self: __main__.D3, obj: None, objtype: Any) -> __main__ c.d3 # E: overload def (self: __main__.D3, obj: __main__.C, objtype: Any) -> builtins.int of function __main__.D3.__get__ is deprecated: use E3.__get__ instead c.d3 = 1 c.d3 = "x" # E: overload def (self: __main__.D3, obj: __main__.C, value: builtins.str) of function __main__.D3.__set__ is deprecated: use E3.__set__ instead + +C.d4 +c.d4 # E: overload def (self: __main__.D4[T`1], obj: __main__.C, objtype: Any) -> T`1 of function __main__.D4.__get__ is deprecated: deprecated instance access [builtins fixtures/property.pyi]