Repository: python/mypy

Issue:
Modules imported to top level of class cause error.
When you have an import statement inside a class, mypy throws an error. An example that causes this error is:

```
class _coconut:
    import collections, functools, imp, itertools, operator, types, copy
```

The error thrown is:

```
Traceback (most recent call last):
  File "C:\Users\fred\Anaconda3\Scripts\mypy", line 6, in <module>
    main(__file__)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\main.py", line 54, in main
    res = type_check_only(sources, bin_dir, options)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\main.py", line 102, in type_check_only
    python_path=options.python_path)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\build.py", line 211, in build
    dispatch(sources, manager)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\build.py", line 1336, in dispatch
    process_graph(graph, manager)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\build.py", line 1467, in process_graph
    process_stale_scc(graph, scc)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\build.py", line 1497, in process_stale_scc
    graph[id].type_check()
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\build.py", line 1316, in type_check
    manager.type_checker.visit_file(self.tree, self.xpath)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 426, in visit_file
    self.accept(d)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 476, in accept
    typ = node.accept(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\nodes.py", line 531, in accept
    return visitor.visit_decorator(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 1956, in visit_decorator
    dec = self.accept(d)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 476, in accept
    typ = node.accept(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\nodes.py", line 1185, in accept
    return visitor.visit_call_expr(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 2009, in visit_call_expr
    return self.expr_checker.visit_call_expr(e)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 135, in visit_call_expr
    callee_type = self.accept(e.callee)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 1531, in accept
    return self.chk.accept(node, context)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 476, in accept
    typ = node.accept(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\nodes.py", line 1141, in accept
    return visitor.visit_member_expr(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 2053, in visit_member_expr
    return self.expr_checker.visit_member_expr(e)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 836, in visit_member_expr
    result = self.analyze_ordinary_member_access(e, False)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 847, in analyze_ordinary_member_access
    return analyze_member_access(e.name, self.accept(e.expr), e,
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 1531, in accept
    return self.chk.accept(node, context)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 476, in accept
    typ = node.accept(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\nodes.py", line 1141, in accept
    return visitor.visit_member_expr(self)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checker.py", line 2053, in visit_member_expr
    return self.expr_checker.visit_member_expr(e)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 836, in visit_member_expr
    result = self.analyze_ordinary_member_access(e, False)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkexpr.py", line 849, in analyze_ordinary_member_access
    self.named_type, self.not_ready_callback, self.msg)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkmember.py", line 96, in analyze_member_access
    builtin_type, not_ready_callback, msg)
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\checkmember.py", line 319, in analyze_class_attribute_access
    return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))
  File "C:\Users\fred\Anaconda3\lib\site-packages\mypy\nodes.py", line 2129, in function_type
    if func.type:
AttributeError: 'MypyFile' object has no attribute 'type'
```

Thanks in advance.



Discussion:
I can't repro this. What version of mypy were you using? (IIUC this is on Windows.)

Can you show the last few lines of the output, right below the AttributeError? It should tell you which file and line caused the crash, which would be helpful.

OK, so the error actually occurs when you try to access the imported module. I'm so sorry I didn't look further into this.
So for example the code:

``` python
class test:
    import random

print(test.random.random()) #This is where it internal errors
```

produces:

```
AttributeError: 'MypyFile' object has no attribute 'type'

*** INTERNAL ERROR ***

test.py:4: error: Internal error -- please report a bug at https://github.com/python/mypy/issues

NOTE: you can use "mypy --pdb ..." to drop into the debugger when this happens.
```

Edit: It's mypy 0.4.2

Thanks! Now I can repro it too. Simplest repro seems to be

```
class test: import random
test.random
```

Thanks so much!


Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -7,7 +7,7 @@
     Overloaded, TypeVarType, TypeTranslator, UnionType, PartialType,
     DeletedType, NoneTyp, TypeType
 )
-from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context
+from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile
 from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, OpExpr, ComparisonExpr
 from mypy.nodes import function_type, Decorator, OverloadedFuncDef
 from mypy.messages import MessageBuilder
@@ -349,6 +349,10 @@ def analyze_class_attribute_access(itype: Instance,
     if isinstance(node.node, TypeInfo):
         return type_object_type(node.node, builtin_type)
 
+    if isinstance(node.node, MypyFile):
+        # Reference to a module object.
+        return builtin_type('builtins.module')
+
     if is_decorated:
         # TODO: Return type of decorated function. This is quick hack to work around #998.
         return AnyType()
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2107,6 +2107,9 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
         base.accept(self)
         # Bind references to module attributes.
         if isinstance(base, RefExpr) and base.kind == MODULE_REF:
+            # This branch handles the case foo.bar where foo is a module.
+            # In this case base.node is the module's MypyFile and we look up
+            # bar in its namespace.  This must be done for all types of bar.
             file = cast(MypyFile, base.node)
             n = file.names.get(expr.name, None) if file is not None else None
             if n:
@@ -2128,6 +2131,21 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
                 if full_name in obsolete_name_mapping:
                     self.fail("Module has no attribute %r (it's now called %r)" % (
                         expr.name, obsolete_name_mapping[full_name]), expr)
+        elif isinstance(base, RefExpr) and isinstance(base.node, TypeInfo):
+            # This branch handles the case C.bar where C is a class
+            # and bar is a module resulting from `import bar` inside
+            # class C.  Here base.node is a TypeInfo, and again we
+            # look up the name in its namespace.  This is done only
+            # when bar is a module; other things (e.g. methods)
+            # are handled by other code in checkmember.
+            n = base.node.names.get(expr.name)
+            if n is not None and n.kind == MODULE_REF:
+                n = self.normalize_type_alias(n, expr)
+                if not n:
+                    return
+                expr.kind = n.kind
+                expr.fullname = n.fullname
+                expr.node = n.node
 
     def visit_op_expr(self, expr: OpExpr) -> None:
         expr.left.accept(self)


Test patch:
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -974,6 +974,15 @@ from a import x
 x = 0
 [out]
 
+[case testImportInClass]
+class C:
+    import foo
+reveal_type(C.foo.bar)  # E: Revealed type is 'builtins.int'
+[file foo.py]
+bar = 0
+[builtins fixtures/module.pyi]
+[out]
+
 
 -- Test stability under import cycles
 -- ----------------------------------