Skip to content

Commit 73acdb8

Browse files
pkchilevkivskyi
authored andcommitted
Handle types.ModuleType (#3107)
* Allowed use of types.ModuleType * Allow module name override for stub files * Remove superfluous class module * Hard code module renaming * Address CR * Updated typeshed * Fix fine-grained tests * Fix whitespace
1 parent 77e3237 commit 73acdb8

18 files changed

+80
-48
lines changed

mypy/checkexpr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
151151
result = type_object_type(node, self.named_type)
152152
elif isinstance(node, MypyFile):
153153
# Reference to a module object.
154-
result = self.named_type('builtins.module')
154+
result = self.named_type('types.ModuleType')
155155
elif isinstance(node, Decorator):
156156
result = self.analyze_var_ref(node.var, e)
157157
else:

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ def analyze_class_attribute_access(itype: Instance,
415415

416416
if isinstance(node.node, MypyFile):
417417
# Reference to a module object.
418-
return builtin_type('builtins.module')
418+
return builtin_type('types.ModuleType')
419419

420420
if is_decorated:
421421
# TODO: Return type of decorated function. This is quick hack to work around #998.

mypy/semanal.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@
113113
'typing.typevar': 'typing.TypeVar',
114114
}
115115

116+
# Rename objects placed in _importlib_modulespec due to circular imports
117+
module_rename_map = {
118+
'_importlib_modulespec.ModuleType': 'types.ModuleType',
119+
'_importlib_modulespec.ModuleSpec': 'importlib.machinery.ModuleSpec',
120+
'_importlib_modulespec.Loader': 'importlib.abc.Loader'
121+
}
122+
116123
# Hard coded type promotions (shared between all Python versions).
117124
# These add extra ad-hoc edges to the subtyping relation. For example,
118125
# int is considered a subtype of float, even though there is no
@@ -3445,6 +3452,8 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -
34453452

34463453
for d in defs:
34473454
d.accept(self)
3455+
if isinstance(d, ClassDef):
3456+
d.info._fullname = module_rename_map.get(d.info._fullname, d.info._fullname)
34483457

34493458
# Add implicit definition of literals/keywords to builtins, as we
34503459
# cannot define a variable with them explicitly.

mypy/server/deps.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,12 @@ def visit_tuple_type(self, typ: TupleType) -> List[str]:
207207
raise NotImplementedError
208208

209209
def visit_type_type(self, typ: TypeType) -> List[str]:
210-
raise NotImplementedError
210+
# TODO: replace with actual implementation
211+
return []
211212

212213
def visit_type_var(self, typ: TypeVarType) -> List[str]:
213-
raise NotImplementedError
214+
# TODO: replace with actual implementation
215+
return []
214216

215217
def visit_typeddict_type(self, typ: TypedDictType) -> List[str]:
216218
raise NotImplementedError

test-data/unit/check-ignore.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import b # type: ignore
4646
reveal_type(a.foo) # E: Revealed type is 'Any'
4747
reveal_type(b.foo) # E: Revealed type is 'builtins.int'
4848
a.bar()
49-
b.bar() # E: "module" has no attribute "bar"
49+
b.bar() # E: "ModuleType" has no attribute "bar"
5050

5151
[file b.py]
5252
foo = 3
@@ -76,7 +76,7 @@ class B(A):
7676
import m
7777
m.x = object # type: ignore
7878
m.f() # type: ignore
79-
m.y # E: "module" has no attribute "y"
79+
m.y # E: "ModuleType" has no attribute "y"
8080
[file m.py]
8181
[builtins fixtures/module.pyi]
8282

test-data/unit/check-incremental.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ const = 3
300300
[stale mod3]
301301
[builtins fixtures/module.pyi]
302302
[out2]
303-
tmp/mod1.py:3: error: "module" has no attribute "mod4"
303+
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod4"
304304

305305
[case testIncrementalLongBrokenCascade]
306306
import mod1
@@ -335,7 +335,7 @@ const = 3
335335
[stale mod6]
336336
[builtins fixtures/module.pyi]
337337
[out2]
338-
tmp/mod1.py:3: error: "module" has no attribute "mod7"
338+
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod7"
339339

340340
[case testIncrementalNestedBrokenCascade]
341341
import mod1
@@ -361,7 +361,7 @@ const = 3
361361
[stale mod2.mod3]
362362
[builtins fixtures/module.pyi]
363363
[out2]
364-
tmp/mod1.py:3: error: "module" has no attribute "mod4"
364+
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod4"
365365

366366
[case testIncrementalNestedBrokenCascadeWithType1]
367367
import mod1, mod2.mod3.mod5

test-data/unit/check-modules.test

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,18 @@ def f(c:str) -> None: pass
153153
[case testInvalidOperationsOnModules]
154154
import m
155155
import typing
156+
156157
class A: pass
157-
m() # E: "module" not callable
158-
a = m # type: A # E: Incompatible types in assignment (expression has type "module", variable has type "A")
159-
m + None # E: Unsupported left operand type for + ("module")
158+
m() # E: "ModuleType" not callable
159+
a = m # type: A # E: Incompatible types in assignment (expression has type "ModuleType", variable has type "A")
160+
m + None # E: Unsupported left operand type for + ("ModuleType")
160161
[file m.py]
161162
[builtins fixtures/module.pyi]
162163

163164
[case testNameDefinedInDifferentModule]
164165
import m, n
165166
import typing
166-
m.x # E: "module" has no attribute "x"
167+
m.x # E: "ModuleType" has no attribute "x"
167168
[file m.py]
168169
y = object()
169170
[file n.py]
@@ -329,7 +330,7 @@ import nonexistent
329330
[out]
330331
tmp/x.py:1: error: Cannot find module named 'nonexistent'
331332
tmp/x.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)
332-
main:3: error: "module" has no attribute "z"
333+
main:3: error: "ModuleType" has no attribute "z"
333334

