Repository: python/mypy

Issue:
mypy --fast-parser fails with .encode and .decode
```
mypy --py2 --fast-parser -c 's = u"abc"; s = s.encode("utf-8").decode("utf-8");'
```

gives the error

```
<string>:1: error: Incompatible types in assignment
(expression has type "unicode", variable has type "str")
```

I get this error only when both `--py2` and `--fast-parser` are specified (i.e no errors are thrown in python 3 mode (both with and without --fast-parser) and no errors are thrown in python 2 mode without fast-parser).



Discussion:

Resolving patch:
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -64,6 +64,7 @@ def parse(source: Union[str, bytes], fnam: str = None, errors: Errors = None,
             raise
     else:
         tree = ASTConverter(pyversion=pyversion,
+                            is_stub=is_stub_file,
                             custom_typing_module=custom_typing_module,
                             ).visit(ast)
         tree.path = fnam
@@ -100,11 +101,15 @@ def find(f: Callable[[T], bool], seq: Sequence[T]) -> T:
 
 
 class ASTConverter(ast35.NodeTransformer):
-    def __init__(self, pyversion: Tuple[int, int], custom_typing_module: str = None) -> None:
+    def __init__(self,
+                 pyversion: Tuple[int, int],
+                 is_stub: bool,
+                 custom_typing_module: str = None) -> None:
         self.class_nesting = 0
         self.imports = []  # type: List[ImportBase]
 
         self.pyversion = pyversion
+        self.is_stub = is_stub
         self.custom_typing_module = custom_typing_module
 
     def generic_visit(self, node: ast35.AST) -> None:
@@ -644,16 +649,31 @@ def visit_Num(self, n: ast35.Num) -> Node:
 
         raise RuntimeError('num not implemented for ' + str(type(n.n)))
 
-    # Str(string s) -- need to specify raw, unicode, etc?
+    # Str(string s)
     @with_line
     def visit_Str(self, n: ast35.Str) -> Node:
-        return StrExpr(n.s)
+        if self.pyversion[0] >= 3 or self.is_stub:
+            # Hack: assume all string literals in Python 2 stubs are normal
+            # strs (i.e. not unicode).  All stubs are parsed with the Python 3
+            # parser, which causes unprefixed string literals to be interpreted
+            # as unicode instead of bytes.  This hack is generally okay,
+            # because mypy considers str literals to be compatible with
+            # unicode.
+            return StrExpr(n.s)
+        else:
+            return UnicodeExpr(n.s)
 
     # Bytes(bytes s)
     @with_line
     def visit_Bytes(self, n: ast35.Bytes) -> Node:
-        # TODO: this is kind of hacky
-        return BytesExpr(str(n.s)[2:-1])
+        # The following line is a bit hacky, but is the best way to maintain
+        # compatibility with how mypy currently parses the contents of bytes literals.
+        contents = str(n.s)[2:-1]
+
+        if self.pyversion[0] >= 3:
+            return BytesExpr(contents)
+        else:
+            return StrExpr(contents)
 
     # NameConstant(singleton value)
     def visit_NameConstant(self, n: ast35.NameConstant) -> Node:
diff --git a/mypy/nodes.py b/mypy/nodes.py
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -961,6 +961,17 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
         return visitor.visit_int_expr(self)
 
 
+# How mypy uses StrExpr, BytesExpr, and UnicodeExpr:
+# In Python 2 mode:
+# b'x', 'x' -> StrExpr
+# u'x' -> UnicodeExpr
+# BytesExpr is unused
+#
+# In Python 3 mode:
+# b'x' -> BytesExpr
+# 'x', u'x' -> StrExpr
+# UnicodeExpr is unused
+
 class StrExpr(Node):
     """String literal"""
 


Test patch: