-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Detect and support module aliasing via assignment. #3435
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
Changes from all commits
ad646b9
c498e2a
fdb3af1
14d7d0f
ee8bd80
7efbdb7
fd3eb84
4006d0a
7aeb65f
6dc0757
9aa8169
6e7cd24
d3d8f9f
1cbe94c
325ae94
2326425
9592ce9
f438f9c
e1ce5b8
c51a916
4d40207
a8a4f44
6584b0b
b292673
f62d58e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ def safe_rmdir(dirname: str) -> None: | |
|
||
class GenericTest(unittest.TestCase): | ||
# The path module to be tested | ||
pathmodule = genericpath # type: Any | ||
pathmodule = genericpath # type: Any | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
common_attributes = ['commonprefix', 'getsize', 'getatime', 'getctime', | ||
'getmtime', 'exists', 'isdir', 'isfile'] | ||
attributes = [] # type: List[str] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1439,3 +1439,204 @@ class C: | |
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAlias] | ||
import m | ||
m2 = m | ||
reveal_type(m2.a) # E: Revealed type is 'builtins.str' | ||
m2.b # E: Module has no attribute "b" | ||
m2.c = 'bar' # E: Module has no attribute "c" | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testClassModuleAlias] | ||
import m | ||
|
||
class C: | ||
x = m | ||
def foo(self) -> None: | ||
reveal_type(self.x.a) # E: Revealed type is 'builtins.str' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testLocalModuleAlias] | ||
import m | ||
|
||
def foo() -> None: | ||
x = m | ||
reveal_type(x.a) # E: Revealed type is 'builtins.str' | ||
|
||
class C: | ||
def foo(self) -> None: | ||
x = m | ||
reveal_type(x.a) # E: Revealed type is 'builtins.str' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testChainedModuleAlias] | ||
import m | ||
m3 = m2 = m | ||
m4 = m3 | ||
m5 = m4 | ||
reveal_type(m2.a) # E: Revealed type is 'builtins.str' | ||
reveal_type(m3.a) # E: Revealed type is 'builtins.str' | ||
reveal_type(m4.a) # E: Revealed type is 'builtins.str' | ||
reveal_type(m5.a) # E: Revealed type is 'builtins.str' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testMultiModuleAlias] | ||
import m, n | ||
m2, n2, (m3, n3) = m, n, [m, n] | ||
reveal_type(m2.a) # E: Revealed type is 'builtins.str' | ||
reveal_type(n2.b) # E: Revealed type is 'builtins.str' | ||
reveal_type(m3.a) # E: Revealed type is 'builtins.str' | ||
reveal_type(n3.b) # E: Revealed type is 'builtins.str' | ||
|
||
x, y = m # E: 'types.ModuleType' object is not iterable | ||
x, y, z = m, n # E: Need more than 2 values to unpack (3 expected) | ||
x, y = m, m, m # E: Too many values to unpack (2 expected, 3 provided) | ||
x, (y, z) = m, n # E: 'types.ModuleType' object is not iterable | ||
x, (y, z) = m, (n, n, n) # E: Too many values to unpack (2 expected, 3 provided) | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[file n.py] | ||
b = 'bar' | ||
|
||
[builtins fixtures/module.pyi] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to see few more tests. For example, a test that shows this failing: import m
x, y = m # must be an error here (Module object is not iterable or similar) and in general more failures, like failure on attempted access/assignment to a non-existing module attribute. Also probably some chained assignments: import m
x = m
y = x
reveal_type(y.a) # 'builtins.str' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Very good point, will add these tests. |
||
|
||
[case testModuleAliasWithExplicitAnnotation] | ||
from typing import Any | ||
import types | ||
import m | ||
mod_mod: types.ModuleType = m | ||
mod_mod2: types.ModuleType | ||
mod_mod2 = m | ||
mod_mod3 = m # type: types.ModuleType | ||
mod_any: Any = m | ||
mod_int: int = m # E: Incompatible types in assignment (expression has type Module, variable has type "int") | ||
|
||
reveal_type(mod_mod) # E: Revealed type is 'types.ModuleType' | ||
mod_mod.a # E: Module has no attribute "a" | ||
reveal_type(mod_mod2) # E: Revealed type is 'types.ModuleType' | ||
mod_mod2.a # E: Module has no attribute "a" | ||
reveal_type(mod_mod3) # E: Revealed type is 'types.ModuleType' | ||
mod_mod3.a # E: Module has no attribute "a" | ||
reveal_type(mod_any) # E: Revealed type is 'Any' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAliasPassedToFunction] | ||
import types | ||
import m | ||
|
||
def takes_module(x: types.ModuleType): | ||
reveal_type(x.__file__) # E: Revealed type is 'builtins.str' | ||
|
||
n = m | ||
takes_module(m) | ||
takes_module(n) | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAliasRepeated] | ||
import m, n | ||
|
||
if bool(): | ||
x = m | ||
else: | ||
x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type Module) | ||
|
||
if bool(): | ||
y = 3 | ||
else: | ||
y = m # E: Incompatible types in assignment (expression has type Module, variable has type "int") | ||
|
||
if bool(): | ||
z = m | ||
else: | ||
z = n # E: Cannot assign multiple modules to name 'z' without explicit 'types.ModuleType' annotation | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[file n.py] | ||
a = 3 | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAliasRepeatedWithAnnotation] | ||
import types | ||
import m, n | ||
|
||
x: types.ModuleType | ||
if bool(): | ||
x = m | ||
else: | ||
x = n | ||
|
||
x.a # E: Module has no attribute "a" | ||
reveal_type(x.__file__) # E: Revealed type is 'builtins.str' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[file n.py] | ||
a = 3 | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAliasRepeatedComplex] | ||
import m, n, o | ||
|
||
x = m | ||
x = n # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation | ||
x = o # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation | ||
|
||
y = o | ||
y, z = m, n # E: Cannot assign multiple modules to name 'y' without explicit 'types.ModuleType' annotation | ||
|
||
xx = m | ||
xx = m | ||
reveal_type(xx.a) # E: Revealed type is 'builtins.str' | ||
|
||
[file m.py] | ||
a = 'foo' | ||
|
||
[file n.py] | ||
a = 3 | ||
|
||
[file o.py] | ||
a = 'bar' | ||
|
||
[builtins fixtures/module.pyi] | ||
|
||
[case testModuleAliasToOtherModule] | ||
import m, n | ||
m = n # E: Cannot assign multiple modules to name 'm' without explicit 'types.ModuleType' annotation | ||
|
||
[file m.py] | ||
|
||
[file n.py] | ||
|
||
[builtins fixtures/module.pyi] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is somehow not convincing. Could you please add a test that
is flagged as an error? (Plus also some length mismatches in nested scenarios.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yesterday I added a test for the
x, y, z = m, n
case; will also push a test forx, y = m, m, m
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and also for length mismatches in nested scenarios.