334335
[case testUnknownModuleImportedWithinFunction]
335336
def f():
@@ -647,7 +648,7 @@ def f(x: str) -> None: pass
647648
if object():
648649
import m
649650
else:
650-
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "module")
651+
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "ModuleType")
651652
[file m.py]
652653
[builtins fixtures/module.pyi]
653654
[out]
@@ -751,7 +752,7 @@ value = 3.2
751752
[case testSubmoduleImportFromDoesNotAddParents]
752753
from a import b
753754
reveal_type(b.value) # E: Revealed type is 'builtins.str'
754-
b.c.value # E: "module" has no attribute "c"
755+
b.c.value # E: "ModuleType" has no attribute "c"
755756
a.value # E: Name 'a' is not defined
756757

757758
[file a/__init__.py]
@@ -852,7 +853,7 @@ bar = parent.unrelated.ShouldNotLoad()
852853
[builtins fixtures/module.pyi]
853854
[out]
854855
tmp/parent/child.py:8: error: Revealed type is 'parent.common.SomeClass'
855-
tmp/parent/child.py:9: error: "module" has no attribute "unrelated"
856+
tmp/parent/child.py:9: error: "ModuleType" has no attribute "unrelated"
856857

857858
[case testSubmoduleMixingImportFromAndImport2]
858859
import parent.child
@@ -1406,3 +1407,12 @@ reveal_type(cb) # E: Revealed type is 'def (*Any, **Any) -> Any'
14061407
from typing import Callable, Any
14071408
AnyCallable = Callable[..., Any]
14081409
[out]
1410+
1411+
[case testRevealType]
1412+
import types
1413+
def f() -> types.ModuleType:
1414+
return types
1415+
reveal_type(f()) # E: Revealed type is 'types.ModuleType'
1416+
reveal_type(types) # E: Revealed type is 'types.ModuleType'
1417+
1418+
[builtins fixtures/module.pyi]

test-data/unit/cmdline.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,8 @@ x += '' # Error reported here
365365
a.py:2: error: Unsupported operand types for + ("int" and "str")
366366
main.py:3: error: Unsupported operand types for + ("int" and "str")
367367
main.py:6: error: Unsupported operand types for + ("int" and "str")
368-
main.py:7: error: "module" has no attribute "y"
369-
main.py:8: error: Unsupported operand types for + ("module" and "int")
368+
main.py:7: error: "ModuleType" has no attribute "y"
369+
main.py:8: error: Unsupported operand types for + ("ModuleType" and "int")
370370

