3737from mypy .options import Options
3838from mypy .state import state
3939from mypy .types import (
40+ MAX_PROTOCOL_DEPTH ,
4041 MYPYC_NATIVE_INT_NAMES ,
4142 TUPLE_LIKE_INSTANCE_NAMES ,
4243 TYPED_NAMEDTUPLE_NAMES ,
@@ -740,17 +741,16 @@ def visit_callable_type(self, left: CallableType) -> bool:
740741 # Similarly, if one function has `TypeIs` and the other does not,
741742 # they are not compatible.
742743 return False
744+ strict_concatenate = False
745+ if options := self .options :
746+ strict_concatenate = options .extra_checks or options .strict_concatenate
743747 return is_callable_compatible (
744748 left ,
745749 right ,
746750 is_compat = self ._is_subtype ,
747751 is_proper_subtype = self .proper_subtype ,
748752 ignore_pos_arg_names = self .subtype_context .ignore_pos_arg_names ,
749- strict_concatenate = (
750- (self .options .extra_checks or self .options .strict_concatenate )
751- if self .options
752- else False
753- ),
753+ strict_concatenate = strict_concatenate ,
754754 )
755755 elif isinstance (right , Overloaded ):
756756 return all (self ._is_subtype (left , item ) for item in right .items )
@@ -1309,6 +1309,8 @@ def f(self) -> A: ...
13091309 if not members_right .issubset (members_left ):
13101310 return False
13111311 assuming = right .type .assuming_proper if proper_subtype else right .type .assuming
1312+ if len (assuming ) > MAX_PROTOCOL_DEPTH :
1313+ return True
13121314 for l , r in reversed (assuming ):
13131315 if l == left and r == right :
13141316 return True
@@ -2162,21 +2164,26 @@ def unify_generic_callable(
21622164 if return_constraint_direction is None :
21632165 return_constraint_direction = mypy .constraints .SUBTYPE_OF
21642166
2165- constraints : list [mypy .constraints .Constraint ] = []
2166- # There is some special logic for inference in callables, so better use them
2167- # as wholes instead of picking separate arguments.
2168- cs = mypy .constraints .infer_constraints (
2169- type .copy_modified (ret_type = UninhabitedType ()),
2170- target .copy_modified (ret_type = UninhabitedType ()),
2171- mypy .constraints .SUBTYPE_OF ,
2172- skip_neg_op = True ,
2173- )
2174- constraints .extend (cs )
2175- if not ignore_return :
2176- c = mypy .constraints .infer_constraints (
2177- type .ret_type , target .ret_type , return_constraint_direction
2167+ # This code is hot in numerical libraries, so we use a faster inference
2168+ # algorithm (that may give slightly worse results in rare cases).
2169+ keep_unions_old = type_state .keep_unions
2170+ type_state .keep_unions = True
2171+ try :
2172+ # There is some special logic for inference in callables, so better use
2173+ # them as wholes instead of picking separate arguments.
2174+ constraints = mypy .constraints .infer_constraints (
2175+ type .copy_modified (ret_type = UninhabitedType ()),
2176+ target .copy_modified (ret_type = UninhabitedType ()),
2177+ mypy .constraints .SUBTYPE_OF ,
2178+ skip_neg_op = True ,
21782179 )
2179- constraints .extend (c )
2180+ if not ignore_return :
2181+ c = mypy .constraints .infer_constraints (
2182+ type .ret_type , target .ret_type , return_constraint_direction
2183+ )
2184+ constraints .extend (c )
2185+ finally :
2186+ type_state .keep_unions = keep_unions_old
21802187 inferred_vars , _ = mypy .solve .solve_constraints (
21812188 type .variables , constraints , allow_polymorphic = True
21822189 )
@@ -2201,7 +2208,9 @@ def report(*args: Any) -> None:
22012208 return cast (NormalizedCallableType , applied )
22022209
22032210
2204- def union_function_signatures (callables : list [CallableType ]) -> CallableType | None :
2211+ def union_function_signatures (
2212+ callables : list [CallableType ], * , simplify_unions : bool = False
2213+ ) -> CallableType | None :
22052214 """Combine a list of functions by taking the union of all the arguments and return types."""
22062215 if len (callables ) == 1 :
22072216 return callables [0 ]
@@ -2219,15 +2228,27 @@ def union_function_signatures(callables: list[CallableType]) -> CallableType | N
22192228 # confusing and ought to be re-written anyway.)
22202229 callables , variables = merge_typevars_in_callables_by_name (callables )
22212230
2222- new_args : list [list [Type ]] = [[] for _ in callables [0 ].arg_types ]
2223- new_kinds = list (callables [0 ].arg_kinds )
2231+ new_callable = callables [0 ].with_unpacked_kwargs ().with_normalized_var_args ()
2232+ new_args : list [list [Type ]] = [[] for _ in new_callable .arg_types ]
2233+ new_kinds = list (new_callable .arg_kinds )
2234+ new_names = list (new_callable .arg_names )
22242235 new_returns : list [Type ] = []
22252236
22262237 for target in callables :
2238+ target = target .with_unpacked_kwargs ().with_normalized_var_args ()
22272239 # TODO: Enhance the merging logic to handle a wider variety of signatures.
2240+ # In particular, allow name-only arguments that appear in different order.
22282241 if len (new_kinds ) != len (target .arg_kinds ):
22292242 return None
22302243 for i , (new_kind , target_kind ) in enumerate (zip (new_kinds , target .arg_kinds )):
2244+ if target .arg_names [i ] != new_callable .arg_names [i ]:
2245+ if target_kind .is_named ():
2246+ return None
2247+ if target_kind .is_positional ():
2248+ new_names [i ] = None
2249+ if isinstance (target .arg_types [i ], (ParamSpecType , UnpackType )):
2250+ # It is risky to put these inside a union.
2251+ return None
22312252 if new_kind == target_kind :
22322253 continue
22332254 if new_kind .is_positional () and target_kind .is_positional ():
@@ -2239,12 +2260,19 @@ def union_function_signatures(callables: list[CallableType]) -> CallableType | N
22392260 new_args [i ].append (arg )
22402261 new_returns .append (target .ret_type )
22412262
2242- return callables [0 ].copy_modified (
2243- arg_types = [mypy .typeops .make_simplified_union (args ) for args in new_args ],
2263+ if simplify_unions :
2264+ arg_types = [mypy .typeops .make_simplified_union (args ) for args in new_args ]
2265+ ret_type = mypy .typeops .make_simplified_union (new_returns )
2266+ else :
2267+ arg_types = [UnionType .make_union (args ) for args in new_args ]
2268+ ret_type = UnionType .make_union (new_returns )
2269+
2270+ return new_callable .copy_modified (
2271+ arg_types = arg_types ,
22442272 arg_kinds = new_kinds ,
2245- ret_type = mypy .typeops .make_simplified_union (new_returns ),
2273+ arg_names = new_names ,
2274+ ret_type = ret_type ,
22462275 variables = variables ,
2247- implicit = True ,
22482276 )
22492277
22502278
0 commit comments