Skip to content

Handle types.ModuleType #3107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 13, 2017
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -415,7 +415,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
9 changes: 9 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@
'typing.typevar': 'typing.TypeVar',
}

# Rename objects placed in _importlib_modulespec due to circular imports
module_rename_map = {
'_importlib_modulespec.ModuleType': 'types.ModuleType',
'_importlib_modulespec.ModuleSpec': 'importlib.machinery.ModuleSpec',
'_importlib_modulespec.Loader': 'importlib.abc.Loader'
}

# Hard coded type promotions (shared between all Python versions).
# These add extra ad-hoc edges to the subtyping relation. For example,
# int is considered a subtype of float, even though there is no
Expand Down Expand Up @@ -3445,6 +3452,8 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -

for d in defs:
d.accept(self)
if isinstance(d, ClassDef):
d.info._fullname = module_rename_map.get(d.info._fullname, d.info._fullname)

# Add implicit definition of literals/keywords to builtins, as we
# cannot define a variable with them explicitly.
Expand Down
6 changes: 4 additions & 2 deletions mypy/server/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,12 @@ def visit_tuple_type(self, typ: TupleType) -> List[str]:
raise NotImplementedError

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

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

def visit_typeddict_type(self, typ: TypedDictType) -> List[str]:
raise NotImplementedError
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]
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
24 changes: 12 additions & 12 deletions test-data/unit/fine-grained.test
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def g(x: str) -> None: pass
[builtins fixtures/fine_grained.pyi]
[out]
==
main:3: error: "module" has no attribute "f"
main:3: error: "ModuleType" has no attribute "f"

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

[case testClassChangedIntoFunction]
import m
Expand Down Expand Up @@ -241,7 +241,7 @@ class A: pass
[builtins fixtures/fine_grained.pyi]
[out]
==
main:3: error: "module" has no attribute "A"
main:3: error: "ModuleType" has no attribute "A"
==

[case testContinueToReportTypeCheckError]
Expand Down Expand Up @@ -281,10 +281,10 @@ class A: pass
[builtins fixtures/fine_grained.pyi]
[out]
==
main:3: error: "module" has no attribute "A"
main:5: error: "module" has no attribute "B"
main:3: error: "ModuleType" has no attribute "A"
main:5: error: "ModuleType" has no attribute "B"
==
main:5: error: "module" has no attribute "B"
main:5: error: "ModuleType" has no attribute "B"

[case testContinueToReportErrorAtTopLevel]
import n
Expand Down Expand Up @@ -348,9 +348,9 @@ def g() -> None: pass
[builtins fixtures/fine_grained.pyi]
[out]
main:3: error: Too few arguments for "f"
main:5: error: "module" has no attribute "g"
main:5: error: "ModuleType" has no attribute "g"
==
main:5: error: "module" has no attribute "g"
main:5: error: "ModuleType" has no attribute "g"
==

[case testKeepReportingErrorIfNoChanges]
Expand All @@ -361,9 +361,9 @@ def h() -> None:
[file m.py.2]
[builtins fixtures/fine_grained.pyi]
[out]
main:3: error: "module" has no attribute "g"
main:3: error: "ModuleType" has no attribute "g"
==
main:3: error: "module" has no attribute "g"
main:3: error: "ModuleType" has no attribute "g"

[case testFixErrorAndReintroduce]
import m
Expand All @@ -375,10 +375,10 @@ def g() -> None: pass
[file m.py.3]
[builtins fixtures/fine_grained.pyi]
[out]
main:3: error: "module" has no attribute "g"
main:3: error: "ModuleType" has no attribute "g"
==
==
main:3: error: "module" has no attribute "g"
main:3: error: "ModuleType" has no attribute "g"

[case testAddBaseClassMethodCausingInvalidOverride]
import m
Expand Down
1 change: 0 additions & 1 deletion test-data/unit/fixtures/args.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,3 @@ class int:
class str: pass
class bool: pass
class function: pass
class module: pass
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: ...
4 changes: 3 additions & 1 deletion test-data/unit/fixtures/fine_grained.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# TODO: Migrate to regular stubs once fine-grained incremental is robust
# enough to handle them.

import types

class Any: pass

class object:
Expand All @@ -21,4 +23,4 @@ class bytes: pass
class tuple: pass
class function: pass
class ellipsis: pass
class module: pass
class list: pass
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
1 change: 0 additions & 1 deletion test-data/unit/fixtures/module_all_python2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ _T = TypeVar('_T')

class object:
def __init__(self) -> None: pass
class module: pass
class type: pass
class function: pass
class int: pass
Expand Down
2 changes: 0 additions & 2 deletions test-data/unit/fixtures/ops.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,3 @@ class float: pass
class BaseException: pass

def __print(a1=None, a2=None, a3=None, a4=None): pass

class module: pass
7 changes: 6 additions & 1 deletion test-data/unit/lib-stub/types.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
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 ModuleType: ...
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