bpo-45292: [PEP-654] add except* (GH-29581)

This commit is contained in:
Irit Katriel 2021-12-14 16:48:15 +00:00 committed by GitHub
parent 850aefc2c6
commit d60457a667
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 7070 additions and 3332 deletions

229
Python/Python-ast.c generated
View file

@ -146,6 +146,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
Py_CLEAR(state->Sub_singleton);
Py_CLEAR(state->Sub_type);
Py_CLEAR(state->Subscript_type);
Py_CLEAR(state->TryStar_type);
Py_CLEAR(state->Try_type);
Py_CLEAR(state->Tuple_type);
Py_CLEAR(state->TypeIgnore_type);
@ -486,6 +487,12 @@ static const char * const Try_fields[]={
"orelse",
"finalbody",
};
static const char * const TryStar_fields[]={
"body",
"handlers",
"orelse",
"finalbody",
};
static const char * const Assert_fields[]={
"test",
"msg",
@ -1139,6 +1146,7 @@ init_types(struct ast_state *state)
" | Match(expr subject, match_case* cases)\n"
" | Raise(expr? exc, expr? cause)\n"
" | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
" | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
" | Assert(expr test, expr? msg)\n"
" | Import(alias* names)\n"
" | ImportFrom(identifier? module, alias* names, int? level)\n"
@ -1254,6 +1262,10 @@ init_types(struct ast_state *state)
state->Try_type = make_type(state, "Try", state->stmt_type, Try_fields, 4,
"Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
if (!state->Try_type) return 0;
state->TryStar_type = make_type(state, "TryStar", state->stmt_type,
TryStar_fields, 4,
"TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)");
if (!state->TryStar_type) return 0;
state->Assert_type = make_type(state, "Assert", state->stmt_type,
Assert_fields, 2,
"Assert(expr test, expr? msg)");
@ -2379,6 +2391,28 @@ _PyAST_Try(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
return p;
}
stmt_ty
_PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int lineno,
int col_offset, int end_lineno, int end_col_offset, PyArena
*arena)
{
stmt_ty p;
p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p));
if (!p)
return NULL;
p->kind = TryStar_kind;
p->v.TryStar.body = body;
p->v.TryStar.handlers = handlers;
p->v.TryStar.orelse = orelse;
p->v.TryStar.finalbody = finalbody;
p->lineno = lineno;
p->col_offset = col_offset;
p->end_lineno = end_lineno;
p->end_col_offset = end_col_offset;
return p;
}
stmt_ty
_PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena *arena)
@ -4049,6 +4083,34 @@ ast2obj_stmt(struct ast_state *state, void* _o)
goto failed;
Py_DECREF(value);
break;
case TryStar_kind:
tp = (PyTypeObject *)state->TryStar_type;
result = PyType_GenericNew(tp, NULL, NULL);
if (!result) goto failed;
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.body, ast2obj_stmt);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->body, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.handlers,
ast2obj_excepthandler);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->handlers, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.orelse,
ast2obj_stmt);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->orelse, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.finalbody,
ast2obj_stmt);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->finalbody, value) == -1)
goto failed;
Py_DECREF(value);
break;
case Assert_kind:
tp = (PyTypeObject *)state->Assert_type;
result = PyType_GenericNew(tp, NULL, NULL);
@ -7477,6 +7539,170 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
if (*out == NULL) goto failed;
return 0;
}
tp = state->TryStar_type;
isinstance = PyObject_IsInstance(obj, tp);
if (isinstance == -1) {
return 1;
}
if (isinstance) {
asdl_stmt_seq* body;
asdl_excepthandler_seq* handlers;
asdl_stmt_seq* orelse;
asdl_stmt_seq* finalbody;
if (_PyObject_LookupAttr(obj, state->body, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from TryStar");
return 1;
}
else {
int res;
Py_ssize_t len;
Py_ssize_t i;
if (!PyList_Check(tmp)) {
PyErr_Format(PyExc_TypeError, "TryStar field \"body\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
goto failed;
}
len = PyList_GET_SIZE(tmp);
body = _Py_asdl_stmt_seq_new(len, arena);
if (body == NULL) goto failed;
for (i = 0; i < len; i++) {
stmt_ty val;
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
Py_INCREF(tmp2);
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
goto failed;
}
res = obj2ast_stmt(state, tmp2, &val, arena);
Py_LeaveRecursiveCall();
Py_DECREF(tmp2);
if (res != 0) goto failed;
if (len != PyList_GET_SIZE(tmp)) {
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"body\" changed size during iteration");
goto failed;
}
asdl_seq_SET(body, i, val);
}
Py_CLEAR(tmp);
}
if (_PyObject_LookupAttr(obj, state->handlers, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
PyErr_SetString(PyExc_TypeError, "required field \"handlers\" missing from TryStar");
return 1;
}
else {
int res;
Py_ssize_t len;
Py_ssize_t i;
if (!PyList_Check(tmp)) {
PyErr_Format(PyExc_TypeError, "TryStar field \"handlers\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
goto failed;
}
len = PyList_GET_SIZE(tmp);
handlers = _Py_asdl_excepthandler_seq_new(len, arena);
if (handlers == NULL) goto failed;
for (i = 0; i < len; i++) {
excepthandler_ty val;
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
Py_INCREF(tmp2);
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
goto failed;
}
res = obj2ast_excepthandler(state, tmp2, &val, arena);
Py_LeaveRecursiveCall();
Py_DECREF(tmp2);
if (res != 0) goto failed;
if (len != PyList_GET_SIZE(tmp)) {
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"handlers\" changed size during iteration");
goto failed;
}
asdl_seq_SET(handlers, i, val);
}
Py_CLEAR(tmp);
}
if (_PyObject_LookupAttr(obj, state->orelse, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from TryStar");
return 1;
}
else {
int res;
Py_ssize_t len;
Py_ssize_t i;
if (!PyList_Check(tmp)) {
PyErr_Format(PyExc_TypeError, "TryStar field \"orelse\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
goto failed;
}
len = PyList_GET_SIZE(tmp);
orelse = _Py_asdl_stmt_seq_new(len, arena);
if (orelse == NULL) goto failed;
for (i = 0; i < len; i++) {
stmt_ty val;
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
Py_INCREF(tmp2);
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
goto failed;
}
res = obj2ast_stmt(state, tmp2, &val, arena);
Py_LeaveRecursiveCall();
Py_DECREF(tmp2);
if (res != 0) goto failed;
if (len != PyList_GET_SIZE(tmp)) {
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"orelse\" changed size during iteration");
goto failed;
}
asdl_seq_SET(orelse, i, val);
}
Py_CLEAR(tmp);
}
if (_PyObject_LookupAttr(obj, state->finalbody, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
PyErr_SetString(PyExc_TypeError, "required field \"finalbody\" missing from TryStar");
return 1;
}
else {
int res;
Py_ssize_t len;
Py_ssize_t i;
if (!PyList_Check(tmp)) {
PyErr_Format(PyExc_TypeError, "TryStar field \"finalbody\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
goto failed;
}
len = PyList_GET_SIZE(tmp);
finalbody = _Py_asdl_stmt_seq_new(len, arena);
if (finalbody == NULL) goto failed;
for (i = 0; i < len; i++) {
stmt_ty val;
PyObject *tmp2 = PyList_GET_ITEM(tmp, i);
Py_INCREF(tmp2);
if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) {
goto failed;
}
res = obj2ast_stmt(state, tmp2, &val, arena);
Py_LeaveRecursiveCall();
Py_DECREF(tmp2);
if (res != 0) goto failed;
if (len != PyList_GET_SIZE(tmp)) {
PyErr_SetString(PyExc_RuntimeError, "TryStar field \"finalbody\" changed size during iteration");
goto failed;
}
asdl_seq_SET(finalbody, i, val);
}
Py_CLEAR(tmp);
}
*out = _PyAST_TryStar(body, handlers, orelse, finalbody, lineno,
col_offset, end_lineno, end_col_offset, arena);
if (*out == NULL) goto failed;
return 0;
}
tp = state->Assert_type;
isinstance = PyObject_IsInstance(obj, tp);
if (isinstance == -1) {
@ -11687,6 +11913,9 @@ astmodule_exec(PyObject *m)
if (PyModule_AddObjectRef(m, "Try", state->Try_type) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "TryStar", state->TryStar_type) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "Assert", state->Assert_type) < 0) {
return -1;
}

