Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Now typechecks traceback in raise e, msg, traceback on py2" #11743

Merged
merged 1 commit into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 12 additions & 76 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3492,7 +3492,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,
Expand All @@ -3501,88 +3501,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.
Expand Down
4 changes: 0 additions & 4 deletions mypy/fastparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 1 addition & 4 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 6 additions & 61 deletions test-data/unit/check-python2.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down