Repository: python/mypy

Issue:
Module alias as instance attribute does not work
The error generated by the following program is unexpected:

```py
import os

class C:
    def __init__(self) -> None:
        self.os = os
        self.os.chdir('/')  # Error: Module has no attribute "chdir"
        os.chdir('/')  # Ok

C()  # No runtime error
```



Discussion:
Looks like just an oversight in #3435, this
```python
if not isinstance(lval, NameExpr):
    continue
```
could be probably just replaced with
```python
if not isinstance(lval, RefExpr):
    continue
```
plus corresponding minor changes.

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -3120,21 +3120,30 @@ 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)
+
+                    # 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
 


Test patch:
diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test
--- a/test-data/unit/check-basic.test
+++ b/test-data/unit/check-basic.test
@@ -474,3 +474,29 @@ from typing import Any
 def f() -> object:
     x: Any = 1
     return x
+
+[case testImportModuleAsClassMember]
+import test
+
+class A:
+    def __init__(self) -> None:
+        self.test = test
+
+    def __call__(self) -> None:
+      self.test.foo("Message")
+
+[file test.py]
+def foo(s: str) -> None: ...
+
+[case testLocalImportModuleAsClassMember]
+class A:
+    def __init__(self) -> None:
+        import test
+
+        self.test = test
+
+    def __call__(self) -> None:
+      self.test.foo("Message")
+
+[file test.py]
+def foo(s: str) -> None: ...