Skip to content

Commit f31d162

Browse files
authored
Fail-fast on missing builtins (#14550)
As discussed in #14547, some mypy features were degrading rather than failing-fast when certain built-in types (list, dict) were not present in the test environment. - The degraded state (e.g. lack of `__annotations__`) didn't make the culprit (sparse fixture) obvious, making tests harder to debug. - Having the code work around quirks of the testing environment ("sparse fixtures") is an anti-pattern.
1 parent 8e9f89a commit f31d162

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+131
-82
lines changed

mypy/messages.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@
122122
# test-data/unit/fixtures/) that provides the definition. This is used for
123123
# generating better error messages when running mypy tests only.
124124
SUGGESTED_TEST_FIXTURES: Final = {
125-
"builtins.list": "list.pyi",
126-
"builtins.dict": "dict.pyi",
127125
"builtins.set": "set.pyi",
128126
"builtins.tuple": "tuple.pyi",
129127
"builtins.bool": "bool.pyi",

mypy/semanal.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -625,23 +625,23 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None:
625625
continue
626626
# Need to construct the type ourselves, to avoid issues with __builtins__.list
627627
# not being subscriptable or typing.List not getting bound
628-
sym = self.lookup_qualified("__builtins__.list", Context())
629-
if not sym:
630-
continue
631-
node = sym.node
632-
if not isinstance(node, TypeInfo):
633-
self.defer(node)
628+
inst = self.named_type_or_none("builtins.list", [str_type])
629+
if inst is None:
630+
assert not self.final_iteration, "Cannot find builtins.list to add __path__"
631+
self.defer()
634632
return
635-
typ = Instance(node, [str_type])
633+
typ = inst
636634
elif name == "__annotations__":
637-
sym = self.lookup_qualified("__builtins__.dict", Context(), suppress_errors=True)
638-
if not sym:
639-
continue
640-
node = sym.node
641-
if not isinstance(node, TypeInfo):
642-
self.defer(node)
635+
inst = self.named_type_or_none(
636+
"builtins.dict", [str_type, AnyType(TypeOfAny.special_form)]
637+
)
638+
if inst is None:
639+
assert (
640+
not self.final_iteration
641+
), "Cannot find builtins.dict to add __annotations__"
642+
self.defer()
643643
return
644-
typ = Instance(node, [str_type, AnyType(TypeOfAny.special_form)])
644+
typ = inst
645645
else:
646646
assert t is not None, f"type should be specified for {name}"
647647
typ = UnboundType(t)

mypy/semanal_namedtuple.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,9 @@ def build_namedtuple_typeinfo(
481481
strtype = self.api.named_type("builtins.str")
482482
implicit_any = AnyType(TypeOfAny.special_form)
483483
basetuple_type = self.api.named_type("builtins.tuple", [implicit_any])
484-
dictype = self.api.named_type_or_none(
485-
"builtins.dict", [strtype, implicit_any]
486-
) or self.api.named_type("builtins.object")
484+
dictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
487485
# Actual signature should return OrderedDict[str, Union[types]]
488-
ordereddictype = self.api.named_type_or_none(
489-
"builtins.dict", [strtype, implicit_any]
490-
) or self.api.named_type("builtins.object")
486+
ordereddictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
491487
fallback = self.api.named_type("builtins.tuple", [implicit_any])
492488
# Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
493489
# but it can't be expressed. 'new' and 'len' should be callable types.

test-data/unit/check-dynamic-typing.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class int: pass
147147
class type: pass
148148
class function: pass
149149
class str: pass
150+
class dict: pass
150151

151152
[case testBinaryOperationsWithDynamicAsRightOperand]
152153
from typing import Any
@@ -219,6 +220,7 @@ class int: pass
219220
class type: pass
220221
class function: pass
221222
class str: pass
223+
class dict: pass
222224

223225
[case testDynamicWithUnaryExpressions]
224226
from typing import Any

test-data/unit/check-generics.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,7 @@ class type: pass
13311331
class tuple: pass
13321332
class function: pass
13331333
class str: pass
1334+
class dict: pass
13341335

13351336
[case testMultipleAssignmentWithIterable]
13361337
from typing import Iterable, TypeVar

test-data/unit/check-incomplete-fixture.test

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ import m
1212
m.x # E: "object" has no attribute "x"
1313
[file m.py]
1414

15-
[case testDictMissingFromStubs]
16-
from typing import Dict
17-
def f(x: Dict[int]) -> None: pass
18-
[out]
19-
main:1: error: Module "typing" has no attribute "Dict"
20-
main:1: note: Maybe your test fixture does not define "builtins.dict"?
21-
main:1: note: Consider adding [builtins fixtures/dict.pyi] to your test description
22-
2315
[case testSetMissingFromStubs]
2416
from typing import Set
2517
def f(x: Set[int]) -> None: pass

test-data/unit/check-tuples.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ class str: pass
774774
class bool: pass
775775
class type: pass
776776
class function: pass
777+
class dict: pass
777778

778779

779780
-- For loop over tuple

test-data/unit/cmdline.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,8 @@ a.py:2: note: By default the bodies of untyped functions are not checked, consid
15161516
class object: pass
15171517
class str(object): pass
15181518
class int(object): pass
1519+
class list: pass
1520+
class dict: pass
15191521
[file dir/stdlib/sys.pyi]
15201522
[file dir/stdlib/types.pyi]
15211523
[file dir/stdlib/typing.pyi]

test-data/unit/fine-grained.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,8 +1809,8 @@ def f() -> Iterator[None]:
18091809
[typing fixtures/typing-medium.pyi]
18101810
[builtins fixtures/list.pyi]
18111811
[triggered]
1812-
2: <b>, __main__
1813-
3: <b>, __main__, a
1812+
2: <b>, <b[wildcard]>, __main__
1813+
3: <b>, <b[wildcard]>, __main__, a
18141814
[out]
18151815
main:2: note: Revealed type is "contextlib.GeneratorContextManager[None]"
18161816
==

test-data/unit/fixtures/__init_subclass__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ class int: pass
1111
class bool: pass
1212
class str: pass
1313
class function: pass
14+
class dict: pass

0 commit comments

Comments
 (0)