371371
[case testConfigFollowImportsSilent]
372372
# cmd: mypy main.py
@@ -386,8 +386,8 @@ x += '' # No error reported
386386
[out]
387387
main.py:2: error: Unsupported operand types for + ("int" and "str")
388388
main.py:4: error: Unsupported operand types for + ("int" and "str")
389-
main.py:5: error: "module" has no attribute "y"
390-
main.py:6: error: Unsupported operand types for + ("module" and "int")
389+
main.py:5: error: "ModuleType" has no attribute "y"
390+
main.py:6: error: Unsupported operand types for + ("ModuleType" and "int")
391391

392392
[case testConfigFollowImportsSkip]
393393
# cmd: mypy main.py

test-data/unit/fine-grained.test

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def g(x: str) -> None: pass
6868
[builtins fixtures/fine_grained.pyi]
6969
[out]
7070
==
71-
main:3: error: "module" has no attribute "f"
71+
main:3: error: "ModuleType" has no attribute "f"
7272

7373
[case testTopLevelMissingModuleAttribute]
7474
import m
@@ -81,7 +81,7 @@ def g(x: int) -> None: pass
8181
[builtins fixtures/fine_grained.pyi]
8282
[out]
8383
==
84-
main:2: error: "module" has no attribute "f"
84+
main:2: error: "ModuleType" has no attribute "f"
8585

8686
[case testClassChangedIntoFunction]
8787
import m
@@ -241,7 +241,7 @@ class A: pass
241241
[builtins fixtures/fine_grained.pyi]
242242
[out]
243243
==
244-
main:3: error: "module" has no attribute "A"
244+
main:3: error: "ModuleType" has no attribute "A"
245245
==
246246

247247
[case testContinueToReportTypeCheckError]
@@ -281,10 +281,10 @@ class A: pass
281281
[builtins fixtures/fine_grained.pyi]
282282
[out]
283283
==
284-
main:3: error: "module" has no attribute "A"
285-
main:5: error: "module" has no attribute "B"
284+
main:3: error: "ModuleType" has no attribute "A"
285+
main:5: error: "ModuleType" has no attribute "B"
286286
==
287-
main:5: error: "module" has no attribute "B"
287+
main:5: error: "ModuleType" has no attribute "B"
288288

289289
[case testContinueToReportErrorAtTopLevel]
290290
import n
@@ -348,9 +348,9 @@ def g() -> None: pass
348348
[builtins fixtures/fine_grained.pyi]
349349
[out]
350350
main:3: error: Too few arguments for "f"
351-
main:5: error: "module" has no attribute "g"
351+
main:5: error: "ModuleType" has no attribute "g"
352352
==
353-
main:5: error: "module" has no attribute "g"
353+
main:5: error: "ModuleType" has no attribute "g"
354354
==
355355

356356
[case testKeepReportingErrorIfNoChanges]
@@ -361,9 +361,9 @@ def h() -> None:
361361
[file m.py.2]
362362
[builtins fixtures/fine_grained.pyi]
363363
[out]
364-
main:3: error: "module" has no attribute "g"
364+
main:3: error: "ModuleType" has no attribute "g"
365365
==
366-
main:3: error: "module" has no attribute "g"
366+
main:3: error: "ModuleType" has no attribute "g"
367367

368368
[case testFixErrorAndReintroduce]
369369
import m
@@ -375,10 +375,10 @@ def g() -> None: pass
375375
[file m.py.3]
376376
[builtins fixtures/fine_grained.pyi]
377377
[out]
378-
main:3: error: "module" has no attribute "g"
378+
main:3: error: "ModuleType" has no attribute "g"
379379
==
380380
==
381-
main:3: error: "module" has no attribute "g"
381+
main:3: error: "ModuleType" has no attribute "g"
382382

383383
[case testAddBaseClassMethodCausingInvalidOverride]
384384
import m

