From dc78471a035473d9134ccdbd54cb4be075b42876 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 8 Jan 2020 08:02:18 +0100 Subject: [PATCH 1/4] Fix module alias as instance attribute --- mypy/semanal.py | 4 +++- test-data/unit/fine-grained.test | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 42770e1a0389..3863da8154a6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3120,12 +3120,14 @@ def process_module_assignment(self, lvals: List[Lvalue], rval: Expression, rnode = self.lookup_type_node(rval) if rnode and isinstance(rnode.node, MypyFile): for lval in lvals: - if not isinstance(lval, NameExpr): + if not isinstance(lval, RefExpr): continue # respect explicitly annotated type if (isinstance(lval.node, Var) and lval.node.type is not None): continue lnode = self.current_symbol_table().get(lval.name) + if lnode is None and self.type is not None: + lnode = self.type.names.get(lval.name) if lnode: if isinstance(lnode.node, MypyFile) and lnode.node is not rnode.node: self.fail( diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 4e2309b3c5cf..3cb693ce86a4 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -1661,6 +1661,30 @@ def f() -> None: pass [out] == +[case testImportModuleAsClassMember] +import warnings + +class A: + def __init__(self) -> None: + self.warnings = warnings + + def __call__(self) -> None: + self.warnings.warn("Message") +[out] +== + +[case testLocalImportModuleAsClassMember] +class A: + def __init__(self) -> None: + import warnings + + self.warnings = warnings + + def __call__(self) -> None: + self.warnings.warn("Message") +[out] +== + [case testTypeAliasRefresh] from typing import Callable from a import f From ac9898d96f95cc16dffc5e561de6465d79895577 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 15 Jan 2020 14:43:38 -0800 Subject: [PATCH 2/4] move to testcheck --- test-data/unit/check-basic.test | 20 ++++++++++++++++++++ test-data/unit/fine-grained.test | 24 ------------------------ 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 313f0446871c..9fe36e859039 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -474,3 +474,23 @@ from typing import Any def f() -> object: x: Any = 1 return x + +[case testImportModuleAsClassMember] +import warnings + +class A: + def __init__(self) -> None: + self.warnings = warnings + + def __call__(self) -> None: + self.warnings.warn("Message") + +[case testLocalImportModuleAsClassMember] +class A: + def __init__(self) -> None: + import warnings + + self.warnings = warnings + + def __call__(self) -> None: + self.warnings.warn("Message") diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 3cb693ce86a4..4e2309b3c5cf 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -1661,30 +1661,6 @@ def f() -> None: pass [out] == -[case testImportModuleAsClassMember] -import warnings - -class A: - def __init__(self) -> None: - self.warnings = warnings - - def __call__(self) -> None: - self.warnings.warn("Message") -[out] -== - -[case testLocalImportModuleAsClassMember] -class A: - def __init__(self) -> None: - import warnings - - self.warnings = warnings - - def __call__(self) -> None: - self.warnings.warn("Message") -[out] -== - [case testTypeAliasRefresh] from typing import Callable from a import f From 15bccc3be2979bf0996626fa944a9fad763e2d51 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 15 Jan 2020 15:17:11 -0800 Subject: [PATCH 3/4] fix it --- mypy/semanal.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3863da8154a6..b2769373f324 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3125,18 +3125,25 @@ def process_module_assignment(self, lvals: List[Lvalue], rval: Expression, # respect explicitly annotated type if (isinstance(lval.node, Var) and lval.node.type is not None): continue - lnode = self.current_symbol_table().get(lval.name) - if lnode is None and self.type is not None: + + # We can handle these assignments to locals and to self + if isinstance(lval, NameExpr): + lnode = self.current_symbol_table().get(lval.name) + elif isinstance(lval, MemberExpr) and self.is_self_member_ref(lval): + assert self.type is not None lnode = self.type.names.get(lval.name) + else: + continue + if lnode: if isinstance(lnode.node, MypyFile) and lnode.node is not rnode.node: + assert isinstance(lval, (NameExpr, MemberExpr)) self.fail( "Cannot assign multiple modules to name '{}' " "without explicit 'types.ModuleType' annotation".format(lval.name), ctx) # never create module alias except on initial var definition elif lval.is_inferred_def: - lnode.kind = self.current_symbol_kind() assert rnode.node is not None lnode.node = rnode.node From 8a8a9d33fae8c910f868eaeb32e0c1153d53190c Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 21 Jan 2020 13:24:32 -0800 Subject: [PATCH 4/4] fix the tests --- test-data/unit/check-basic.test | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 9fe36e859039..4939c2d5be93 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -476,21 +476,27 @@ def f() -> object: return x [case testImportModuleAsClassMember] -import warnings +import test class A: def __init__(self) -> None: - self.warnings = warnings + self.test = test def __call__(self) -> None: - self.warnings.warn("Message") + self.test.foo("Message") + +[file test.py] +def foo(s: str) -> None: ... [case testLocalImportModuleAsClassMember] class A: def __init__(self) -> None: - import warnings + import test - self.warnings = warnings + self.test = test def __call__(self) -> None: - self.warnings.warn("Message") + self.test.foo("Message") + +[file test.py] +def foo(s: str) -> None: ...