@@ -271,7 +271,11 @@ def infer_constraints_for_callable(
271
271
272
272
273
273
def infer_constraints (
274
- template : Type , actual : Type , direction : int , skip_neg_op : bool = False
274
+ template : Type ,
275
+ actual : Type ,
276
+ direction : int ,
277
+ skip_neg_op : bool = False ,
278
+ can_have_union_overlaping : bool = True ,
275
279
) -> list [Constraint ]:
276
280
"""Infer type constraints.
277
281
@@ -311,11 +315,15 @@ def infer_constraints(
311
315
res = _infer_constraints (template , actual , direction , skip_neg_op )
312
316
type_state .inferring .pop ()
313
317
return res
314
- return _infer_constraints (template , actual , direction , skip_neg_op )
318
+ return _infer_constraints (template , actual , direction , skip_neg_op , can_have_union_overlaping )
315
319
316
320
317
321
def _infer_constraints (
318
- template : Type , actual : Type , direction : int , skip_neg_op : bool
322
+ template : Type ,
323
+ actual : Type ,
324
+ direction : int ,
325
+ skip_neg_op : bool ,
326
+ can_have_union_overlaping : bool = True ,
319
327
) -> list [Constraint ]:
320
328
orig_template = template
321
329
template = get_proper_type (template )
@@ -368,8 +376,41 @@ def _infer_constraints(
368
376
return res
369
377
if direction == SUPERTYPE_OF and isinstance (actual , UnionType ):
370
378
res = []
379
+
380
+ def _can_have_overlaping (_item : Type , _actual : UnionType ) -> bool :
381
+ # There is a special overlaping case, where we have a Union of where two types
382
+ # are the same, but one of them contains the other.
383
+ # For example, we have Union[Sequence[T], Sequence[Sequence[T]]]
384
+ # In this case, only the second one can have overlaping because it contains the other.
385
+ # So, in case of list[list[int]], second one would be chosen.
386
+ if isinstance (p_item := get_proper_type (_item ), Instance ) and p_item .args :
387
+ other_items = [o_item for o_item in _actual .items if o_item is not a_item ]
388
+
389
+ if len (other_items ) == 1 and other_items [0 ] in p_item .args :
390
+ return True
391
+
392
+ if len (other_items ) > 1 :
393
+ union_args = [
394
+ p_arg
395
+ for arg in p_item .args
396
+ if isinstance (p_arg := get_proper_type (arg ), UnionType )
397
+ ]
398
+
399
+ for union_arg in union_args :
400
+ if all (o_item in union_arg .items for o_item in other_items ):
401
+ return True
402
+
403
+ return False
404
+
371
405
for a_item in actual .items :
372
- res .extend (infer_constraints (orig_template , a_item , direction ))
406
+ res .extend (
407
+ infer_constraints (
408
+ orig_template ,
409
+ a_item ,
410
+ direction ,
411
+ can_have_union_overlaping = _can_have_overlaping (a_item , actual ),
412
+ )
413
+ )
373
414
return res
374
415
375
416
# Now the potential subtype is known not to be a Union or a type
@@ -391,22 +432,28 @@ def _infer_constraints(
391
432
# type variables indeterminate. This helps with some special
392
433
# cases, though this isn't very principled.
393
434
394
- def _is_item_being_overlaped_by_other (item : Type ) -> bool :
395
- # It returns true if the item is an argument of other item
435
+ def _is_item_overlaping_actual_type (_item : Type ) -> bool :
436
+ # Overlaping occurs when we have a Union where two types are
437
+ # compatible and the more generic one is chosen.
438
+ # For example, in Union[T, Sequence[T]], we have to choose
439
+ # Sequence[T] if actual type is list[int].
440
+ # This returns true if the item is an argument of other item
396
441
# that is subtype of the actual type
397
442
return any (
398
- isinstance (p_type := get_proper_type (item_to_compare ), Instance )
399
- and mypy .subtypes .is_subtype (actual , erase_typevars (p_type ))
400
- and item in p_type .args
443
+ isinstance (p_item_to_compare := get_proper_type (item_to_compare ), Instance )
444
+ and mypy .subtypes .is_subtype (actual , erase_typevars (p_item_to_compare ))
445
+ and _item in p_item_to_compare .args
401
446
for item_to_compare in template .items
402
- if item is not item_to_compare
447
+ if _item is not item_to_compare
403
448
)
404
449
405
450
result = any_constraints (
406
451
[
407
452
infer_constraints_if_possible (t_item , actual , direction )
408
453
for t_item in [
409
- item for item in template .items if not _is_item_being_overlaped_by_other (item )
454
+ item
455
+ for item in template .items
456
+ if not (can_have_union_overlaping and _is_item_overlaping_actual_type (item ))
410
457
]
411
458
],
412
459
eager = False ,
0 commit comments