test-data/unit/fixtures/args.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,3 @@ class int:
2626
class str: pass
2727
class bool: pass
2828
class function: pass
29-
class module: pass
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import typing
2+
3+
T = typing.TypeVar('T')
4+
class list(typing.Generic[T], typing.Sequence[T]): pass
5+
26
class object:
37
def __init__(self): pass
48
class type: pass
59
class function: pass
610
class int: pass
711
class str: pass
812
class dict: pass
9-
class list: pass
1013
class set: pass
1114
class tuple: pass
1215
class BaseException: pass
1316
class StopIteration(BaseException): pass
1417
class StopAsyncIteration(BaseException): pass
1518
def iter(obj: typing.Any) -> typing.Any: pass
1619
def next(obj: typing.Any) -> typing.Any: pass
20+
class ellipsis: ...

test-data/unit/fixtures/fine_grained.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# TODO: Migrate to regular stubs once fine-grained incremental is robust
44
# enough to handle them.
55

6+
import types
7+
68
class Any: pass
79

810
class object:
@@ -21,4 +23,4 @@ class bytes: pass
2123
class tuple: pass
2224
class function: pass
2325
class ellipsis: pass
24-
class module: pass
26+
class list: pass

test-data/unit/fixtures/module.pyi

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
from typing import Any, Dict, Generic, TypeVar
1+
from typing import Any, Dict, Generic, TypeVar, Sequence
2+
from types import ModuleType
23

34
T = TypeVar('T')
45
S = TypeVar('S')
56

7+
class list(Generic[T], Sequence[T]): pass
8+
69
class object:
710
def __init__(self) -> None: pass
8-
class module:
9-
__name__ = ... # type: str
10-
__file__ = ... # type: str
11-
__dict__ = ... # type: Dict[str, Any]
1211
class type: pass
1312
class function: pass
1413
class int: pass
1514
class str: pass
1615
class bool: pass
1716
class tuple: pass
1817
class dict(Generic[T, S]): pass
18+
class ellipsis: pass
19+
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from typing import Generic, Sequence, TypeVar
2+
from types import ModuleType
3+
24
_T = TypeVar('_T')
35

46
class object:
57
def __init__(self) -> None: pass
6-
class module: pass
78
class type: pass
89
class function: pass
910
class int: pass
1011
class str: pass
12+
class bool: pass
1113
class list(Generic[_T], Sequence[_T]):
1214
def append(self, x: _T): pass
1315
def extend(self, x: Sequence[_T]): pass
1416
def __add__(self, rhs: Sequence[_T]) -> list[_T]: pass
1517
class tuple: pass
18+
class ellipsis: pass

test-data/unit/fixtures/module_all_python2.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ _T = TypeVar('_T')
33

44
class object:
55
def __init__(self) -> None: pass
6-
class module: pass
76
class type: pass
87
class function: pass
98
class int: pass

test-data/unit/fixtures/ops.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,3 @@ class float: pass
5454
class BaseException: pass
5555

5656
def __print(a1=None, a2=None, a3=None, a4=None): pass
57-
58-
class module: pass

test-data/unit/lib-stub/types.pyi

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from typing import TypeVar
1+
from typing import TypeVar, Optional, List, Any, Generic, Sequence
22
T = TypeVar('T')
3+
34
def coroutine(func: T) -> T:
45
return func
6+
7+
class bool: ...
8+
9+
class ModuleType: ...

test-data/unit/pythoneval.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,8 +1169,8 @@ collections.Deque()
11691169
typing.deque()
11701170

11711171
[out]
1172-
_testDequeWrongCase.py:4: error: "module" has no attribute "Deque"
1173-
_testDequeWrongCase.py:5: error: "module" has no attribute "deque"
1172+
_testDequeWrongCase.py:4: error: "ModuleType" has no attribute "Deque"
1173+
_testDequeWrongCase.py:5: error: "ModuleType" has no attribute "deque"
11741174

11751175
[case testDictUpdateInference]
11761176
from typing import Dict, Optional

0 commit comments

Comments
 (0)