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=[])
|
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)
|
.. class:: ExceptHandler(type, name, body)
|
||||||
|
|
||||||
A single ``except`` clause. ``type`` is the exception type it will match,
|
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
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
|
||||||
.. opcode:: JUMP_IF_NOT_EXC_MATCH (target)
|
.. 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,
|
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.
|
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.
|
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)
|
.. opcode:: JUMP_IF_TRUE_OR_POP (target)
|
||||||
|
|
||||||
If TOS is true, sets the bytecode counter to *target* and leaves TOS on the
|
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-sized items next.
|
||||||
|
|
||||||
|
PEP-654: Exception Groups and ``except*``.
|
||||||
|
(Contributed by Irit Katriel in :issue:`45292`.)
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
============
|
============
|
||||||
|
|
|
@ -403,6 +403,8 @@ try_stmt[stmt_ty]:
|
||||||
| invalid_try_stmt
|
| invalid_try_stmt
|
||||||
| 'try' &&':' b=block f=finally_block { _PyAST_Try(b, NULL, NULL, f, EXTRA) }
|
| '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_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
|
# Except statement
|
||||||
# ----------------
|
# ----------------
|
||||||
|
@ -413,6 +415,11 @@ except_block[excepthandler_ty]:
|
||||||
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
|
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
|
||||||
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
|
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
|
||||||
| invalid_except_stmt
|
| 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*]:
|
finally_block[asdl_stmt_seq*]:
|
||||||
| invalid_finally_stmt
|
| invalid_finally_stmt
|
||||||
| 'finally' &&':' a=block { a }
|
| 'finally' &&':' a=block { a }
|
||||||
|
@ -1192,11 +1199,14 @@ invalid_try_stmt:
|
||||||
| a='try' ':' NEWLINE !INDENT {
|
| a='try' ':' NEWLINE !INDENT {
|
||||||
RAISE_INDENTATION_ERROR("expected an indented block after 'try' statement on line %d", a->lineno) }
|
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' | '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:
|
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") }
|
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 ':'") }
|
||||||
|
| a='except' '*' (NEWLINE | ':') { RAISE_SYNTAX_ERROR("expected one or more exception types") }
|
||||||
invalid_finally_stmt:
|
invalid_finally_stmt:
|
||||||
| a='finally' ':' NEWLINE !INDENT {
|
| a='finally' ':' NEWLINE !INDENT {
|
||||||
RAISE_INDENTATION_ERROR("expected an indented block after 'finally' statement on line %d", a->lineno) }
|
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 {
|
| a='except' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
||||||
RAISE_INDENTATION_ERROR("expected an indented block after 'except' statement on line %d", a->lineno) }
|
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) }
|
| 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:
|
invalid_match_stmt:
|
||||||
| "match" subject_expr !':' { CHECK_VERSION(void*, 10, "Pattern matching is", RAISE_SYNTAX_ERROR("expected ':'") ) }
|
| "match" subject_expr !':' { CHECK_VERSION(void*, 10, "Pattern matching is", RAISE_SYNTAX_ERROR("expected ':'") ) }
|
||||||
| a="match" subject=subject_expr ':' NEWLINE !INDENT {
|
| 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,
|
AugAssign_kind=7, AnnAssign_kind=8, For_kind=9,
|
||||||
AsyncFor_kind=10, While_kind=11, If_kind=12, With_kind=13,
|
AsyncFor_kind=10, While_kind=11, If_kind=12, With_kind=13,
|
||||||
AsyncWith_kind=14, Match_kind=15, Raise_kind=16, Try_kind=17,
|
AsyncWith_kind=14, Match_kind=15, Raise_kind=16, Try_kind=17,
|
||||||
Assert_kind=18, Import_kind=19, ImportFrom_kind=20,
|
TryStar_kind=18, Assert_kind=19, Import_kind=20,
|
||||||
Global_kind=21, Nonlocal_kind=22, Expr_kind=23, Pass_kind=24,
|
ImportFrom_kind=21, Global_kind=22, Nonlocal_kind=23,
|
||||||
Break_kind=25, Continue_kind=26};
|
Expr_kind=24, Pass_kind=25, Break_kind=26, Continue_kind=27};
|
||||||
struct _stmt {
|
struct _stmt {
|
||||||
enum _stmt_kind kind;
|
enum _stmt_kind kind;
|
||||||
union {
|
union {
|
||||||
|
@ -295,6 +295,13 @@ struct _stmt {
|
||||||
asdl_stmt_seq *finalbody;
|
asdl_stmt_seq *finalbody;
|
||||||
} Try;
|
} Try;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
asdl_stmt_seq *body;
|
||||||
|
asdl_excepthandler_seq *handlers;
|
||||||
|
asdl_stmt_seq *orelse;
|
||||||
|
asdl_stmt_seq *finalbody;
|
||||||
|
} TryStar;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
expr_ty test;
|
expr_ty test;
|
||||||
expr_ty msg;
|
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
|
asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int
|
||||||
lineno, int col_offset, int end_lineno, int end_col_offset,
|
lineno, int col_offset, int end_lineno, int end_col_offset,
|
||||||
PyArena *arena);
|
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,
|
stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset,
|
||||||
int end_lineno, int end_col_offset, PyArena *arena);
|
int end_lineno, int end_col_offset, PyArena *arena);
|
||||||
stmt_ty _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
|
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_singleton;
|
||||||
PyObject *Sub_type;
|
PyObject *Sub_type;
|
||||||
PyObject *Subscript_type;
|
PyObject *Subscript_type;
|
||||||
|
PyObject *TryStar_type;
|
||||||
PyObject *Try_type;
|
PyObject *Try_type;
|
||||||
PyObject *Tuple_type;
|
PyObject *Tuple_type;
|
||||||
PyObject *TypeIgnore_type;
|
PyObject *TypeIgnore_type;
|
||||||
|
|
|
@ -92,6 +92,14 @@ PyAPI_FUNC(PyObject *) _PyErr_FormatFromCauseTstate(
|
||||||
const char *format,
|
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(int) _PyErr_CheckSignalsTstate(PyThreadState *tstate);
|
||||||
|
|
||||||
PyAPI_FUNC(void) _Py_DumpExtensionModules(int fd, PyInterpreterState *interp);
|
PyAPI_FUNC(void) _Py_DumpExtensionModules(int fd, PyInterpreterState *interp);
|
||||||
|
|
|
@ -45,6 +45,7 @@ extern "C" {
|
||||||
#define IMPORT_STAR 84
|
#define IMPORT_STAR 84
|
||||||
#define SETUP_ANNOTATIONS 85
|
#define SETUP_ANNOTATIONS 85
|
||||||
#define YIELD_VALUE 86
|
#define YIELD_VALUE 86
|
||||||
|
#define PREP_RERAISE_STAR 88
|
||||||
#define POP_EXCEPT 89
|
#define POP_EXCEPT 89
|
||||||
#define HAVE_ARGUMENT 90
|
#define HAVE_ARGUMENT 90
|
||||||
#define STORE_NAME 90
|
#define STORE_NAME 90
|
||||||
|
@ -83,6 +84,7 @@ extern "C" {
|
||||||
#define LOAD_FAST 124
|
#define LOAD_FAST 124
|
||||||
#define STORE_FAST 125
|
#define STORE_FAST 125
|
||||||
#define DELETE_FAST 126
|
#define DELETE_FAST 126
|
||||||
|
#define JUMP_IF_NOT_EG_MATCH 127
|
||||||
#define GEN_START 129
|
#define GEN_START 129
|
||||||
#define RAISE_VARARGS 130
|
#define RAISE_VARARGS 130
|
||||||
#define CALL_FUNCTION 131
|
#define CALL_FUNCTION 131
|
||||||
|
@ -161,7 +163,7 @@ extern "C" {
|
||||||
#define STORE_FAST__LOAD_FAST 80
|
#define STORE_FAST__LOAD_FAST 80
|
||||||
#define LOAD_FAST__LOAD_CONST 81
|
#define LOAD_FAST__LOAD_CONST 81
|
||||||
#define LOAD_CONST__LOAD_FAST 87
|
#define LOAD_CONST__LOAD_FAST 87
|
||||||
#define STORE_FAST__STORE_FAST 88
|
#define STORE_FAST__STORE_FAST 123
|
||||||
#define DO_TRACING 255
|
#define DO_TRACING 255
|
||||||
#ifdef NEED_OPCODE_JUMP_TABLES
|
#ifdef NEED_OPCODE_JUMP_TABLES
|
||||||
static uint32_t _PyOpcode_RelativeJump[8] = {
|
static uint32_t _PyOpcode_RelativeJump[8] = {
|
||||||
|
@ -178,7 +180,7 @@ static uint32_t _PyOpcode_Jump[8] = {
|
||||||
0U,
|
0U,
|
||||||
0U,
|
0U,
|
||||||
536870912U,
|
536870912U,
|
||||||
34586624U,
|
2182070272U,
|
||||||
0U,
|
0U,
|
||||||
0U,
|
0U,
|
||||||
0U,
|
0U,
|
||||||
|
|
21
Lib/ast.py
21
Lib/ast.py
|
@ -683,6 +683,7 @@ class _Unparser(NodeVisitor):
|
||||||
self._type_ignores = {}
|
self._type_ignores = {}
|
||||||
self._indent = 0
|
self._indent = 0
|
||||||
self._avoid_backslashes = _avoid_backslashes
|
self._avoid_backslashes = _avoid_backslashes
|
||||||
|
self._in_try_star = False
|
||||||
|
|
||||||
def interleave(self, inter, f, seq):
|
def interleave(self, inter, f, seq):
|
||||||
"""Call f on each item in seq, calling inter() in between."""
|
"""Call f on each item in seq, calling inter() in between."""
|
||||||
|
@ -953,7 +954,7 @@ class _Unparser(NodeVisitor):
|
||||||
self.write(" from ")
|
self.write(" from ")
|
||||||
self.traverse(node.cause)
|
self.traverse(node.cause)
|
||||||
|
|
||||||
def visit_Try(self, node):
|
def do_visit_try(self, node):
|
||||||
self.fill("try")
|
self.fill("try")
|
||||||
with self.block():
|
with self.block():
|
||||||
self.traverse(node.body)
|
self.traverse(node.body)
|
||||||
|
@ -968,8 +969,24 @@ class _Unparser(NodeVisitor):
|
||||||
with self.block():
|
with self.block():
|
||||||
self.traverse(node.finalbody)
|
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):
|
def visit_ExceptHandler(self, node):
|
||||||
self.fill("except")
|
self.fill("except*" if self._in_try_star else "except")
|
||||||
if node.type:
|
if node.type:
|
||||||
self.write(" ")
|
self.write(" ")
|
||||||
self.traverse(node.type)
|
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
|
# Python 3.11a3 3464 (bpo-45636: Merge numeric BINARY_*/INPLACE_* into
|
||||||
# BINARY_OP)
|
# BINARY_OP)
|
||||||
# Python 3.11a3 3465 (Add COPY_FREE_VARS opcode)
|
# 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
|
# 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
|
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||||
# in PC/launcher.c must also be updated.
|
# 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
|
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||||
|
|
||||||
_PYCACHE = '__pycache__'
|
_PYCACHE = '__pycache__'
|
||||||
|
|
|
@ -103,6 +103,7 @@ def_op('IMPORT_STAR', 84)
|
||||||
def_op('SETUP_ANNOTATIONS', 85)
|
def_op('SETUP_ANNOTATIONS', 85)
|
||||||
def_op('YIELD_VALUE', 86)
|
def_op('YIELD_VALUE', 86)
|
||||||
|
|
||||||
|
def_op('PREP_RERAISE_STAR', 88)
|
||||||
def_op('POP_EXCEPT', 89)
|
def_op('POP_EXCEPT', 89)
|
||||||
|
|
||||||
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
||||||
|
@ -150,6 +151,8 @@ haslocal.append(125)
|
||||||
def_op('DELETE_FAST', 126) # Local variable number
|
def_op('DELETE_FAST', 126) # Local variable number
|
||||||
haslocal.append(126)
|
haslocal.append(126)
|
||||||
|
|
||||||
|
jabs_op('JUMP_IF_NOT_EG_MATCH', 127)
|
||||||
|
|
||||||
def_op('GEN_START', 129) # Kind of generator/coroutine
|
def_op('GEN_START', 129) # Kind of generator/coroutine
|
||||||
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
|
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
|
||||||
def_op('CALL_FUNCTION', 131) # #args
|
def_op('CALL_FUNCTION', 131) # #args
|
||||||
|
|
|
@ -86,6 +86,8 @@ exec_tests = [
|
||||||
"try:\n pass\nexcept Exception:\n pass",
|
"try:\n pass\nexcept Exception:\n pass",
|
||||||
# TryFinally
|
# TryFinally
|
||||||
"try:\n pass\nfinally:\n pass",
|
"try:\n pass\nfinally:\n pass",
|
||||||
|
# TryStarExcept
|
||||||
|
"try:\n pass\nexcept* Exception:\n pass",
|
||||||
# Assert
|
# Assert
|
||||||
"assert v",
|
"assert v",
|
||||||
# Import
|
# Import
|
||||||
|
@ -1310,6 +1312,26 @@ class ASTValidatorTests(unittest.TestCase):
|
||||||
t = ast.Try([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
t = ast.Try([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
||||||
self.stmt(t, "must have Load context")
|
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):
|
def test_assert(self):
|
||||||
self.stmt(ast.Assert(ast.Name("x", ast.Store()), None),
|
self.stmt(ast.Assert(ast.Name("x", ast.Store()), None),
|
||||||
"must have Load context")
|
"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', [('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))], [('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', [('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', [('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', [('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)], []),
|
('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)
|
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):
|
def test_try_finally(self):
|
||||||
snippet = """
|
snippet = """
|
||||||
try:
|
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='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_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='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='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='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),
|
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='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='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='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='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='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),
|
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_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=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='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=192, starts_line=28, is_jump_target=True, 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=194, 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=196, 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='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=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=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=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=204, starts_line=23, is_jump_target=True, 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=206, starts_line=28, is_jump_target=False, 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=208, 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=210, 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='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=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=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=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='PUSH_EXC_INFO', opcode=35, 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=220, starts_line=None, is_jump_target=False, 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=222, 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=224, 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='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=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='RERAISE', opcode=119, arg=0, argval=0, 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='POP_EXCEPT_AND_RERAISE', opcode=37, arg=None, argval=None, argrepr='', 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),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# One last piece of inspect fodder to check the default line number handling
|
# 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):
|
def test_basics_subgroup_split__bad_arg_type(self):
|
||||||
bad_args = ["bad arg",
|
bad_args = ["bad arg",
|
||||||
OSError('instance not type'),
|
OSError('instance not type'),
|
||||||
[OSError('instance not type')],]
|
[OSError, TypeError],
|
||||||
|
(OSError, 42)]
|
||||||
for arg in bad_args:
|
for arg in bad_args:
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
self.eg.subgroup(arg)
|
self.eg.subgroup(arg)
|
||||||
|
|
|
@ -172,5 +172,283 @@ class ExceptionTestCase(unittest.TestCase):
|
||||||
self.assertTrue(hit_finally)
|
self.assertTrue(hit_finally)
|
||||||
self.assertTrue(hit_except)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -241,6 +241,8 @@ class ExceptionTests(unittest.TestCase):
|
||||||
check('def f():\n continue', 2, 3)
|
check('def f():\n continue', 2, 3)
|
||||||
check('def f():\n break', 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\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
|
# Errors thrown by tokenizer.c
|
||||||
check('(0x+1)', 1, 3)
|
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")
|
||||||
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):
|
def test_suite(self):
|
||||||
# simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
|
# simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
|
||||||
if 1: pass
|
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):
|
Traceback (most recent call last):
|
||||||
SyntaxError: expected 'except' or 'finally' block
|
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
|
Ensure that early = are not matched by the parser as invalid comparisons
|
||||||
>>> f(2, 4, x=34); 1 $ 2
|
>>> f(2, 4, x=34); 1 $ 2
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
|
@ -1068,6 +1110,13 @@ Specialized indentation errors:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
IndentationError: expected an indented block after 'except' statement on line 3
|
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:
|
>>> try:
|
||||||
... something()
|
... something()
|
||||||
... except A:
|
... except A:
|
||||||
|
@ -1077,6 +1126,15 @@ Specialized indentation errors:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
IndentationError: expected an indented block after 'finally' statement on line 5
|
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:
|
>>> with A:
|
||||||
... pass
|
... pass
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
|
@ -1180,6 +1238,48 @@ raise a custom exception
|
||||||
SyntaxError: multiple exception types must be parenthesized
|
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)
|
>>> f(a=23, a=234)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
|
|
|
@ -1213,6 +1213,181 @@ class TraceTestCase(unittest.TestCase):
|
||||||
(5, 'line'),
|
(5, 'line'),
|
||||||
(5, 'return')])
|
(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):
|
class SkipLineEventsTraceTestCase(TraceTestCase):
|
||||||
"""Repeat the trace tests, but with per-line events skipped"""
|
"""Repeat the trace tests, but with per-line events skipped"""
|
||||||
|
|
|
@ -93,6 +93,19 @@ finally:
|
||||||
suite5
|
suite5
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
try_except_star_finally = """\
|
||||||
|
try:
|
||||||
|
suite1
|
||||||
|
except* ex1:
|
||||||
|
suite2
|
||||||
|
except* ex2:
|
||||||
|
suite3
|
||||||
|
else:
|
||||||
|
suite4
|
||||||
|
finally:
|
||||||
|
suite5
|
||||||
|
"""
|
||||||
|
|
||||||
with_simple = """\
|
with_simple = """\
|
||||||
with f():
|
with f():
|
||||||
suite1
|
suite1
|
||||||
|
@ -304,6 +317,9 @@ class UnparseTestCase(ASTTestCase):
|
||||||
def test_try_except_finally(self):
|
def test_try_except_finally(self):
|
||||||
self.check_ast_roundtrip(try_except_finally)
|
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):
|
def test_starred_assignment(self):
|
||||||
self.check_ast_roundtrip("a, *b, c = seq")
|
self.check_ast_roundtrip("a, *b, c = seq")
|
||||||
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;
|
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
|
static int
|
||||||
BaseExceptionGroup_init(PyBaseExceptionGroupObject *self,
|
BaseExceptionGroup_init(PyBaseExceptionGroupObject *self,
|
||||||
PyObject *args, PyObject *kwds)
|
PyObject *args, PyObject *kwds)
|
||||||
|
@ -878,7 +895,7 @@ exceptiongroup_subset(
|
||||||
if (tb) {
|
if (tb) {
|
||||||
int res = PyException_SetTraceback(eg, tb);
|
int res = PyException_SetTraceback(eg, tb);
|
||||||
Py_DECREF(tb);
|
Py_DECREF(tb);
|
||||||
if (res == -1) {
|
if (res < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -896,26 +913,41 @@ typedef enum {
|
||||||
EXCEPTION_GROUP_MATCH_BY_TYPE = 0,
|
EXCEPTION_GROUP_MATCH_BY_TYPE = 0,
|
||||||
/* A PyFunction returning True for matching exceptions */
|
/* A PyFunction returning True for matching exceptions */
|
||||||
EXCEPTION_GROUP_MATCH_BY_PREDICATE = 1,
|
EXCEPTION_GROUP_MATCH_BY_PREDICATE = 1,
|
||||||
/* An instance or container thereof, checked with equality
|
/* A set of leaf exceptions to include in the result.
|
||||||
* This matcher type is only used internally by the
|
* This matcher type is used internally by the interpreter
|
||||||
* interpreter, it is not exposed to python code */
|
* to construct reraised exceptions.
|
||||||
|
*/
|
||||||
EXCEPTION_GROUP_MATCH_INSTANCES = 2
|
EXCEPTION_GROUP_MATCH_INSTANCES = 2
|
||||||
} _exceptiongroup_split_matcher_type;
|
} _exceptiongroup_split_matcher_type;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_matcher_type(PyObject *value,
|
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 */
|
assert(value);
|
||||||
if (PyExceptionClass_Check(value) ||
|
|
||||||
PyTuple_CheckExact(value)) {
|
|
||||||
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (PyFunction_Check(value)) {
|
if (PyFunction_Check(value)) {
|
||||||
*type = EXCEPTION_GROUP_MATCH_BY_PREDICATE;
|
*type = EXCEPTION_GROUP_MATCH_BY_PREDICATE;
|
||||||
return 0;
|
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(
|
PyErr_SetString(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
"expected a function, exception type or tuple of exception types");
|
"expected a function, exception type or tuple of exception types");
|
||||||
|
@ -944,10 +976,11 @@ exceptiongroup_split_check_match(PyObject *exc,
|
||||||
return is_true;
|
return is_true;
|
||||||
}
|
}
|
||||||
case EXCEPTION_GROUP_MATCH_INSTANCES: {
|
case EXCEPTION_GROUP_MATCH_INSTANCES: {
|
||||||
if (PySequence_Check(matcher_value)) {
|
assert(PySet_Check(matcher_value));
|
||||||
return PySequence_Contains(matcher_value, exc);
|
if (!_PyBaseExceptionGroup_Check(exc)) {
|
||||||
|
return PySet_Contains(matcher_value, exc);
|
||||||
}
|
}
|
||||||
return matcher_value == exc;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1019,7 +1052,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
||||||
}
|
}
|
||||||
if (exceptiongroup_split_recursive(
|
if (exceptiongroup_split_recursive(
|
||||||
e, matcher_type, matcher_value,
|
e, matcher_type, matcher_value,
|
||||||
construct_rest, &rec_result) == -1) {
|
construct_rest, &rec_result) < 0) {
|
||||||
assert(!rec_result.match);
|
assert(!rec_result.match);
|
||||||
assert(!rec_result.rest);
|
assert(!rec_result.rest);
|
||||||
Py_LeaveRecursiveCall();
|
Py_LeaveRecursiveCall();
|
||||||
|
@ -1028,7 +1061,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
||||||
Py_LeaveRecursiveCall();
|
Py_LeaveRecursiveCall();
|
||||||
if (rec_result.match) {
|
if (rec_result.match) {
|
||||||
assert(PyList_CheckExact(match_list));
|
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);
|
Py_DECREF(rec_result.match);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -1037,7 +1070,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
||||||
if (rec_result.rest) {
|
if (rec_result.rest) {
|
||||||
assert(construct_rest);
|
assert(construct_rest);
|
||||||
assert(PyList_CheckExact(rest_list));
|
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);
|
Py_DECREF(rec_result.rest);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -1046,13 +1079,13 @@ exceptiongroup_split_recursive(PyObject *exc,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* construct result */
|
/* construct result */
|
||||||
if (exceptiongroup_subset(eg, match_list, &result->match) == -1) {
|
if (exceptiongroup_subset(eg, match_list, &result->match) < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (construct_rest) {
|
if (construct_rest) {
|
||||||
assert(PyList_CheckExact(rest_list));
|
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);
|
Py_CLEAR(result->match);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -1061,7 +1094,7 @@ exceptiongroup_split_recursive(PyObject *exc,
|
||||||
done:
|
done:
|
||||||
Py_DECREF(match_list);
|
Py_DECREF(match_list);
|
||||||
Py_XDECREF(rest_list);
|
Py_XDECREF(rest_list);
|
||||||
if (retval == -1) {
|
if (retval < 0) {
|
||||||
Py_CLEAR(result->match);
|
Py_CLEAR(result->match);
|
||||||
Py_CLEAR(result->rest);
|
Py_CLEAR(result->rest);
|
||||||
}
|
}
|
||||||
|
@ -1077,7 +1110,7 @@ BaseExceptionGroup_split(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exceptiongroup_split_matcher_type matcher_type;
|
_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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1085,7 +1118,7 @@ BaseExceptionGroup_split(PyObject *self, PyObject *args)
|
||||||
bool construct_rest = true;
|
bool construct_rest = true;
|
||||||
if (exceptiongroup_split_recursive(
|
if (exceptiongroup_split_recursive(
|
||||||
self, matcher_type, matcher_value,
|
self, matcher_type, matcher_value,
|
||||||
construct_rest, &split_result) == -1) {
|
construct_rest, &split_result) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1108,7 +1141,7 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exceptiongroup_split_matcher_type matcher_type;
|
_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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,7 +1149,7 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
||||||
bool construct_rest = false;
|
bool construct_rest = false;
|
||||||
if (exceptiongroup_split_recursive(
|
if (exceptiongroup_split_recursive(
|
||||||
self, matcher_type, matcher_value,
|
self, matcher_type, matcher_value,
|
||||||
construct_rest, &split_result) == -1) {
|
construct_rest, &split_result) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,6 +1161,85 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
|
||||||
return result;
|
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[] = {
|
static PyMemberDef BaseExceptionGroup_members[] = {
|
||||||
{"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY,
|
{"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY,
|
||||||
PyDoc_STR("exception message")},
|
PyDoc_STR("exception message")},
|
||||||
|
|
|
@ -207,6 +207,7 @@ mark_stacks(PyCodeObject *code_obj, int len)
|
||||||
case POP_JUMP_IF_FALSE:
|
case POP_JUMP_IF_FALSE:
|
||||||
case POP_JUMP_IF_TRUE:
|
case POP_JUMP_IF_TRUE:
|
||||||
case JUMP_IF_NOT_EXC_MATCH:
|
case JUMP_IF_NOT_EXC_MATCH:
|
||||||
|
case JUMP_IF_NOT_EG_MATCH:
|
||||||
{
|
{
|
||||||
int64_t target_stack;
|
int64_t target_stack;
|
||||||
int j = get_arg(code, i);
|
int j = get_arg(code, i);
|
||||||
|
@ -214,7 +215,9 @@ mark_stacks(PyCodeObject *code_obj, int len)
|
||||||
if (stacks[j] == UNINITIALIZED && j < i) {
|
if (stacks[j] == UNINITIALIZED && j < i) {
|
||||||
todo = 1;
|
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));
|
next_stack = pop_value(pop_value(next_stack));
|
||||||
target_stack = next_stack;
|
target_stack = next_stack;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ module Python
|
||||||
|
|
||||||
| Raise(expr? exc, expr? cause)
|
| Raise(expr? exc, expr? cause)
|
||||||
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
|
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
|
||||||
|
| TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
|
||||||
| Assert(expr test, expr? msg)
|
| Assert(expr test, expr? msg)
|
||||||
|
|
||||||
| Import(alias* names)
|
| 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_singleton);
|
||||||
Py_CLEAR(state->Sub_type);
|
Py_CLEAR(state->Sub_type);
|
||||||
Py_CLEAR(state->Subscript_type);
|
Py_CLEAR(state->Subscript_type);
|
||||||
|
Py_CLEAR(state->TryStar_type);
|
||||||
Py_CLEAR(state->Try_type);
|
Py_CLEAR(state->Try_type);
|
||||||
Py_CLEAR(state->Tuple_type);
|
Py_CLEAR(state->Tuple_type);
|
||||||
Py_CLEAR(state->TypeIgnore_type);
|
Py_CLEAR(state->TypeIgnore_type);
|
||||||
|
@ -486,6 +487,12 @@ static const char * const Try_fields[]={
|
||||||
"orelse",
|
"orelse",
|
||||||
"finalbody",
|
"finalbody",
|
||||||
};
|
};
|
||||||
|
static const char * const TryStar_fields[]={
|
||||||
|
"body",
|
||||||
|
"handlers",
|
||||||
|
"orelse",
|
||||||
|
"finalbody",
|
||||||
|
};
|
||||||
static const char * const Assert_fields[]={
|
static const char * const Assert_fields[]={
|
||||||
"test",
|
"test",
|
||||||
"msg",
|
"msg",
|
||||||
|
@ -1139,6 +1146,7 @@ init_types(struct ast_state *state)
|
||||||
" | Match(expr subject, match_case* cases)\n"
|
" | Match(expr subject, match_case* cases)\n"
|
||||||
" | Raise(expr? exc, expr? cause)\n"
|
" | Raise(expr? exc, expr? cause)\n"
|
||||||
" | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\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"
|
" | Assert(expr test, expr? msg)\n"
|
||||||
" | Import(alias* names)\n"
|
" | Import(alias* names)\n"
|
||||||
" | ImportFrom(identifier? module, alias* names, int? level)\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,
|
state->Try_type = make_type(state, "Try", state->stmt_type, Try_fields, 4,
|
||||||
"Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
|
"Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
|
||||||
if (!state->Try_type) return 0;
|
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,
|
state->Assert_type = make_type(state, "Assert", state->stmt_type,
|
||||||
Assert_fields, 2,
|
Assert_fields, 2,
|
||||||
"Assert(expr test, expr? msg)");
|
"Assert(expr test, expr? msg)");
|
||||||
|
@ -2379,6 +2391,28 @@ _PyAST_Try(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
||||||
return p;
|
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
|
stmt_ty
|
||||||
_PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int
|
_PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int
|
||||||
end_lineno, int end_col_offset, PyArena *arena)
|
end_lineno, int end_col_offset, PyArena *arena)
|
||||||
|
@ -4049,6 +4083,34 @@ ast2obj_stmt(struct ast_state *state, void* _o)
|
||||||
goto failed;
|
goto failed;
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
break;
|
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:
|
case Assert_kind:
|
||||||
tp = (PyTypeObject *)state->Assert_type;
|
tp = (PyTypeObject *)state->Assert_type;
|
||||||
result = PyType_GenericNew(tp, NULL, NULL);
|
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;
|
if (*out == NULL) goto failed;
|
||||||
return 0;
|
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;
|
tp = state->Assert_type;
|
||||||
isinstance = PyObject_IsInstance(obj, tp);
|
isinstance = PyObject_IsInstance(obj, tp);
|
||||||
if (isinstance == -1) {
|
if (isinstance == -1) {
|
||||||
|
@ -11687,6 +11913,9 @@ astmodule_exec(PyObject *m)
|
||||||
if (PyModule_AddObjectRef(m, "Try", state->Try_type) < 0) {
|
if (PyModule_AddObjectRef(m, "Try", state->Try_type) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (PyModule_AddObjectRef(m, "TryStar", state->TryStar_type) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (PyModule_AddObjectRef(m, "Assert", state->Assert_type) < 0) {
|
if (PyModule_AddObjectRef(m, "Assert", state->Assert_type) < 0) {
|
||||||
return -1;
|
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) ||
|
(!asdl_seq_LEN(stmt->v.Try.orelse) ||
|
||||||
validate_stmts(state, stmt->v.Try.orelse));
|
validate_stmts(state, stmt->v.Try.orelse));
|
||||||
break;
|
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:
|
case Assert_kind:
|
||||||
ret = validate_expr(state, stmt->v.Assert.test, Load) &&
|
ret = validate_expr(state, stmt->v.Assert.test, Load) &&
|
||||||
(!stmt->v.Assert.msg || validate_expr(state, stmt->v.Assert.msg, 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.orelse);
|
||||||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody);
|
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody);
|
||||||
break;
|
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:
|
case Assert_kind:
|
||||||
CALL(astfold_expr, expr_ty, node_->v.Assert.test);
|
CALL(astfold_expr, expr_ty, node_->v.Assert.test);
|
||||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Assert.msg);
|
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 "structmember.h" // struct PyMemberDef, T_OFFSET_EX
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
/* For debugging the interpreter: */
|
/* 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 void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
|
||||||
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
|
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
|
||||||
static int check_except_type_valid(PyThreadState *tstate, PyObject* right);
|
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_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
|
||||||
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
|
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
|
||||||
static int get_exception_handler(PyCodeObject *, int, int*, 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 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 **);
|
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
|
||||||
|
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
|
@ -2727,6 +2734,7 @@ check_eval_breaker:
|
||||||
type = exc_info->exc_type;
|
type = exc_info->exc_type;
|
||||||
value = exc_info->exc_value;
|
value = exc_info->exc_value;
|
||||||
traceback = exc_info->exc_traceback;
|
traceback = exc_info->exc_traceback;
|
||||||
|
|
||||||
exc_info->exc_type = POP();
|
exc_info->exc_type = POP();
|
||||||
exc_info->exc_value = POP();
|
exc_info->exc_value = POP();
|
||||||
exc_info->exc_traceback = POP();
|
exc_info->exc_traceback = POP();
|
||||||
|
@ -2791,6 +2799,36 @@ check_eval_breaker:
|
||||||
goto exception_unwind;
|
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) {
|
TARGET(END_ASYNC_FOR) {
|
||||||
PyObject *exc = POP();
|
PyObject *exc = POP();
|
||||||
PyObject *val = POP();
|
PyObject *val = POP();
|
||||||
|
@ -3922,6 +3960,83 @@ check_eval_breaker:
|
||||||
DISPATCH();
|
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) {
|
TARGET(JUMP_IF_NOT_EXC_MATCH) {
|
||||||
PyObject *right = POP();
|
PyObject *right = POP();
|
||||||
ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND());
|
ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND());
|
||||||
|
@ -3931,17 +4046,12 @@ check_eval_breaker:
|
||||||
Py_DECREF(right);
|
Py_DECREF(right);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = PyErr_GivenExceptionMatches(left, right);
|
int res = PyErr_GivenExceptionMatches(left, right);
|
||||||
Py_DECREF(right);
|
Py_DECREF(right);
|
||||||
if (res > 0) {
|
if (res == 0) {
|
||||||
/* Exception matches -- Do nothing */;
|
|
||||||
}
|
|
||||||
else if (res == 0) {
|
|
||||||
JUMPTO(oparg);
|
JUMPTO(oparg);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6127,6 +6237,196 @@ raise_error:
|
||||||
return 0;
|
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
|
/* Iterate v argcnt times and store the results on the stack (via decreasing
|
||||||
sp). Return 1 for success, 0 if error.
|
sp). Return 1 for success, 0 if error.
|
||||||
|
|
||||||
|
@ -7020,10 +7320,12 @@ import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
|
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
|
||||||
"BaseException is not allowed"
|
"BaseException is not allowed"
|
||||||
|
|
||||||
|
#define CANNOT_EXCEPT_STAR_EG "catching ExceptionGroup with except* "\
|
||||||
|
"is not allowed. Use except instead."
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_except_type_valid(PyThreadState *tstate, PyObject* right)
|
check_except_type_valid(PyThreadState *tstate, PyObject* right)
|
||||||
{
|
{
|
||||||
|
@ -7049,6 +7351,43 @@ check_except_type_valid(PyThreadState *tstate, PyObject* right)
|
||||||
return 0;
|
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
|
static int
|
||||||
check_args_iterable(PyThreadState *tstate, PyObject *func, PyObject *args)
|
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,
|
enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
|
||||||
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER,
|
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER,
|
||||||
ASYNC_COMPREHENSION_GENERATOR };
|
EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR };
|
||||||
|
|
||||||
struct fblockinfo {
|
struct fblockinfo {
|
||||||
enum fblocktype fb_type;
|
enum fblocktype fb_type;
|
||||||
|
@ -323,6 +323,7 @@ static int compiler_call_helper(struct compiler *c, int n,
|
||||||
asdl_expr_seq *args,
|
asdl_expr_seq *args,
|
||||||
asdl_keyword_seq *keywords);
|
asdl_keyword_seq *keywords);
|
||||||
static int compiler_try_except(struct compiler *, stmt_ty);
|
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_set_qualname(struct compiler *);
|
||||||
|
|
||||||
static int compiler_sync_comprehension_generator(
|
static int compiler_sync_comprehension_generator(
|
||||||
|
@ -1094,6 +1095,8 @@ stack_effect(int opcode, int oparg, int jump)
|
||||||
return -1;
|
return -1;
|
||||||
case JUMP_IF_NOT_EXC_MATCH:
|
case JUMP_IF_NOT_EXC_MATCH:
|
||||||
return -1;
|
return -1;
|
||||||
|
case JUMP_IF_NOT_EG_MATCH:
|
||||||
|
return jump > 0 ? -1 : 2;
|
||||||
case IMPORT_NAME:
|
case IMPORT_NAME:
|
||||||
return -1;
|
return -1;
|
||||||
case IMPORT_FROM:
|
case IMPORT_FROM:
|
||||||
|
@ -1131,6 +1134,8 @@ stack_effect(int opcode, int oparg, int jump)
|
||||||
* if an exception be raised. */
|
* if an exception be raised. */
|
||||||
return jump ? -1 + 4 : 0;
|
return jump ? -1 + 4 : 0;
|
||||||
|
|
||||||
|
case PREP_RERAISE_STAR:
|
||||||
|
return 2;
|
||||||
case RERAISE:
|
case RERAISE:
|
||||||
return -3;
|
return -3;
|
||||||
case PUSH_EXC_INFO:
|
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.finalbody) ||
|
||||||
find_ann(st->v.Try.orelse);
|
find_ann(st->v.Try.orelse);
|
||||||
break;
|
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:
|
default:
|
||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
|
@ -1816,6 +1833,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
||||||
switch (info->fb_type) {
|
switch (info->fb_type) {
|
||||||
case WHILE_LOOP:
|
case WHILE_LOOP:
|
||||||
case EXCEPTION_HANDLER:
|
case EXCEPTION_HANDLER:
|
||||||
|
case EXCEPTION_GROUP_HANDLER:
|
||||||
case ASYNC_COMPREHENSION_GENERATOR:
|
case ASYNC_COMPREHENSION_GENERATOR:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -1919,6 +1937,10 @@ compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblock
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-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)) {
|
if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) {
|
||||||
*loop = top;
|
*loop = top;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -3202,6 +3224,62 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
||||||
return 1;
|
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 ...":
|
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
|
(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);
|
ADDOP(c, POP_EXCEPT_AND_RERAISE);
|
||||||
compiler_use_next_block(c, orelse);
|
compiler_use_next_block(c, orelse);
|
||||||
VISIT_SEQ(c, stmt, s->v.Try.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);
|
compiler_use_next_block(c, end);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -3372,6 +3697,16 @@ compiler_try(struct compiler *c, stmt_ty s) {
|
||||||
return compiler_try_except(c, 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
|
static int
|
||||||
compiler_import_as(struct compiler *c, identifier name, identifier asname)
|
compiler_import_as(struct compiler *c, identifier name, identifier asname)
|
||||||
|
@ -3634,6 +3969,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
||||||
break;
|
break;
|
||||||
case Try_kind:
|
case Try_kind:
|
||||||
return compiler_try(c, s);
|
return compiler_try(c, s);
|
||||||
|
case TryStar_kind:
|
||||||
|
return compiler_try_star(c, s);
|
||||||
case Assert_kind:
|
case Assert_kind:
|
||||||
return compiler_assert(c, s);
|
return compiler_assert(c, s);
|
||||||
case Import_kind:
|
case Import_kind:
|
||||||
|
|
|
@ -87,7 +87,7 @@ static void *opcode_targets[256] = {
|
||||||
&&TARGET_SETUP_ANNOTATIONS,
|
&&TARGET_SETUP_ANNOTATIONS,
|
||||||
&&TARGET_YIELD_VALUE,
|
&&TARGET_YIELD_VALUE,
|
||||||
&&TARGET_LOAD_CONST__LOAD_FAST,
|
&&TARGET_LOAD_CONST__LOAD_FAST,
|
||||||
&&TARGET_STORE_FAST__STORE_FAST,
|
&&TARGET_PREP_RERAISE_STAR,
|
||||||
&&TARGET_POP_EXCEPT,
|
&&TARGET_POP_EXCEPT,
|
||||||
&&TARGET_STORE_NAME,
|
&&TARGET_STORE_NAME,
|
||||||
&&TARGET_DELETE_NAME,
|
&&TARGET_DELETE_NAME,
|
||||||
|
@ -122,11 +122,11 @@ static void *opcode_targets[256] = {
|
||||||
&&TARGET_COPY,
|
&&TARGET_COPY,
|
||||||
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
|
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
|
||||||
&&TARGET_BINARY_OP,
|
&&TARGET_BINARY_OP,
|
||||||
&&_unknown_opcode,
|
&&TARGET_STORE_FAST__STORE_FAST,
|
||||||
&&TARGET_LOAD_FAST,
|
&&TARGET_LOAD_FAST,
|
||||||
&&TARGET_STORE_FAST,
|
&&TARGET_STORE_FAST,
|
||||||
&&TARGET_DELETE_FAST,
|
&&TARGET_DELETE_FAST,
|
||||||
&&_unknown_opcode,
|
&&TARGET_JUMP_IF_NOT_EG_MATCH,
|
||||||
&&_unknown_opcode,
|
&&_unknown_opcode,
|
||||||
&&TARGET_GEN_START,
|
&&TARGET_GEN_START,
|
||||||
&&TARGET_RAISE_VARARGS,
|
&&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, excepthandler, s->v.Try.handlers);
|
||||||
VISIT_SEQ(st, stmt, s->v.Try.finalbody);
|
VISIT_SEQ(st, stmt, s->v.Try.finalbody);
|
||||||
break;
|
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:
|
case Assert_kind:
|
||||||
VISIT(st, expr, s->v.Assert.test);
|
VISIT(st, expr, s->v.Assert.test);
|
||||||
if (s->v.Assert.msg)
|
if (s->v.Assert.msg)
|
||||||
|
|
Loading…
Reference in New Issue