diff --git a/mypy/checker.py b/mypy/checker.py index e4c79a4c1c9f..b90221a0a5a5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3486,7 +3486,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> None: if s.expr: self.type_check_raise(s.expr, s) if s.from_expr: - self.type_check_raise(s.from_expr, s, optional=True) + self.type_check_raise(s.from_expr, s, True) self.binder.unreachable() def type_check_raise(self, e: Expression, s: RaiseStmt, @@ -3495,88 +3495,24 @@ def type_check_raise(self, e: Expression, s: RaiseStmt, if isinstance(typ, DeletedType): self.msg.deleted_as_rvalue(typ, e) return - - if self.options.python_version[0] == 2: - # Since `raise` has very different rule on python2, we use a different helper. - # https://github.com/python/mypy/pull/11289 - self._type_check_raise_python2(e, s, typ) - return - - # Python3 case: exc_type = self.named_type('builtins.BaseException') - expected_type_items = [exc_type, TypeType(exc_type)] + expected_type = UnionType([exc_type, TypeType(exc_type)]) if optional: - # This is used for `x` part in a case like `raise e from x`, - # where we allow `raise e from None`. - expected_type_items.append(NoneType()) - - self.check_subtype( - typ, UnionType.make_union(expected_type_items), s, - message_registry.INVALID_EXCEPTION, - ) + expected_type.items.append(NoneType()) + if self.options.python_version[0] == 2: + # allow `raise type, value, traceback` + # https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement + # TODO: Also check tuple item types. + any_type = AnyType(TypeOfAny.implementation_artifact) + tuple_type = self.named_type('builtins.tuple') + expected_type.items.append(TupleType([any_type, any_type], tuple_type)) + expected_type.items.append(TupleType([any_type, any_type, any_type], tuple_type)) + self.check_subtype(typ, expected_type, s, message_registry.INVALID_EXCEPTION) if isinstance(typ, FunctionLike): # https://github.com/python/mypy/issues/11089 self.expr_checker.check_call(typ, [], [], e) - def _type_check_raise_python2(self, e: Expression, s: RaiseStmt, typ: ProperType) -> None: - # Python2 has two possible major cases: - # 1. `raise expr`, where `expr` is some expression, it can be: - # - Exception typ - # - Exception instance - # - Old style class (not supported) - # - Tuple, where 0th item is exception type or instance - # 2. `raise exc, msg, traceback`, where: - # - `exc` is exception type (not instance!) - # - `traceback` is `types.TracebackType | None` - # Important note: `raise exc, msg` is not the same as `raise (exc, msg)` - # We call `raise exc, msg, traceback` - legacy mode. - exc_type = self.named_type('builtins.BaseException') - - if (not s.legacy_mode and (isinstance(typ, TupleType) and typ.items - or (isinstance(typ, Instance) and typ.args - and typ.type.fullname == 'builtins.tuple'))): - # `raise (exc, ...)` case: - item = typ.items[0] if isinstance(typ, TupleType) else typ.args[0] - self.check_subtype( - item, UnionType([exc_type, TypeType(exc_type)]), s, - 'When raising a tuple, first element must by derived from BaseException', - ) - return - elif s.legacy_mode: - # `raise Exception, msg` case - # `raise Exception, msg, traceback` case - # https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement - assert isinstance(typ, TupleType) # Is set in fastparse2.py - self.check_subtype( - typ.items[0], TypeType(exc_type), s, - 'First argument must be BaseException subtype', - ) - - # Typecheck `traceback` part: - if len(typ.items) == 3: - # Now, we typecheck `traceback` argument if it is present. - # We do this after the main check for better error message - # and better ordering: first about `BaseException` subtype, - # then about `traceback` type. - traceback_type = UnionType.make_union([ - self.named_type('types.TracebackType'), - NoneType(), - ]) - self.check_subtype( - typ.items[2], traceback_type, s, - 'Third argument to raise must have "{}" type'.format(traceback_type), - ) - else: - expected_type_items = [ - # `raise Exception` and `raise Exception()` cases: - exc_type, TypeType(exc_type), - ] - self.check_subtype( - typ, UnionType.make_union(expected_type_items), - s, message_registry.INVALID_EXCEPTION, - ) - def visit_try_stmt(self, s: TryStmt) -> None: """Type check a try statement.""" # Our enclosing frame will get the result if the try/except falls through. diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index 33f34082cf83..2d288bf158e5 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -664,23 +664,19 @@ def visit_With(self, n: ast27.With) -> WithStmt: typ) return self.set_line(stmt, n) - # 'raise' [test [',' test [',' test]]] def visit_Raise(self, n: ast27.Raise) -> RaiseStmt: - legacy_mode = False if n.type is None: e = None else: if n.inst is None: e = self.visit(n.type) else: - legacy_mode = True if n.tback is None: e = TupleExpr([self.visit(n.type), self.visit(n.inst)]) else: e = TupleExpr([self.visit(n.type), self.visit(n.inst), self.visit(n.tback)]) stmt = RaiseStmt(e, None) - stmt.legacy_mode = legacy_mode return self.set_line(stmt, n) # TryExcept(stmt* body, excepthandler* handlers, stmt* orelse) diff --git a/mypy/nodes.py b/mypy/nodes.py index 98c3582bb5c2..156d756030ae 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1294,19 +1294,16 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class RaiseStmt(Statement): - __slots__ = ('expr', 'from_expr', 'legacy_mode') + __slots__ = ('expr', 'from_expr') # Plain 'raise' is a valid statement. expr: Optional[Expression] from_expr: Optional[Expression] - # Is set when python2 has `raise exc, msg, traceback`. - legacy_mode: bool def __init__(self, expr: Optional[Expression], from_expr: Optional[Expression]) -> None: super().__init__() self.expr = expr self.from_expr = from_expr - self.legacy_mode = False def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_raise_stmt(self) diff --git a/test-data/unit/check-python2.test b/test-data/unit/check-python2.test index 30213ba6a26c..d658fe013401 100644 --- a/test-data/unit/check-python2.test +++ b/test-data/unit/check-python2.test @@ -68,73 +68,18 @@ A.f(1) A.f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" [builtins_py2 fixtures/staticmethod.pyi] +[case testRaiseTuple] +import typing +raise BaseException, "a" +raise BaseException, "a", None +[builtins_py2 fixtures/exception.pyi] + [case testRaiseTupleTypeFail] import typing x = None # type: typing.Type[typing.Tuple[typing.Any, typing.Any, typing.Any]] raise x # E: Exception must be derived from BaseException [builtins_py2 fixtures/exception.pyi] -[case testRaiseTupleOfThreeOnPython2] -from types import TracebackType -from typing import Optional, Tuple, Type - -e = None # type: Optional[TracebackType] - -raise BaseException # ok -raise BaseException(1) # ok -raise (BaseException,) # ok -raise (BaseException(1),) # ok -raise BaseException, 1 # ok -raise BaseException, 1, e # ok -raise BaseException, 1, None # ok - -raise Exception # ok -raise Exception(1) # ok -raise (Exception,) # ok -raise (Exception(1),) # ok -raise Exception, 1 # ok -raise Exception, 1, e # ok -raise Exception, 1, None # ok - -raise int, 1 # E: First argument must be BaseException subtype -raise Exception(1), 1 # E: First argument must be BaseException subtype -raise Exception(1), 1, None # E: First argument must be BaseException subtype -raise Exception, 1, 1 # E: Third argument to raise must have "Union[types.TracebackType, None]" type -raise int, 1, 1 # E: First argument must be BaseException subtype \ - # E: Third argument to raise must have "Union[types.TracebackType, None]" type - -t1 = (BaseException,) -t2 = (Exception(1), 2, 3, 4) # type: Tuple[Exception, int, int, int] -t3 = (Exception,) # type: Tuple[Type[Exception], ...] -t4 = (Exception(1),) # type: Tuple[Exception, ...] - -raise t1 # ok -raise t2 # ok -raise t3 # ok -raise t4 # ok - -raise t1, 1, None # E: First argument must be BaseException subtype -raise t2, 1 # E: First argument must be BaseException subtype -raise t3, 1, e # E: First argument must be BaseException subtype -raise t4, 1, 1 # E: First argument must be BaseException subtype \ - # E: Third argument to raise must have "Union[types.TracebackType, None]" type - -w1 = () -w2 = (1, Exception) -w3 = (1,) # type: Tuple[int, ...] - -raise w1 # E: Exception must be derived from BaseException -raise w2 # E: When raising a tuple, first element must by derived from BaseException -raise w3 # E: When raising a tuple, first element must by derived from BaseException - -try: - pass -except Exception: - raise # ok -[builtins_py2 fixtures/exception.pyi] -[file types.pyi] -class TracebackType: pass - [case testTryExceptWithTuple] try: None