Skip to content

Commit

Permalink
Improve CallableType join in simple cases
Browse files Browse the repository at this point in the history
Fixes python#17479 , although as you can see in the test case the logic still
remains far from perfect
  • Loading branch information
hauntsaninja committed Jan 1, 2025
1 parent 9bf5169 commit c30c4df
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
29 changes: 19 additions & 10 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,23 +402,32 @@ def visit_instance(self, t: Instance) -> ProperType:
return self.default(self.s)

def visit_callable_type(self, t: CallableType) -> ProperType:
if isinstance(self.s, CallableType) and is_similar_callables(t, self.s):
if is_equivalent(t, self.s):
return combine_similar_callables(t, self.s)
result = join_similar_callables(t, self.s)
if isinstance(self.s, CallableType):
if is_similar_callables(t, self.s):
if is_equivalent(t, self.s):
return combine_similar_callables(t, self.s)
result = join_similar_callables(t, self.s)
if any(
isinstance(tp, (NoneType, UninhabitedType))
for tp in get_proper_types(result.arg_types)
):
# We don't want to return unusable Callable, attempt fallback instead.
return join_types(t.fallback, self.s)
else:
if is_subtype(self.s, t):
result = t.copy_modified()
elif is_subtype(t, self.s):
result = self.s.copy_modified()
else:
return join_types(t.fallback, self.s)

# We set the from_type_type flag to suppress error when a collection of
# concrete class objects gets inferred as their common abstract superclass.
if not (
(t.is_type_obj() and t.type_object().is_abstract)
or (self.s.is_type_obj() and self.s.type_object().is_abstract)
):
result.from_type_type = True
if any(
isinstance(tp, (NoneType, UninhabitedType))
for tp in get_proper_types(result.arg_types)
):
# We don't want to return unusable Callable, attempt fallback instead.
return join_types(t.fallback, self.s)
return result
elif isinstance(self.s, Overloaded):
# Switch the order of arguments to that we'll get to visit_overloaded.
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -3472,3 +3472,19 @@ class Qux(Bar):
def baz(self, x) -> None:
pass
[builtins fixtures/tuple.pyi]

[case testCallableJoinWithDefaults]
from typing import Callable, TypeVar

T = TypeVar("T")

def join(t1: T, t2: T) -> T: ...

def f1() -> None: ...
def f2(i: int = 0) -> None: ...
def f3(i: str = "") -> None: ...

reveal_type(join(f1, f2)) # N: Revealed type is "def ()"
reveal_type(join(f1, f3)) # N: Revealed type is "def ()"
reveal_type(join(f2, f3)) # N: Revealed type is "builtins.function" # TODO: this could be better
[builtins fixtures/tuple.pyi]

0 comments on commit c30c4df

Please sign in to comment.