View file

@ -817,6 +817,31 @@ validate_stmt(struct validator *state, stmt_ty stmt)
(!asdl_seq_LEN(stmt->v.Try.orelse) ||
validate_stmts(state, stmt->v.Try.orelse));
break;
case TryStar_kind:
if (!validate_body(state, stmt->v.TryStar.body, "TryStar"))
return 0;
if (!asdl_seq_LEN(stmt->v.TryStar.handlers) &&
!asdl_seq_LEN(stmt->v.TryStar.finalbody)) {
PyErr_SetString(PyExc_ValueError, "TryStar has neither except handlers nor finalbody");
return 0;
}
if (!asdl_seq_LEN(stmt->v.TryStar.handlers) &&
asdl_seq_LEN(stmt->v.TryStar.orelse)) {
PyErr_SetString(PyExc_ValueError, "TryStar has orelse but no except handlers");
return 0;
}
for (i = 0; i < asdl_seq_LEN(stmt->v.TryStar.handlers); i++) {
excepthandler_ty handler = asdl_seq_GET(stmt->v.TryStar.handlers, i);
if ((handler->v.ExceptHandler.type &&
!validate_expr(state, handler->v.ExceptHandler.type, Load)) ||
!validate_body(state, handler->v.ExceptHandler.body, "ExceptHandler"))
return 0;
}
ret = (!asdl_seq_LEN(stmt->v.TryStar.finalbody) ||
validate_stmts(state, stmt->v.TryStar.finalbody)) &&
(!asdl_seq_LEN(stmt->v.TryStar.orelse) ||
validate_stmts(state, stmt->v.TryStar.orelse));
break;
case Assert_kind:
ret = validate_expr(state, stmt->v.Assert.test, Load) &&
(!stmt->v.Assert.msg || validate_expr(state, stmt->v.Assert.msg, Load));

