Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
result = type_object_type(node, self.named_type)
elif isinstance(node, MypyFile):
# Reference to a module object.
result = self.named_type('builtins.module')
result = self.named_type('types.ModuleType')
elif isinstance(node, Decorator):
result = self.analyze_var_ref(node.var, e)
else:
Expand Down
2 changes: 1 addition & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ def analyze_class_attribute_access(itype: Instance,

if isinstance(node.node, MypyFile):
# Reference to a module object.
return builtin_type('builtins.module')
return builtin_type('types.ModuleType')

if is_decorated:
# TODO: Return type of decorated function. This is quick hack to work around #998.
Expand Down
13 changes: 13 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3353,6 +3353,14 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -
we generate dummy symbol table nodes for the imported names,
and these will get resolved in later phases of semantic
analysis.

MYPY_MODULE has a special meaning in stubs. If present, its current
value will be used as the module name prefix for qualified names of
all objects found in the stub. This is to provide correct output from
reveal_type for definitions placed in the "wrong" module for circular
import reasons (e.g., the definition of ModuleType in
_importlib_modulespec.pyi instead of type.pyi).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

types.pyi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just in time for the last commit =)


"""
sem = self.sem
self.sem.options = options # Needed because we sometimes call into it
Expand Down Expand Up @@ -3385,6 +3393,11 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -

for d in defs:
d.accept(self)
if file.is_stub and isinstance(d, AssignmentStmt):
lvalue = d.lvalues[0]
if isinstance(lvalue, NameExpr) and lvalue.name == 'MYPY_MODULE':
assert isinstance(d.rvalue, StrExpr)
sem.cur_mod_id = d.rvalue.value

# Add implicit definition of literals/keywords to builtins, as we
# cannot define a variable with them explicitly.
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-ignore.test
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import b # type: ignore
reveal_type(a.foo) # E: Revealed type is 'Any'
reveal_type(b.foo) # E: Revealed type is 'builtins.int'
a.bar()
b.bar() # E: "module" has no attribute "bar"
b.bar() # E: "ModuleType" has no attribute "bar"

[file b.py]
foo = 3
Expand Down Expand Up @@ -76,7 +76,7 @@ class B(A):
import m
m.x = object # type: ignore
m.f() # type: ignore
m.y # E: "module" has no attribute "y"
m.y # E: "ModuleType" has no attribute "y"
[file m.py]
[builtins fixtures/module.pyi]

Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ const = 3
[stale mod3]
[builtins fixtures/module.pyi]
[out2]
tmp/mod1.py:3: error: "module" has no attribute "mod4"
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod4"

[case testIncrementalLongBrokenCascade]
import mod1
Expand Down Expand Up @@ -335,7 +335,7 @@ const = 3
[stale mod6]
[builtins fixtures/module.pyi]
[out2]
tmp/mod1.py:3: error: "module" has no attribute "mod7"
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod7"

[case testIncrementalNestedBrokenCascade]
import mod1
Expand All @@ -361,7 +361,7 @@ const = 3
[stale mod2.mod3]
[builtins fixtures/module.pyi]
[out2]
tmp/mod1.py:3: error: "module" has no attribute "mod4"
tmp/mod1.py:3: error: "ModuleType" has no attribute "mod4"

[case testIncrementalNestedBrokenCascadeWithType1]
import mod1, mod2.mod3.mod5
Expand Down
26 changes: 18 additions & 8 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,18 @@ def f(c:str) -> None: pass
[case testInvalidOperationsOnModules]
import m
import typing

class A: pass
m() # E: "module" not callable
a = m # type: A # E: Incompatible types in assignment (expression has type "module", variable has type "A")
m + None # E: Unsupported left operand type for + ("module")
m() # E: "ModuleType" not callable
a = m # type: A # E: Incompatible types in assignment (expression has type "ModuleType", variable has type "A")
m + None # E: Unsupported left operand type for + ("ModuleType")
[file m.py]
[builtins fixtures/module.pyi]

[case testNameDefinedInDifferentModule]
import m, n
import typing
m.x # E: "module" has no attribute "x"
m.x # E: "ModuleType" has no attribute "x"
[file m.py]
y = object()
[file n.py]
Expand Down Expand Up @@ -329,7 +330,7 @@ import nonexistent
[out]
tmp/x.py:1: error: Cannot find module named 'nonexistent'
tmp/x.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)
main:3: error: "module" has no attribute "z"
main:3: error: "ModuleType" has no attribute "z"

[case testUnknownModuleImportedWithinFunction]
def f():
Expand Down Expand Up @@ -647,7 +648,7 @@ def f(x: str) -> None: pass
if object():
import m
else:
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "module")
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "ModuleType")
[file m.py]
[builtins fixtures/module.pyi]
[out]
Expand Down Expand Up @@ -751,7 +752,7 @@ value = 3.2
[case testSubmoduleImportFromDoesNotAddParents]
from a import b
reveal_type(b.value) # E: Revealed type is 'builtins.str'
b.c.value # E: "module" has no attribute "c"
b.c.value # E: "ModuleType" has no attribute "c"
a.value # E: Name 'a' is not defined

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

[case testSubmoduleMixingImportFromAndImport2]
import parent.child
Expand Down Expand Up @@ -1406,3 +1407,12 @@ reveal_type(cb) # E: Revealed type is 'def (*Any, **Any) -> Any'
from typing import Callable, Any
AnyCallable = Callable[..., Any]
[out]

[case testRevealType]
import types
def f() -> types.ModuleType:
return types
reveal_type(f()) # E: Revealed type is 'types.ModuleType'
reveal_type(types) # E: Revealed type is 'types.ModuleType'

[builtins fixtures/module.pyi]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a \n character at the end of this line.

8 changes: 4 additions & 4 deletions test-data/unit/cmdline.test
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ x += '' # Error reported here
a.py:2: error: Unsupported operand types for + ("int" and "str")
main.py:3: error: Unsupported operand types for + ("int" and "str")
main.py:6: error: Unsupported operand types for + ("int" and "str")
main.py:7: error: "module" has no attribute "y"
main.py:8: error: Unsupported operand types for + ("module" and "int")
main.py:7: error: "ModuleType" has no attribute "y"
main.py:8: error: Unsupported operand types for + ("ModuleType" and "int")

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

[case testConfigFollowImportsSkip]
# cmd: mypy main.py
Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/fixtures/async_await.pyi
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import typing

T = typing.TypeVar('T')
class list(typing.Generic[T], typing.Sequence[T]): pass

class object:
def __init__(self): pass
class type: pass
class function: pass
class int: pass
class str: pass
class dict: pass
class list: pass
class set: pass
class tuple: pass
class BaseException: pass
class StopIteration(BaseException): pass
class StopAsyncIteration(BaseException): pass
def iter(obj: typing.Any) -> typing.Any: pass
def next(obj: typing.Any) -> typing.Any: pass
class ellipsis: ...
11 changes: 6 additions & 5 deletions test-data/unit/fixtures/module.pyi
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from typing import Any, Dict, Generic, TypeVar
from typing import Any, Dict, Generic, TypeVar, Sequence
from types import ModuleType

T = TypeVar('T')
S = TypeVar('S')

class list(Generic[T], Sequence[T]): pass

class object:
def __init__(self) -> None: pass
class module:
__name__ = ... # type: str
__file__ = ... # type: str
__dict__ = ... # type: Dict[str, Any]
class type: pass
class function: pass
class int: pass
class str: pass
class bool: pass
class tuple: pass
class dict(Generic[T, S]): pass
class ellipsis: pass

5 changes: 4 additions & 1 deletion test-data/unit/fixtures/module_all.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from typing import Generic, Sequence, TypeVar
from types import ModuleType

_T = TypeVar('_T')

class object:
def __init__(self) -> None: pass
class module: pass
class type: pass
class function: pass
class int: pass
class str: pass
class bool: pass
class list(Generic[_T], Sequence[_T]):
def append(self, x: _T): pass
def extend(self, x: Sequence[_T]): pass
def __add__(self, rhs: Sequence[_T]) -> list[_T]: pass
class tuple: pass
class ellipsis: pass
32 changes: 31 additions & 1 deletion test-data/unit/lib-stub/types.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
from typing import TypeVar
from typing import TypeVar, Optional, List, Any, Generic, Sequence
T = TypeVar('T')

def coroutine(func: T) -> T:
return func

class bool: ...

class ModuleSpec:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to prune the contents of this file some? It looks like you just copied from typeshed -- but in order to avoid slowing down the tests we should really only include attributes that tests reference.

def __init__(self, name: str, loader: Optional['Loader'], *,
origin: str = None, loader_state: Any = None,
is_package: bool = None) -> None: ...
name = ... # type: str
loader = ... # type: Optional[Loader]
origin = ... # type: Optional[str]
submodule_search_locations = ... # type: Optional[List[str]]
loader_state = ... # type: Any
cached = ... # type: Optional[str]
parent = ... # type: Optional[str]
has_location = ... # type: bool

class Loader:
def load_module(self, fullname: str) -> ModuleType: ...
def module_repr(self, module: ModuleType) -> str: ...
def create_module(self, spec: ModuleSpec) -> Optional[ModuleType]: ...
def exec_module(self, module: ModuleType) -> None: ...

class ModuleType:
__name__ = ... # type: str
__file__ = ... # type: str
__loader__ = ... # type: Optional[Loader]
__package__ = ... # type: Optional[str]
__spec__ = ... # type: Optional[ModuleSpec]
def __init__(self, name: str, doc: Optional[str] = ...) -> None: ...
4 changes: 2 additions & 2 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,8 @@ collections.Deque()
typing.deque()

[out]
_testDequeWrongCase.py:4: error: "module" has no attribute "Deque"
_testDequeWrongCase.py:5: error: "module" has no attribute "deque"
_testDequeWrongCase.py:4: error: "ModuleType" has no attribute "Deque"
_testDequeWrongCase.py:5: error: "ModuleType" has no attribute "deque"

[case testDictUpdateInference]
from typing import Dict, Optional
Expand Down