Skip to content

Commit d0d1fe2

Browse files
Fix join_instances_via_supertype
1 parent df947c3 commit d0d1fe2

File tree

5 files changed

+55
-21
lines changed

5 files changed

+55
-21
lines changed

mypy/disallow_str_iteration_state.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from mypy.types import Instance
23

34
from collections.abc import Iterator
45
from contextlib import contextmanager
@@ -23,3 +24,23 @@ def set(self, value: bool) -> Iterator[None]:
2324

2425

2526
disallow_str_iteration_state: Final = DisallowStrIterationState(disallow_str_iteration=False)
27+
28+
29+
STR_ITERATION_PROTOCOL_BASES: Final = frozenset(
30+
{
31+
"collections.abc.Collection",
32+
"collections.abc.Iterable",
33+
"collections.abc.Sequence",
34+
"typing.Collection",
35+
"typing.Iterable",
36+
"typing.Sequence",
37+
}
38+
)
39+
40+
41+
def is_subtype_relation_ignored_to_disallow_str_iteration(left: Instance, right: Instance) -> bool:
42+
return (
43+
left.type.has_base("builtins.str")
44+
and not right.type.has_base("builtins.str")
45+
and any(right.type.has_base(base) for base in STR_ITERATION_PROTOCOL_BASES)
46+
)

mypy/join.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
from mypy.expandtype import expand_type
1010
from mypy.maptype import map_instance_to_supertype
1111
from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY, TypeInfo
12+
from mypy.disallow_str_iteration_state import (
13+
disallow_str_iteration_state,
14+
STR_ITERATION_PROTOCOL_BASES,
15+
)
1216
from mypy.state import state
1317
from mypy.subtypes import (
1418
SubtypeContext,
@@ -169,12 +173,24 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType:
169173
# The definition of "best" may evolve; for now it is the one with
170174
# the longest MRO. Ties are broken by using the earlier base.
171175

176+
should_skip_str_iteration_protocol_bases = (
177+
disallow_str_iteration_state.disallow_str_iteration and t.type.has_base("builtins.str")
178+
)
179+
172180
# Go over both sets of bases in case there's an explicit Protocol base. This is important
173181
# to ensure commutativity of join (although in cases where both classes have relevant
174182
# Protocol bases this maybe might still not be commutative)
175183
base_types: dict[TypeInfo, None] = {} # dict to deduplicate but preserve order
176184
for base in t.type.bases:
185+
if (
186+
should_skip_str_iteration_protocol_bases
187+
and base.type.fullname in STR_ITERATION_PROTOCOL_BASES
188+
):
189+
base_types[object_from_instance(t).type] = None
190+
continue
191+
177192
base_types[base.type] = None
193+
178194
for base in s.type.bases:
179195
if base.type.is_protocol and is_subtype(t, base):
180196
base_types[base.type] = None

mypy/meet.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
from mypy import join
66
from mypy.erasetype import erase_type
77
from mypy.maptype import map_instance_to_supertype
8-
from mypy.disallow_str_iteration_state import disallow_str_iteration_state
8+
from mypy.disallow_str_iteration_state import (
9+
disallow_str_iteration_state,
10+
is_subtype_relation_ignored_to_disallow_str_iteration,
11+
)
912
from mypy.state import state
1013
from mypy.subtypes import (
1114
are_parameters_compatible,
@@ -15,7 +18,6 @@
1518
is_proper_subtype,
1619
is_same_type,
1720
is_subtype,
18-
is_subtype_relation_ignored_to_disallow_str_iteration,
1921
)
2022
from mypy.typeops import is_recursive_pair, make_simplified_union, tuple_fallback
2123
from mypy.types import (

mypy/subtypes.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@
3434
Var,
3535
)
3636
from mypy.options import Options
37-
from mypy.disallow_str_iteration_state import disallow_str_iteration_state
37+
from mypy.disallow_str_iteration_state import (
38+
disallow_str_iteration_state,
39+
is_subtype_relation_ignored_to_disallow_str_iteration,
40+
)
3841
from mypy.state import state
3942
from mypy.types import (
4043
MYPYC_NATIVE_INT_NAMES,
@@ -1200,6 +1203,11 @@ def f(self) -> A: ...
12001203
as well.
12011204
"""
12021205
assert right.type.is_protocol
1206+
if (
1207+
disallow_str_iteration_state.disallow_str_iteration
1208+
and is_subtype_relation_ignored_to_disallow_str_iteration(left, right)
1209+
):
1210+
return False
12031211
if skip is None:
12041212
skip = []
12051213
# We need to record this check to generate protocol fine-grained dependencies.
@@ -2319,21 +2327,3 @@ def is_erased_instance(t: Instance) -> bool:
23192327
elif not isinstance(get_proper_type(arg), AnyType):
23202328
return False
23212329
return True
2322-
2323-
2324-
def is_subtype_relation_ignored_to_disallow_str_iteration(left: Instance, right: Instance) -> bool:
2325-
return (
2326-
left.type.has_base("builtins.str")
2327-
and not right.type.has_base("builtins.str")
2328-
and any(
2329-
right.type.has_base(base)
2330-
for base in (
2331-
"collections.abc.Collection",
2332-
"collections.abc.Iterable",
2333-
"collections.abc.Sequence",
2334-
"typing.Collection",
2335-
"typing.Iterable",
2336-
"typing.Sequence",
2337-
)
2338-
)
2339-
)

test-data/unit/check-flags.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,11 @@ def narrowing(x: "str | Sequence[str]"):
25172517
else:
25182518
reveal_type(x) # N: Revealed type is "typing.Sequence[builtins.str]"
25192519

2520+
Item = TypeVar("Item")
2521+
def takes_generic_list(x: list[Item]) -> None: ...
2522+
takes_generic_list(reveal_type([s, seq])) # N: Revealed type is "builtins.list[builtins.object]"
2523+
takes_generic_list(reveal_type([seq, s])) # N: Revealed type is "builtins.list[builtins.object]"
2524+
25202525
[builtins fixtures/str-iter.pyi]
25212526
[typing fixtures/typing-str-iter.pyi]
25222527

0 commit comments

Comments
 (0)