Repository: python/mypy

Issue:
Class level __getattr__ causes error: __getattr__ is not valid at the module level outside a stub file
After updating to mypy 0.530, I am getting an error on a `__getattr__()` method defined on a class:

```
$ mypy --version
mypy 0.530
$ python3 --version
Python 3.6.3
[~/cretonne/lib/cretonne/meta]
$ cat mypy.ini 
[mypy]
disallow_untyped_defs = True
warn_unused_ignores = True
warn_return_any = True
[~/cretonne/lib/cretonne/meta]
$ mypy cdsl/formats.py
cdsl/formats.py:143: error: __getattr__ is not valid at the module level outside a stub file
```

I don't have any module level `__getattr__()` functions.

This didn't reproduce with a smaller test case, but you can check out the [Cretonne repo](https://github.com/stoklund/cretonne) to reproduce.


Discussion:
Confirmed on master, I am stepping through now. I appears the issue only occurs in the second pass of the checker (the class is added to the scope stack in the first pass, but not in the second).
It appears that when we defer functions to the second pass, we [try to put the class context information](https://github.com/python/mypy/blob/master/mypy/checker.py#L270). However, the function `node` is not removed on L261, so the class is not resolved, and that returns `None`. This later causes issues when we try to append the class context, but cannot, so the `__getattr__` method appears to be in the file context (this occurs in `check_second_pass`). The `__getattr__` checking for module levels if the first thing that relied on the size of the scope being 1 (module level), so it exposed this issue.
Do you have a smaller repro yet?

@ethanhs Your diagnosis seems right. Here is a simple repro:
```python
class C:
    def __getattr__(self, attr: str) -> int:
        x: F
        return x.f

class F:
    def __init__(self, f: int) -> None:
        self.f = f
```
A related bug:
```python
from typing import TypeVar

T = TypeVar('T', bound=C)
class C:
    def m(self: T) -> T:
        class Inner:
            x: F
            f = x.f
        return self

class F:
    def __init__(self, f: int) -> None:
        self.f = f
```
results in a funny error:
```
main:5: error: The erased type of self '__main__.C' is not a supertype of its class '__main__.Inner@5'
```

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -259,16 +259,15 @@ def check_top_level(self, node: MypyFile) -> None:
 
     def handle_cannot_determine_type(self, name: str, context: Context) -> None:
         node = self.scope.top_function()
-        if (self.pass_num < LAST_PASS and node is not None
-                and isinstance(node, (FuncDef, LambdaExpr))):
+        if self.pass_num < LAST_PASS and isinstance(node, (FuncDef, LambdaExpr)):
             # Don't report an error yet. Just defer.
             if self.errors.type_name:
                 type_name = self.errors.type_name[-1]
             else:
                 type_name = None
             # Shouldn't we freeze the entire scope?
-            active_class = self.scope.active_class()
-            self.deferred_nodes.append(DeferredNode(node, type_name, active_class))
+            enclosing_class = self.scope.enclosing_class()
+            self.deferred_nodes.append(DeferredNode(node, type_name, enclosing_class))
             # Set a marker so that we won't infer additional types in this
             # function. Any inferred types could be bogus, because there's at
             # least one type that we don't know.
@@ -3323,6 +3322,16 @@ def active_class(self) -> Optional[TypeInfo]:
             return self.stack[-1]
         return None
 
+    def enclosing_class(self) -> Optional[TypeInfo]:
+        top = self.top_function()
+        assert top, "This method must be called from inside a function"
+        index = self.stack.index(top)
+        assert index, "Scope stack must always start with a module"
+        enclosing = self.stack[index - 1]
+        if isinstance(enclosing, TypeInfo):
+            return enclosing
+        return None
+
     def active_self_type(self) -> Optional[Union[Instance, TupleType]]:
         info = self.active_class()
         if info:


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -3996,6 +3996,41 @@ class F(six.with_metaclass(t.M)): pass
 @six.add_metaclass(t.M)
 class G: pass
 
+[case testCorrectEnclosingClassPushedInDeferred]
+class C:
+    def __getattr__(self, attr: str) -> int:
+        x: F
+        return x.f
+
+class F:
+    def __init__(self, f: int) -> None:
+        self.f = f
+[out]
+
+[case testCorrectEnclosingClassPushedInDeferred2]
+from typing import TypeVar
+T = TypeVar('T', bound=C)
+class C:
+    def m(self: T) -> T:
+        class Inner:
+            x: F
+            f = x.f
+        return self
+
+class F:
+    def __init__(self, f: int) -> None:
+        self.f = f
+[out]
+
+[case testCorrectEnclosingClassPushedInDeferred3]
+class A:
+    def f(self) -> None:
+        def g(x: int) -> int:
+            return y
+
+y = int()
+[out]
+
 [case testMetaclassMemberAccessViaType]
 from typing import Type
 class M(type):