Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2746,7 +2746,8 @@ def check_overload_call(
# Record if we succeeded. Next we need to see if maybe normal procedure
# gives a narrower type.
if unioned_return:
returns, inferred_types = zip(*unioned_return)
returns = tuple(u[0] for u in unioned_return)
inferred_types = tuple(u[1] for u in unioned_return)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should use regular list comprehensions for these, not tuple(...), they are converted to lists few lines below anyway (and then you can remove the list(...) call below).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems I wanted to stick to the functionality of zip as close as possible, which is, in fact, not necessary. I changed it.

# Note that we use `combine_function_signatures` instead of just returning
# a union of inferred callables because for example a call
# Union[int -> int, str -> str](Union[int, str]) is invalid and
Expand All @@ -2767,19 +2768,25 @@ def check_overload_call(
object_type,
context,
)
# If any of checks succeed, stop early.
# If any of checks succeed, perform deprecation tests and stop early.
if inferred_result is not None and unioned_result is not None:
# Both unioned and direct checks succeeded, choose the more precise type.
if (
is_subtype(inferred_result[0], unioned_result[0])
and not isinstance(get_proper_type(inferred_result[0]), AnyType)
and not none_type_var_overlap
):
return inferred_result
return unioned_result
elif unioned_result is not None:
unioned_result = None
else:
inferred_result = None
if unioned_result is not None:
for inferred_type in inferred_types:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised that mypy doesn't yell at you that inferred_types may be undefined.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't be surprised, we don't have possibly-undefined enabled for selfcheck for some reason. There were ~50 violations last time I tried to enable it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now initialise inferred_types with None and make an is None check here. It looks a little redundant to me, but maybe better than adding the 51st violation.

if isinstance(c := get_proper_type(inferred_type), CallableType):
self.chk.warn_deprecated(c.definition, context)
return unioned_result
elif inferred_result is not None:
if inferred_result is not None:
if isinstance(c := get_proper_type(inferred_result[1]), CallableType):
self.chk.warn_deprecated(c.definition, context)
return inferred_result

# Step 4: Failure. At this point, we know there is no match. We fall back to trying
Expand Down Expand Up @@ -2933,8 +2940,6 @@ def infer_overload_return_type(
# check for ambiguity due to 'Any' below.
if not args_contain_any:
self.chk.store_types(m)
if isinstance(infer_type, ProperType) and isinstance(infer_type, CallableType):
self.chk.warn_deprecated(infer_type.definition, context)
return ret_type, infer_type
p_infer_type = get_proper_type(infer_type)
if isinstance(p_infer_type, CallableType):
Expand Down Expand Up @@ -2971,11 +2976,6 @@ def infer_overload_return_type(
else:
# Success! No ambiguity; return the first match.
self.chk.store_types(type_maps[0])
inferred_callable = inferred_types[0]
if isinstance(inferred_callable, ProperType) and isinstance(
inferred_callable, CallableType
):
self.chk.warn_deprecated(inferred_callable.definition, context)
return return_types[0], inferred_types[0]

def overload_erased_call_targets(
Expand Down
76 changes: 73 additions & 3 deletions test-data/unit/check-deprecated.test
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,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, overload
from typing import Any, Generic, Optional, overload, TypeVar, Union
from typing_extensions import deprecated

T = TypeVar("T")

@deprecated("use E1 instead")
class D1:
def __get__(self, obj: Optional[C], objtype: Any) -> Union[D1, int]: ...
Expand Down Expand Up @@ -701,10 +703,19 @@ 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: ...
def __get__(self, obj: Optional[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
Expand All @@ -719,15 +730,21 @@ 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]


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

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

int_or_str: Union[int, str]
any: Any

@overload
def f(x: int) -> int: ...
@overload
Expand All @@ -738,6 +755,8 @@ def f(x: Union[int, str]) -> Union[int, str]: ...
f # E: function __main__.f is deprecated: use f2 instead
f(1) # E: function __main__.f is deprecated: use f2 instead
f("x") # E: function __main__.f is deprecated: use f2 instead
f(int_or_str) # E: function __main__.f is deprecated: use f2 instead
f(any) # E: function __main__.f is deprecated: use f2 instead
f(1.0) # E: function __main__.f is deprecated: use f2 instead \
# E: No overload variant of "f" matches argument type "float" \
# N: Possible overload variants: \
Expand All @@ -754,6 +773,8 @@ def g(x: Union[int, str]) -> Union[int, str]: ...
g
g(1) # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead
g("x")
g(int_or_str) # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead
g(any)
g(1.0) # E: No overload variant of "g" matches argument type "float" \
# N: Possible overload variants: \
# N: def g(x: int) -> int \
Expand All @@ -769,13 +790,62 @@ def h(x: Union[int, str]) -> Union[int, str]: ...
h
h(1)
h("x") # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead
h(int_or_str) # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead
h(any)
h(1.0) # E: No overload variant of "h" matches argument type "float" \
# N: Possible overload variants: \
# N: def h(x: int) -> int \
# N: def h(x: str) -> str

[builtins fixtures/tuple.pyi]
@overload
def i(x: int) -> int: ...
@overload
@deprecated("work with int instead")
def i(x: str) -> str: ...
@overload
def i(x: Any) -> Any: ...
def i(x: Union[int, str]) -> Union[int, str]: ...

i
i(1)
i("x") # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead
i(int_or_str) # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead
i(any)
i(1.0)

@overload
def j(x: int) -> int: ...
@overload
def j(x: str) -> str: ...
@overload
@deprecated("work with int or str instead")
def j(x: Any) -> Any: ...
def j(x: Union[int, str]) -> Union[int, str]: ...

j
j(1)
j("x")
j(int_or_str)
j(any)
j(1.0) # E: overload def (x: Any) -> Any of function __main__.j is deprecated: work with int or str instead

@overload
@deprecated("work with str instead")
def k(x: int) -> int: ...
@overload
def k(x: str) -> str: ...
@overload
@deprecated("work with str instead")
def k(x: object) -> Any: ...
def k(x: object) -> Union[int, str]: ...

k
k(1) # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead
k("x")
k(int_or_str) # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead
k(any)
k(1.0) # E: overload def (x: builtins.object) -> Any of function __main__.k is deprecated: work with str instead
[builtins fixtures/tuple.pyi]

[case testDeprecatedImportedOverloadedFunction]
# flags: --enable-error-code=deprecated
Expand Down
Loading