mirror of https://github.com/python/cpython
bpo-45292: [PEP-654] add except* (GH-29581)
This commit is contained in:
parent
850aefc2c6
commit
d60457a667
|
@ -1167,6 +1167,37 @@ Control flow
|
|||
type_ignores=[])
|
||||
|
||||
|
||||
.. class:: TryStar(body, handlers, orelse, finalbody)
|
||||
|
||||
``try`` blocks which are followed by ``except*`` clauses. The attributes are the
|
||||
same as for :class:`Try` but the :class:`ExceptHandler` nodes in ``handlers``
|
||||
are interpreted as ``except*`` blocks rather then ``except``.
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> print(ast.dump(ast.parse("""
|
||||
... try:
|
||||
... ...
|
||||
... except* Exception:
|
||||
... ...
|
||||
... """), indent=4))
|
||||
Module(
|
||||
body=[
|
||||
TryStar(
|
||||
body=[
|
||||
Expr(
|
||||
value=Constant(value=Ellipsis))],
|
||||
handlers=[
|
||||
ExceptHandler(
|
||||
type=Name(id='Exception', ctx=Load()),
|
||||
body=[
|
||||
Expr(
|
||||
value=Constant(value=Ellipsis))])],
|
||||
orelse=[],
|
||||
finalbody=[])],
|
||||
type_ignores=[])
|
||||
|
||||
|
||||
.. class:: ExceptHandler(type, name, body)
|
||||
|
||||
A single ``except`` clause. ``type`` is the exception type it will match,
|
||||
|
|
|
@ -872,8 +872,10 @@ All of the following opcodes use their arguments.
|
|||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
|
||||
.. opcode:: JUMP_IF_NOT_EXC_MATCH (target)
|
||||
|
||||
Performs exception matching for ``except``.
|
||||
Tests whether the second value on the stack is an exception matching TOS,
|
||||
and jumps if it is not. Pops one value from the stack.
|
||||
|
||||
|
@ -883,6 +885,30 @@ All of the following opcodes use their arguments.
|
|||
This opcode no longer pops the active exception.
|
||||
|
||||
|
||||
.. opcode:: JUMP_IF_NOT_EG_MATCH (target)
|
||||
|
||||
Performs exception matching for ``except*``. Applies ``split(TOS)`` on
|
||||
the exception group representing TOS1. Jumps if no match is found.
|
||||
|
||||
Pops one item from the stack. If a match was found, pops the 3 items representing
|
||||
the exception and pushes the 3 items representing the non-matching part of
|
||||
the exception group, followed by the 3 items representing the matching part.
|
||||
In other words, in case of a match it pops 4 items and pushes 6.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
||||
.. opcode:: PREP_RERAISE_STAR
|
||||
|
||||
Combines the raised and reraised exceptions list from TOS, into an exception
|
||||
group to propagate from a try-except* block. Uses the original exception
|
||||
group from TOS1 to reconstruct the structure of reraised exceptions. Pops
|
||||
two items from the stack and pushes a triplet representing the exception to
|
||||
reraise or three ``None`` if there isn't one.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
||||
.. opcode:: JUMP_IF_TRUE_OR_POP (target)
|
||||
|
||||
If TOS is true, sets the bytecode counter to *target* and leaves TOS on the
|
||||
|
|
|
@ -65,6 +65,8 @@ Summary -- Release highlights
|
|||
|
||||
.. PEP-sized items next.
|
||||
|
||||
PEP-654: Exception Groups and ``except*``.
|
||||
(Contributed by Irit Katriel in :issue:`45292`.)
|
||||
|
||||
New Features
|
||||
============
|
||||
|
|
|
@ -403,6 +403,8 @@ try_stmt[stmt_ty]:
|
|||
| invalid_try_stmt
|
||||
| 'try' &&':' b=block f=finally_block { _PyAST_Try(b, NULL, NULL, f, EXTRA) }
|
||||
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_block+ el=[else_block] f=[finally_block] { _PyAST_Try(b, ex, el, f, EXTRA) }
|
||||
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_star_block+ el=[else_block] f=[finally_block] { _PyAST_TryStar(b, ex, el, f, EXTRA) }
|
||||
|
||||
|
||||
# Except statement
|
||||
# ----------------
|
||||
|
@ -413,6 +415,11 @@ except_block[excepthandler_ty]:
|
|||
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
|
||||
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
|
||||
| invalid_except_stmt
|
||||
except_star_block[excepthandler_ty]:
|
||||
| invalid_except_star_stmt_indent
|
||||
| 'except' '*' e=expression t=['as' z=NAME { z }] ':' b=block {
|
||||
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
|
||||
| invalid_except_stmt
|
||||
finally_block[asdl_stmt_seq*]:
|
||||
| invalid_finally_stmt
|
||||
| 'finally' &&':' a=block { a }
|
||||
|
@ -1192,11 +1199,14 @@ invalid_try_stmt:
|
|||
| a='try' ':' NEWLINE !INDENT {
|
||||
RAISE_INDENTATION_ERROR("expected an indented block after 'try' statement on line %d", a->lineno) }
|
||||
| 'try' ':' block !('except' | 'finally') { RAISE_SYNTAX_ERROR("expected 'except' or 'finally' block") }
|
||||
| 'try' ':' block* ((except_block+ except_star_block) | (except_star_block+ except_block)) block* {
|
||||
RAISE_SYNTAX_ERROR("cannot have both 'except' and 'except*' on the same 'try'") }
|
||||
invalid_except_stmt:
|
||||
| 'except' a=expression ',' expressions ['as' NAME ] ':' {
|
||||
| 'except' '*'? a=expression ',' expressions ['as' NAME ] ':' {
|
||||
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized") }
|
||||
| a='except' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
|
||||
| a='except' '*'? expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
|
||||
| a='except' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
|
||||
| a='except' '*' (NEWLINE | ':') { RAISE_SYNTAX_ERROR("expected one or more exception types") }
|
||||
invalid_finally_stmt:
|
||||
| a='finally' ':' NEWLINE !INDENT {
|
||||
RAISE_INDENTATION_ERROR("expected an indented block after 'finally' statement on line %d", a->lineno) }
|
||||
|
@ -1204,6 +1214,9 @@ invalid_except_stmt_indent:
|
|||
| a='except' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
||||
RAISE_INDENTATION_ERROR("expected an indented block after 'except' statement on line %d", a->lineno) }
|
||||
| a='except' ':' NEWLINE !INDENT { RAISE_SYNTAX_ERROR("expected an indented block after except statement on line %d", a->lineno) }
|
||||
invalid_except_star_stmt_indent:
|
||||
| a='except' '*' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
||||
RAISE_INDENTATION_ERROR("expected an indented block after 'except*' statement on line %d", a->lineno) }
|
||||
invalid_match_stmt:
|
||||
| "match" subject_expr !':' { CHECK_VERSION(void*, 10, "Pattern matching is", RAISE_SYNTAX_ERROR("expected ':'") ) }
|
||||
| a="match" subject=subject_expr ':' NEWLINE !INDENT {
|
||||
|
|
|
@ -179,9 +179,9 @@ enum _stmt_kind {FunctionDef_kind=1, AsyncFunctionDef_kind=2, ClassDef_kind=3,
|
|||
AugAssign_kind=7, AnnAssign_kind=8, For_kind=9,
|
||||
AsyncFor_kind=10, While_kind=11, If_kind=12, With_kind=13,
|
||||
AsyncWith_kind=14, Match_kind=15, Raise_kind=16, Try_kind=17,
|
||||
Assert_kind=18, Import_kind=19, ImportFrom_kind=20,
|
||||
Global_kind=21, Nonlocal_kind=22, Expr_kind=23, Pass_kind=24,
|
||||
Break_kind=25, Continue_kind=26};
|
||||
TryStar_kind=18, Assert_kind=19, Import_kind=20,
|
||||
ImportFrom_kind=21, Global_kind=22, Nonlocal_kind=23,
|
||||
Expr_kind=24, Pass_kind=25, Break_kind=26, Continue_kind=27};
|
||||
struct _stmt {
|
||||
enum _stmt_kind kind;
|
||||
union {
|
||||
|
@ -295,6 +295,13 @@ struct _stmt {
|
|||
asdl_stmt_seq *finalbody;
|
||||
} Try;
|
||||
|
||||
struct {
|
||||
asdl_stmt_seq *body;
|
||||
asdl_excepthandler_seq *handlers;
|
||||
asdl_stmt_seq *orelse;
|
||||
asdl_stmt_seq *finalbody;
|
||||
} TryStar;
|
||||
|
||||
struct {
|
||||
expr_ty test;
|
||||
expr_ty msg;
|
||||
|
@ -688,6 +695,10 @@ stmt_ty _PyAST_Try(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
|||
asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int
|
||||
lineno, int col_offset, int end_lineno, int end_col_offset,
|
||||
PyArena *arena);
|
||||
stmt_ty _PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
||||
asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int
|
||||
lineno, int col_offset, int end_lineno, int
|
||||
end_col_offset, PyArena *arena);
|
||||
stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset,
|
||||
int end_lineno, int end_col_offset, PyArena *arena);
|
||||
stmt_ty _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
|
||||
|
|
|
@ -132,6 +132,7 @@ struct ast_state {
|
|||
PyObject *Sub_singleton;
|
||||
PyObject *Sub_type;
|
||||
PyObject *Subscript_type;
|
||||
PyObject *TryStar_type;
|
||||
PyObject *Try_type;
|
||||
PyObject *Tuple_type;
|
||||
PyObject *TypeIgnore_type;
|
||||
|
|
|
@ -92,6 +92,14 @@ PyAPI_FUNC(PyObject *) _PyErr_FormatFromCauseTstate(
|
|||
const char *format,
|
||||
...);
|
||||
|
||||
PyAPI_FUNC(PyObject *) _PyExc_CreateExceptionGroup(
|
||||
const char *msg,
|
||||
PyObject *excs);
|
||||
|
||||
PyAPI_FUNC(PyObject *) _PyExc_ExceptionGroupProjection(
|
||||
PyObject *left,
|
||||
PyObject *right);
|
||||
|
||||
PyAPI_FUNC(int) _PyErr_CheckSignalsTstate(PyThreadState *tstate);
|
||||
|
||||
PyAPI_FUNC(void) _Py_DumpExtensionModules(int fd, PyInterpreterState *interp);
|
||||
|
|
|
@ -45,6 +45,7 @@ extern "C" {
|
|||
#define IMPORT_STAR 84
|
||||
#define SETUP_ANNOTATIONS 85
|
||||
#define YIELD_VALUE 86
|
||||
#define PREP_RERAISE_STAR 88
|
||||
#define POP_EXCEPT 89
|
||||
#define HAVE_ARGUMENT 90
|
||||
#define STORE_NAME 90
|
||||
|
@ -83,6 +84,7 @@ extern "C" {
|
|||
#define LOAD_FAST 124
|
||||
#define STORE_FAST 125
|
||||
#define DELETE_FAST 126
|
||||
#define JUMP_IF_NOT_EG_MATCH 127
|
||||
#define GEN_START 129
|
||||
#define RAISE_VARARGS 130
|
||||
#define CALL_FUNCTION 131
|
||||
|
@ -161,7 +163,7 @@ extern "C" {
|
|||
#define STORE_FAST__LOAD_FAST 80
|
||||
#define LOAD_FAST__LOAD_CONST 81
|
||||
#define LOAD_CONST__LOAD_FAST 87
|
||||
#define STORE_FAST__STORE_FAST 88
|
||||
#define STORE_FAST__STORE_FAST 123
|
||||
#define DO_TRACING 255
|
||||
#ifdef NEED_OPCODE_JUMP_TABLES
|
||||
static uint32_t _PyOpcode_RelativeJump[8] = {
|
||||
|
@ -178,7 +180,7 @@ static uint32_t _PyOpcode_Jump[8] = {
|
|||
0U,
|
||||
0U,
|
||||
536870912U,
|
||||
34586624U,
|
||||
2182070272U,
|
||||
0U,
|
||||
0U,
|
||||
0U,
|
||||
|
|
21
Lib/ast.py
21
Lib/ast.py
|
@ -683,6 +683,7 @@ class _Unparser(NodeVisitor):
|
|||
self._type_ignores = {}
|
||||
self._indent = 0
|
||||
self._avoid_backslashes = _avoid_backslashes
|
||||
self._in_try_star = False
|
||||
|
||||
def interleave(self, inter, f, seq):
|
||||
"""Call f on each item in seq, calling inter() in between."""
|
||||
|
@ -953,7 +954,7 @@ class _Unparser(NodeVisitor):
|
|||
self.write(" from ")
|
||||
self.traverse(node.cause)
|
||||
|
||||
def visit_Try(self, node):
|
||||
def do_visit_try(self, node):
|
||||
self.fill("try")
|
||||
with self.block():
|
||||
self.traverse(node.body)
|
||||
|
@ -968,8 +969,24 @@ class _Unparser(NodeVisitor):
|
|||
with self.block():
|
||||
self.traverse(node.finalbody)
|
||||
|
||||
def visit_Try(self, node):
|
||||
prev_in_try_star = self._in_try_star
|
||||
try:
|
||||
self._in_try_star = False
|
||||
self.do_visit_try(node)
|
||||
finally:
|
||||
self._in_try_star = prev_in_try_star
|
||||
|
||||
def visit_TryStar(self, node):
|
||||
prev_in_try_star = self._in_try_star
|
||||
try:
|
||||
self._in_try_star = True
|
||||
self.do_visit_try(node)
|
||||
finally:
|
||||
self._in_try_star = prev_in_try_star
|
||||
|
||||
def visit_ExceptHandler(self, node):
|
||||
self.fill("except")
|
||||
self.fill("except*" if self._in_try_star else "except")
|
||||
if node.type:
|
||||
self.write(" ")
|
||||
self.traverse(node.type)
|
||||
|
|
|
@ -371,6 +371,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Python 3.11a3 3464 (bpo-45636: Merge numeric BINARY_*/INPLACE_* into
|
||||
# BINARY_OP)
|
||||
# Python 3.11a3 3465 (Add COPY_FREE_VARS opcode)
|
||||
# Python 3.11a3 3466 (bpo-45292: PEP-654 except*)
|
||||
|
||||
#
|
||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||
|
@ -380,7 +381,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# in PC/launcher.c must also be updated.
|
||||
|
||||
MAGIC_NUMBER = (3465).to_bytes(2, 'little') + b'\r\n'
|
||||
MAGIC_NUMBER = (3466).to_bytes(2, 'little') + b'\r\n'
|
||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
|
|
@ -103,6 +103,7 @@ def_op('IMPORT_STAR', 84)
|
|||
def_op('SETUP_ANNOTATIONS', 85)
|
||||
def_op('YIELD_VALUE', 86)
|
||||
|
||||
def_op('PREP_RERAISE_STAR', 88)
|
||||
def_op('POP_EXCEPT', 89)
|
||||
|
||||
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
||||
|
@ -150,6 +151,8 @@ haslocal.append(125)
|
|||
def_op('DELETE_FAST', 126) # Local variable number
|
||||
haslocal.append(126)
|
||||
|
||||
jabs_op('JUMP_IF_NOT_EG_MATCH', 127)
|
||||
|
||||
def_op('GEN_START', 129) # Kind of generator/coroutine
|
||||
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
|
||||
def_op('CALL_FUNCTION', 131) # #args
|
||||
|
|
|
@ -86,6 +86,8 @@ exec_tests = [
|
|||
"try:\n pass\nexcept Exception:\n pass",
|
||||
# TryFinally
|
||||
"try:\n pass\nfinally:\n pass",
|
||||
# TryStarExcept
|
||||
"try:\n pass\nexcept* Exception:\n pass",
|
||||
# Assert
|
||||
"assert v",
|
||||
# Import
|
||||
|
@ -1310,6 +1312,26 @@ class ASTValidatorTests(unittest.TestCase):
|
|||
t = ast.Try([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(t, "must have Load context")
|
||||
|
||||
def test_try_star(self):
|
||||
p = ast.Pass()
|
||||
t = ast.TryStar([], [], [], [p])
|
||||
self.stmt(t, "empty body on TryStar")
|
||||
t = ast.TryStar([ast.Expr(ast.Name("x", ast.Store()))], [], [], [p])
|
||||
self.stmt(t, "must have Load context")
|
||||
t = ast.TryStar([p], [], [], [])
|
||||
self.stmt(t, "TryStar has neither except handlers nor finalbody")
|
||||
t = ast.TryStar([p], [], [p], [p])
|
||||
self.stmt(t, "TryStar has orelse but no except handlers")
|
||||
t = ast.TryStar([p], [ast.ExceptHandler(None, "x", [])], [], [])
|
||||
self.stmt(t, "empty body on ExceptHandler")
|
||||
e = [ast.ExceptHandler(ast.Name("x", ast.Store()), "y", [p])]
|
||||
self.stmt(ast.TryStar([p], e, [], []), "must have Load context")
|
||||
e = [ast.ExceptHandler(None, "x", [p])]
|
||||
t = ast.TryStar([p], e, [ast.Expr(ast.Name("x", ast.Store()))], [p])
|
||||
self.stmt(t, "must have Load context")
|
||||
t = ast.TryStar([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(t, "must have Load context")
|
||||
|
||||
def test_assert(self):
|
||||
self.stmt(ast.Assert(ast.Name("x", ast.Store()), None),
|
||||
"must have Load context")
|
||||
|
@ -2316,6 +2338,7 @@ exec_results = [
|
|||
('Module', [('Raise', (1, 0, 1, 25), ('Call', (1, 6, 1, 25), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), [('Constant', (1, 16, 1, 24), 'string', None)], []), None)], []),
|
||||
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
|
||||
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [], [], [('Pass', (4, 2, 4, 6))])], []),
|
||||
('Module', [('TryStar', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
|
||||
('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
|
||||
('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
|
||||
('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
|
||||
|
|
|
@ -1263,6 +1263,39 @@ class TestStackSizeStability(unittest.TestCase):
|
|||
"""
|
||||
self.check_stack_size(snippet)
|
||||
|
||||
def test_try_except_star_qualified(self):
|
||||
snippet = """
|
||||
try:
|
||||
a
|
||||
except* ImportError:
|
||||
b
|
||||
else:
|
||||
c
|
||||
"""
|
||||
self.check_stack_size(snippet)
|
||||
|
||||
def test_try_except_star_as(self):
|
||||
snippet = """
|
||||
try:
|
||||
a
|
||||
except* ImportError as e:
|
||||
b
|
||||
else:
|
||||
c
|
||||
"""
|
||||
self.check_stack_size(snippet)
|
||||
|
||||
def test_try_except_star_finally(self):
|
||||
snippet = """
|
||||
try:
|
||||
a
|
||||
except* A:
|
||||
b
|
||||
finally:
|
||||
c
|
||||
"""
|
||||
self.check_stack_size(snippet)
|
||||
|
||||
def test_try_finally(self):
|
||||
snippet = """
|
||||
try:
|
||||
|
|
|
@ -1119,7 +1119,7 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=132, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=134, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=136, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=33, argval=206, argrepr='to 206', offset=138, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=32, argval=204, argrepr='to 204', offset=138, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=140, starts_line=22, is_jump_target=True, positions=None),
|
||||
Instruction(opname='POP_EXCEPT_AND_RERAISE', opcode=37, arg=None, argval=None, argrepr='', offset=142, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=144, starts_line=25, is_jump_target=True, positions=None),
|
||||
|
@ -1134,7 +1134,7 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=162, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=164, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=25, argval=220, argrepr='to 220', offset=168, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=11, argval=192, argrepr='to 192', offset=168, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=90, argval=180, argrepr='to 180', offset=174, starts_line=None, is_jump_target=False, positions=None),
|
||||
|
@ -1146,34 +1146,26 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=190, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=192, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=194, starts_line=28, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=196, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=198, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=200, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=202, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=204, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=206, starts_line=23, is_jump_target=True, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=208, starts_line=28, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=210, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=212, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=216, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=218, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=220, starts_line=25, is_jump_target=True, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=222, starts_line=28, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=224, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=226, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=228, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=230, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=232, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=234, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=236, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=238, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=240, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=242, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=244, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_EXCEPT_AND_RERAISE', opcode=37, arg=None, argval=None, argrepr='', offset=246, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=192, starts_line=28, is_jump_target=True, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=194, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=196, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=198, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=200, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=202, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=204, starts_line=23, is_jump_target=True, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=206, starts_line=28, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=208, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=210, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=214, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=216, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=218, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=220, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=222, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=224, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=226, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=228, starts_line=None, is_jump_target=False, positions=None),
|
||||
Instruction(opname='POP_EXCEPT_AND_RERAISE', opcode=37, arg=None, argval=None, argrepr='', offset=230, starts_line=None, is_jump_target=False, positions=None),
|
||||
]
|
||||
|
||||
# One last piece of inspect fodder to check the default line number handling
|
||||
|
|
|
@ -0,0 +1,976 @@
|
|||
import sys
|
||||
import unittest
|
||||
import textwrap
|
||||
|
||||
class TestInvalidExceptStar(unittest.TestCase):
|
||||
def test_mixed_except_and_except_star_is_syntax_error(self):
|
||||
errors = [
|
||||
"try: pass\nexcept ValueError: pass\nexcept* TypeError: pass\n",
|
||||
"try: pass\nexcept* ValueError: pass\nexcept TypeError: pass\n",
|
||||
"try: pass\nexcept ValueError as e: pass\nexcept* TypeError: pass\n",
|
||||
"try: pass\nexcept* ValueError as e: pass\nexcept TypeError: pass\n",
|
||||
"try: pass\nexcept ValueError: pass\nexcept* TypeError as e: pass\n",
|
||||
"try: pass\nexcept* ValueError: pass\nexcept TypeError as e: pass\n",
|
||||
"try: pass\nexcept ValueError: pass\nexcept*: pass\n",
|
||||
"try: pass\nexcept* ValueError: pass\nexcept: pass\n",
|
||||
]
|
||||
|
||||
for err in errors:
|
||||
with self.assertRaises(SyntaxError):
|
||||
compile(err, "<string>", "exec")
|
||||
|
||||
def test_except_star_ExceptionGroup_is_runtime_error_single(self):
|
||||
with self.assertRaises(TypeError):
|
||||
try:
|
||||
raise OSError("blah")
|
||||
except* ExceptionGroup as e:
|
||||
pass
|
||||
|
||||
def test_except_star_ExceptionGroup_is_runtime_error_tuple(self):
|
||||
with self.assertRaises(TypeError):
|
||||
try:
|
||||
raise ExceptionGroup("eg", [ValueError(42)])
|
||||
except* (TypeError, ExceptionGroup):
|
||||
pass
|
||||
|
||||
def test_except_star_invalid_exception_type(self):
|
||||
with self.assertRaises(TypeError):
|
||||
try:
|
||||
raise ValueError
|
||||
except* 42:
|
||||
pass
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
try:
|
||||
raise ValueError
|
||||
except* (ValueError, 42):
|
||||
pass
|
||||
|
||||
|
||||
class TestBreakContinueReturnInExceptStarBlock(unittest.TestCase):
|
||||
MSG = (r"'break', 'continue' and 'return'"
|
||||
r" cannot appear in an except\* block")
|
||||
|
||||
def check_invalid(self, src):
|
||||
with self.assertRaisesRegex(SyntaxError, self.MSG):
|
||||
compile(textwrap.dedent(src), "<string>", "exec")
|
||||
|
||||
def test_break_in_except_star(self):
|
||||
self.check_invalid(
|
||||
"""
|
||||
try:
|
||||
raise ValueError
|
||||
except* Exception as e:
|
||||
break
|
||||
""")
|
||||
|
||||
self.check_invalid(
|
||||
"""
|
||||
for i in range(5):
|
||||
try:
|
||||
pass
|
||||
except* Exception as e:
|
||||
if i == 2:
|
||||
break
|
||||
""")
|
||||
|
||||
self.check_invalid(
|
||||
"""
|
||||
for i in range(5):
|
||||
try:
|
||||
pass
|
||||
except* Exception as e:
|
||||
if i == 2:
|
||||
break
|
||||
finally:
|
||||
return 0
|
||||
""")
|
||||
|
||||
|
||||
def test_continue_in_except_star_block_invalid(self):
|
||||
self.check_invalid(
|
||||
"""
|
||||
for i in range(5):
|
||||
try:
|
||||
raise ValueError
|
||||
except* Exception as e:
|
||||
continue
|
||||
""")
|
||||
|
||||
self.check_invalid(
|
||||
"""
|
||||
for i in range(5):
|
||||
try:
|
||||
pass
|
||||
except* Exception as e:
|
||||
if i == 2:
|
||||
continue
|
||||
""")
|
||||
|
||||
self.check_invalid(
|
||||
"""
|
||||
for i in range(5):
|
||||
try:
|
||||
pass
|
||||
except* Exception as e:
|
||||
if i == 2:
|
||||
continue
|
||||
finally:
|
||||
return 0
|
||||
""")
|
||||
|
||||
def test_return_in_except_star_block_invalid(self):
|
||||
self.check_invalid(
|
||||
"""
|
||||
def f():
|
||||
try:
|
||||
raise ValueError
|
||||
except* Exception as e:
|
||||
return 42
|
||||
""")
|
||||
|
||||
self.check_invalid(
|
||||
"""
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except* Exception as e:
|
||||
return 42
|
||||
finally:
|
||||
finished = True
|
||||
""")
|
||||
|
||||
def test_break_continue_in_except_star_block_valid(self):
|
||||
try:
|
||||
raise ValueError(42)
|
||||
except* Exception as e:
|
||||
count = 0
|
||||
for i in range(5):
|
||||
if i == 0:
|
||||
continue
|
||||
if i == 4:
|
||||
break
|
||||
count += 1
|
||||
|
||||
self.assertEqual(count, 3)
|
||||
self.assertEqual(i, 4)
|
||||
exc = e
|
||||
self.assertIsInstance(exc, ExceptionGroup)
|
||||
|
||||
def test_return_in_except_star_block_valid(self):
|
||||
try:
|
||||
raise ValueError(42)
|
||||
except* Exception as e:
|
||||
def f(x):
|
||||
return 2*x
|
||||
r = f(3)
|
||||
exc = e
|
||||
self.assertEqual(r, 6)
|
||||
self.assertIsInstance(exc, ExceptionGroup)
|
||||
|
||||
|
||||
class ExceptStarTest(unittest.TestCase):
|
||||
def assertExceptionIsLike(self, exc, template):
|
||||
if exc is None and template is None:
|
||||
return
|
||||
|
||||
if template is None:
|
||||
self.fail(f"unexpected exception: {exc}")
|
||||
|
||||
if exc is None:
|
||||
self.fail(f"expected an exception like {template!r}, got None")
|
||||
|
||||
if not isinstance(exc, ExceptionGroup):
|
||||
self.assertEqual(exc.__class__, template.__class__)
|
||||
self.assertEqual(exc.args[0], template.args[0])
|
||||
else:
|
||||
self.assertEqual(exc.message, template.message)
|
||||
self.assertEqual(len(exc.exceptions), len(template.exceptions))
|
||||
for e, t in zip(exc.exceptions, template.exceptions):
|
||||
self.assertExceptionIsLike(e, t)
|
||||
|
||||
def assertMetadataEqual(self, e1, e2):
|
||||
if e1 is None or e2 is None:
|
||||
self.assertTrue(e1 is None and e2 is None)
|
||||
else:
|
||||
self.assertEqual(e1.__context__, e2.__context__)
|
||||
self.assertEqual(e1.__cause__, e2.__cause__)
|
||||
self.assertEqual(e1.__traceback__, e2.__traceback__)
|
||||
|
||||
def assertMetadataNotEqual(self, e1, e2):
|
||||
if e1 is None or e2 is None:
|
||||
self.assertNotEqual(e1, e2)
|
||||
else:
|
||||
return not (e1.__context__ == e2.__context__
|
||||
and e1.__cause__ == e2.__cause__
|
||||
and e1.__traceback__ == e2.__traceback__)
|
||||
|
||||
|
||||
class TestExceptStarSplitSemantics(ExceptStarTest):
|
||||
def doSplitTestNamed(self, exc, T, match_template, rest_template):
|
||||
initial_exc_info = sys.exc_info()
|
||||
exc_info = match = rest = None
|
||||
try:
|
||||
try:
|
||||
raise exc
|
||||
except* T as e:
|
||||
exc_info = sys.exc_info()
|
||||
match = e
|
||||
except BaseException as e:
|
||||
rest = e
|
||||
|
||||
if match_template:
|
||||
self.assertEqual(exc_info[1], match)
|
||||
else:
|
||||
self.assertIsNone(exc_info)
|
||||
self.assertExceptionIsLike(match, match_template)
|
||||
self.assertExceptionIsLike(rest, rest_template)
|
||||
self.assertEqual(sys.exc_info(), initial_exc_info)
|
||||
|
||||
def doSplitTestUnnamed(self, exc, T, match_template, rest_template):
|
||||
initial_exc_info = sys.exc_info()
|
||||
exc_info = match = rest = None
|
||||
try:
|
||||
try:
|
||||
raise exc
|
||||
except* T:
|
||||
exc_info = sys.exc_info()
|
||||
match = sys.exc_info()[1]
|
||||
else:
|
||||
if rest_template:
|
||||
self.fail("Exception not raised")
|
||||
except BaseException as e:
|
||||
rest = e
|
||||
self.assertExceptionIsLike(match, match_template)
|
||||
if match_template:
|
||||
self.assertEqual(exc_info[0], type(match_template))
|
||||
self.assertExceptionIsLike(rest, rest_template)
|
||||
self.assertEqual(sys.exc_info(), initial_exc_info)
|
||||
|
||||
def doSplitTestInExceptHandler(self, exc, T, match_template, rest_template):
|
||||
try:
|
||||
raise ExceptionGroup('eg', [TypeError(1), ValueError(2)])
|
||||
except Exception:
|
||||
self.doSplitTestNamed(exc, T, match_template, rest_template)
|
||||
self.doSplitTestUnnamed(exc, T, match_template, rest_template)
|
||||
|
||||
def doSplitTestInExceptStarHandler(self, exc, T, match_template, rest_template):
|
||||
try:
|
||||
raise ExceptionGroup('eg', [TypeError(1), ValueError(2)])
|
||||
except* Exception:
|
||||
self.doSplitTestNamed(exc, T, match_template, rest_template)
|
||||
self.doSplitTestUnnamed(exc, T, match_template, rest_template)
|
||||
|
||||
def doSplitTest(self, exc, T, match_template, rest_template):
|
||||
self.doSplitTestNamed(exc, T, match_template, rest_template)
|
||||
self.doSplitTestUnnamed(exc, T, match_template, rest_template)
|
||||
self.doSplitTestInExceptHandler(exc, T, match_template, rest_template)
|
||||
self.doSplitTestInExceptStarHandler(exc, T, match_template, rest_template)
|
||||
|
||||
def test_no_match_single_type(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup("test1", [ValueError("V"), TypeError("T")]),
|
||||
SyntaxError,
|
||||
None,
|
||||
ExceptionGroup("test1", [ValueError("V"), TypeError("T")]))
|
||||
|
||||
def test_match_single_type(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup("test2", [ValueError("V1"), ValueError("V2")]),
|
||||
ValueError,
|
||||
ExceptionGroup("test2", [ValueError("V1"), ValueError("V2")]),
|
||||
None)
|
||||
|
||||
def test_match_single_type_partial_match(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup(
|
||||
"test3",
|
||||
[ValueError("V1"), OSError("OS"), ValueError("V2")]),
|
||||
ValueError,
|
||||
ExceptionGroup("test3", [ValueError("V1"), ValueError("V2")]),
|
||||
ExceptionGroup("test3", [OSError("OS")]))
|
||||
|
||||
def test_match_single_type_nested(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup(
|
||||
"g1", [
|
||||
ValueError("V1"),
|
||||
OSError("OS1"),
|
||||
ExceptionGroup(
|
||||
"g2", [
|
||||
OSError("OS2"),
|
||||
ValueError("V2"),
|
||||
TypeError("T")])]),
|
||||
ValueError,
|
||||
ExceptionGroup(
|
||||
"g1", [
|
||||
ValueError("V1"),
|
||||
ExceptionGroup("g2", [ValueError("V2")])]),
|
||||
ExceptionGroup("g1", [
|
||||
OSError("OS1"),
|
||||
ExceptionGroup("g2", [
|
||||
OSError("OS2"), TypeError("T")])]))
|
||||
|
||||
def test_match_type_tuple_nested(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup(
|
||||
"h1", [
|
||||
ValueError("V1"),
|
||||
OSError("OS1"),
|
||||
ExceptionGroup(
|
||||
"h2", [OSError("OS2"), ValueError("V2"), TypeError("T")])]),
|
||||
(ValueError, TypeError),
|
||||
ExceptionGroup(
|
||||
"h1", [
|
||||
ValueError("V1"),
|
||||
ExceptionGroup("h2", [ValueError("V2"), TypeError("T")])]),
|
||||
ExceptionGroup(
|
||||
"h1", [
|
||||
OSError("OS1"),
|
||||
ExceptionGroup("h2", [OSError("OS2")])]))
|
||||
|
||||
def test_empty_groups_removed(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup(
|
||||
"eg", [
|
||||
ExceptionGroup("i1", [ValueError("V1")]),
|
||||
ExceptionGroup("i2", [ValueError("V2"), TypeError("T1")]),
|
||||
ExceptionGroup("i3", [TypeError("T2")])]),
|
||||
TypeError,
|
||||
ExceptionGroup("eg", [
|
||||
ExceptionGroup("i2", [TypeError("T1")]),
|
||||
ExceptionGroup("i3", [TypeError("T2")])]),
|
||||
ExceptionGroup("eg", [
|
||||
ExceptionGroup("i1", [ValueError("V1")]),
|
||||
ExceptionGroup("i2", [ValueError("V2")])]))
|
||||
|
||||
def test_singleton_groups_are_kept(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup("j1", [
|
||||
ExceptionGroup("j2", [
|
||||
ExceptionGroup("j3", [ValueError("V1")]),
|
||||
ExceptionGroup("j4", [TypeError("T")])])]),
|
||||
TypeError,
|
||||
ExceptionGroup(
|
||||
"j1",
|
||||
[ExceptionGroup("j2", [ExceptionGroup("j4", [TypeError("T")])])]),
|
||||
ExceptionGroup(
|
||||
"j1",
|
||||
[ExceptionGroup("j2", [ExceptionGroup("j3", [ValueError("V1")])])]))
|
||||
|
||||
def test_naked_exception_matched_wrapped1(self):
|
||||
self.doSplitTest(
|
||||
ValueError("V"),
|
||||
ValueError,
|
||||
ExceptionGroup("", [ValueError("V")]),
|
||||
None)
|
||||
|
||||
def test_naked_exception_matched_wrapped2(self):
|
||||
self.doSplitTest(
|
||||
ValueError("V"),
|
||||
Exception,
|
||||
ExceptionGroup("", [ValueError("V")]),
|
||||
None)
|
||||
|
||||
def test_exception_group_except_star_Exception_not_wrapped(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup("eg", [ValueError("V")]),
|
||||
Exception,
|
||||
ExceptionGroup("eg", [ValueError("V")]),
|
||||
None)
|
||||
|
||||
def test_plain_exception_not_matched(self):
|
||||
self.doSplitTest(
|
||||
ValueError("V"),
|
||||
TypeError,
|
||||
None,
|
||||
ValueError("V"))
|
||||
|
||||
def test_match__supertype(self):
|
||||
self.doSplitTest(
|
||||
ExceptionGroup("st", [BlockingIOError("io"), TypeError("T")]),
|
||||
OSError,
|
||||
ExceptionGroup("st", [BlockingIOError("io")]),
|
||||
ExceptionGroup("st", [TypeError("T")]))
|
||||
|
||||
def test_multiple_matches_named(self):
|
||||
try:
|
||||
raise ExceptionGroup("mmn", [OSError("os"), BlockingIOError("io")])
|
||||
except* BlockingIOError as e:
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("mmn", [BlockingIOError("io")]))
|
||||
except* OSError as e:
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("mmn", [OSError("os")]))
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
def test_multiple_matches_unnamed(self):
|
||||
try:
|
||||
raise ExceptionGroup("mmu", [OSError("os"), BlockingIOError("io")])
|
||||
except* BlockingIOError:
|
||||
e = sys.exc_info()[1]
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("mmu", [BlockingIOError("io")]))
|
||||
except* OSError:
|
||||
e = sys.exc_info()[1]
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("mmu", [OSError("os")]))
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
def test_first_match_wins_named(self):
|
||||
try:
|
||||
raise ExceptionGroup("fst", [BlockingIOError("io")])
|
||||
except* OSError as e:
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("fst", [BlockingIOError("io")]))
|
||||
except* BlockingIOError:
|
||||
self.fail("Should have been matched as OSError")
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
def test_first_match_wins_unnamed(self):
|
||||
try:
|
||||
raise ExceptionGroup("fstu", [BlockingIOError("io")])
|
||||
except* OSError:
|
||||
e = sys.exc_info()[1]
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("fstu", [BlockingIOError("io")]))
|
||||
except* BlockingIOError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
def test_nested_except_stars(self):
|
||||
try:
|
||||
raise ExceptionGroup("n", [BlockingIOError("io")])
|
||||
except* BlockingIOError:
|
||||
try:
|
||||
raise ExceptionGroup("n", [ValueError("io")])
|
||||
except* ValueError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
e = sys.exc_info()[1]
|
||||
self.assertExceptionIsLike(e,
|
||||
ExceptionGroup("n", [BlockingIOError("io")]))
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
def test_nested_in_loop(self):
|
||||
for _ in range(2):
|
||||
try:
|
||||
raise ExceptionGroup("nl", [BlockingIOError("io")])
|
||||
except* BlockingIOError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Exception not raised")
|
||||
|
||||
|
||||
class TestExceptStarReraise(ExceptStarTest):
|
||||
def test_reraise_all_named(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2), OSError(3)])
|
||||
except* TypeError as e:
|
||||
raise
|
||||
except* ValueError as e:
|
||||
raise
|
||||
# OSError not handled
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2), OSError(3)]))
|
||||
|
||||
def test_reraise_all_unnamed(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2), OSError(3)])
|
||||
except* TypeError:
|
||||
raise
|
||||
except* ValueError:
|
||||
raise
|
||||
# OSError not handled
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2), OSError(3)]))
|
||||
|
||||
def test_reraise_some_handle_all_named(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2), OSError(3)])
|
||||
except* TypeError as e:
|
||||
raise
|
||||
except* ValueError as e:
|
||||
pass
|
||||
# OSError not handled
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("eg", [TypeError(1), OSError(3)]))
|
||||
|
||||
def test_reraise_partial_handle_all_unnamed(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2)])
|
||||
except* TypeError:
|
||||
raise
|
||||
except* ValueError:
|
||||
pass
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
def test_reraise_partial_handle_some_named(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2), OSError(3)])
|
||||
except* TypeError as e:
|
||||
raise
|
||||
except* ValueError as e:
|
||||
pass
|
||||
# OSError not handled
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("eg", [TypeError(1), OSError(3)]))
|
||||
|
||||
def test_reraise_partial_handle_some_unnamed(self):
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
"eg", [TypeError(1), ValueError(2), OSError(3)])
|
||||
except* TypeError:
|
||||
raise
|
||||
except* ValueError:
|
||||
pass
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("eg", [TypeError(1), OSError(3)]))
|
||||
|
||||
def test_reraise_plain_exception_named(self):
|
||||
try:
|
||||
try:
|
||||
raise ValueError(42)
|
||||
except* ValueError as e:
|
||||
raise
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [ValueError(42)]))
|
||||
|
||||
def test_reraise_plain_exception_unnamed(self):
|
||||
try:
|
||||
try:
|
||||
raise ValueError(42)
|
||||
except* ValueError:
|
||||
raise
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [ValueError(42)]))
|
||||
|
||||
|
||||
class TestExceptStarRaise(ExceptStarTest):
|
||||
def test_raise_named(self):
|
||||
orig = ExceptionGroup("eg", [ValueError(1), OSError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* OSError as e:
|
||||
raise TypeError(3)
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup(
|
||||
"", [TypeError(3), ExceptionGroup("eg", [ValueError(1)])]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
|
||||
def test_raise_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [ValueError(1), OSError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* OSError:
|
||||
raise TypeError(3)
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup(
|
||||
"", [TypeError(3), ExceptionGroup("eg", [ValueError(1)])]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
|
||||
def test_raise_handle_all_raise_one_named(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* (TypeError, ValueError) as e:
|
||||
raise SyntaxError(3)
|
||||
except BaseException as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
|
||||
def test_raise_handle_all_raise_one_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* (TypeError, ValueError) as e:
|
||||
raise SyntaxError(3)
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
|
||||
def test_raise_handle_all_raise_two_named(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* TypeError as e:
|
||||
raise SyntaxError(3)
|
||||
except* ValueError as e:
|
||||
raise SyntaxError(4)
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3), SyntaxError(4)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__context__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[1].__context__)
|
||||
|
||||
def test_raise_handle_all_raise_two_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* TypeError:
|
||||
raise SyntaxError(3)
|
||||
except* ValueError:
|
||||
raise SyntaxError(4)
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3), SyntaxError(4)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__context__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[1].__context__)
|
||||
|
||||
|
||||
class TestExceptStarRaiseFrom(ExceptStarTest):
|
||||
def test_raise_named(self):
|
||||
orig = ExceptionGroup("eg", [ValueError(1), OSError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* OSError as e:
|
||||
raise TypeError(3) from e
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup(
|
||||
"", [TypeError(3), ExceptionGroup("eg", [ValueError(1)])]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
self.assertMetadataNotEqual(orig, exc.exceptions[1].__context__)
|
||||
self.assertMetadataNotEqual(orig, exc.exceptions[1].__cause__)
|
||||
|
||||
def test_raise_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [ValueError(1), OSError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* OSError:
|
||||
e = sys.exc_info()[1]
|
||||
raise TypeError(3) from e
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc,
|
||||
ExceptionGroup(
|
||||
"", [TypeError(3), ExceptionGroup("eg", [ValueError(1)])]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [OSError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
self.assertMetadataNotEqual(orig, exc.exceptions[1].__context__)
|
||||
self.assertMetadataNotEqual(orig, exc.exceptions[1].__cause__)
|
||||
|
||||
def test_raise_handle_all_raise_one_named(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* (TypeError, ValueError) as e:
|
||||
raise SyntaxError(3) from e
|
||||
except BaseException as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
|
||||
def test_raise_handle_all_raise_one_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* (TypeError, ValueError) as e:
|
||||
e = sys.exc_info()[1]
|
||||
raise SyntaxError(3) from e
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [TypeError(1), ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
|
||||
def test_raise_handle_all_raise_two_named(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* TypeError as e:
|
||||
raise SyntaxError(3) from e
|
||||
except* ValueError as e:
|
||||
raise SyntaxError(4) from e
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3), SyntaxError(4)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__context__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__cause__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
|
||||
def test_raise_handle_all_raise_two_unnamed(self):
|
||||
orig = ExceptionGroup("eg", [TypeError(1), ValueError(2)])
|
||||
try:
|
||||
try:
|
||||
raise orig
|
||||
except* TypeError:
|
||||
e = sys.exc_info()[1]
|
||||
raise SyntaxError(3) from e
|
||||
except* ValueError:
|
||||
e = sys.exc_info()[1]
|
||||
raise SyntaxError(4) from e
|
||||
except ExceptionGroup as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc, ExceptionGroup("", [SyntaxError(3), SyntaxError(4)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__context__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[0].__cause__,
|
||||
ExceptionGroup("eg", [TypeError(1)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__context__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertExceptionIsLike(
|
||||
exc.exceptions[1].__cause__,
|
||||
ExceptionGroup("eg", [ValueError(2)]))
|
||||
|
||||
self.assertMetadataNotEqual(orig, exc)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[0].__cause__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[1].__context__)
|
||||
self.assertMetadataEqual(orig, exc.exceptions[1].__cause__)
|
||||
|
||||
|
||||
class TestExceptStarExceptionGroupSubclass(ExceptStarTest):
|
||||
def test_except_star_EG_subclass(self):
|
||||
class EG(ExceptionGroup):
|
||||
def __new__(cls, message, excs, code):
|
||||
obj = super().__new__(cls, message, excs)
|
||||
obj.code = code
|
||||
return obj
|
||||
|
||||
def derive(self, excs):
|
||||
return EG(self.message, excs, self.code)
|
||||
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
raise TypeError(2)
|
||||
except TypeError as te:
|
||||
raise EG("nested", [te], 101) from None
|
||||
except EG as nested:
|
||||
try:
|
||||
raise ValueError(1)
|
||||
except ValueError as ve:
|
||||
raise EG("eg", [ve, nested], 42)
|
||||
except* ValueError as eg:
|
||||
veg = eg
|
||||
except EG as eg:
|
||||
teg = eg
|
||||
|
||||
self.assertIsInstance(veg, EG)
|
||||
self.assertIsInstance(teg, EG)
|
||||
self.assertIsInstance(teg.exceptions[0], EG)
|
||||
self.assertMetadataEqual(veg, teg)
|
||||
self.assertEqual(veg.code, 42)
|
||||
self.assertEqual(teg.code, 42)
|
||||
self.assertEqual(teg.exceptions[0].code, 101)
|
||||
|
||||
|
||||
class TestExceptStarCleanup(ExceptStarTest):
|
||||
def test_exc_info_restored(self):
|
||||
try:
|
||||
try:
|
||||
raise ValueError(42)
|
||||
except:
|
||||
try:
|
||||
raise TypeError(int)
|
||||
except* Exception:
|
||||
pass
|
||||
1/0
|
||||
except Exception as e:
|
||||
exc = e
|
||||
|
||||
self.assertExceptionIsLike(exc, ZeroDivisionError('division by zero'))
|
||||
self.assertExceptionIsLike(exc.__context__, ValueError(42))
|
||||
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -211,7 +211,8 @@ class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
|
|||
def test_basics_subgroup_split__bad_arg_type(self):
|
||||
bad_args = ["bad arg",
|
||||
OSError('instance not type'),
|
||||
[OSError('instance not type')],]
|
||||
[OSError, TypeError],
|
||||
(OSError, 42)]
|
||||
for arg in bad_args:
|
||||
with self.assertRaises(TypeError):
|
||||
self.eg.subgroup(arg)
|
||||
|
|
|
@ -172,5 +172,283 @@ class ExceptionTestCase(unittest.TestCase):
|
|||
self.assertTrue(hit_finally)
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
|
||||
class ExceptStarTestCases(unittest.TestCase):
|
||||
def test_try_except_else_finally(self):
|
||||
hit_except = False
|
||||
hit_else = False
|
||||
hit_finally = False
|
||||
|
||||
try:
|
||||
raise Exception('nyaa!')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
self.assertFalse(hit_else)
|
||||
|
||||
def test_try_except_else_finally_no_exception(self):
|
||||
hit_except = False
|
||||
hit_else = False
|
||||
hit_finally = False
|
||||
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertFalse(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
self.assertTrue(hit_else)
|
||||
|
||||
def test_try_except_finally(self):
|
||||
hit_except = False
|
||||
hit_finally = False
|
||||
|
||||
try:
|
||||
raise Exception('yarr!')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
def test_try_except_finally_no_exception(self):
|
||||
hit_except = False
|
||||
hit_finally = False
|
||||
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertFalse(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
def test_try_except(self):
|
||||
hit_except = False
|
||||
|
||||
try:
|
||||
raise Exception('ahoy!')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
def test_try_except_no_exception(self):
|
||||
hit_except = False
|
||||
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
|
||||
self.assertFalse(hit_except)
|
||||
|
||||
def test_try_except_else(self):
|
||||
hit_except = False
|
||||
hit_else = False
|
||||
|
||||
try:
|
||||
raise Exception('foo!')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
|
||||
self.assertFalse(hit_else)
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
def test_try_except_else_no_exception(self):
|
||||
hit_except = False
|
||||
hit_else = False
|
||||
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
|
||||
self.assertFalse(hit_except)
|
||||
self.assertTrue(hit_else)
|
||||
|
||||
def test_try_finally_no_exception(self):
|
||||
hit_finally = False
|
||||
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
def test_nested(self):
|
||||
hit_finally = False
|
||||
hit_inner_except = False
|
||||
hit_inner_finally = False
|
||||
|
||||
try:
|
||||
try:
|
||||
raise Exception('inner exception')
|
||||
except* BaseException:
|
||||
hit_inner_except = True
|
||||
finally:
|
||||
hit_inner_finally = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_inner_except)
|
||||
self.assertTrue(hit_inner_finally)
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
def test_nested_else(self):
|
||||
hit_else = False
|
||||
hit_finally = False
|
||||
hit_except = False
|
||||
hit_inner_except = False
|
||||
hit_inner_else = False
|
||||
|
||||
try:
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_inner_except = True
|
||||
else:
|
||||
hit_inner_else = True
|
||||
|
||||
raise Exception('outer exception')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertFalse(hit_inner_except)
|
||||
self.assertTrue(hit_inner_else)
|
||||
self.assertFalse(hit_else)
|
||||
self.assertTrue(hit_finally)
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
def test_nested_mixed1(self):
|
||||
hit_except = False
|
||||
hit_finally = False
|
||||
hit_inner_except = False
|
||||
hit_inner_finally = False
|
||||
|
||||
try:
|
||||
try:
|
||||
raise Exception('inner exception')
|
||||
except* BaseException:
|
||||
hit_inner_except = True
|
||||
finally:
|
||||
hit_inner_finally = True
|
||||
except:
|
||||
hit_except = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_inner_except)
|
||||
self.assertTrue(hit_inner_finally)
|
||||
self.assertFalse(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
def test_nested_mixed2(self):
|
||||
hit_except = False
|
||||
hit_finally = False
|
||||
hit_inner_except = False
|
||||
hit_inner_finally = False
|
||||
|
||||
try:
|
||||
try:
|
||||
raise Exception('inner exception')
|
||||
except:
|
||||
hit_inner_except = True
|
||||
finally:
|
||||
hit_inner_finally = True
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertTrue(hit_inner_except)
|
||||
self.assertTrue(hit_inner_finally)
|
||||
self.assertFalse(hit_except)
|
||||
self.assertTrue(hit_finally)
|
||||
|
||||
|
||||
def test_nested_else_mixed1(self):
|
||||
hit_else = False
|
||||
hit_finally = False
|
||||
hit_except = False
|
||||
hit_inner_except = False
|
||||
hit_inner_else = False
|
||||
|
||||
try:
|
||||
try:
|
||||
pass
|
||||
except* BaseException:
|
||||
hit_inner_except = True
|
||||
else:
|
||||
hit_inner_else = True
|
||||
|
||||
raise Exception('outer exception')
|
||||
except:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertFalse(hit_inner_except)
|
||||
self.assertTrue(hit_inner_else)
|
||||
self.assertFalse(hit_else)
|
||||
self.assertTrue(hit_finally)
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
def test_nested_else_mixed2(self):
|
||||
hit_else = False
|
||||
hit_finally = False
|
||||
hit_except = False
|
||||
hit_inner_except = False
|
||||
hit_inner_else = False
|
||||
|
||||
try:
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
hit_inner_except = True
|
||||
else:
|
||||
hit_inner_else = True
|
||||
|
||||
raise Exception('outer exception')
|
||||
except* BaseException:
|
||||
hit_except = True
|
||||
else:
|
||||
hit_else = True
|
||||
finally:
|
||||
hit_finally = True
|
||||
|
||||
self.assertFalse(hit_inner_except)
|
||||
self.assertTrue(hit_inner_else)
|
||||
self.assertFalse(hit_else)
|
||||
self.assertTrue(hit_finally)
|
||||
self.assertTrue(hit_except)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -241,6 +241,8 @@ class ExceptionTests(unittest.TestCase):
|
|||
check('def f():\n continue', 2, 3)
|
||||
check('def f():\n break', 2, 3)
|
||||
check('try:\n pass\nexcept:\n pass\nexcept ValueError:\n pass', 3, 1)
|
||||
check('try:\n pass\nexcept*:\n pass', 3, 8)
|
||||
check('try:\n pass\nexcept*:\n pass\nexcept* ValueError:\n pass', 3, 8)
|
||||
|
||||
# Errors thrown by tokenizer.c
|
||||
check('(0x+1)', 1, 3)
|
||||
|
|
|
@ -1419,6 +1419,30 @@ class GrammarTests(unittest.TestCase):
|
|||
compile("try:\n pass\nexcept Exception as a.b:\n pass", "?", "exec")
|
||||
compile("try:\n pass\nexcept Exception as a[b]:\n pass", "?", "exec")
|
||||
|
||||
def test_try_star(self):
|
||||
### try_stmt: 'try': suite (except_star_clause : suite) + ['else' ':' suite]
|
||||
### except_star_clause: 'except*' expr ['as' NAME]
|
||||
try:
|
||||
1/0
|
||||
except* ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
try: 1/0
|
||||
except* EOFError: pass
|
||||
except* ZeroDivisionError as msg: pass
|
||||
else: pass
|
||||
try: 1/0
|
||||
except* (EOFError, TypeError, ZeroDivisionError): pass
|
||||
try: 1/0
|
||||
except* (EOFError, TypeError, ZeroDivisionError) as msg: pass
|
||||
try: pass
|
||||
finally: pass
|
||||
with self.assertRaises(SyntaxError):
|
||||
compile("try:\n pass\nexcept* Exception as a.b:\n pass", "?", "exec")
|
||||
compile("try:\n pass\nexcept* Exception as a[b]:\n pass", "?", "exec")
|
||||
compile("try:\n pass\nexcept*:\n pass", "?", "exec")
|
||||
|
||||
def test_suite(self):
|
||||
# simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
|
||||
if 1: pass
|
||||
|
|
|
@ -955,6 +955,48 @@ Custom error messages for try blocks that are not followed by except/finally
|
|||
Traceback (most recent call last):
|
||||
SyntaxError: expected 'except' or 'finally' block
|
||||
|
||||
Custom error message for try block mixing except and except*
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except TypeError:
|
||||
... pass
|
||||
... except* ValueError:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: cannot have both 'except' and 'except*' on the same 'try'
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* TypeError:
|
||||
... pass
|
||||
... except ValueError:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: cannot have both 'except' and 'except*' on the same 'try'
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except TypeError:
|
||||
... pass
|
||||
... except TypeError:
|
||||
... pass
|
||||
... except* ValueError:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: cannot have both 'except' and 'except*' on the same 'try'
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* TypeError:
|
||||
... pass
|
||||
... except* TypeError:
|
||||
... pass
|
||||
... except ValueError:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: cannot have both 'except' and 'except*' on the same 'try'
|
||||
|
||||
Ensure that early = are not matched by the parser as invalid comparisons
|
||||
>>> f(2, 4, x=34); 1 $ 2
|
||||
Traceback (most recent call last):
|
||||
|
@ -1068,6 +1110,13 @@ Specialized indentation errors:
|
|||
Traceback (most recent call last):
|
||||
IndentationError: expected an indented block after 'except' statement on line 3
|
||||
|
||||
>>> try:
|
||||
... something()
|
||||
... except* A:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
IndentationError: expected an indented block after 'except*' statement on line 3
|
||||
|
||||
>>> try:
|
||||
... something()
|
||||
... except A:
|
||||
|
@ -1077,6 +1126,15 @@ Specialized indentation errors:
|
|||
Traceback (most recent call last):
|
||||
IndentationError: expected an indented block after 'finally' statement on line 5
|
||||
|
||||
>>> try:
|
||||
... something()
|
||||
... except* A:
|
||||
... pass
|
||||
... finally:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
IndentationError: expected an indented block after 'finally' statement on line 5
|
||||
|
||||
>>> with A:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
|
@ -1180,6 +1238,48 @@ raise a custom exception
|
|||
SyntaxError: multiple exception types must be parenthesized
|
||||
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* A, B:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: multiple exception types must be parenthesized
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* A, B, C:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: multiple exception types must be parenthesized
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* A, B, C as blech:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: multiple exception types must be parenthesized
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* A, B, C as blech:
|
||||
... pass
|
||||
... finally:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: multiple exception types must be parenthesized
|
||||
|
||||
Custom exception for 'except*' without an exception type
|
||||
|
||||
>>> try:
|
||||
... pass
|
||||
... except* A as a:
|
||||
... pass
|
||||
... except*:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expected one or more exception types
|
||||
|
||||
|
||||
>>> f(a=23, a=234)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
|
|
|
@ -1213,6 +1213,181 @@ class TraceTestCase(unittest.TestCase):
|
|||
(5, 'line'),
|
||||
(5, 'return')])
|
||||
|
||||
def test_try_except_star_no_exception(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
2
|
||||
except* Exception:
|
||||
4
|
||||
else:
|
||||
6
|
||||
finally:
|
||||
8
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(6, 'line'),
|
||||
(8, 'line'),
|
||||
(8, 'return')])
|
||||
|
||||
def test_try_except_star_named_no_exception(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
2
|
||||
except* Exception as e:
|
||||
4
|
||||
else:
|
||||
6
|
||||
finally:
|
||||
8
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(6, 'line'),
|
||||
(8, 'line'),
|
||||
(8, 'return')])
|
||||
|
||||
def test_try_except_star_exception_caught(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
raise ValueError(2)
|
||||
except* ValueError:
|
||||
4
|
||||
else:
|
||||
6
|
||||
finally:
|
||||
8
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(2, 'exception'),
|
||||
(3, 'line'),
|
||||
(4, 'line'),
|
||||
(8, 'line'),
|
||||
(8, 'return')])
|
||||
|
||||
def test_try_except_star_named_exception_caught(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
raise ValueError(2)
|
||||
except* ValueError as e:
|
||||
4
|
||||
else:
|
||||
6
|
||||
finally:
|
||||
8
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(2, 'exception'),
|
||||
(3, 'line'),
|
||||
(4, 'line'),
|
||||
(8, 'line'),
|
||||
(8, 'return')])
|
||||
|
||||
def test_try_except_star_exception_not_caught(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
try:
|
||||
raise ValueError(3)
|
||||
except* TypeError:
|
||||
5
|
||||
except ValueError:
|
||||
7
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(3, 'line'),
|
||||
(3, 'exception'),
|
||||
(4, 'line'),
|
||||
(6, 'line'),
|
||||
(7, 'line'),
|
||||
(7, 'return')])
|
||||
|
||||
def test_try_except_star_named_exception_not_caught(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
try:
|
||||
raise ValueError(3)
|
||||
except* TypeError as e:
|
||||
5
|
||||
except ValueError:
|
||||
7
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(3, 'line'),
|
||||
(3, 'exception'),
|
||||
(4, 'line'),
|
||||
(6, 'line'),
|
||||
(7, 'line'),
|
||||
(7, 'return')])
|
||||
|
||||
def test_try_except_star_nested(self):
|
||||
|
||||
def func():
|
||||
try:
|
||||
try:
|
||||
raise ExceptionGroup(
|
||||
'eg',
|
||||
[ValueError(5), TypeError('bad type')])
|
||||
except* TypeError as e:
|
||||
7
|
||||
except* OSError:
|
||||
9
|
||||
except* ValueError:
|
||||
raise
|
||||
except* ValueError:
|
||||
try:
|
||||
raise TypeError(14)
|
||||
except* OSError:
|
||||
16
|
||||
except* TypeError as e:
|
||||
18
|
||||
return 0
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(3, 'line'),
|
||||
(4, 'line'),
|
||||
(5, 'line'),
|
||||
(3, 'line'),
|
||||
(3, 'exception'),
|
||||
(6, 'line'),
|
||||
(7, 'line'),
|
||||
(8, 'line'),
|
||||
(10, 'line'),
|
||||
(11, 'line'),
|
||||
(12, 'line'),
|
||||
(13, 'line'),
|
||||
(14, 'line'),
|
||||
(14, 'exception'),
|
||||
(15, 'line'),
|
||||
(17, 'line'),
|
||||
(18, 'line'),
|
||||
(19, 'line'),
|
||||
(19, 'return')])
|
||||
|
||||
|
||||
class SkipLineEventsTraceTestCase(TraceTestCase):
|
||||
"""Repeat the trace tests, but with per-line events skipped"""
|
||||
|
|
|
@ -93,6 +93,19 @@ finally:
|
|||
suite5
|
||||
"""
|
||||
|
||||
try_except_star_finally = """\
|
||||
try:
|
||||
suite1
|
||||
except* ex1:
|
||||
suite2
|
||||
except* ex2:
|
||||
suite3
|
||||
else:
|
||||
suite4
|
||||
finally:
|
||||
suite5
|
||||
"""
|
||||
|
||||
with_simple = """\
|
||||
with f():
|
||||
suite1
|
||||
|
@ -304,6 +317,9 @@ class UnparseTestCase(ASTTestCase):
|
|||
def test_try_except_finally(self):
|
||||
self.check_ast_roundtrip(try_except_finally)
|
||||
|
||||
def test_try_except_star_finally(self):
|
||||
self.check_ast_roundtrip(try_except_star_finally)
|
||||
|
||||
def test_starred_assignment(self):
|
||||
self.check_ast_roundtrip("a, *b, c = seq")
|
||||
self.check_ast_roundtrip("a, (*b, c) = seq")
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Complete the :pep:`654` implementation: add ``except*``.
|
|
@ -773,6 +773,23 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyExc_CreateExceptionGroup(const char *msg_str, PyObject *excs)
|
||||
{
|
||||
PyObject *msg = PyUnicode_FromString(msg_str);
|
||||
if (!msg) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *args = PyTuple_Pack(2, msg, excs);
|
||||
Py_DECREF(msg);
|
||||
if (!args) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *result = PyObject_CallObject(PyExc_BaseExceptionGroup, args);
|
||||
Py_DECREF(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
BaseExceptionGroup_init(PyBaseExceptionGroupObject *self,
|
||||
PyObject *args, PyObject *kwds)
|
||||
|
@ -878,7 +895,7 @@ exceptiongroup_subset(
|
|||
if (tb) {
|
||||
int res = PyException_SetTraceback(eg, tb);
|
||||
Py_DECREF(tb);
|
||||
if (res == -1) {
|
||||
if (res < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
@ -896,26 +913,41 @@ typedef enum {
|
|||
EXCEPTION_GROUP_MATCH_BY_TYPE = 0,
|
||||
/* A PyFunction returning True for matching exceptions */
|
||||
EXCEPTION_GROUP_MATCH_BY_PREDICATE = 1,
|
||||
/* An instance or container thereof, checked with equality
|
||||
* This matcher type is only used internally by the
|
||||
* interpreter, it is not exposed to python code */
|
||||
/* A set of leaf exceptions to include in the result.
|
||||
* This matcher type is used internally by the interpreter
|
||||
* to construct reraised exceptions.
|
||||
*/
|
||||
EXCEPTION_GROUP_MATCH_INSTANCES = 2
|
||||
} _exceptiongroup_split_matcher_type;
|
||||
|
||||
static int
|
||||
get_matcher_type(PyObject *value,
|
||||
_exceptiongroup_split_matcher_type *type)
|
||||
_exceptiongroup_split_matcher_type *type)
|
||||
{
|
||||
/* the python API supports only BY_TYPE and BY_PREDICATE */
|
||||
if (PyExceptionClass_Check(value) ||
|
||||
PyTuple_CheckExact(value)) {
|
||||
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
|
||||
return 0;
|
||||
}
|
||||
assert(value);
|
||||
|
||||
if (PyFunction_Check(value)) {
|
||||
*type = EXCEPTION_GROUP_MATCH_BY_PREDICATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (PyExceptionClass_Check(value)) {
|
||||
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (PyTuple_CheckExact(value)) {
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(value);
|
||||
for (Py_ssize_t i=0; i<n; i++) {
|
||||
if (!PyExceptionClass_Check(PyTuple_GET_ITEM(value, i))) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
error:
|
||||
PyErr_SetString(
|
||||
PyExc_TypeError,
|
||||
"expected a function, exception type or tuple of exception types");
|
||||
|
@ -944,10 +976,11 @@ exceptiongroup_split_check_match(PyObject *exc,
|
|||
return is_true;
|
||||
}
|
||||
case EXCEPTION_GROUP_MATCH_INSTANCES: {
|
||||
if (PySequence_Check(matcher_value)) {
|
||||
return PySequence_Contains(matcher_value, exc);
|
||||
assert(PySet_Check(matcher_value));
|
||||
if (!_PyBaseExceptionGroup_Check(exc)) {
|
||||
return PySet_Contains(matcher_value, exc);
|
||||
}
|
||||
return matcher_value == exc;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -1019,7 +1052,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
|||
}
|
||||
if (exceptiongroup_split_recursive(
|
||||
e, matcher_type, matcher_value,
|
||||
construct_rest, &rec_result) == -1) {
|
||||
construct_rest, &rec_result) < 0) {
|
||||
assert(!rec_result.match);
|
||||
assert(!rec_result.rest);
|
||||
Py_LeaveRecursiveCall();
|
||||
|
@ -1028,7 +1061,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
|||
Py_LeaveRecursiveCall();
|
||||
if (rec_result.match) {
|
||||
assert(PyList_CheckExact(match_list));
|
||||
if (PyList_Append(match_list, rec_result.match) == -1) {
|
||||
if (PyList_Append(match_list, rec_result.match) < 0) {
|
||||
Py_DECREF(rec_result.match);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1037,7 +1070,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
|||
if (rec_result.rest) {
|
||||
assert(construct_rest);
|
||||
assert(PyList_CheckExact(rest_list));
|
||||
if (PyList_Append(rest_list, rec_result.rest) == -1) {
|
||||
if (PyList_Append(rest_list, rec_result.rest) < 0) {
|
||||
Py_DECREF(rec_result.rest);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1046,13 +1079,13 @@ exceptiongroup_split_recursive(PyObject *exc,
|
|||
}
|
||||
|
||||
/* construct result */
|
||||
if (exceptiongroup_subset(eg, match_list, &result->match) == -1) {
|
||||
if (exceptiongroup_subset(eg, match_list, &result->match) < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (construct_rest) {
|
||||
assert(PyList_CheckExact(rest_list));
|
||||
if (exceptiongroup_subset(eg, rest_list, &result->rest) == -1) {
|
||||
if (exceptiongroup_subset(eg, rest_list, &result->rest) < 0) {
|
||||
Py_CLEAR(result->match);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1061,7 +1094,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
|||
done:
|
||||
Py_DECREF(match_list);
|
||||
Py_XDECREF(rest_list);
|
||||
if (retval == -1) {
|
||||
if (retval < 0) {
|
||||
Py_CLEAR(result->match);
|
||||
Py_CLEAR(result->rest);
|
||||
}
|
||||
|
@ -1077,7 +1110,7 @@ BaseExceptionGroup_split(PyObject *self, PyObject *args)
|
|||
}
|
||||
|
||||
_exceptiongroup_split_matcher_type matcher_type;
|
||||
if (get_matcher_type(matcher_value, &matcher_type) == -1) {
|
||||
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1085,7 +1118,7 @@ BaseExceptionGroup_split(PyObject *self, PyObject *args)
|
|||
bool construct_rest = true;
|
||||
if (exceptiongroup_split_recursive(
|
||||
self, matcher_type, matcher_value,
|
||||
construct_rest, &split_result) == -1) {
|
||||
construct_rest, &split_result) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1108,7 +1141,7 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
|||
}
|
||||
|
||||
_exceptiongroup_split_matcher_type matcher_type;
|
||||
if (get_matcher_type(matcher_value, &matcher_type) == -1) {
|
||||
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1116,7 +1149,7 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
|||
bool construct_rest = false;
|
||||
if (exceptiongroup_split_recursive(
|
||||
self, matcher_type, matcher_value,
|
||||
construct_rest, &split_result) == -1) {
|
||||
construct_rest, &split_result) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1128,6 +1161,85 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
|||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
collect_exception_group_leaves(PyObject *exc, PyObject *leaves)
|
||||
{
|
||||
if (Py_IsNone(exc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(PyExceptionInstance_Check(exc));
|
||||
assert(PySet_Check(leaves));
|
||||
|
||||
/* Add all leaf exceptions in exc to the leaves set */
|
||||
|
||||
if (!_PyBaseExceptionGroup_Check(exc)) {
|
||||
if (PySet_Add(leaves, exc) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
PyBaseExceptionGroupObject *eg = _PyBaseExceptionGroupObject_cast(exc);
|
||||
Py_ssize_t num_excs = PyTuple_GET_SIZE(eg->excs);
|
||||
/* recursive calls */
|
||||
for (Py_ssize_t i = 0; i < num_excs; i++) {
|
||||
PyObject *e = PyTuple_GET_ITEM(eg->excs, i);
|
||||
if (Py_EnterRecursiveCall(" in collect_exception_group_leaves")) {
|
||||
return -1;
|
||||
}
|
||||
int res = collect_exception_group_leaves(e, leaves);
|
||||
Py_LeaveRecursiveCall();
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function is used by the interpreter to construct reraised
|
||||
* exception groups. It takes an exception group eg and a list
|
||||
* of exception groups keep and returns the sub-exception group
|
||||
* of eg which contains all leaf exceptions that are contained
|
||||
* in any exception group in keep.
|
||||
*/
|
||||
PyObject *
|
||||
_PyExc_ExceptionGroupProjection(PyObject *eg, PyObject *keep)
|
||||
{
|
||||
assert(_PyBaseExceptionGroup_Check(eg));
|
||||
assert(PyList_CheckExact(keep));
|
||||
|
||||
PyObject *leaves = PySet_New(NULL);
|
||||
if (!leaves) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_ssize_t n = PyList_GET_SIZE(keep);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *e = PyList_GET_ITEM(keep, i);
|
||||
assert(e != NULL);
|
||||
assert(_PyBaseExceptionGroup_Check(e));
|
||||
if (collect_exception_group_leaves(e, leaves) < 0) {
|
||||
Py_DECREF(leaves);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_exceptiongroup_split_result split_result;
|
||||
bool construct_rest = false;
|
||||
int err = exceptiongroup_split_recursive(
|
||||
eg, EXCEPTION_GROUP_MATCH_INSTANCES, leaves,
|
||||
construct_rest, &split_result);
|
||||
Py_DECREF(leaves);
|
||||
if (err < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *result = split_result.match ?
|
||||
split_result.match : Py_NewRef(Py_None);
|
||||
assert(split_result.rest == NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMemberDef BaseExceptionGroup_members[] = {
|
||||
{"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY,
|
||||
PyDoc_STR("exception message")},
|
||||
|
|
|
@ -207,6 +207,7 @@ mark_stacks(PyCodeObject *code_obj, int len)
|
|||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_IF_NOT_EXC_MATCH:
|
||||
case JUMP_IF_NOT_EG_MATCH:
|
||||
{
|
||||
int64_t target_stack;
|
||||
int j = get_arg(code, i);
|
||||
|
@ -214,7 +215,9 @@ mark_stacks(PyCodeObject *code_obj, int len)
|
|||
if (stacks[j] == UNINITIALIZED && j < i) {
|
||||
todo = 1;
|
||||
}
|
||||
if (opcode == JUMP_IF_NOT_EXC_MATCH) {
|
||||
if (opcode == JUMP_IF_NOT_EXC_MATCH ||
|
||||
opcode == JUMP_IF_NOT_EG_MATCH)
|
||||
{
|
||||
next_stack = pop_value(pop_value(next_stack));
|
||||
target_stack = next_stack;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ module Python
|
|||
|
||||
| Raise(expr? exc, expr? cause)
|
||||
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
|
||||
| TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
|
||||
| Assert(expr test, expr? msg)
|
||||
|
||||
| Import(alias* names)
|
||||
|
|
7452
Parser/parser.c
7452
Parser/parser.c
File diff suppressed because it is too large
Load Diff
|
@ -146,6 +146,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
|
|||
Py_CLEAR(state->Sub_singleton);
|
||||
Py_CLEAR(state->Sub_type);
|
||||
Py_CLEAR(state->Subscript_type);
|
||||
Py_CLEAR(state->TryStar_type);
|
||||
Py_CLEAR(state->Try_type);
|
||||
Py_CLEAR(state->Tuple_type);
|
||||
Py_CLEAR(state->TypeIgnore_type);
|
||||
|
@ -486,6 +487,12 @@ static const char * const Try_fields[]={
|
|||
"orelse",
|
||||
"finalbody",
|
||||
};
|
||||
static const char * const TryStar_fields[]={
|
||||
"body",
|
||||
"handlers",
|
||||
"orelse",
|
||||
"finalbody",
|
||||
};
|
||||
static const char * const Assert_fields[]={
|
||||
"test",
|
||||
"msg",
|
||||
|
@ -1139,6 +1146,7 @@ init_types(struct ast_state *state)
|
|||
" | Match(expr subject, match_case* cases)\n"
|
||||
" | Raise(expr? exc, expr? cause)\n"
|
||||
" | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
|
||||
" | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
|
||||
" | Assert(expr test, expr? msg)\n"
|
||||
" | Import(alias* names)\n"
|
||||
" | ImportFrom(identifier? module, alias* names, int? level)\n"
|
||||
|
@ -1254,6 +1262,10 @@ init_types(struct ast_state *state)
|
|||
state->Try_type = make_type(state, "Try", state->stmt_type, Try_fields, 4,
|
||||
"Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
|
||||
if (!state->Try_type) return 0;
|
||||
state->TryStar_type = make_type(state, "TryStar", state->stmt_type,
|
||||
TryStar_fields, 4,
|
||||
"TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
|
||||
if (!state->TryStar_type) return 0;
|
||||
state->Assert_type = make_type(state, "Assert", state->stmt_type,
|
||||
Assert_fields, 2,
|
||||
"Assert(expr test, expr? msg)");
|
||||
|
@ -2379,6 +2391,28 @@ _PyAST_Try(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
|||
return p;
|
||||
}
|
||||
|
||||
stmt_ty
|
||||
_PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
||||
asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int lineno,
|
||||
int col_offset, int end_lineno, int end_col_offset, PyArena
|
||||
*arena)
|
||||
{
|
||||
stmt_ty p;
|
||||
p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p));
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->kind = TryStar_kind;
|
||||
p->v.TryStar.body = body;
|
||||
p->v.TryStar.handlers = handlers;
|
||||
p->v.TryStar.orelse = orelse;
|
||||
p->v.TryStar.finalbody = finalbody;
|
||||
p->lineno = lineno;
|
||||
p->col_offset = col_offset;
|
||||
p->end_lineno = end_lineno;
|
||||
p->end_col_offset = end_col_offset;
|
||||
return p;
|
||||
}
|
||||
|
||||
stmt_ty
|
||||
_PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int
|
||||
end_lineno, int end_col_offset, PyArena *arena)
|
||||
|
@ -4049,6 +4083,34 @@ ast2obj_stmt(struct ast_state *state, void* _o)
|
|||
goto failed;
|
||||
Py_DECREF(value);
|
||||
break;
|
||||
case TryStar_kind:
|
||||
tp = (PyTypeObject *)state->TryStar_type;
|
||||
result = PyType_GenericNew(tp, NULL, NULL);
|
||||
if (!result) goto failed;
|
||||
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.body, ast2obj_stmt);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttr(result, state->body, value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.handlers,
|
||||
ast2obj_excepthandler);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttr(result, state->handlers, value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.orelse,
|
||||
ast2obj_stmt);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttr(result, state->orelse, value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.finalbody,
|
||||
ast2obj_stmt);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttr(result, state->finalbody, value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
break;
|
||||
case Assert_kind:
|
||||
tp = (PyTypeObject *)state->Assert_type;
|
||||
result = PyType_GenericNew(tp, NULL, NULL);
|
||||
|
@ -7477,6 +7539,170 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
|
|||
if (*out == NULL) goto failed;
|
||||
return 0;
|
||||
}
|
||||
tp = state->TryStar_type;
|
||||
isinstance = PyObject_IsInstance(obj, tp);
|
||||
if (isinstance == -1) {
|
||||
return 1;
|
||||
}
|
||||
if (isinstance) {
|
||||
asdl_stmt_seq* body;
|
||||
asdl_excepthandler_seq* handlers;
|
||||
asdl_stmt_seq* orelse;
|
||||
asdl_stmt_seq* finalbody;
|
||||
|
||||
if (_PyObject_LookupAttr(obj, state->body, &tmp) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (tmp == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from TryStar");
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
Py_ssize_t len;
|
||||
Py_ssize_t i;
|
||||
if (!PyList_Check(tmp)) {
|
||||
PyErr_Format(PyExc_TypeError, "TryStar field \"body\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
|
||||
goto failed;
|
||||
}
|
||||
len = PyList_GET_SIZE(tmp);
|
||||
body = _Py_asdl_stmt_seq_new(len, arena);
|
||||
if (body == NULL) goto failed;
|
||||
for (i = 0; i < len; i++) {
|
||||
stmt_ty val;
|
||||
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
|
||||
Py_INCREF(tmp2);
|
||||
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
|
||||
goto failed;
|
||||
}
|
||||
res = obj2ast_stmt(state, tmp2, &val, arena);
|
||||
Py_LeaveRecursiveCall();
|
||||
Py_DECREF(tmp2);
|
||||
if (res != 0) goto failed;
|
||||
if (len != PyList_GET_SIZE(tmp)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"body\" changed size during iteration");
|
||||
goto failed;
|
||||
}
|
||||
asdl_seq_SET(body, i, val);
|
||||
}
|
||||
Py_CLEAR(tmp);
|
||||
}
|
||||
if (_PyObject_LookupAttr(obj, state->handlers, &tmp) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (tmp == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "required field \"handlers\" missing from TryStar");
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
Py_ssize_t len;
|
||||
Py_ssize_t i;
|
||||
if (!PyList_Check(tmp)) {
|
||||
PyErr_Format(PyExc_TypeError, "TryStar field \"handlers\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
|
||||
goto failed;
|
||||
}
|
||||
len = PyList_GET_SIZE(tmp);
|
||||
handlers = _Py_asdl_excepthandler_seq_new(len, arena);
|
||||
if (handlers == NULL) goto failed;
|
||||
for (i = 0; i < len; i++) {
|
||||
excepthandler_ty val;
|
||||
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
|
||||
Py_INCREF(tmp2);
|
||||
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
|
||||
goto failed;
|
||||
}
|
||||
res = obj2ast_excepthandler(state, tmp2, &val, arena);
|
||||
Py_LeaveRecursiveCall();
|
||||
Py_DECREF(tmp2);
|
||||
if (res != 0) goto failed;
|
||||
if (len != PyList_GET_SIZE(tmp)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"handlers\" changed size during iteration");
|
||||
goto failed;
|
||||
}
|
||||
asdl_seq_SET(handlers, i, val);
|
||||
}
|
||||
Py_CLEAR(tmp);
|
||||
}
|
||||
if (_PyObject_LookupAttr(obj, state->orelse, &tmp) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (tmp == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from TryStar");
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
Py_ssize_t len;
|
||||
Py_ssize_t i;
|
||||
if (!PyList_Check(tmp)) {
|
||||
PyErr_Format(PyExc_TypeError, "TryStar field \"orelse\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
|
||||
goto failed;
|
||||
}
|
||||
len = PyList_GET_SIZE(tmp);
|
||||
orelse = _Py_asdl_stmt_seq_new(len, arena);
|
||||
if (orelse == NULL) goto failed;
|
||||
for (i = 0; i < len; i++) {
|
||||
stmt_ty val;
|
||||
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
|
||||
Py_INCREF(tmp2);
|
||||
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
|
||||
goto failed;
|
||||
}
|
||||
res = obj2ast_stmt(state, tmp2, &val, arena);
|
||||
Py_LeaveRecursiveCall();
|
||||
Py_DECREF(tmp2);
|
||||
if (res != 0) goto failed;
|
||||
if (len != PyList_GET_SIZE(tmp)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"orelse\" changed size during iteration");
|
||||
goto failed;
|
||||
}
|
||||
asdl_seq_SET(orelse, i, val);
|
||||
}
|
||||
Py_CLEAR(tmp);
|
||||
}
|
||||
if (_PyObject_LookupAttr(obj, state->finalbody, &tmp) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (tmp == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "required field \"finalbody\" missing from TryStar");
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
Py_ssize_t len;
|
||||
Py_ssize_t i;
|
||||
if (!PyList_Check(tmp)) {
|
||||
PyErr_Format(PyExc_TypeError, "TryStar field \"finalbody\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
|
||||
goto failed;
|
||||
}
|
||||
len = PyList_GET_SIZE(tmp);
|
||||
finalbody = _Py_asdl_stmt_seq_new(len, arena);
|
||||
if (finalbody == NULL) goto failed;
|
||||
for (i = 0; i < len; i++) {
|
||||
stmt_ty val;
|
||||
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
|
||||
Py_INCREF(tmp2);
|
||||
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
|
||||
goto failed;
|
||||
}
|
||||
res = obj2ast_stmt(state, tmp2, &val, arena);
|
||||
Py_LeaveRecursiveCall();
|
||||
Py_DECREF(tmp2);
|
||||
if (res != 0) goto failed;
|
||||
if (len != PyList_GET_SIZE(tmp)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"finalbody\" changed size during iteration");
|
||||
goto failed;
|
||||
}
|
||||
asdl_seq_SET(finalbody, i, val);
|
||||
}
|
||||
Py_CLEAR(tmp);
|
||||
}
|
||||
*out = _PyAST_TryStar(body, handlers, orelse, finalbody, lineno,
|
||||
col_offset, end_lineno, end_col_offset, arena);
|
||||
if (*out == NULL) goto failed;
|
||||
return 0;
|
||||
}
|
||||
tp = state->Assert_type;
|
||||
isinstance = PyObject_IsInstance(obj, tp);
|
||||
if (isinstance == -1) {
|
||||
|
@ -11687,6 +11913,9 @@ astmodule_exec(PyObject *m)
|
|||
if (PyModule_AddObjectRef(m, "Try", state->Try_type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (PyModule_AddObjectRef(m, "TryStar", state->TryStar_type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (PyModule_AddObjectRef(m, "Assert", state->Assert_type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
|
25
Python/ast.c
25
Python/ast.c
|
@ -817,6 +817,31 @@ validate_stmt(struct validator *state, stmt_ty stmt)
|
|||
(!asdl_seq_LEN(stmt->v.Try.orelse) ||
|
||||
validate_stmts(state, stmt->v.Try.orelse));
|
||||
break;
|
||||
case TryStar_kind:
|
||||
if (!validate_body(state, stmt->v.TryStar.body, "TryStar"))
|
||||
return 0;
|
||||
if (!asdl_seq_LEN(stmt->v.TryStar.handlers) &&
|
||||
!asdl_seq_LEN(stmt->v.TryStar.finalbody)) {
|
||||
PyErr_SetString(PyExc_ValueError, "TryStar has neither except handlers nor finalbody");
|
||||
return 0;
|
||||
}
|
||||
if (!asdl_seq_LEN(stmt->v.TryStar.handlers) &&
|
||||
asdl_seq_LEN(stmt->v.TryStar.orelse)) {
|
||||
PyErr_SetString(PyExc_ValueError, "TryStar has orelse but no except handlers");
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < asdl_seq_LEN(stmt->v.TryStar.handlers); i++) {
|
||||
excepthandler_ty handler = asdl_seq_GET(stmt->v.TryStar.handlers, i);
|
||||
if ((handler->v.ExceptHandler.type &&
|
||||
!validate_expr(state, handler->v.ExceptHandler.type, Load)) ||
|
||||
!validate_body(state, handler->v.ExceptHandler.body, "ExceptHandler"))
|
||||
return 0;
|
||||
}
|
||||
ret = (!asdl_seq_LEN(stmt->v.TryStar.finalbody) ||
|
||||
validate_stmts(state, stmt->v.TryStar.finalbody)) &&
|
||||
(!asdl_seq_LEN(stmt->v.TryStar.orelse) ||
|
||||
validate_stmts(state, stmt->v.TryStar.orelse));
|
||||
break;
|
||||
case Assert_kind:
|
||||
ret = validate_expr(state, stmt->v.Assert.test, Load) &&
|
||||
(!stmt->v.Assert.msg || validate_expr(state, stmt->v.Assert.msg, Load));
|
||||
|
|
|
@ -972,6 +972,12 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
|||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.orelse);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody);
|
||||
break;
|
||||
case TryStar_kind:
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.body);
|
||||
CALL_SEQ(astfold_excepthandler, excepthandler, node_->v.TryStar.handlers);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.orelse);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.finalbody);
|
||||
break;
|
||||
case Assert_kind:
|
||||
CALL(astfold_expr, expr_ty, node_->v.Assert.test);
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Assert.msg);
|
||||
|
|
355
Python/ceval.c
355
Python/ceval.c
|
@ -37,6 +37,7 @@
|
|||
#include "structmember.h" // struct PyMemberDef, T_OFFSET_EX
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef Py_DEBUG
|
||||
/* For debugging the interpreter: */
|
||||
|
@ -95,6 +96,7 @@ static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyOb
|
|||
static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
|
||||
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
|
||||
static int check_except_type_valid(PyThreadState *tstate, PyObject* right);
|
||||
static int check_except_star_type_valid(PyThreadState *tstate, PyObject* right);
|
||||
static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
|
||||
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
|
||||
static int get_exception_handler(PyCodeObject *, int, int*, int*, int*);
|
||||
|
@ -1090,6 +1092,11 @@ fail:
|
|||
|
||||
|
||||
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
|
||||
static PyObject *do_reraise_star(PyObject *excs, PyObject *orig);
|
||||
static int exception_group_match(
|
||||
PyObject *exc_type, PyObject* exc_value, PyObject *match_type,
|
||||
PyObject **match, PyObject **rest);
|
||||
|
||||
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
|
||||
|
||||
#ifdef Py_DEBUG
|
||||
|
@ -2727,6 +2734,7 @@ check_eval_breaker:
|
|||
type = exc_info->exc_type;
|
||||
value = exc_info->exc_value;
|
||||
traceback = exc_info->exc_traceback;
|
||||
|
||||
exc_info->exc_type = POP();
|
||||
exc_info->exc_value = POP();
|
||||
exc_info->exc_traceback = POP();
|
||||
|
@ -2791,6 +2799,36 @@ check_eval_breaker:
|
|||
goto exception_unwind;
|
||||
}
|
||||
|
||||
TARGET(PREP_RERAISE_STAR) {
|
||||
PyObject *excs = POP();
|
||||
assert(PyList_Check(excs));
|
||||
PyObject *orig = POP();
|
||||
|
||||
PyObject *val = do_reraise_star(excs, orig);
|
||||
Py_DECREF(excs);
|
||||
Py_DECREF(orig);
|
||||
|
||||
if (val == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
PyObject *lasti_unused = Py_NewRef(_PyLong_GetZero());
|
||||
PUSH(lasti_unused);
|
||||
if (!Py_IsNone(val)) {
|
||||
PyObject *tb = PyException_GetTraceback(val);
|
||||
PUSH(tb ? tb : Py_NewRef(Py_None));
|
||||
PUSH(val);
|
||||
PUSH(Py_NewRef(Py_TYPE(val)));
|
||||
}
|
||||
else {
|
||||
// nothing to reraise
|
||||
PUSH(Py_NewRef(Py_None));
|
||||
PUSH(val);
|
||||
PUSH(Py_NewRef(Py_None));
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(END_ASYNC_FOR) {
|
||||
PyObject *exc = POP();
|
||||
PyObject *val = POP();
|
||||
|
@ -3922,6 +3960,83 @@ check_eval_breaker:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(JUMP_IF_NOT_EG_MATCH) {
|
||||
PyObject *match_type = POP();
|
||||
PyObject *exc_type = TOP();
|
||||
PyObject *exc_value = SECOND();
|
||||
if (check_except_star_type_valid(tstate, match_type) < 0) {
|
||||
Py_DECREF(match_type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
PyObject *match = NULL, *rest = NULL;
|
||||
int res = exception_group_match(exc_type, exc_value,
|
||||
match_type, &match, &rest);
|
||||
Py_DECREF(match_type);
|
||||
if (res < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (match == NULL || rest == NULL) {
|
||||
assert(match == NULL);
|
||||
assert(rest == NULL);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (Py_IsNone(match)) {
|
||||
Py_DECREF(match);
|
||||
Py_XDECREF(rest);
|
||||
/* no match - jump to target */
|
||||
JUMPTO(oparg);
|
||||
}
|
||||
else {
|
||||
|
||||
/* Total or partial match - update the stack from
|
||||
* [tb, val, exc]
|
||||
* to
|
||||
* [tb, rest, exc, tb, match, exc]
|
||||
* (rest can be Py_None)
|
||||
*/
|
||||
|
||||
|
||||
PyObject *type = TOP();
|
||||
PyObject *val = SECOND();
|
||||
PyObject *tb = THIRD();
|
||||
|
||||
if (!Py_IsNone(rest)) {
|
||||
/* tb remains the same */
|
||||
SET_TOP(Py_NewRef(Py_TYPE(rest)));
|
||||
SET_SECOND(Py_NewRef(rest));
|
||||
SET_THIRD(Py_NewRef(tb));
|
||||
}
|
||||
else {
|
||||
SET_TOP(Py_NewRef(Py_None));
|
||||
SET_SECOND(Py_NewRef(Py_None));
|
||||
SET_THIRD(Py_NewRef(Py_None));
|
||||
}
|
||||
/* Push match */
|
||||
|
||||
PUSH(Py_NewRef(tb));
|
||||
PUSH(Py_NewRef(match));
|
||||
PUSH(Py_NewRef(Py_TYPE(match)));
|
||||
|
||||
// set exc_info to the current match
|
||||
PyErr_SetExcInfo(
|
||||
Py_NewRef(Py_TYPE(match)),
|
||||
Py_NewRef(match),
|
||||
Py_NewRef(tb));
|
||||
|
||||
Py_DECREF(tb);
|
||||
Py_DECREF(val);
|
||||
Py_DECREF(type);
|
||||
|
||||
Py_DECREF(match);
|
||||
Py_DECREF(rest);
|
||||
}
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(JUMP_IF_NOT_EXC_MATCH) {
|
||||
PyObject *right = POP();
|
||||
ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND());
|
||||
|
@ -3931,17 +4046,12 @@ check_eval_breaker:
|
|||
Py_DECREF(right);
|
||||
goto error;
|
||||
}
|
||||
|
||||
int res = PyErr_GivenExceptionMatches(left, right);
|
||||
Py_DECREF(right);
|
||||
if (res > 0) {
|
||||
/* Exception matches -- Do nothing */;
|
||||
}
|
||||
else if (res == 0) {
|
||||
if (res == 0) {
|
||||
JUMPTO(oparg);
|
||||
}
|
||||
else {
|
||||
goto error;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
@ -6127,6 +6237,196 @@ raise_error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Logic for matching an exception in an except* clause (too
|
||||
complicated for inlining).
|
||||
*/
|
||||
|
||||
static int
|
||||
exception_group_match(PyObject *exc_type, PyObject* exc_value,
|
||||
PyObject *match_type, PyObject **match, PyObject **rest)
|
||||
{
|
||||
if (Py_IsNone(exc_type)) {
|
||||
assert(Py_IsNone(exc_value));
|
||||
*match = Py_NewRef(Py_None);
|
||||
*rest = Py_NewRef(Py_None);
|
||||
return 0;
|
||||
}
|
||||
assert(PyExceptionClass_Check(exc_type));
|
||||
assert(PyExceptionInstance_Check(exc_value));
|
||||
|
||||
if (PyErr_GivenExceptionMatches(exc_type, match_type)) {
|
||||
/* Full match of exc itself */
|
||||
bool is_eg = _PyBaseExceptionGroup_Check(exc_value);
|
||||
if (is_eg) {
|
||||
*match = Py_NewRef(exc_value);
|
||||
}
|
||||
else {
|
||||
/* naked exception - wrap it */
|
||||
PyObject *excs = PyTuple_Pack(1, exc_value);
|
||||
if (excs == NULL) {
|
||||
return -1;
|
||||
}
|
||||
PyObject *wrapped = _PyExc_CreateExceptionGroup("", excs);
|
||||
Py_DECREF(excs);
|
||||
if (wrapped == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*match = wrapped;
|
||||
}
|
||||
*rest = Py_NewRef(Py_None);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* exc_value does not match match_type.
|
||||
* Check for partial match if it's an exception group.
|
||||
*/
|
||||
if (_PyBaseExceptionGroup_Check(exc_value)) {
|
||||
PyObject *pair = PyObject_CallMethod(exc_value, "split", "(O)",
|
||||
match_type);
|
||||
if (pair == NULL) {
|
||||
return -1;
|
||||
}
|
||||
assert(PyTuple_CheckExact(pair));
|
||||
assert(PyTuple_GET_SIZE(pair) == 2);
|
||||
*match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
|
||||
*rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
|
||||
Py_DECREF(pair);
|
||||
return 0;
|
||||
}
|
||||
/* no match */
|
||||
*match = Py_NewRef(Py_None);
|
||||
*rest = Py_NewRef(Py_None);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Logic for the final raise/reraise of a try-except* contruct
|
||||
(too complicated for inlining).
|
||||
*/
|
||||
|
||||
static bool
|
||||
is_same_exception_metadata(PyObject *exc1, PyObject *exc2)
|
||||
{
|
||||
assert(PyExceptionInstance_Check(exc1));
|
||||
assert(PyExceptionInstance_Check(exc2));
|
||||
|
||||
PyObject *tb1 = PyException_GetTraceback(exc1);
|
||||
PyObject *ctx1 = PyException_GetContext(exc1);
|
||||
PyObject *cause1 = PyException_GetCause(exc1);
|
||||
PyObject *tb2 = PyException_GetTraceback(exc2);
|
||||
PyObject *ctx2 = PyException_GetContext(exc2);
|
||||
PyObject *cause2 = PyException_GetCause(exc2);
|
||||
|
||||
bool result = (Py_Is(tb1, tb2) &&
|
||||
Py_Is(ctx1, ctx2) &&
|
||||
Py_Is(cause1, cause2));
|
||||
|
||||
Py_XDECREF(tb1);
|
||||
Py_XDECREF(ctx1);
|
||||
Py_XDECREF(cause1);
|
||||
Py_XDECREF(tb2);
|
||||
Py_XDECREF(ctx2);
|
||||
Py_XDECREF(cause2);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
excs: a list of exceptions to raise/reraise
|
||||
orig: the original except that was caught
|
||||
|
||||
Calculates an exception group to raise. It contains
|
||||
all exceptions in excs, where those that were reraised
|
||||
have same nesting structure as in orig, and those that
|
||||
were raised (if any) are added as siblings in a new EG.
|
||||
|
||||
Returns NULL and sets an exception on failure.
|
||||
*/
|
||||
static PyObject *
|
||||
do_reraise_star(PyObject *excs, PyObject *orig)
|
||||
{
|
||||
assert(PyList_Check(excs));
|
||||
assert(PyExceptionInstance_Check(orig));
|
||||
|
||||
Py_ssize_t numexcs = PyList_GET_SIZE(excs);
|
||||
|
||||
if (numexcs == 0) {
|
||||
return Py_NewRef(Py_None);
|
||||
}
|
||||
|
||||
if (!_PyBaseExceptionGroup_Check(orig)) {
|
||||
/* a naked exception was caught and wrapped. Only one except* clause
|
||||
* could have executed,so there is at most one exception to raise.
|
||||
*/
|
||||
|
||||
assert(numexcs == 1 || (numexcs == 2 && PyList_GET_ITEM(excs, 1) == Py_None));
|
||||
|
||||
PyObject *e = PyList_GET_ITEM(excs, 0);
|
||||
assert(e != NULL);
|
||||
return Py_NewRef(e);
|
||||
}
|
||||
|
||||
|
||||
PyObject *raised_list = PyList_New(0);
|
||||
if (raised_list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject* reraised_list = PyList_New(0);
|
||||
if (reraised_list == NULL) {
|
||||
Py_DECREF(raised_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now we are holding refs to raised_list and reraised_list */
|
||||
|
||||
PyObject *result = NULL;
|
||||
|
||||
/* Split excs into raised and reraised by comparing metadata with orig */
|
||||
for (Py_ssize_t i = 0; i < numexcs; i++) {
|
||||
PyObject *e = PyList_GET_ITEM(excs, i);
|
||||
assert(e != NULL);
|
||||
if (Py_IsNone(e)) {
|
||||
continue;
|
||||
}
|
||||
bool is_reraise = is_same_exception_metadata(e, orig);
|
||||
PyObject *append_list = is_reraise ? reraised_list : raised_list;
|
||||
if (PyList_Append(append_list, e) < 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *reraised_eg = _PyExc_ExceptionGroupProjection(orig, reraised_list);
|
||||
if (reraised_eg == NULL) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!Py_IsNone(reraised_eg)) {
|
||||
assert(is_same_exception_metadata(reraised_eg, orig));
|
||||
}
|
||||
|
||||
Py_ssize_t num_raised = PyList_GET_SIZE(raised_list);
|
||||
if (num_raised == 0) {
|
||||
result = reraised_eg;
|
||||
}
|
||||
else if (num_raised > 0) {
|
||||
int res = 0;
|
||||
if (!Py_IsNone(reraised_eg)) {
|
||||
res = PyList_Append(raised_list, reraised_eg);
|
||||
}
|
||||
Py_DECREF(reraised_eg);
|
||||
if (res < 0) {
|
||||
goto done;
|
||||
}
|
||||
result = _PyExc_CreateExceptionGroup("", raised_list);
|
||||
if (result == NULL) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
Py_XDECREF(raised_list);
|
||||
Py_XDECREF(reraised_list);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Iterate v argcnt times and store the results on the stack (via decreasing
|
||||
sp). Return 1 for success, 0 if error.
|
||||
|
||||
|
@ -7020,10 +7320,12 @@ import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v)
|
|||
return err;
|
||||
}
|
||||
|
||||
|
||||
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
|
||||
"BaseException is not allowed"
|
||||
|
||||
#define CANNOT_EXCEPT_STAR_EG "catching ExceptionGroup with except* "\
|
||||
"is not allowed. Use except instead."
|
||||
|
||||
static int
|
||||
check_except_type_valid(PyThreadState *tstate, PyObject* right)
|
||||
{
|
||||
|
@ -7049,6 +7351,43 @@ check_except_type_valid(PyThreadState *tstate, PyObject* right)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_except_star_type_valid(PyThreadState *tstate, PyObject* right)
|
||||
{
|
||||
if (check_except_type_valid(tstate, right) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reject except *ExceptionGroup */
|
||||
|
||||
int is_subclass = 0;
|
||||
if (PyTuple_Check(right)) {
|
||||
Py_ssize_t length = PyTuple_GET_SIZE(right);
|
||||
for (Py_ssize_t i = 0; i < length; i++) {
|
||||
PyObject *exc = PyTuple_GET_ITEM(right, i);
|
||||
is_subclass = PyObject_IsSubclass(exc, PyExc_BaseExceptionGroup);
|
||||
if (is_subclass < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (is_subclass) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
is_subclass = PyObject_IsSubclass(right, PyExc_BaseExceptionGroup);
|
||||
if (is_subclass < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (is_subclass) {
|
||||
_PyErr_SetString(tstate, PyExc_TypeError,
|
||||
CANNOT_EXCEPT_STAR_EG);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_args_iterable(PyThreadState *tstate, PyObject *func, PyObject *args)
|
||||
{
|
||||
|
|
339
Python/compile.c
339
Python/compile.c
|
@ -175,7 +175,7 @@ compiler IR.
|
|||
|
||||
enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
|
||||
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER,
|
||||
ASYNC_COMPREHENSION_GENERATOR };
|
||||
EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR };
|
||||
|
||||
struct fblockinfo {
|
||||
enum fblocktype fb_type;
|
||||
|
@ -323,6 +323,7 @@ static int compiler_call_helper(struct compiler *c, int n,
|
|||
asdl_expr_seq *args,
|
||||
asdl_keyword_seq *keywords);
|
||||
static int compiler_try_except(struct compiler *, stmt_ty);
|
||||
static int compiler_try_star_except(struct compiler *, stmt_ty);
|
||||
static int compiler_set_qualname(struct compiler *);
|
||||
|
||||
static int compiler_sync_comprehension_generator(
|
||||
|
@ -1094,6 +1095,8 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
return -1;
|
||||
case JUMP_IF_NOT_EXC_MATCH:
|
||||
return -1;
|
||||
case JUMP_IF_NOT_EG_MATCH:
|
||||
return jump > 0 ? -1 : 2;
|
||||
case IMPORT_NAME:
|
||||
return -1;
|
||||
case IMPORT_FROM:
|
||||
|
@ -1131,6 +1134,8 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
* if an exception be raised. */
|
||||
return jump ? -1 + 4 : 0;
|
||||
|
||||
case PREP_RERAISE_STAR:
|
||||
return 2;
|
||||
case RERAISE:
|
||||
return -3;
|
||||
case PUSH_EXC_INFO:
|
||||
|
@ -1755,6 +1760,18 @@ find_ann(asdl_stmt_seq *stmts)
|
|||
find_ann(st->v.Try.finalbody) ||
|
||||
find_ann(st->v.Try.orelse);
|
||||
break;
|
||||
case TryStar_kind:
|
||||
for (j = 0; j < asdl_seq_LEN(st->v.TryStar.handlers); j++) {
|
||||
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
|
||||
st->v.TryStar.handlers, j);
|
||||
if (find_ann(handler->v.ExceptHandler.body)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
res = find_ann(st->v.TryStar.body) ||
|
||||
find_ann(st->v.TryStar.finalbody) ||
|
||||
find_ann(st->v.TryStar.orelse);
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
@ -1816,6 +1833,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
|||
switch (info->fb_type) {
|
||||
case WHILE_LOOP:
|
||||
case EXCEPTION_HANDLER:
|
||||
case EXCEPTION_GROUP_HANDLER:
|
||||
case ASYNC_COMPREHENSION_GENERATOR:
|
||||
return 1;
|
||||
|
||||
|
@ -1919,6 +1937,10 @@ compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblock
|
|||
return 1;
|
||||
}
|
||||
struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-1];
|
||||
if (top->fb_type == EXCEPTION_GROUP_HANDLER) {
|
||||
return compiler_error(
|
||||
c, "'break', 'continue' and 'return' cannot appear in an except* block");
|
||||
}
|
||||
if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) {
|
||||
*loop = top;
|
||||
return 1;
|
||||
|
@ -3202,6 +3224,62 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_try_star_finally(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
basicblock *body = compiler_new_block(c);
|
||||
if (body == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *end = compiler_new_block(c);
|
||||
if (!end) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *exit = compiler_new_block(c);
|
||||
if (!exit) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *cleanup = compiler_new_block(c);
|
||||
if (!cleanup) {
|
||||
return 0;
|
||||
}
|
||||
/* `try` block */
|
||||
ADDOP_JUMP(c, SETUP_FINALLY, end);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.TryStar.finalbody)) {
|
||||
return 0;
|
||||
}
|
||||
if (s->v.TryStar.handlers && asdl_seq_LEN(s->v.TryStar.handlers)) {
|
||||
if (!compiler_try_star_except(c, s)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VISIT_SEQ(c, stmt, s->v.TryStar.body);
|
||||
}
|
||||
ADDOP_NOLINE(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, FINALLY_TRY, body);
|
||||
VISIT_SEQ(c, stmt, s->v.TryStar.finalbody);
|
||||
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, exit);
|
||||
/* `finally` block */
|
||||
compiler_use_next_block(c, end);
|
||||
|
||||
UNSET_LOC(c);
|
||||
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
|
||||
ADDOP(c, PUSH_EXC_INFO);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
VISIT_SEQ(c, stmt, s->v.TryStar.finalbody);
|
||||
compiler_pop_fblock(c, FINALLY_END, end);
|
||||
ADDOP_I(c, RERAISE, 0);
|
||||
compiler_use_next_block(c, cleanup);
|
||||
ADDOP(c, POP_EXCEPT_AND_RERAISE);
|
||||
compiler_use_next_block(c, exit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...":
|
||||
(The contents of the value stack is shown in [], with the top
|
||||
|
@ -3360,6 +3438,253 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
ADDOP(c, POP_EXCEPT_AND_RERAISE);
|
||||
compiler_use_next_block(c, orelse);
|
||||
VISIT_SEQ(c, stmt, s->v.Try.orelse);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Code generated for "try: S except* E1 as V1: S1 except* E2 as V2: S2 ...":
|
||||
(The contents of the value stack is shown in [], with the top
|
||||
at the right; 'tb' is trace-back info, 'val' the exception instance,
|
||||
and 'typ' the exception's type.)
|
||||
|
||||
Value stack Label Instruction Argument
|
||||
[] SETUP_FINALLY L1
|
||||
[] <code for S>
|
||||
[] POP_BLOCK
|
||||
[] JUMP_FORWARD L0
|
||||
|
||||
[tb, val, typ] L1: DUP_TOP_TWO ) save a copy of the
|
||||
[tb, val, typ, orig, typ] POP_TOP ) original raised exception
|
||||
[tb, val, typ, orig] ROT_FOUR )
|
||||
[orig, tb, val, typ] BUILD_LIST ) list for raised/reraised
|
||||
[orig, tb, val, typ, res] ROT_FOUR ) exceptions ("result")
|
||||
|
||||
[orig, res, tb, val, typ] <evaluate E1> )
|
||||
[orig, res, tb, val, typ, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1
|
||||
|
||||
[orig, res, tb, rest, typ, tb, match, typ] POP
|
||||
[orig, res, tb, rest, typ, tb, match] <assign to V1> (or POP if no V1)
|
||||
[orig, res, tb, rest, typ, tb] POP
|
||||
|
||||
[orig, res, tb, rest, typ] SETUP_FINALLY R1
|
||||
[orig, res, tb, rest, typ] <code for S1>
|
||||
[orig, res, tb, rest, typ] JUMP_FORWARD L2
|
||||
|
||||
[orig, res, tb, rest, typ, i, tb, v, t] R1: POP ) exception raised in except* body
|
||||
[orig, res, tb, rest, typ, i, tb, v] LIST_APPEND 6 ) add it to res
|
||||
[orig, res, tb, rest, typ, i, tb] POP
|
||||
[orig, res, tb, rest, typ, i] POP
|
||||
|
||||
[orig, res, tb, rest, typ] L2: <evaluate E2>
|
||||
.............................etc.......................
|
||||
|
||||
[orig, res, tb, rest, typ] Ln+1: POP ) add unhandled exception
|
||||
[orig, res, tb, rest] LIST_APPEND 2 ) to res (could be None)
|
||||
[orig, res, tb] POP
|
||||
|
||||
[orig, res] PREP_RERAISE_STAR
|
||||
[i, tb, val, typ] POP_JUMP_IF_TRUE RER
|
||||
[i, tb, val, typ] POP
|
||||
[i, tb, val] POP
|
||||
[i, tb] POP
|
||||
[i] POP
|
||||
[] JUMP_FORWARD L0
|
||||
|
||||
[i, tb, val, typ] RER: POP_EXCEPT_AND_RERAISE
|
||||
|
||||
[] L0: <next statement>
|
||||
*/
|
||||
static int
|
||||
compiler_try_star_except(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
basicblock *body = compiler_new_block(c);
|
||||
if (body == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *except = compiler_new_block(c);
|
||||
if (except == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *orelse = compiler_new_block(c);
|
||||
if (orelse == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *end = compiler_new_block(c);
|
||||
if (end == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *cleanup = compiler_new_block(c);
|
||||
if (cleanup == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *reraise_star = compiler_new_block(c);
|
||||
if (reraise_star == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ADDOP_JUMP(c, SETUP_FINALLY, except);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
VISIT_SEQ(c, stmt, s->v.TryStar.body);
|
||||
compiler_pop_fblock(c, TRY_EXCEPT, body);
|
||||
ADDOP_NOLINE(c, POP_BLOCK);
|
||||
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, orelse);
|
||||
Py_ssize_t n = asdl_seq_LEN(s->v.TryStar.handlers);
|
||||
compiler_use_next_block(c, except);
|
||||
|
||||
UNSET_LOC(c);
|
||||
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
|
||||
ADDOP(c, PUSH_EXC_INFO);
|
||||
/* Runtime will push a block here, so we need to account for that */
|
||||
if (!compiler_push_fblock(c, EXCEPTION_GROUP_HANDLER,
|
||||
NULL, NULL, "except handler")) {
|
||||
return 0;
|
||||
}
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
|
||||
s->v.TryStar.handlers, i);
|
||||
SET_LOC(c, handler);
|
||||
except = compiler_new_block(c);
|
||||
if (except == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (i == 0) {
|
||||
/* Push the original EG into the stack */
|
||||
/*
|
||||
[tb, val, exc] DUP_TOP_TWO
|
||||
[tb, val, exc, val, exc] POP_TOP
|
||||
[tb, val, exc, val] ROT_FOUR
|
||||
[val, tb, val, exc]
|
||||
*/
|
||||
ADDOP(c, DUP_TOP_TWO);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, ROT_FOUR);
|
||||
|
||||
/* create empty list for exceptions raised/reraise in the except* blocks */
|
||||
/*
|
||||
[val, tb, val, exc] BUILD_LIST
|
||||
[val, tb, val, exc, []] ROT_FOUR
|
||||
[val, [], tb, val, exc]
|
||||
*/
|
||||
ADDOP_I(c, BUILD_LIST, 0);
|
||||
ADDOP(c, ROT_FOUR);
|
||||
}
|
||||
if (handler->v.ExceptHandler.type) {
|
||||
VISIT(c, expr, handler->v.ExceptHandler.type);
|
||||
ADDOP_JUMP(c, JUMP_IF_NOT_EG_MATCH, except);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
ADDOP(c, POP_TOP); // exc_type
|
||||
|
||||
basicblock *cleanup_end = compiler_new_block(c);
|
||||
if (cleanup_end == NULL) {
|
||||
return 0;
|
||||
}
|
||||
basicblock *cleanup_body = compiler_new_block(c);
|
||||
if (cleanup_body == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (handler->v.ExceptHandler.name) {
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
}
|
||||
else {
|
||||
ADDOP(c, POP_TOP); // val
|
||||
}
|
||||
ADDOP(c, POP_TOP); // tb
|
||||
|
||||
/*
|
||||
try:
|
||||
# body
|
||||
except type as name:
|
||||
try:
|
||||
# body
|
||||
finally:
|
||||
name = None # in case body contains "del name"
|
||||
del name
|
||||
*/
|
||||
/* second try: */
|
||||
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup_end);
|
||||
compiler_use_next_block(c, cleanup_body);
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name))
|
||||
return 0;
|
||||
|
||||
/* second # body */
|
||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
|
||||
/* name = None; del name; # Mark as artificial */
|
||||
UNSET_LOC(c);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
if (handler->v.ExceptHandler.name) {
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
|
||||
}
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, except);
|
||||
|
||||
/* except: */
|
||||
compiler_use_next_block(c, cleanup_end);
|
||||
|
||||
/* name = None; del name; # Mark as artificial */
|
||||
UNSET_LOC(c);
|
||||
|
||||
if (handler->v.ExceptHandler.name) {
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
|
||||
}
|
||||
|
||||
/* add exception raised to the res list */
|
||||
ADDOP(c, POP_TOP); // type
|
||||
ADDOP_I(c, LIST_APPEND, 6); // exc
|
||||
ADDOP(c, POP_TOP); // tb
|
||||
ADDOP(c, POP_TOP); // lasti
|
||||
|
||||
ADDOP_JUMP(c, JUMP_ABSOLUTE, except);
|
||||
compiler_use_next_block(c, except);
|
||||
|
||||
if (i == n - 1) {
|
||||
/* Add exc to the list (if not None it's the unhandled part of the EG) */
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_I(c, LIST_APPEND, 2);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, reraise_star);
|
||||
}
|
||||
}
|
||||
/* Mark as artificial */
|
||||
UNSET_LOC(c);
|
||||
compiler_pop_fblock(c, EXCEPTION_GROUP_HANDLER, NULL);
|
||||
basicblock *reraise = compiler_new_block(c);
|
||||
if (!reraise) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
compiler_use_next_block(c, reraise_star);
|
||||
ADDOP(c, PREP_RERAISE_STAR);
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, reraise);
|
||||
NEXT_BLOCK(c);
|
||||
|
||||
/* Nothing to reraise - pop it */
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, reraise);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, POP_EXCEPT_AND_RERAISE);
|
||||
compiler_use_next_block(c, cleanup);
|
||||
ADDOP(c, POP_EXCEPT_AND_RERAISE);
|
||||
compiler_use_next_block(c, orelse);
|
||||
VISIT_SEQ(c, stmt, s->v.TryStar.orelse);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
@ -3372,6 +3697,16 @@ compiler_try(struct compiler *c, stmt_ty s) {
|
|||
return compiler_try_except(c, s);
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_try_star(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
if (s->v.TryStar.finalbody && asdl_seq_LEN(s->v.TryStar.finalbody)) {
|
||||
return compiler_try_star_finally(c, s);
|
||||
}
|
||||
else {
|
||||
return compiler_try_star_except(c, s);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_import_as(struct compiler *c, identifier name, identifier asname)
|
||||
|
@ -3634,6 +3969,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
|||
break;
|
||||
case Try_kind:
|
||||
return compiler_try(c, s);
|
||||
case TryStar_kind:
|
||||
return compiler_try_star(c, s);
|
||||
case Assert_kind:
|
||||
return compiler_assert(c, s);
|
||||
case Import_kind:
|
||||
|
|
|
@ -87,7 +87,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_SETUP_ANNOTATIONS,
|
||||
&&TARGET_YIELD_VALUE,
|
||||
&&TARGET_LOAD_CONST__LOAD_FAST,
|
||||
&&TARGET_STORE_FAST__STORE_FAST,
|
||||
&&TARGET_PREP_RERAISE_STAR,
|
||||
&&TARGET_POP_EXCEPT,
|
||||
&&TARGET_STORE_NAME,
|
||||
&&TARGET_DELETE_NAME,
|
||||
|
@ -122,11 +122,11 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_COPY,
|
||||
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
|
||||
&&TARGET_BINARY_OP,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_STORE_FAST__STORE_FAST,
|
||||
&&TARGET_LOAD_FAST,
|
||||
&&TARGET_STORE_FAST,
|
||||
&&TARGET_DELETE_FAST,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_JUMP_IF_NOT_EG_MATCH,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_GEN_START,
|
||||
&&TARGET_RAISE_VARARGS,
|
||||
|
|
|
@ -1345,6 +1345,12 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
|||
VISIT_SEQ(st, excepthandler, s->v.Try.handlers);
|
||||
VISIT_SEQ(st, stmt, s->v.Try.finalbody);
|
||||
break;
|
||||
case TryStar_kind:
|
||||
VISIT_SEQ(st, stmt, s->v.TryStar.body);
|
||||
VISIT_SEQ(st, stmt, s->v.TryStar.orelse);
|
||||
VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers);
|
||||
VISIT_SEQ(st, stmt, s->v.TryStar.finalbody);
|
||||
break;
|
||||
case Assert_kind:
|
||||
VISIT(st, expr, s->v.Assert.test);
|
||||
if (s->v.Assert.msg)
|
||||
|
|
Loading…
Reference in New Issue