Skip to content
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

Detect and support module aliasing via assignment. #3435

Merged
merged 25 commits into from
Jun 3, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ad646b9
Detect and support module aliasing via assignment.
carljm May 24, 2017
c498e2a
Handle chained assignment and iterable unpacking assignment.
carljm May 24, 2017
fdb3af1
Remove test case that was only for exploration, not intended for incl…
carljm May 25, 2017
14d7d0f
Merge branch 'master' into module-alias
carljm May 30, 2017
ee8bd80
Add some more tests for module assignment.
carljm May 30, 2017
7efbdb7
Also add tests for access/assignment of nonexistent module attribute.
carljm May 30, 2017
fd3eb84
Break down code and add comments for clarity; add test for mismatch l…
carljm May 30, 2017
4006d0a
Naming improvements.
carljm May 30, 2017
7aeb65f
Merge branch 'master' into module-alias
carljm May 31, 2017
6dc0757
Support tracking module assignment in non-global scope.
carljm May 31, 2017
9aa8169
Add more tests for unpacking mismatch cases.
carljm May 31, 2017
6e7cd24
Keep rvals always on the right.
carljm May 31, 2017
d3d8f9f
Don't use a form of unpacking that is Py35+ only.
carljm May 31, 2017
1cbe94c
It's the zip that is problematic, not just the unpacking.
carljm May 31, 2017
325ae94
Add tests for module assignment in class and local scopes.
carljm May 31, 2017
2326425
Merge branch 'master' into module-alias
carljm Jun 3, 2017
9592ce9
Simplify to single method.
carljm Jun 3, 2017
f438f9c
Go back to annotating genericpath as Any in stdlib-sample.
carljm Jun 3, 2017
e1ce5b8
Respect explicit type annotation and don't propagate module reference.
carljm Jun 3, 2017
c51a916
Merge branch 'master' into module-alias
carljm Jun 3, 2017
4d40207
Backtrack to simple ModuleType var in case of incompatible module ali…
carljm Jun 3, 2017
a8a4f44
Remove stray pdb comment.
carljm Jun 3, 2017
6584b0b
Also handle reassignment of an original (non-alias) module reference.
carljm Jun 3, 2017
b292673
Simplify: fail on assignment of different modules to same variable wi…
carljm Jun 3, 2017
f62d58e
Style and working tweaks.
carljm Jun 3, 2017
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
19 changes: 19 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
self.process_namedtuple_definition(s)
self.process_typeddict_definition(s)
self.process_enum_call(s)
self.process_module_assignment(s)

if (len(s.lvalues) == 1 and isinstance(s.lvalues[0], NameExpr) and
s.lvalues[0].name == '__all__' and s.lvalues[0].kind == GDEF and
Expand Down Expand Up @@ -2355,6 +2356,24 @@ def is_classvar(self, typ: Type) -> bool:
def fail_invalid_classvar(self, context: Context) -> None:
self.fail('ClassVar can only be used for assignments in class body', context)

def process_module_assignment(self, s: AssignmentStmt) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need a function that just unconditionally calls another function? Maybe one is enough?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, good point. This was to present a cleaner API (pass in only the AssignmentStmt), but given there's only one call site and will never be more, that's not very useful. Consolidated.

"""Check if s assigns a module an alias name; if yes, update symbol table."""
# TODO support more complex forms of module alias assignment
# (e.g. `x, y = (mod1, mod2)`) and aliases not in global scope
if (
len(s.lvalues) != 1
or not isinstance(s.lvalues[0], NameExpr)
or not isinstance(s.rvalue, NameExpr)
or not self.is_module_scope()
):
return
rnode = self.lookup(s.rvalue.name, s)
if rnode and rnode.kind == MODULE_REF:
lnode = self.lookup(s.lvalues[0].name, s)
if lnode:
lnode.kind = MODULE_REF
lnode.node = rnode.node

def process_enum_call(self, s: AssignmentStmt) -> None:
"""Check if s defines an Enum; if yes, store the definition in symbol table."""
if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr):
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -1416,3 +1416,13 @@ reveal_type(f()) # E: Revealed type is 'types.ModuleType'
reveal_type(types) # E: Revealed type is 'types.ModuleType'

[builtins fixtures/module.pyi]

[case testModuleAssignment]
import m
m2 = m
reveal_type(m2.a) # E: Revealed type is 'builtins.str'

[file m.py]
a = 'foo'

[builtins fixtures/module.pyi]