Skip to content

Commit

Permalink
PEP 702 (@deprecated): "normal" overloaded methods (#18477)
Browse files Browse the repository at this point in the history
Fixes #18474

It seems I covered overloaded functions, descriptors, and special
methods so far but completely forgot about "normal" methods (thanks to
@sobolevn for pointing this out). This addition should do the trick.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: sobolevn <[email protected]>
  • Loading branch information
3 people authored Jan 28, 2025
1 parent d5628fa commit 93d1ce4
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 2 deletions.
2 changes: 1 addition & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7873,7 +7873,7 @@ def warn_deprecated_overload_item(
if isinstance(item, Decorator) and isinstance(
candidate := item.func.type, CallableType
):
if selftype is not None:
if selftype is not None and not node.is_static:
candidate = bind_self(candidate, selftype)
if candidate == target:
self.warn_deprecated(item.func, context)
Expand Down
9 changes: 8 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,14 @@ def check_call_expr_with_callee_type(
)
proper_callee = get_proper_type(callee_type)
if isinstance(e.callee, (NameExpr, MemberExpr)):
self.chk.warn_deprecated_overload_item(e.callee.node, e, target=callee_type)
node = e.callee.node
if node is None and member is not None and isinstance(object_type, Instance):
if (symbol := object_type.type.get(member)) is not None:
node = symbol.node
self.chk.check_deprecated(node, e)
self.chk.warn_deprecated_overload_item(
node, e, target=callee_type, selftype=object_type
)
if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType):
# Cache it for find_isinstance_check()
if proper_callee.type_guard is not None:
Expand Down
165 changes: 165 additions & 0 deletions test-data/unit/check-deprecated.test
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,171 @@ for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration
[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedInstanceMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
from typing_extensions import deprecated, overload

class A:
@overload
@deprecated("pass `str` instead")
def f(self, v: int) -> None: ...
@overload
def f(self, v: str) -> None: ...
def f(self, v: Union[int, str]) -> None: ...

@overload
def g(self, v: int) -> None: ...
@overload
@deprecated("pass `int` instead")
def g(self, v: str) -> None: ...
def g(self, v: Union[int, str]) -> None: ...

@overload
def h(self, v: int) -> A: ...
@overload
def h(self, v: str) -> A: ...
@deprecated("use `h2` instead")
def h(self, v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
a.g(1)
a.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedClassMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
from typing_extensions import deprecated, overload

class A:
@overload
@classmethod
@deprecated("pass `str` instead")
def f(cls, v: int) -> None: ...
@overload
@classmethod
def f(cls, v: str) -> None: ...
@classmethod
def f(cls, v: Union[int, str]) -> None: ...

@overload
@classmethod
def g(cls, v: int) -> None: ...
@overload
@classmethod
@deprecated("pass `int` instead")
def g(cls, v: str) -> None: ...
@classmethod
def g(cls, v: Union[int, str]) -> None: ...

@overload
@classmethod
def h(cls, v: int) -> A: ...
@overload
@classmethod
def h(cls, v: str) -> A: ...
@deprecated("use `h2` instead")
@classmethod
def h(cls, v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
a.g(1)
a.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedStaticMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
from typing_extensions import deprecated, overload

class A:
@overload
@staticmethod
@deprecated("pass `str` instead")
def f(v: int) -> None: ...
@overload
@staticmethod
def f(v: str) -> None: ...
@staticmethod
def f(v: Union[int, str]) -> None: ...

@overload
@staticmethod
def g(v: int) -> None: ...
@overload
@staticmethod
@deprecated("pass `int` instead")
def g(v: str) -> None: ...
@staticmethod
def g(v: Union[int, str]) -> None: ...

@overload
@staticmethod
def h(v: int) -> A: ...
@overload
@staticmethod
def h(v: str) -> A: ...
@deprecated("use `h2` instead")
@staticmethod
def h(v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
a.g(1)
a.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/classmethod.pyi]


[case testDeprecatedOverloadedSpecialMethods]
# flags: --enable-error-code=deprecated

Expand Down

0 comments on commit 93d1ce4

Please sign in to comment.