Repository: python/mypy

Issue:
"Exception type must be derived from BaseException" for standard exceptions
I don't really understand what's going on here but this seems to be a fairly minimal reproducer:

```
from typing import Any

try:
    pass
except (AttributeError, KeyError):
    pass

def test(payload, keys, default=''):
    # type: (Any, Any, str) -> Any
    try:
        for key in keys:
            payload = payload[key]
    except (AttributeError, KeyError, TypeError):
        return default
    return payload
```



Discussion:
In case this helps debug, I also had `error: "KeyError" has no attribute "message"` errors thrown against use of KeyError latre in the original file.

FWIW I can repro this in PY2 only. There's some spooky action at a distance going on...

It's a parsing bug, where `except (foo, bar):` is treated like `except foo, bar:` which means `except foo as bar:` in Python 2.  A simpler working example is the following:

```
try:
    pass
except (AttributeError, x):
    pass
x = 2
```

gives

```
/home/ecprice/moo.py:6: error: Incompatible types in assignment (expression has type "int", variable has type "AttributeError")
```

OK, I'll see how easy this is to fix. It's pretty embarrassing. :-)

Also, the parser accept `except E, e:` even in Python 3 mode.


Resolving patch:
diff --git a/mypy/parse.py b/mypy/parse.py
--- a/mypy/parse.py
+++ b/mypy/parse.py
@@ -1132,21 +1132,15 @@ def parse_try_stmt(self) -> Node:
             if not isinstance(self.current(), Colon):
                 try:
                     t = self.current()
-                    types.append(self.parse_expression().set_line(t))
+                    types.append(self.parse_expression(precedence[',']).set_line(t))
                     if self.current_str() == 'as':
                         self.expect('as')
                         vars.append(self.parse_name_expr())
+                    elif self.pyversion[0] == 2 and self.current_str() == ',':
+                        self.expect(',')
+                        vars.append(self.parse_name_expr())
                     else:
-                        if (self.pyversion[0] == 2 and
-                                isinstance(types[-1], TupleExpr) and
-                                len(cast(TupleExpr, types[-1]).items) == 2 and
-                                isinstance(cast(TupleExpr, types[-1]).items[1], NameExpr)):
-                            # Handle "except T, e:".
-                            tuple_expr = cast(TupleExpr, types[-1])
-                            vars.append(cast(NameExpr, tuple_expr.items[1]))
-                            types[-1] = tuple_expr.items[0]
-                        else:
-                            vars.append(None)
+                        vars.append(None)
                 except ParseError:
                     is_error = True
             else:


Test patch:
diff --git a/mypy/test/data/parse-errors.test b/mypy/test/data/parse-errors.test
--- a/mypy/test/data/parse-errors.test
+++ b/mypy/test/data/parse-errors.test
@@ -477,3 +477,13 @@ file:1: error: Unrecognized character `
 foo = ‘bar’
 [out]
 file:1: error: Unrecognized character
+
+[case testExceptCommaInPython3]
+try:
+    pass
+except KeyError, IndexError:
+    pass
+[out]
+file:3: error: Parse error before ,
+file:3: error: Parse error before :
+file:4: error: Inconsistent indentation
diff --git a/mypy/test/data/parse-python2.test b/mypy/test/data/parse-python2.test
--- a/mypy/test/data/parse-python2.test
+++ b/mypy/test/data/parse-python2.test
@@ -142,6 +142,24 @@ MypyFile:1(
       ExpressionStmt:4(
         NameExpr(y)))))
 
+[case testTryExceptWithNestedComma]
+try:
+    x
+except (KeyError, IndexError):
+    y
+[out]
+MypyFile:1(
+  TryStmt:1(
+    Block:1(
+      ExpressionStmt:2(
+        NameExpr(x)))
+    TupleExpr:3(
+      NameExpr(KeyError)
+      NameExpr(IndexError))
+    Block:3(
+      ExpressionStmt:4(
+        NameExpr(y)))))
+
 [case testExecStatement]
 exec a
 [out]