Repository: python/mypy

Issue:
sys.version_info check fails with --fast-parser
`mypy --fast-parser -c "import urllib.request"` outputs:

```
<string>:1: note: In module imported here:
.../lib/mypy/typeshed/stdlib/3/urllib/request.pyi: note: In class "Request":
.../lib/mypy/typeshed/stdlib/3/urllib/request.pyi:36: error: Callable[[Request], str] has no attribute "setter"
```

I looked at the relevant code in typeshed/stdlib/3/urllib/request.pyi:

``` py
class Request:
    if sys.version_info >= (3, 4):
        @property
        def full_url(self) -> str: ...
        @full_url.setter
        def full_url(self, value: str) -> None: ...
        @full_url.deleter
        def full_url(self) -> None: ...
    else:
        full_url = ...  # type: ignore # type: str
```

I use python 3.4. I removed the version check like this:

``` py
class Request:
    @property
    def full_url(self) -> str: ...
    @full_url.setter
    def full_url(self, value: str) -> None: ...
    @full_url.deleter
    def full_url(self) -> None: ...
```

Then there was no error message.

It looks like mypy's fast parser is having trouble with the version check.



Discussion:
Because of this bug, `six.moves` cannot be used with `--fast-parser` in python 3 mode.

Thanks for the bug report!  I should have a fix for this shortly.

This isn't actually to do with the fast parser's understanding of version checks.  What's actually going on is this: mypy's current parser combines repeated decorated function definitions with the same name into an `OverloadedFunctionDef` object, without checking if they're actually overloads.  The (later) code that understands properties assumes this is the case.  The faster parser only does this overload fixup at the top level of class definitions, so it's not happening when you wrap the property in an if statement.

Incidentally, this means that properties won't work properly if the lines aren't contiguous.


Resolving patch:
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -176,7 +176,7 @@ def from_comp_operator(self, op: ast35.cmpop) -> str:
     def as_block(self, stmts: List[ast35.stmt], lineno: int) -> Block:
         b = None
         if stmts:
-            b = Block(self.visit_list(stmts))
+            b = Block(self.fix_function_overloads(self.visit_list(stmts)))
             b.set_line(lineno)
         return b
 
@@ -353,7 +353,7 @@ def visit_ClassDef(self, n: ast35.ClassDef) -> Node:
             metaclass = self.stringify_name(metaclass_arg.value)
 
         cdef = ClassDef(n.name,
-                        Block(self.fix_function_overloads(self.visit_list(n.body))),
+                        self.as_block(n.body, n.lineno),
                         None,
                         self.visit_list(n.bases),
                         metaclass=metaclass)


Test patch:
diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test
--- a/test-data/unit/check-fastparse.test
+++ b/test-data/unit/check-fastparse.test
@@ -23,3 +23,22 @@ def f():  # E: syntax error in type comment
 def f():  # E: invalid type comment
   # type: (a + b) -> None
   pass
+
+[case testFastParseProperty]
+# flags: fast-parser
+class C:
+  @property
+  def x(self) -> str: pass
+  @x.setter
+  def x(self, value: str) -> None: pass
+[builtins fixtures/property.py]
+
+[case testFastParseConditionalProperty]
+# flags: fast-parser
+class C:
+  if 1:
+    @property
+    def x(self) -> str: pass
+    @x.setter
+    def x(self, value: str) -> None: pass
+[builtins fixtures/property.py]