FFFF cpython/Objects/exceptions.c at 3.12 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
3862 lines (3341 loc) · 108 KB

File metadata and controls

3862 lines (3341 loc) · 108 KB
/*
* New exceptions.c written in Iceland by Richard Jones and Georg Brandl.
*
* Thanks go to Tim Peters and Michael Hudson for debugging.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdbool.h>
#include "pycore_ceval.h" // _Py_EnterRecursiveCall
#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException
#include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_initconfig.h"
#include "pycore_object.h"
#include "structmember.h" // PyMemberDef
#include "osdefs.h" // SEP
/* Compatibility aliases */
PyObject *PyExc_EnvironmentError = NULL; // borrowed ref
PyObject *PyExc_IOError = NULL; // borrowed ref
#ifdef MS_WINDOWS
PyObject *PyExc_WindowsError = NULL; // borrowed ref
#endif
static struct _Py_exc_state*
get_exc_state(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->exc_state;
}
/* NOTE: If the exception class hierarchy changes, don't forget to update
* Lib/test/exception_hierarchy.txt
*/
/*
* BaseException
*/
static PyObject *
BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyBaseExceptionObject *self;
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self)
return NULL;
/* the dict is created on the fly in PyObject_GenericSetAttr */
self->dict = NULL;
self->notes = NULL;
self->traceback = self->cause = self->context = NULL;
self->suppress_context = 0;
if (args) {
self->args = Py_NewRef(args);
return (PyObject *)self;
}
self->args = PyTuple_New(0);
if (!self->args) {
Py_DECREF(self);
return NULL;
}
return (PyObject *)self;
}
static int
BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
{
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
return -1;
Py_XSETREF(self->args, Py_NewRef(args));
return 0;
}
static int
BaseException_clear(PyBaseExceptionObject *self)
{
Py_CLEAR(self->dict);
Py_CLEAR(self->args);
Py_CLEAR(self->notes);
Py_CLEAR(self->traceback);
Py_CLEAR(self->cause);
Py_CLEAR(self->context);
return 0;
}
static void
BaseException_dealloc(PyBaseExceptionObject *self)
{
PyObject_GC_UnTrack(self);
// bpo-44348: The trashcan mechanism prevents stack overflow when deleting
// long chains of exceptions. For example, exceptions can be chained
// through the __context__ attributes or the __traceback__ attribute.
Py_TRASHCAN_BEGIN(self, BaseException_dealloc)
BaseException_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END
}
static int
BaseException_traverse(PyBaseExceptionObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->dict);
Py_VISIT(self->args);
Py_VISIT(self->notes);
Py_VISIT(self->traceback);
Py_VISIT(self->cause);
Py_VISIT(self->context);
return 0;
}
static PyObject *
BaseException_str(PyBaseExceptionObject *self)
{
switch (PyTuple_GET_SIZE(self->args)) {
case 0:
return PyUnicode_FromString("");
case 1:
return PyObject_Str(PyTuple_GET_ITEM(self->args, 0));
default:
return PyObject_Str(self->args);
}
}
static PyObject *
BaseException_repr(PyBaseExceptionObject *self)
{
const char *name = _PyType_Name(Py_TYPE(self));
if (PyTuple_GET_SIZE(self->args) == 1)
return PyUnicode_FromFormat("%s(%R)", name,
PyTuple_GET_ITEM(self->args, 0));
else
return PyUnicode_FromFormat("%s%R", name, self->args);
}
/* Pickling support */
static PyObject *
BaseException_reduce(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored))
{
if (self->args && self->dict)
return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict);
else
return PyTuple_Pack(2, Py_TYPE(self), self->args);
}
/*
* Needed for backward compatibility, since exceptions used to store
* all their attributes in the __dict__. Code is taken from cPickle's
* load_build function.
*/
static PyObject *
BaseException_setstate(PyObject *self, PyObject *state)
{
PyObject *d_key, *d_value;
Py_ssize_t i = 0;
if (state != Py_None) {
if (!PyDict_Check(state)) {
PyErr_SetString(PyExc_TypeError, "state is not a dictionary");
return NULL;
}
while (PyDict_Next(state, &i, &d_key, &d_value)) {
Py_INCREF(d_key);
Py_INCREF(d_value);
int res = PyObject_SetAttr(self, d_key, d_value);
Py_DECREF(d_value);
Py_DECREF(d_key);
if (res < 0) {
return NULL;
}
}
}
Py_RETURN_NONE;
}
static PyObject *
BaseException_with_traceback(PyObject *self, PyObject *tb) {
if (PyException_SetTraceback(self, tb))
return NULL;
return Py_NewRef(self);
}
PyDoc_STRVAR(with_traceback_doc,
"Exception.with_traceback(tb) --\n\
set self.__traceback__ to tb and return self.");
static inline PyBaseExceptionObject*
_PyBaseExceptionObject_cast(PyObject *exc)
{
assert(PyExceptionInstance_Check(exc));
return (PyBaseExceptionObject *)exc;
}
static PyObject *
BaseException_add_note(PyObject *self, PyObject *note)
{
if (!PyUnicode_Check(note)) {
PyErr_Format(PyExc_TypeError,
"note must be a str, not '%s'",
Py_TYPE(note)->tp_name);
return NULL;
}
PyObject *notes;
if (_PyObject_LookupAttr(self, &_Py_ID(__notes__), &notes) < 0) {
return NULL;
}
if (notes == NULL) {
notes = PyList_New(0);
if (notes == NULL) {
return NULL;
}
if (PyObject_SetAttr(self, &_Py_ID(__notes__), notes) < 0) {
Py_DECREF(notes);
return NULL;
}
}
else if (!PyList_Check(notes)) {
Py_DECREF(notes);
PyErr_SetString(PyExc_TypeError, "Cannot add note: __notes__ is not a list");
return NULL;
}
if (PyList_Append(notes, note) < 0) {
Py_DECREF(notes);
return NULL;
}
Py_DECREF(notes);
Py_RETURN_NONE;
}
PyDoc_STRVAR(add_note_doc,
"Exception.add_note(note) --\n\
add a note to the exception");
static PyMethodDef BaseException_methods[] = {
{"__reduce__", (PyCFunction)BaseException_reduce, METH_NOARGS },
{"__setstate__", (PyCFunction)BaseException_setstate, METH_O },
{"with_traceback", (PyCFunction)BaseException_with_traceback, METH_O,
with_traceback_doc},
{"add_note", (PyCFunction)BaseException_add_note, METH_O,
add_note_doc},
{NULL, NULL, 0, NULL},
};
static PyObject *
BaseException_get_args(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
{
if (self->args == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(self->args);
}
static int
BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUSED(ignored))
{
PyObject *seq;
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "args may not be deleted");
return -1;
}
seq = PySequence_Tuple(val);
if (!seq)
return -1;
Py_XSETREF(self->args, seq);
return 0;
}
static PyObject *
BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
{
if (self->traceback == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(self->traceback);
}
static int
BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored))
{
if (tb == NULL) {
PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted");
return -1;
}
if (PyTraceBack_Check(tb)) {
Py_XSETREF(self->traceback, Py_NewRef(tb));
}
else if (tb == Py_None) {
Py_CLEAR(self->traceback);
}
else {
PyErr_SetString(PyExc_TypeError,
"__traceback__ must be a traceback or None");
return -1;
}
return 0;
}
static PyObject *
BaseException_get_context(PyObject *self, void *Py_UNUSED(ignored))
{
PyObject *res = PyException_GetContext(self);
if (res)
return res; /* new reference already returned above */
Py_RETURN_NONE;
}
static int
BaseException_set_context(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored))
{
if (arg == NULL) {
PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted");
return -1;
} else if (arg == Py_None) {
arg = NULL;
} else if (!PyExceptionInstance_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "exception context must be None "
"or derive from BaseException");
return -1;
} else {
/* PyException_SetContext steals this reference */
Py_INCREF(arg);
}
PyException_SetContext(self, arg);
return 0;
}
static PyObject *
BaseException_get_cause(PyObject *self, void *Py_UNUSED(ignored))
{
PyObject *res = PyException_GetCause(self);
if (res)
return res; /* new reference already returned above */
Py_RETURN_NONE;
}
static int
BaseException_set_cause(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored))
{
if (arg == NULL) {
PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
return -1;
} else if (arg == Py_None) {
arg = NULL;
} else if (!PyExceptionInstance_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "exception cause must be None "
"or derive from BaseException");
return -1;
} else {
/* PyException_SetCause steals this reference */
Py_INCREF(arg);
}
PyException_SetCause(self, arg);
return 0;
}
static PyGetSetDef BaseException_getset[] = {
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
{"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
{"__context__", BaseException_get_context,
BaseException_set_context, PyDoc_STR("exception context")},
{"__cause__", BaseException_get_cause,
BaseException_set_cause, PyDoc_STR("exception cause")},
{NULL},
};
PyObject *
PyException_GetTraceback(PyObject *self)
{
PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self);
return Py_XNewRef(base_self->traceback);
}
int
PyException_SetTraceback(PyObject *self, PyObject *tb)
{
return BaseException_set_tb(_PyBaseExceptionObject_cast(self), tb, NULL);
}
PyObject *
PyException_GetCause(PyObject *self)
{
PyObject *cause = _PyBaseExceptionObject_cast(self)->cause;
return Py_XNewRef(cause);
}
/* Steals a reference to cause */
void
PyException_SetCause(PyObject *self, PyObject *cause)
{
PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self);
base_self->suppress_context = 1;
Py_XSETREF(base_self->cause, cause);
}
PyObject *
PyException_GetContext(PyObject *self)
{
PyObject *context = _PyBaseExceptionObject_cast(self)->context;
return Py_XNewRef(context);
}
/* Steals a reference to context */
void
PyException_SetContext(PyObject *self, PyObject *context)
{
Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context);
}
PyObject *
PyException_GetArgs(PyObject *self)
{
PyObject *args = _PyBaseExceptionObject_cast(self)->args;
return Py_NewRef(args);
}
void
PyException_SetArgs(PyObject *self, PyObject *args)
{
Py_INCREF(args);
Py_XSETREF(_PyBaseExceptionObject_cast(self)->args, args);
}
const char *
PyExceptionClass_Name(PyObject *ob)
{
assert(PyExceptionClass_Check(ob));
return ((PyTypeObject*)ob)->tp_name;
}
static struct PyMemberDef BaseException_members[] = {
{"__suppress_context__", T_BOOL,
offsetof(PyBaseExceptionObject, suppress_context)},
{NULL}
};
static PyTypeObject _PyExc_BaseException = {
PyVarObject_HEAD_INIT(NULL, 0)
"BaseException", /*tp_name*/
sizeof(PyBaseExceptionObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)BaseException_dealloc, /*tp_dealloc*/
0, /*tp_vectorcall_offset*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_as_async*/
(reprfunc)BaseException_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)BaseException_str, /*tp_str*/
PyObject_GenericGetAttr, /*tp_getattro*/
PyObject_GenericSetAttr, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASE_EXC_SUBCLASS, /*tp_flags*/
PyDoc_STR("Common base class for all exceptions"), /* tp_doc */
(traverseproc)BaseException_traverse, /* tp_traverse */
(inquiry)BaseException_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
BaseException_methods, /* tp_methods */
BaseException_members, /* tp_members */
BaseException_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
offsetof(PyBaseExceptionObject, dict), /* tp_dictoffset */
(initproc)BaseException_init, /* tp_init */
0, /* tp_alloc */
BaseException_new, /* tp_new */
};
/* the CPython API expects exceptions to be (PyObject *) - both a hold-over
from the previous implementation and also allowing Python objects to be used
in the API */
PyObject *PyExc_BaseException = (PyObject *)&_PyExc_BaseException;
/* note these macros omit the last semicolon so the macro invocation may
* include it and not look strange.
*/
#define SimpleExtendsException(EXCBASE, EXCNAME, EXCDOC) \
static PyTypeObject _PyExc_ ## EXCNAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
# EXCNAME, \
sizeof(PyBaseExceptionObject), \
0, (destructor)BaseException_dealloc, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)BaseException_traverse, \
(inquiry)BaseException_clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(PyBaseExceptionObject, dict), \
(initproc)BaseException_init, 0, BaseException_new,\
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
#define MiddlingExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCDOC) \
static PyTypeObject _PyExc_ ## EXCNAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
# EXCNAME, \
sizeof(Py ## EXCSTORE ## Object), \
0, (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
(initproc)EXCSTORE ## _init, 0, 0, \
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
#define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \
EXCMETHODS, EXCMEMBERS, EXCGETSET, \
EXCSTR, EXCDOC) \
static PyTypeObject _PyExc_ ## EXCNAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
# EXCNAME, \
sizeof(Py ## EXCSTORE ## Object), 0, \
(destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
(reprfunc)EXCSTR, 0, 0, 0, \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, EXCMETHODS, \
EXCMEMBERS, EXCGETSET, &_ ## EXCBASE, \
0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
(initproc)EXCSTORE ## _init, 0, EXCNEW,\
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
/*
* Exception extends BaseException
*/
SimpleExtendsException(PyExc_BaseException, Exception,
"Common base class for all non-exit exceptions.");
/*
* TypeError extends Exception
*/
SimpleExtendsException(PyExc_Exception, TypeError,
"Inappropriate argument type.");
/*
* StopAsyncIteration extends Exception
*/
SimpleExtendsException(PyExc_Exception, StopAsyncIteration,
"Signal the end from iterator.__anext__().");
/*
* StopIteration extends Exception
*/
static PyMemberDef StopIteration_members[] = {
{"value", T_OBJECT, offsetof(PyStopIterationObject, value), 0,
PyDoc_STR("generator return value")},
{NULL} /* Sentinel */
};
static int
StopIteration_init(PyStopIterationObject *self, PyObject *args, PyObject *kwds)
{
Py_ssize_t size = PyTuple_GET_SIZE(args);
PyObject *value;
if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
return -1;
Py_CLEAR(self->value);
if (size > 0)
value = PyTuple_GET_ITEM(args, 0);
else
value = Py_None;
self->value = Py_NewRef(value);
return 0;
}
static int
StopIteration_clear(PyStopIterationObject *self)
{
Py_CLEAR(self->value);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
StopIteration_dealloc(PyStopIterationObject *self)
{
PyObject_GC_UnTrack(self);
StopIteration_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->value);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration,
0, 0, StopIteration_members, 0, 0,
"Signal the end from iterator.__next__().");
/*
* GeneratorExit extends BaseException
*/
SimpleExtendsException(PyExc_BaseException, GeneratorExit,
"Request that a generator exit.");
/*
* SystemExit extends BaseException
*/
static int
SystemExit_init(PySystemExitObject *self, PyObject *args, PyObject *kwds)
{
Py_ssize_t size = PyTuple_GET_SIZE(args);
if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
return -1;
if (size == 0)
return 0;
if (size == 1) {
Py_XSETREF(self->code, Py_NewRef(PyTuple_GET_ITEM(args, 0)));
}
else { /* size > 1 */
Py_XSETREF(self->code, Py_NewRef(args));
}
return 0;
}
static int
SystemExit_clear(PySystemExitObject *self)
{
Py_CLEAR(self->code);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
SystemExit_dealloc(PySystemExitObject *self)
{
_PyObject_GC_UNTRACK(self);
SystemExit_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
SystemExit_traverse(PySystemExitObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->code);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyMemberDef SystemExit_members[] = {
{"code", T_OBJECT, offsetof(PySystemExitObject, code), 0,
PyDoc_STR("exception code")},
{NULL} /* Sentinel */
};
ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit,
0, 0, SystemExit_members, 0, 0,
"Request to exit from the interpreter.");
/*
* BaseExceptionGroup extends BaseException
* ExceptionGroup extends BaseExceptionGroup and Exception
*/
static inline PyBaseExceptionGroupObject*
_PyBaseExceptionGroupObject_cast(PyObject *exc)
{
assert(_PyBaseExceptionGroup_Check(exc));
return (PyBaseExceptionGroupObject *)exc;
}
static PyObject *
BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
struct _Py_exc_state *state = get_exc_state();
PyTypeObject *PyExc_ExceptionGroup =
(PyTypeObject*)state->PyExc_ExceptionGroup;
PyObject *message = NULL;
PyObject *exceptions = NULL;
if (!PyArg_ParseTuple(args,
"UO:BaseExceptionGroup.__new__",
&message,
&exceptions)) {
return NULL;
}
if (!PySequence_Check(exceptions)) {
PyErr_SetString(
PyExc_TypeError,
"second argument (exceptions) must be a sequence");
return NULL;
}
exceptions = PySequence_Tuple(exceptions);
if (!exceptions) {
return NULL;
}
/* We are now holding a ref to the exceptions tuple */
Py_ssize_t numexcs = PyTuple_GET_SIZE(exceptions);
if (numexcs == 0) {
PyErr_SetString(
PyExc_ValueError,
"second argument (exceptions) must be a non-empty sequence");
goto error;
}
bool nested_base_exceptions = false;
for (Py_ssize_t i = 0; i < numexcs; i++) {
PyObject *exc = PyTuple_GET_ITEM(exceptions, i);
if (!exc) {
goto error;
}
if (!PyExceptionInstance_Check(exc)) {
PyErr_Format(
PyExc_ValueError,
"Item %d of second argument (exceptions) is not an exception",
i);
goto error;
}
int is_nonbase_exception = PyObject_IsInstance(exc, PyExc_Exception);
if (is_nonbase_exception < 0) {
goto error;
}
else if (is_nonbase_exception == 0) {
nested_base_exceptions = true;
}
}
PyTypeObject *cls = type;
if (cls == PyExc_ExceptionGroup) {
if (nested_base_exceptions) {
PyErr_SetString(PyExc_TypeError,
"Cannot nest BaseExceptions in an ExceptionGroup");
goto error;
}
}
else if (cls == (PyTypeObject*)PyExc_BaseExceptionGroup) {
if (!nested_base_exceptions) {
/* All nested exceptions are Exception subclasses,
* wrap them in an ExceptionGroup
*/
cls = PyExc_ExceptionGroup;
}
}
else {
/* user-defined subclass */
if (nested_base_exceptions) {
int nonbase = PyObject_IsSubclass((PyObject*)cls, PyExc_Exception);
if (nonbase == -1) {
goto error;
}
else if (nonbase == 1) {
PyErr_Format(PyExc_TypeError,
"Cannot nest BaseExceptions in '%.200s'",
cls->tp_name);
goto error;
}
}
}
if (!cls) {
/* Don't crash during interpreter shutdown
* (PyExc_ExceptionGroup may have been cleared)
*/
cls = (PyTypeObject*)PyExc_BaseExceptionGroup;
}
PyBaseExceptionGroupObject *self =
_PyBaseExceptionGroupObject_cast(BaseException_new(cls, args, kwds));
if (!self) {
goto error;
}
self->msg = Py_NewRef(message);
self->excs = exceptions;
return (PyObject*)self;
error:
Py_DECREF(exceptions);
return NULL;
}
PyObject *
_PyExc_CreateExceptionGroup(const char *msg_str, PyObject *excs)
{
PyObject *msg = PyUnicode_FromString(msg_str);
if (!msg) {
return NULL;
}
PyObject *args = PyTuple_Pack(2, msg, excs);
Py_DECREF(msg);
if (!args) {
return NULL;
}
PyObject *result = PyObject_CallObject(PyExc_BaseExceptionGroup, args);
Py_DECREF(args);
return result;
}
static int
BaseExceptionGroup_init(PyBaseExceptionGroupObject *self,
PyObject *args, PyObject *kwds)
{
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds)) {
return -1;
}
if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1) {
return -1;
}
return 0;
}
static int
BaseExceptionGroup_clear(PyBaseExceptionGroupObject *self)
{
Py_CLEAR(self->msg);
Py_CLEAR(self->excs);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
BaseExceptionGroup_dealloc(PyBaseExceptionGroupObject *self)
{
_PyObject_GC_UNTRACK(self);
BaseExceptionGroup_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
BaseExceptionGroup_traverse(PyBaseExceptionGroupObject *self,
visitproc visit, void *arg)
{
Py_VISIT(self->msg);
Py_VISIT(self->excs);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyObject *
BaseExceptionGroup_str(PyBaseExceptionGroupObject *self)
{
assert(self->msg);
assert(PyUnicode_Check(self->msg));
assert(PyTuple_CheckExact(self->excs));
Py_ssize_t num_excs = PyTuple_Size(self->excs);
return PyUnicode_FromFormat(
"%S (%zd sub-exception%s)",
self->msg, num_excs, num_excs > 1 ? "s" : "");
}
static PyObject *
BaseExceptionGroup_derive(PyObject *self_, PyObject *args)
{
PyBaseExceptionGroupObject *self = _PyBaseExceptionGroupObject_cast(self_);
PyObject *excs = NULL;
if (!PyArg_ParseTuple(args, "O", &excs)) {
return NULL;
}
PyObject *init_args = PyTuple_Pack(2, self->msg, excs);
if (!init_args) {
return NULL;
}
PyObject *eg = PyObject_CallObject(
PyExc_BaseExceptionGroup, init_args);
Py_DECREF(init_args);
return eg;
}
static int
exceptiongroup_subset(
PyBaseExceptionGroupObject *_orig, PyObject *excs, PyObject **result)
{
/* Sets *result to an ExceptionGroup wrapping excs with metadata from
* _orig. If excs is empty, sets *result to NULL.
* Returns 0 on success and -1 on error.
* This function is used by split() to construct the match/rest parts,
* so excs is the matching or non-matching sub-sequence of orig->excs
* (this function does not verify that it is a subsequence).
*/
PyObject *orig = (PyObject *)_orig;
*result = NULL;
Py_ssize_t num_excs = PySequence_Size(excs);
if (num_excs < 0) {
return -1;
}
else if (num_excs == 0) {
return 0;
}
PyObject *eg = PyObject_CallMethod(
orig, "derive", "(O)", excs);
if (!eg) {
return -1;
}
if (!_PyBaseExceptionGroup_Check(eg)) {
PyErr_SetString(PyExc_TypeError,
"derive must return an instance of BaseExceptionGroup");
goto error;
}
/* Now we hold a reference to the new eg */
PyObject *tb = PyException_GetTraceback(orig);
if (tb) {
int res = PyException_SetTraceback(eg, tb);
Py_DECREF(tb);
if (res < 0) {
goto error;
}
}
PyException_SetContext(eg, PyException_GetContext(orig));
PyException_SetCause(eg, PyException_GetCause(orig));
PyObject *notes;
if (_PyObject_LookupAttr(orig, &_Py_ID(__notes__), &notes) < 0) {
goto error;
}
if (notes) {
if (PySequence_Check(notes)) {
/* Make a copy so the parts have independent notes lists. */
PyObject *notes_copy = PySequence_List(notes);
Py_DECREF(notes);
if (notes_copy == NULL) {
goto error;
}
int res = PyObject_SetAttr(eg, &_Py_ID(__notes__), notes_copy);
Py_DECREF(notes_copy);
if (res < 0) {
goto error;
}
}
else {
/* __notes__ is supposed to be a list, and split() is not a
* good place to report earlier user errors, so we just ignore
* notes of non-sequence type.
*/
Py_DECREF(notes);
}
}
*result = eg;
return 0;
error:
Py_DECREF(eg);
return -1;
}
typedef enum {
/* Exception type or tuple of thereof */
EXCEPTION_GROUP_MATCH_BY_TYPE = 0,
/* A PyFunction returning True for matching exceptions */
EXCEPTION_GROUP_MATCH_BY_PREDICATE = 1,
/* A set of the IDs of leaf exceptions to include in the result.
* This matcher type is used internally by the interpreter
* to construct reraised exceptions.
*/
EXCEPTION_GROUP_MATCH_INSTANCE_IDS = 2
} _exceptiongroup_split_matcher_type;
static int
get_matcher_type(PyObject *value,
_exceptiongroup_split_matcher_type *type)
{
assert(value);
if (PyFunction_Check(value)) {
*type = EXCEPTION_GROUP_MATCH_BY_PREDICATE;
return 0;
}
if (PyExceptionClass_Check(value)) {
0