View file

@ -972,6 +972,12 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.orelse);
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody);
break;
case TryStar_kind:
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.body);
CALL_SEQ(astfold_excepthandler, excepthandler, node_->v.TryStar.handlers);
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.orelse);
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.finalbody);
break;
case Assert_kind:
CALL(astfold_expr, expr_ty, node_->v.Assert.test);
CALL_OPT(astfold_expr, expr_ty, node_->v.Assert.msg);

View file

@ -37,6 +37,7 @@
#include "structmember.h" // struct PyMemberDef, T_OFFSET_EX
#include <ctype.h>
#include <stdbool.h>
#ifdef Py_DEBUG
/* For debugging the interpreter: */
@ -95,6 +96,7 @@ static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyOb
static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
static int check_except_type_valid(PyThreadState *tstate, PyObject* right);
static int check_except_star_type_valid(PyThreadState *tstate, PyObject* right);
static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
static int get_exception_handler(PyCodeObject *, int, int*, int*, int*);
@ -1090,6 +1092,11 @@ fail:
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
static PyObject *do_reraise_star(PyObject *excs, PyObject *orig);
static int exception_group_match(
PyObject *exc_type, PyObject* exc_value, PyObject *match_type,
PyObject **match, PyObject **rest);
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
#ifdef Py_DEBUG
@ -2727,6 +2734,7 @@ check_eval_breaker:
type = exc_info->exc_type;
value = exc_info->exc_value;
traceback = exc_info->exc_traceback;
exc_info->exc_type = POP();
exc_info->exc_value = POP();
exc_info->exc_traceback = POP();
@ -2791,6 +2799,36 @@ check_eval_breaker:
goto exception_unwind;
}
TARGET(PREP_RERAISE_STAR) {
PyObject *excs = POP();
assert(PyList_Check(excs));
PyObject *orig = POP();
PyObject *val = do_reraise_star(excs, orig);
Py_DECREF(excs);
Py_DECREF(orig);
if (val == NULL) {
goto error;
}
PyObject *lasti_unused = Py_NewRef(_PyLong_GetZero());
PUSH(lasti_unused);
if (!Py_IsNone(val)) {
PyObject *tb = PyException_GetTraceback(val);
PUSH(tb ? tb : Py_NewRef(Py_None));
PUSH(val);
PUSH(Py_NewRef(Py_TYPE(val)));
}
else {
// nothing to reraise
PUSH(Py_NewRef(Py_None));
PUSH(val);
PUSH(Py_NewRef(Py_None));
}
DISPATCH();
}
TARGET(END_ASYNC_FOR) {
PyObject *exc = POP();
PyObject *val = POP();
@ -3922,6 +3960,83 @@ check_eval_breaker:
DISPATCH();
}
TARGET(JUMP_IF_NOT_EG_MATCH) {
PyObject *match_type = POP();
PyObject *exc_type = TOP();
PyObject *exc_value = SECOND();
if (check_except_star_type_valid(tstate, match_type) < 0) {
Py_DECREF(match_type);
goto error;
}
PyObject *match = NULL, *rest = NULL;
int res = exception_group_match(exc_type, exc_value,
match_type, &match, &rest);
Py_DECREF(match_type);
if (res < 0) {
goto error;
}
if (match == NULL || rest == NULL) {
assert(match == NULL);
assert(rest == NULL);
goto error;
}
if (Py_IsNone(match)) {
Py_DECREF(match);
Py_XDECREF(rest);
/* no match - jump to target */
JUMPTO(oparg);
}
else {
/* Total or partial match - update the stack from
* [tb, val, exc]
* to
* [tb, rest, exc, tb, match, exc]
* (rest can be Py_None)
*/
PyObject *type = TOP();
PyObject *val = SECOND();
PyObject *tb = THIRD();
if (!Py_IsNone(rest)) {
/* tb remains the same */
SET_TOP(Py_NewRef(Py_TYPE(rest)));
SET_SECOND(Py_NewRef(rest));
SET_THIRD(Py_NewRef(tb));
}
else {
SET_TOP(Py_NewRef(Py_None));
SET_SECOND(Py_NewRef(Py_None));
SET_THIRD(Py_NewRef(Py_None));
}
/* Push match */
PUSH(Py_NewRef(tb));
PUSH(Py_NewRef(match));
PUSH(Py_NewRef(Py_TYPE(match)));
// set exc_info to the current match
PyErr_SetExcInfo(
Py_NewRef(Py_TYPE(match)),
Py_NewRef(match),
Py_NewRef(tb));
Py_DECREF(tb);
Py_DECREF(val);
Py_DECREF(type);
Py_DECREF(match);
Py_DECREF(rest);
}
DISPATCH();
}
TARGET(JUMP_IF_NOT_EXC_MATCH) {
PyObject *right = POP();
ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND());
@ -3931,17 +4046,12 @@ check_eval_breaker:
Py_DECREF(right);
goto error;
}
int res = PyErr_GivenExceptionMatches(left, right);
Py_DECREF(right);
if (res > 0) {
/* Exception matches -- Do nothing */;
}
else if (res == 0) {
if (res == 0) {
JUMPTO(oparg);
}
else {
goto error;
}
DISPATCH();
}
@ -6127,6 +6237,196 @@ raise_error:
return 0;
}
/* Logic for matching an exception in an except* clause (too
complicated for inlining).
*/
static int
exception_group_match(PyObject *exc_type, PyObject* exc_value,
PyObject *match_type, PyObject **match, PyObject **rest)
{
if (Py_IsNone(exc_type)) {
assert(Py_IsNone(exc_value));
*match = Py_NewRef(Py_None);
*rest = Py_NewRef(Py_None);
return 0;
}
assert(PyExceptionClass_Check(exc_type));
assert(PyExceptionInstance_Check(exc_value));
if (PyErr_GivenExceptionMatches(exc_type, match_type)) {
/* Full match of exc itself */
bool is_eg = _PyBaseExceptionGroup_Check(exc_value);
if (is_eg) {
*match = Py_NewRef(exc_value);
}
else {
/* naked exception - wrap it */
PyObject *excs = PyTuple_Pack(1, exc_value);
if (excs == NULL) {
return -1;
}
PyObject *wrapped = _PyExc_CreateExceptionGroup("", excs);
Py_DECREF(excs);
if (wrapped == NULL) {
return -1;
}
*match = wrapped;
}
*rest = Py_NewRef(Py_None);
return 0;
}
/* exc_value does not match match_type.
* Check for partial match if it's an exception group.
*/
if (_PyBaseExceptionGroup_Check(exc_value)) {
PyObject *pair = PyObject_CallMethod(exc_value, "split", "(O)",
match_type);
if (pair == NULL) {
return -1;
}
assert(PyTuple_CheckExact(pair));
assert(PyTuple_GET_SIZE(pair) == 2);
*match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
*rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
Py_DECREF(pair);
return 0;
}
/* no match */
*match = Py_NewRef(Py_None);
*rest = Py_NewRef(Py_None);
return 0;
}
/* Logic for the final raise/reraise of a try-except* contruct
(too complicated for inlining).
*/
static bool
is_same_exception_metadata(PyObject *exc1, PyObject *exc2)
{
assert(PyExceptionInstance_Check(exc1));
assert(PyExceptionInstance_Check(exc2));
PyObject *tb1 = PyException_GetTraceback(exc1);
PyObject *ctx1 = PyException_GetContext(exc1);
PyObject *cause1 = PyException_GetCause(exc1);
PyObject *tb2 = PyException_GetTraceback(exc2);
PyObject *ctx2 = PyException_GetContext(exc2);
PyObject *cause2 = PyException_GetCause(exc2);
bool result = (Py_Is(tb1, tb2) &&
Py_Is(ctx1, ctx2) &&
Py_Is(cause1, cause2));
Py_XDECREF(tb1);
Py_XDECREF(ctx1);
Py_XDECREF(cause1);
Py_XDECREF(tb2);
Py_XDECREF(ctx2);
Py_XDECREF(cause2);
return result;
}
/*
excs: a list of exceptions to raise/reraise
orig: the original except that was caught
Calculates an exception group to raise. It contains
all exceptions in excs, where those that were reraised
have same nesting structure as in orig, and those that
were raised (if any) are added as siblings in a new EG.
Returns NULL and sets an exception on failure.
*/
static PyObject *
do_reraise_star(PyObject *excs, PyObject *orig)
{
assert(PyList_Check(excs));
assert(PyExceptionInstance_Check(orig));
Py_ssize_t numexcs = PyList_GET_SIZE(excs);
if (numexcs == 0) {
return Py_NewRef(Py_None);
}
if (!_PyBaseExceptionGroup_Check(orig)) {
/* a naked exception was caught and wrapped. Only one except* clause
* could have executed,so there is at most one exception to raise.
*/
assert(numexcs == 1 || (numexcs == 2 && PyList_GET_ITEM(excs, 1) == Py_None));
PyObject *e = PyList_GET_ITEM(excs, 0);
assert(e != NULL);
return Py_NewRef(e);
}
PyObject *raised_list = PyList_New(0);
if (raised_list == NULL) {
return NULL;
}
PyObject* reraised_list = PyList_New(0);
if (reraised_list == NULL) {
Py_DECREF(raised_list);
return NULL;
}
/* Now we are holding refs to raised_list and reraised_list */
PyObject *result = NULL;
/* Split excs into raised and reraised by comparing metadata with orig */
for (Py_ssize_t i = 0; i < numexcs; i++) {
PyObject *e = PyList_GET_ITEM(excs, i);
assert(e != NULL);
if (Py_IsNone(e)) {
continue;
}
bool is_reraise = is_same_exception_metadata(e, orig);
PyObject *append_list = is_reraise ? reraised_list : raised_list;
if (PyList_Append(append_list, e) < 0) {
goto done;
}
}
PyObject *reraised_eg = _PyExc_ExceptionGroupProjection(orig, reraised_list);
if (reraised_eg == NULL) {
goto done;
}
if (!Py_IsNone(reraised_eg)) {
assert(is_same_exception_metadata(reraised_eg, orig));
}
Py_ssize_t num_raised = PyList_GET_SIZE(raised_list);
if (num_raised == 0) {
result = reraised_eg;
}
else if (num_raised > 0) {
int res = 0;
if (!Py_IsNone(reraised_eg)) {
res = PyList_Append(raised_list, reraised_eg);
}
Py_DECREF(reraised_eg);
if (res < 0) {
goto done;
}
result = _PyExc_CreateExceptionGroup("", raised_list);
if (result == NULL) {
goto done;
}
}
done:
Py_XDECREF(raised_list);
Py_XDECREF(reraised_list);
return result;
}
/* Iterate v argcnt times and store the results on the stack (via decreasing
sp). Return 1 for success, 0 if error.
@ -7020,10 +7320,12 @@ import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v)
return err;
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
#define CANNOT_EXCEPT_STAR_EG "catching ExceptionGroup with except* "\
"is not allowed. Use except instead."
static int
check_except_type_valid(PyThreadState *tstate, PyObject* right)
{
@ -7049,6 +7351,43 @@ check_except_type_valid(PyThreadState *tstate, PyObject* right)
return 0;
}
static int
check_except_star_type_valid(PyThreadState *tstate, PyObject* right)
{
if (check_except_type_valid(tstate, right) < 0) {
return -1;
}
/* reject except *ExceptionGroup */
int is_subclass = 0;
if (PyTuple_Check(right)) {
Py_ssize_t length = PyTuple_GET_SIZE(right);
for (Py_ssize_t i = 0; i < length; i++) {
PyObject *exc = PyTuple_GET_ITEM(right, i);
is_subclass = PyObject_IsSubclass(exc, PyExc_BaseExceptionGroup);
if (is_subclass < 0) {
return -1;
}
if (is_subclass) {
break;
}
}
}
else {
is_subclass = PyObject_IsSubclass(right, PyExc_BaseExceptionGroup);
if (is_subclass < 0) {
return -1;
}
}
if (is_subclass) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_EXCEPT_STAR_EG);
return -1;
}
return 0;
}
static int
check_args_iterable(PyThreadState *tstate, PyObject *func, PyObject *args)
{

View file

@ -175,7 +175,7 @@ compiler IR.
enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER,
ASYNC_COMPREHENSION_GENERATOR };
EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR };
struct fblockinfo {
enum fblocktype fb_type;
@ -323,6 +323,7 @@ static int compiler_call_helper(struct compiler *c, int n,
asdl_expr_seq *args,
asdl_keyword_seq *keywords);
static int compiler_try_except(struct compiler *, stmt_ty);
static int compiler_try_star_except(struct compiler *, stmt_ty);
static int compiler_set_qualname(struct compiler *);
static int compiler_sync_comprehension_generator(
@ -1094,6 +1095,8 @@ stack_effect(int opcode, int oparg, int jump)
return -1;
case JUMP_IF_NOT_EXC_MATCH:
return -1;
case JUMP_IF_NOT_EG_MATCH:
return jump > 0 ? -1 : 2;
case IMPORT_NAME:
return -1;
case IMPORT_FROM:
@ -1131,6 +1134,8 @@ stack_effect(int opcode, int oparg, int jump)
* if an exception be raised. */
return jump ? -1 + 4 : 0;
case PREP_RERAISE_STAR:
return 2;
case RERAISE:
return -3;
case PUSH_EXC_INFO:
@ -1755,6 +1760,18 @@ find_ann(asdl_stmt_seq *stmts)
find_ann(st->v.Try.finalbody) ||
find_ann(st->v.Try.orelse);
break;
case TryStar_kind:
for (j = 0; j < asdl_seq_LEN(st->v.TryStar.handlers); j++) {
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
st->v.TryStar.handlers, j);
if (find_ann(handler->v.ExceptHandler.body)) {
return 1;
}
}
res = find_ann(st->v.TryStar.body) ||
find_ann(st->v.TryStar.finalbody) ||
find_ann(st->v.TryStar.orelse);
break;
default:
res = 0;
}
@ -1816,6 +1833,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
switch (info->fb_type) {
case WHILE_LOOP:
case EXCEPTION_HANDLER:
case EXCEPTION_GROUP_HANDLER:
case ASYNC_COMPREHENSION_GENERATOR:
return 1;
@ -1919,6 +1937,10 @@ compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblock
return 1;
}
struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-1];
if (top->fb_type == EXCEPTION_GROUP_HANDLER) {
return compiler_error(
c, "'break', 'continue' and 'return' cannot appear in an except* block");
}
if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) {
*loop = top;
return 1;
@ -3202,6 +3224,62 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
return 1;
}
static int
compiler_try_star_finally(struct compiler *c, stmt_ty s)
{
basicblock *body = compiler_new_block(c);
if (body == NULL) {
return 0;
}
basicblock *end = compiler_new_block(c);
if (!end) {
return 0;
}
basicblock *exit = compiler_new_block(c);
if (!exit) {
return 0;
}
basicblock *cleanup = compiler_new_block(c);
if (!cleanup) {
return 0;
}
/* `try` block */
ADDOP_JUMP(c, SETUP_FINALLY, end);
compiler_use_next_block(c, body);
if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.TryStar.finalbody)) {
return 0;
}
if (s->v.TryStar.handlers && asdl_seq_LEN(s->v.TryStar.handlers)) {
if (!compiler_try_star_except(c, s)) {
return 0;
}
}
else {
VISIT_SEQ(c, stmt, s->v.TryStar.body);
}
ADDOP_NOLINE(c, POP_BLOCK);
compiler_pop_fblock(c, FINALLY_TRY, body);
VISIT_SEQ(c, stmt, s->v.TryStar.finalbody);
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, exit);
/* `finally` block */
compiler_use_next_block(c, end);
UNSET_LOC(c);
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
ADDOP(c, PUSH_EXC_INFO);
if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL)) {
return 0;
}
VISIT_SEQ(c, stmt, s->v.TryStar.finalbody);
compiler_pop_fblock(c, FINALLY_END, end);
ADDOP_I(c, RERAISE, 0);
compiler_use_next_block(c, cleanup);
ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, exit);
return 1;
}
/*
Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...":
(The contents of the value stack is shown in [], with the top
@ -3360,6 +3438,253 @@ compiler_try_except(struct compiler *c, stmt_ty s)
ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.Try.orelse);
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, end);
return 1;
}
/*
Code generated for "try: S except* E1 as V1: S1 except* E2 as V2: S2 ...":
(The contents of the value stack is shown in [], with the top
at the right; 'tb' is trace-back info, 'val' the exception instance,
and 'typ' the exception's type.)
Value stack Label Instruction Argument
[] SETUP_FINALLY L1
[] <code for S>
[] POP_BLOCK
[] JUMP_FORWARD L0
[tb, val, typ] L1: DUP_TOP_TWO ) save a copy of the
[tb, val, typ, orig, typ] POP_TOP ) original raised exception
[tb, val, typ, orig] ROT_FOUR )
[orig, tb, val, typ] BUILD_LIST ) list for raised/reraised
[orig, tb, val, typ, res] ROT_FOUR ) exceptions ("result")
[orig, res, tb, val, typ] <evaluate E1> )
[orig, res, tb, val, typ, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1
[orig, res, tb, rest, typ, tb, match, typ] POP
[orig, res, tb, rest, typ, tb, match] <assign to V1> (or POP if no V1)
[orig, res, tb, rest, typ, tb] POP
[orig, res, tb, rest, typ] SETUP_FINALLY R1
[orig, res, tb, rest, typ] <code for S1>
[orig, res, tb, rest, typ] JUMP_FORWARD L2
[orig, res, tb, rest, typ, i, tb, v, t] R1: POP ) exception raised in except* body
[orig, res, tb, rest, typ, i, tb, v] LIST_APPEND 6 ) add it to res
[orig, res, tb, rest, typ, i, tb] POP
[orig, res, tb, rest, typ, i] POP
[orig, res, tb, rest, typ] L2: <evaluate E2>
.............................etc.......................
[orig, res, tb, rest, typ] Ln+1: POP ) add unhandled exception
[orig, res, tb, rest] LIST_APPEND 2 ) to res (could be None)
[orig, res, tb] POP
[orig, res] PREP_RERAISE_STAR
[i, tb, val, typ] POP_JUMP_IF_TRUE RER
[i, tb, val, typ] POP
[i, tb, val] POP
[i, tb] POP
[i] POP
[] JUMP_FORWARD L0
[i, tb, val, typ] RER: POP_EXCEPT_AND_RERAISE
[] L0: <next statement>
*/
static int
compiler_try_star_except(struct compiler *c, stmt_ty s)
{
basicblock *body = compiler_new_block(c);
if (body == NULL) {
return 0;
}
basicblock *except = compiler_new_block(c);
if (except == NULL) {
return 0;
}
basicblock *orelse = compiler_new_block(c);
if (orelse == NULL) {
return 0;
}
basicblock *end = compiler_new_block(c);
if (end == NULL) {
return 0;
}
basicblock *cleanup = compiler_new_block(c);
if (cleanup == NULL) {
return 0;
}
basicblock *reraise_star = compiler_new_block(c);
if (reraise_star == NULL) {
return 0;
}
ADDOP_JUMP(c, SETUP_FINALLY, except);
compiler_use_next_block(c, body);
if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)) {
return 0;
}
VISIT_SEQ(c, stmt, s->v.TryStar.body);
compiler_pop_fblock(c, TRY_EXCEPT, body);
ADDOP_NOLINE(c, POP_BLOCK);
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, orelse);
Py_ssize_t n = asdl_seq_LEN(s->v.TryStar.handlers);
compiler_use_next_block(c, except);
UNSET_LOC(c);
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
ADDOP(c, PUSH_EXC_INFO);
/* Runtime will push a block here, so we need to account for that */
if (!compiler_push_fblock(c, EXCEPTION_GROUP_HANDLER,
NULL, NULL, "except handler")) {
return 0;
}
for (Py_ssize_t i = 0; i < n; i++) {
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
s->v.TryStar.handlers, i);
SET_LOC(c, handler);
except = compiler_new_block(c);
if (except == NULL) {
return 0;
}
if (i == 0) {
/* Push the original EG into the stack */
/*
[tb, val, exc] DUP_TOP_TWO
[tb, val, exc, val, exc] POP_TOP
[tb, val, exc, val] ROT_FOUR
[val, tb, val, exc]
*/
ADDOP(c, DUP_TOP_TWO);
ADDOP(c, POP_TOP);
ADDOP(c, ROT_FOUR);
/* create empty list for exceptions raised/reraise in the except* blocks */
/*
[val, tb, val, exc] BUILD_LIST
[val, tb, val, exc, []] ROT_FOUR
[val, [], tb, val, exc]
*/
ADDOP_I(c, BUILD_LIST, 0);
ADDOP(c, ROT_FOUR);
}
if (handler->v.ExceptHandler.type) {
VISIT(c, expr, handler->v.ExceptHandler.type);
ADDOP_JUMP(c, JUMP_IF_NOT_EG_MATCH, except);
NEXT_BLOCK(c);
}
ADDOP(c, POP_TOP); // exc_type
basicblock *cleanup_end = compiler_new_block(c);
if (cleanup_end == NULL) {
return 0;
}
basicblock *cleanup_body = compiler_new_block(c);
if (cleanup_body == NULL) {
return 0;
}
if (handler->v.ExceptHandler.name) {
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
}
else {
ADDOP(c, POP_TOP); // val
}
ADDOP(c, POP_TOP); // tb
/*
try:
# body
except type as name:
try:
# body
finally:
name = None # in case body contains "del name"
del name
*/
/* second try: */
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup_end);
compiler_use_next_block(c, cleanup_body);
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name))
return 0;
/* second # body */
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
/* name = None; del name; # Mark as artificial */
UNSET_LOC(c);
ADDOP(c, POP_BLOCK);
if (handler->v.ExceptHandler.name) {
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
}
ADDOP_JUMP(c, JUMP_FORWARD, except);
/* except: */
compiler_use_next_block(c, cleanup_end);
/* name = None; del name; # Mark as artificial */
UNSET_LOC(c);
if (handler->v.ExceptHandler.name) {
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
}
/* add exception raised to the res list */
ADDOP(c, POP_TOP); // type
ADDOP_I(c, LIST_APPEND, 6); // exc
ADDOP(c, POP_TOP); // tb
ADDOP(c, POP_TOP); // lasti
ADDOP_JUMP(c, JUMP_ABSOLUTE, except);
compiler_use_next_block(c, except);
if (i == n - 1) {
/* Add exc to the list (if not None it's the unhandled part of the EG) */
ADDOP(c, POP_TOP);
ADDOP_I(c, LIST_APPEND, 2);
ADDOP(c, POP_TOP);
ADDOP_JUMP(c, JUMP_FORWARD, reraise_star);
}
}
/* Mark as artificial */
UNSET_LOC(c);
compiler_pop_fblock(c, EXCEPTION_GROUP_HANDLER, NULL);
basicblock *reraise = compiler_new_block(c);
if (!reraise) {
return 0;
}
compiler_use_next_block(c, reraise_star);
ADDOP(c, PREP_RERAISE_STAR);
ADDOP(c, DUP_TOP);
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, reraise);
NEXT_BLOCK(c);
/* Nothing to reraise - pop it */
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, reraise);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, cleanup);
ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.TryStar.orelse);
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, end);
return 1;
}
@ -3372,6 +3697,16 @@ compiler_try(struct compiler *c, stmt_ty s) {
return compiler_try_except(c, s);
}
static int
compiler_try_star(struct compiler *c, stmt_ty s)
{
if (s->v.TryStar.finalbody && asdl_seq_LEN(s->v.TryStar.finalbody)) {
return compiler_try_star_finally(c, s);
}
else {
return compiler_try_star_except(c, s);
}
}
static int
compiler_import_as(struct compiler *c, identifier name, identifier asname)
@ -3634,6 +3969,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
break;
case Try_kind:
return compiler_try(c, s);
case TryStar_kind:
return compiler_try_star(c, s);
case Assert_kind:
return compiler_assert(c, s);
case Import_kind:

View file

@ -87,7 +87,7 @@ static void *opcode_targets[256] = {
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_YIELD_VALUE,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@ -122,11 +122,11 @@ static void *opcode_targets[256] = {
&&TARGET_COPY,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_BINARY_OP,
&&_unknown_opcode,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_LOAD_FAST,
&&TARGET_STORE_FAST,
&&TARGET_DELETE_FAST,
&&_unknown_opcode,
&&TARGET_JUMP_IF_NOT_EG_MATCH,
&&_unknown_opcode,
&&TARGET_GEN_START,
&&TARGET_RAISE_VARARGS,

View file

@ -1345,6 +1345,12 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, excepthandler, s->v.Try.handlers);
VISIT_SEQ(st, stmt, s->v.Try.finalbody);
break;
case TryStar_kind:
VISIT_SEQ(st, stmt, s->v.TryStar.body);
VISIT_SEQ(st, stmt, s->v.TryStar.orelse);
VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers);
VISIT_SEQ(st, stmt, s->v.TryStar.finalbody);
break;
case Assert_kind:
VISIT(st, expr, s->v.Assert.test);
if (s->v.Assert.msg)