blob: a8d4e3a696ce8e216662e6011f2f67218ea6f961 [file] [log] [blame]
/*
* 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;
}
if (!PyObject_HasAttr(self, &_Py_ID(__notes__))) {
PyObject *new_notes = PyList_New(0);
if (new_notes == NULL) {
return NULL;
}
if (PyObject_SetAttr(self, &_Py_ID(__notes__), new_notes) < 0) {
Py_DECREF(new_notes);
return NULL;
}
Py_DECREF(new_notes);
}
PyObject *notes = PyObject_GetAttr(self, &_Py_ID(__notes__));
if (notes == NULL) {
return NULL;
}
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));
if (PyObject_HasAttr(orig, &_Py_ID(__notes__))) {
PyObject *notes = PyObject_GetAttr(orig, &_Py_ID(__notes__));
if (notes == NULL) {
goto error;
}
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)) {
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
return 0;
}
if (PyTuple_CheckExact(value)) {
Py_ssize_t n = PyTuple_GET_SIZE(value);
for (Py_ssize_t i=0; i<n; i++) {
if (!PyExceptionClass_Check(PyTuple_GET_ITEM(value, i))) {
goto error;
}
}
*type = EXCEPTION_GROUP_MATCH_BY_TYPE;
return 0;
}
error:
PyErr_SetString(
PyExc_TypeError,
"expected a function, exception type or tuple of exception types");
return -1;
}
static int
exceptiongroup_split_check_match(PyObject *exc,
_exceptiongroup_split_matcher_type matcher_type,
PyObject *matcher_value)
{
switch (matcher_type) {
case EXCEPTION_GROUP_MATCH_BY_TYPE: {
assert(PyExceptionClass_Check(matcher_value) ||
PyTuple_CheckExact(matcher_value));
return PyErr_GivenExceptionMatches(exc, matcher_value);
}
case EXCEPTION_GROUP_MATCH_BY_PREDICATE: {
assert(PyFunction_Check(matcher_value));
PyObject *exc_matches = PyObject_CallOneArg(matcher_value, exc);
if (exc_matches == NULL) {
return -1;
}
int is_true = PyObject_IsTrue(exc_matches);
Py_DECREF(exc_matches);
return is_true;
}
case EXCEPTION_GROUP_MATCH_INSTANCE_IDS: {
assert(PySet_Check(matcher_value));
if (!_PyBaseExceptionGroup_Check(exc)) {
PyObject *exc_id = PyLong_FromVoidPtr(exc);
if (exc_id == NULL) {
return -1;
}
int res = PySet_Contains(matcher_value, exc_id);
Py_DECREF(exc_id);
return res;
}
return 0;
}
}
return 0;
}
typedef struct {
PyObject *match;
PyObject *rest;
} _exceptiongroup_split_result;
static int
exceptiongroup_split_recursive(PyObject *exc,
_exceptiongroup_split_matcher_type matcher_type,
PyObject *matcher_value,
bool construct_rest,
_exceptiongroup_split_result *result)
{
result->match = NULL;
result->rest = NULL;
int is_match = exceptiongroup_split_check_match(
exc, matcher_type, matcher_value);
if (is_match < 0) {
return -1;
}
if (is_match) {
/* Full match */
result->match = Py_NewRef(exc);
return 0;
}
else if (!_PyBaseExceptionGroup_Check(exc)) {
/* Leaf exception and no match */
if (construct_rest) {
result->rest = Py_NewRef(exc);
}
return 0;
}
/* Partial match */
PyBaseExceptionGroupObject *eg = _PyBaseExceptionGroupObject_cast(exc);
assert(PyTuple_CheckExact(eg->excs));
Py_ssize_t num_excs = PyTuple_Size(eg->excs);
if (num_excs < 0) {
return -1;
}
assert(num_excs > 0); /* checked in constructor, and excs is read-only */
int retval = -1;
PyObject *match_list = PyList_New(0);
if (!match_list) {
return -1;
}
PyObject *rest_list = NULL;
if (construct_rest) {
rest_list = PyList_New(0);
if (!rest_list) {
goto done;
}
}
/* recursive calls */
for (Py_ssize_t i = 0; i < num_excs; i++) {
PyObject *e = PyTuple_GET_ITEM(eg->excs, i);
_exceptiongroup_split_result rec_result;
if (_Py_EnterRecursiveCall(" in exceptiongroup_split_recursive")) {
goto done;
}
if (exceptiongroup_split_recursive(
e, matcher_type, matcher_value,
construct_rest, &rec_result) < 0) {
assert(!rec_result.match);
assert(!rec_result.rest);
_Py_LeaveRecursiveCall();
goto done;
}
_Py_LeaveRecursiveCall();
if (rec_result.match) {
assert(PyList_CheckExact(match_list));
if (PyList_Append(match_list, rec_result.match) < 0) {
Py_DECREF(rec_result.match);
Py_XDECREF(rec_result.rest);
goto done;
}
Py_DECREF(rec_result.match);
}
if (rec_result.rest) {
assert(construct_rest);
assert(PyList_CheckExact(rest_list));
if (PyList_Append(rest_list, rec_result.rest) < 0) {
Py_DECREF(rec_result.rest);
goto done;
}
Py_DECREF(rec_result.rest);
}
}
/* construct result */
if (exceptiongroup_subset(eg, match_list, &result->match) < 0) {
goto done;
}
if (construct_rest) {
assert(PyList_CheckExact(rest_list));
if (exceptiongroup_subset(eg, rest_list, &result->rest) < 0) {
Py_CLEAR(result->match);
goto done;
}
}
retval = 0;
done:
Py_DECREF(match_list);
Py_XDECREF(rest_list);
if (retval < 0) {
Py_CLEAR(result->match);
Py_CLEAR(result->rest);
}
return retval;
}
static PyObject *
BaseExceptionGroup_split(PyObject *self, PyObject *args)
{
PyObject *matcher_value = NULL;
if (!PyArg_UnpackTuple(args, "split", 1, 1, &matcher_value)) {
return NULL;
}
_exceptiongroup_split_matcher_type matcher_type;
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
return NULL;
}
_exceptiongroup_split_result split_result;
bool construct_rest = true;
if (exceptiongroup_split_recursive(
self, matcher_type, matcher_value,
construct_rest, &split_result) < 0) {
return NULL;
}
PyObject *result = PyTuple_Pack(
2,
split_result.match ? split_result.match : Py_None,
split_result.rest ? split_result.rest : Py_None);
Py_XDECREF(split_result.match);
Py_XDECREF(split_result.rest);
return result;
}
static PyObject *
BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
{
PyObject *matcher_value = NULL;
if (!PyArg_UnpackTuple(args, "subgroup", 1, 1, &matcher_value)) {
return NULL;
}
_exceptiongroup_split_matcher_type matcher_type;
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
return NULL;
}
_exceptiongroup_split_result split_result;
bool construct_rest = false;
if (exceptiongroup_split_recursive(
self, matcher_type, matcher_value,
construct_rest, &split_result) < 0) {
return NULL;
}
PyObject *result = Py_NewRef(
split_result.match ? split_result.match : Py_None);
Py_XDECREF(split_result.match);
assert(!split_result.rest);
return result;
}
static int
collect_exception_group_leaf_ids(PyObject *exc, PyObject *leaf_ids)
{
if (Py_IsNone(exc)) {
return 0;
}
assert(PyExceptionInstance_Check(exc));
assert(PySet_Check(leaf_ids));
/* Add IDs of all leaf exceptions in exc to the leaf_ids set */
if (!_PyBaseExceptionGroup_Check(exc)) {
PyObject *exc_id = PyLong_FromVoidPtr(exc);
if (exc_id == NULL) {
return -1;
}
int res = PySet_Add(leaf_ids, exc_id);
Py_DECREF(exc_id);
return res;
}
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_leaf_ids")) {
return -1;
}
int res = collect_exception_group_leaf_ids(e, leaf_ids);
_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.
*/
static PyObject *
exception_group_projection(PyObject *eg, PyObject *keep)
{
assert(_PyBaseExceptionGroup_Check(eg));
assert(PyList_CheckExact(keep));
PyObject *leaf_ids = PySet_New(NULL);
if (!leaf_ids) {
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_leaf_ids(e, leaf_ids) < 0) {
Py_DECREF(leaf_ids);
return NULL;
}
}
_exceptiongroup_split_result split_result;
bool construct_rest = false;
int err = exceptiongroup_split_recursive(
eg, EXCEPTION_GROUP_MATCH_INSTANCE_IDS, leaf_ids,
construct_rest, &split_result);
Py_DECREF(leaf_ids);
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 bool
is_same_exception_metadata(PyObject *exc1, PyObject *exc2)
{
assert(PyExceptionInstance_Check(exc1));
assert(PyExceptionInstance_Check(exc2));
PyBaseExceptionObject *e1 = (PyBaseExceptionObject *)exc1;
PyBaseExceptionObject *e2 = (PyBaseExceptionObject *)exc2;
return (e1->notes == e2->notes &&
e1->traceback == e2->traceback &&
e1->cause == e2->cause &&
e1->context == e2->context);
}
/*
This function is used by the interpreter to calculate
the exception group to be raised at the end of a
try-except* construct.
orig: the original except that was caught.
excs: a list of exceptions that were raised/reraised
in the except* clauses.
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.
*/
PyObject *
_PyExc_PrepReraiseStar(PyObject *orig, PyObject *excs)
{
assert(PyExceptionInstance_Check(orig));
assert(PyList_Check(excs));
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 = exception_group_projection(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;
}
if (PyList_GET_SIZE(raised_list) > 1) {
result = _PyExc_CreateExceptionGroup("", raised_list);
}
else {
result = Py_NewRef(PyList_GetItem(raised_list, 0));
}
if (result == NULL) {
goto done;
}
}
done:
Py_XDECREF(raised_list);
Py_XDECREF(reraised_list);
return result;
}
static PyMemberDef BaseExceptionGroup_members[] = {
{"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY,
PyDoc_STR("exception message")},
{"exceptions", T_OBJECT, offsetof(PyBaseExceptionGroupObject, excs), READONLY,
PyDoc_STR("nested exceptions")},
{NULL} /* Sentinel */
};
static PyMethodDef BaseExceptionGroup_methods[] = {
{"__class_getitem__", (PyCFunction)Py_GenericAlias,
METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{"derive", (PyCFunction)BaseExceptionGroup_derive, METH_VARARGS},
{"split", (PyCFunction)BaseExceptionGroup_split, METH_VARARGS},
{"subgroup", (PyCFunction)BaseExceptionGroup_subgroup, METH_VARARGS},
{NULL}
};
ComplexExtendsException(PyExc_BaseException, BaseExceptionGroup,
BaseExceptionGroup, BaseExceptionGroup_new /* new */,
BaseExceptionGroup_methods, BaseExceptionGroup_members,
0 /* getset */, BaseExceptionGroup_str,
"A combination of multiple unrelated exceptions.");
/*
* ExceptionGroup extends BaseExceptionGroup, Exception
*/
static PyObject*
create_exception_group_class(void) {
struct _Py_exc_state *state = get_exc_state();
PyObject *bases = PyTuple_Pack(
2, PyExc_BaseExceptionGroup, PyExc_Exception);
if (bases == NULL) {
return NULL;
}
assert(!state->PyExc_ExceptionGroup);
state->PyExc_ExceptionGroup = PyErr_NewException(
"builtins.ExceptionGroup", bases, NULL);
Py_DECREF(bases);
return state->PyExc_ExceptionGroup;
}
/*
* KeyboardInterrupt extends BaseException
*/
SimpleExtendsException(PyExc_BaseException, KeyboardInterrupt,
"Program interrupted by user.");
/*
* ImportError extends Exception
*/
static int
ImportError_init(PyImportErrorObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"name", "path", "name_from", 0};
PyObject *empty_tuple;
PyObject *msg = NULL;
PyObject *name = NULL;
PyObject *path = NULL;
PyObject *name_from = NULL;
if (BaseException_init((PyBaseExceptionObject *)self, args, NULL) == -1)
return -1;
empty_tuple = PyTuple_New(0);
if (!empty_tuple)
return -1;
if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$OOO:ImportError", kwlist,
&name, &path, &name_from)) {
Py_DECREF(empty_tuple);
return -1;
}
Py_DECREF(empty_tuple);
Py_XSETREF(self->name, Py_XNewRef(name));
Py_XSETREF(self->path, Py_XNewRef(path));
Py_XSETREF(self->name_from, Py_XNewRef(name_from));
if (PyTuple_GET_SIZE(args) == 1) {
msg = Py_NewRef(PyTuple_GET_ITEM(args, 0));
}
Py_XSETREF(self->msg, msg);
return 0;
}
static int
ImportError_clear(PyImportErrorObject *self)
{
Py_CLEAR(self->msg);
Py_CLEAR(self->name);
Py_CLEAR(self->path);
Py_CLEAR(self->name_from);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
ImportError_dealloc(PyImportErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
ImportError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
ImportError_traverse(PyImportErrorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->msg);
Py_VISIT(self->name);
Py_VISIT(self->path);
Py_VISIT(self->name_from);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyObject *
ImportError_str(PyImportErrorObject *self)
{
if (self->msg && PyUnicode_CheckExact(self->msg)) {
return Py_NewRef(self->msg);
}
else {
return BaseException_str((PyBaseExceptionObject *)self);
}
}
static PyObject *
ImportError_getstate(PyImportErrorObject *self)
{
PyObject *dict = ((PyBaseExceptionObject *)self)->dict;
if (self->name || self->path || self->name_from) {
dict = dict ? PyDict_Copy(dict) : PyDict_New();
if (dict == NULL)
return NULL;
if (self->name && PyDict_SetItem(dict, &_Py_ID(name), self->name) < 0) {
Py_DECREF(dict);
return NULL;
}
if (self->path && PyDict_SetItem(dict, &_Py_ID(path), self->path) < 0) {
Py_DECREF(dict);
return NULL;
}
if (self->name_from && PyDict_SetItem(dict, &_Py_ID(name_from), self->name_from) < 0) {
Py_DECREF(dict);
return NULL;
}
return dict;
}
else if (dict) {
return Py_NewRef(dict);
}
else {
Py_RETURN_NONE;
}
}
/* Pickling support */
static PyObject *
ImportError_reduce(PyImportErrorObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *res;
PyObject *args;
PyObject *state = ImportError_getstate(self);
if (state == NULL)
return NULL;
args = ((PyBaseExceptionObject *)self)->args;
if (state == Py_None)
res = PyTuple_Pack(2, Py_TYPE(self), args);
else
res = PyTuple_Pack(3, Py_TYPE(self), args, state);
Py_DECREF(state);
return res;
}
static PyMemberDef ImportError_members[] = {
{"msg", T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
PyDoc_STR("exception message")},
{"name", T_OBJECT, offsetof(PyImportErrorObject, name), 0,
PyDoc_STR("module name")},
{"path", T_OBJECT, offsetof(PyImportErrorObject, path), 0,
PyDoc_STR("module path")},
{"name_from", T_OBJECT, offsetof(PyImportErrorObject, name_from), 0,
PyDoc_STR("name imported from module")},
{NULL} /* Sentinel */
};
static PyMethodDef ImportError_methods[] = {
{"__reduce__", (PyCFunction)ImportError_reduce, METH_NOARGS},
{NULL}
};
ComplexExtendsException(PyExc_Exception, ImportError,
ImportError, 0 /* new */,
ImportError_methods, ImportError_members,
0 /* getset */, ImportError_str,
"Import can't find module, or can't find name in "
"module.");
/*
* ModuleNotFoundError extends ImportError
*/
MiddlingExtendsException(PyExc_ImportError, ModuleNotFoundError, ImportError,
"Module not found.");
/*
* OSError extends Exception
*/
#ifdef MS_WINDOWS
#include "errmap.h"
#endif
/* Where a function has a single filename, such as open() or some
* of the os module functions, PyErr_SetFromErrnoWithFilename() is
* called, giving a third argument which is the filename. But, so
* that old code using in-place unpacking doesn't break, e.g.:
*
* except OSError, (errno, strerror):
*
* we hack args so that it only contains two items. This also
* means we need our own __str__() which prints out the filename
* when it was supplied.
*
* (If a function has two filenames, such as rename(), symlink(),
* or copy(), PyErr_SetFromErrnoWithFilenameObjects() is called,
* which allows passing in a second filename.)
*/
/* This function doesn't cleanup on error, the caller should */
static int
oserror_parse_args(PyObject **p_args,
PyObject **myerrno, PyObject **strerror,
PyObject **filename, PyObject **filename2
#ifdef MS_WINDOWS
, PyObject **winerror
#endif
)
{
Py_ssize_t nargs;
PyObject *args = *p_args;
#ifndef MS_WINDOWS
/*
* ignored on non-Windows platforms,
* but parsed so OSError has a consistent signature
*/
PyObject *_winerror = NULL;
PyObject **winerror = &_winerror;
#endif /* MS_WINDOWS */
nargs = PyTuple_GET_SIZE(args);
if (nargs >= 2 && nargs <= 5) {
if (!PyArg_UnpackTuple(args, "OSError", 2, 5,
myerrno, strerror,
filename, winerror, filename2))
return -1;
#ifdef MS_WINDOWS
if (*winerror && PyLong_Check(*winerror)) {
long errcode, winerrcode;
PyObject *newargs;
Py_ssize_t i;
winerrcode = PyLong_AsLong(*winerror);
if (winerrcode == -1 && PyErr_Occurred())
return -1;
errcode = winerror_to_errno(winerrcode);
*myerrno = PyLong_FromLong(errcode);
if (!*myerrno)
return -1;
newargs = PyTuple_New(nargs);
if (!newargs)
return -1;
PyTuple_SET_ITEM(newargs, 0, *myerrno);
for (i = 1; i < nargs; i++) {
PyObject *val = PyTuple_GET_ITEM(args, i);
PyTuple_SET_ITEM(newargs, i, Py_NewRef(val));
}
Py_DECREF(args);
args = *p_args = newargs;
}
#endif /* MS_WINDOWS */
}
return 0;
}
static int
oserror_init(PyOSErrorObject *self, PyObject **p_args,
PyObject *myerrno, PyObject *strerror,
PyObject *filename, PyObject *filename2
#ifdef MS_WINDOWS
, PyObject *winerror
#endif
)
{
PyObject *args = *p_args;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
/* self->filename will remain Py_None otherwise */
if (filename && filename != Py_None) {
if (Py_IS_TYPE(self, (PyTypeObject *) PyExc_BlockingIOError) &&
PyNumber_Check(filename)) {
/* BlockingIOError's 3rd argument can be the number of
* characters written.
*/
self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError);
if (self->written == -1 && PyErr_Occurred())
return -1;
}
else {
self->filename = Py_NewRef(filename);
if (filename2 && filename2 != Py_None) {
self->filename2 = Py_NewRef(filename2);
}
if (nargs >= 2 && nargs <= 5) {
/* filename, filename2, and winerror are removed from the args tuple
(for compatibility purposes, see test_exceptions.py) */
PyObject *subslice = PyTuple_GetSlice(args, 0, 2);
if (!subslice)
return -1;
Py_DECREF(args); /* replacing args */
*p_args = args = subslice;
}
}
}
self->myerrno = Py_XNewRef(myerrno);
self->strerror = Py_XNewRef(strerror);
#ifdef MS_WINDOWS
self->winerror = Py_XNewRef(winerror);
#endif
/* Steals the reference to args */
Py_XSETREF(self->args, args);
*p_args = args = NULL;
return 0;
}
static PyObject *
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static int
OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds);
static int
oserror_use_init(PyTypeObject *type)
{
/* When __init__ is defined in an OSError subclass, we want any
extraneous argument to __new__ to be ignored. The only reasonable
solution, given __new__ takes a variable number of arguments,
is to defer arg parsing and initialization to __init__.
But when __new__ is overridden as well, it should call our __new__
with the right arguments.
(see http://bugs.python.org/issue12555#msg148829 )
*/
if (type->tp_init != (initproc) OSError_init &&
type->tp_new == (newfunc) OSError_new) {
assert((PyObject *) type != PyExc_OSError);
return 1;
}
return 0;
}
static PyObject *
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyOSErrorObject *self = NULL;
PyObject *myerrno = NULL, *strerror = NULL;
PyObject *filename = NULL, *filename2 = NULL;
#ifdef MS_WINDOWS
PyObject *winerror = NULL;
#endif
Py_INCREF(args);
if (!oserror_use_init(type)) {
if (!_PyArg_NoKeywords(type->tp_name, kwds))
goto error;
if (oserror_parse_args(&args, &myerrno, &strerror,
&filename, &filename2
#ifdef MS_WINDOWS
, &winerror
#endif
))
goto error;
struct _Py_exc_state *state = get_exc_state();
if (myerrno && PyLong_Check(myerrno) &&
state->errnomap && (PyObject *) type == PyExc_OSError) {
PyObject *newtype;
newtype = PyDict_GetItemWithError(state->errnomap, myerrno);
if (newtype) {
type = _PyType_CAST(newtype);
}
else if (PyErr_Occurred())
goto error;
}
}
self = (PyOSErrorObject *) type->tp_alloc(type, 0);
if (!self)
goto error;
self->dict = NULL;
self->traceback = self->cause = self->context = NULL;
self->written = -1;
if (!oserror_use_init(type)) {
if (oserror_init(self, &args, myerrno, strerror, filename, filename2
#ifdef MS_WINDOWS
, winerror
#endif
))
goto error;
}
else {
self->args = PyTuple_New(0);
if (self->args == NULL)
goto error;
}
Py_XDECREF(args);
return (PyObject *) self;
error:
Py_XDECREF(args);
Py_XDECREF(self);
return NULL;
}
static int
OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds)
{
PyObject *myerrno = NULL, *strerror = NULL;
PyObject *filename = NULL, *filename2 = NULL;
#ifdef MS_WINDOWS
PyObject *winerror = NULL;
#endif
if (!oserror_use_init(Py_TYPE(self)))
/* Everything already done in OSError_new */
return 0;
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
return -1;
Py_INCREF(args);
if (oserror_parse_args(&args, &myerrno, &strerror, &filename, &filename2
#ifdef MS_WINDOWS
, &winerror
#endif
))
goto error;
if (oserror_init(self, &args, myerrno, strerror, filename, filename2
#ifdef MS_WINDOWS
, winerror
#endif
))
goto error;
return 0;
error:
Py_DECREF(args);
return -1;
}
static int
OSError_clear(PyOSErrorObject *self)
{
Py_CLEAR(self->myerrno);
Py_CLEAR(self->strerror);
Py_CLEAR(self->filename);
Py_CLEAR(self->filename2);
#ifdef MS_WINDOWS
Py_CLEAR(self->winerror);
#endif
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
OSError_dealloc(PyOSErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
OSError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
OSError_traverse(PyOSErrorObject *self, visitproc visit,
void *arg)
{
Py_VISIT(self->myerrno);
Py_VISIT(self->strerror);
Py_VISIT(self->filename);
Py_VISIT(self->filename2);
#ifdef MS_WINDOWS
Py_VISIT(self->winerror);
#endif
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyObject *
OSError_str(PyOSErrorObject *self)
{
#define OR_NONE(x) ((x)?(x):Py_None)
#ifdef MS_WINDOWS
/* If available, winerror has the priority over myerrno */
if (self->winerror && self->filename) {
if (self->filename2) {
return PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
OR_NONE(self->winerror),
OR_NONE(self->strerror),
self->filename,
self->filename2);
} else {
return PyUnicode_FromFormat("[WinError %S] %S: %R",
OR_NONE(self->winerror),
OR_NONE(self->strerror),
self->filename);
}
}
if (self->winerror && self->strerror)
return PyUnicode_FromFormat("[WinError %S] %S",
self->winerror ? self->winerror: Py_None,
self->strerror ? self->strerror: Py_None);
#endif
if (self->filename) {
if (self->filename2) {
return PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
OR_NONE(self->myerrno),
OR_NONE(self->strerror),
self->filename,
self->filename2);
} else {
return PyUnicode_FromFormat("[Errno %S] %S: %R",
OR_NONE(self->myerrno),
OR_NONE(self->strerror),
self->filename);
}
}
if (self->myerrno && self->strerror)
return PyUnicode_FromFormat("[Errno %S] %S",
self->myerrno, self->strerror);
return BaseException_str((PyBaseExceptionObject *)self);
}
static PyObject *
OSError_reduce(PyOSErrorObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *args = self->args;
PyObject *res = NULL;
/* self->args is only the first two real arguments if there was a
* file name given to OSError. */
if (PyTuple_GET_SIZE(args) == 2 && self->filename) {
Py_ssize_t size = self->filename2 ? 5 : 3;
args = PyTuple_New(size);
if (!args)
return NULL;
PyTuple_SET_ITEM(args, 0, Py_NewRef(PyTuple_GET_ITEM(self->args, 0)));
PyTuple_SET_ITEM(args, 1, Py_NewRef(PyTuple_GET_ITEM(self->args, 1)));
PyTuple_SET_ITEM(args, 2, Py_NewRef(self->filename));
if (self->filename2) {
/*
* This tuple is essentially used as OSError(*args).
* So, to recreate filename2, we need to pass in
* winerror as well.
*/
PyTuple_SET_ITEM(args, 3, Py_NewRef(Py_None));
/* filename2 */
PyTuple_SET_ITEM(args, 4, Py_NewRef(self->filename2));
}
} else
Py_INCREF(args);
if (self->dict)
res = PyTuple_Pack(3, Py_TYPE(self), args, self->dict);
else
res = PyTuple_Pack(2, Py_TYPE(self), args);
Py_DECREF(args);
return res;
}
static PyObject *
OSError_written_get(PyOSErrorObject *self, void *context)
{
if (self->written == -1) {
PyErr_SetString(PyExc_AttributeError, "characters_written");
return NULL;
}
return PyLong_FromSsize_t(self->written);
}
static int
OSError_written_set(PyOSErrorObject *self, PyObject *arg, void *context)
{
if (arg == NULL) {
if (self->written == -1) {
PyErr_SetString(PyExc_AttributeError, "characters_written");
return -1;
}
self->written = -1;
return 0;
}
Py_ssize_t n;
n = PyNumber_AsSsize_t(arg, PyExc_ValueError);
if (n == -1 && PyErr_Occurred())
return -1;
self->written = n;
return 0;
}
static PyMemberDef OSError_members[] = {
{"errno", T_OBJECT, offsetof(PyOSErrorObject, myerrno), 0,
PyDoc_STR("POSIX exception code")},
{"strerror", T_OBJECT, offsetof(PyOSErrorObject, strerror), 0,
PyDoc_STR("exception strerror")},
{"filename", T_OBJECT, offsetof(PyOSErrorObject, filename), 0,
PyDoc_STR("exception filename")},
{"filename2", T_OBJECT, offsetof(PyOSErrorObject, filename2), 0,
PyDoc_STR("second exception filename")},
#ifdef MS_WINDOWS
{"winerror", T_OBJECT, offsetof(PyOSErrorObject, winerror), 0,
PyDoc_STR("Win32 exception code")},
#endif
{NULL} /* Sentinel */
};
static PyMethodDef OSError_methods[] = {
{"__reduce__", (PyCFunction)OSError_reduce, METH_NOARGS},
{NULL}
};
static PyGetSetDef OSError_getset[] = {
{"characters_written", (getter) OSError_written_get,
(setter) OSError_written_set, NULL},
{NULL}
};
ComplexExtendsException(PyExc_Exception, OSError,
OSError, OSError_new,
OSError_methods, OSError_members, OSError_getset,
OSError_str,
"Base class for I/O related errors.");
/*
* Various OSError subclasses
*/
MiddlingExtendsException(PyExc_OSError, BlockingIOError, OSError,
"I/O operation would block.");
MiddlingExtendsException(PyExc_OSError, ConnectionError, OSError,
"Connection error.");
MiddlingExtendsException(PyExc_OSError, ChildProcessError, OSError,
"Child process error.");
MiddlingExtendsException(PyExc_ConnectionError, BrokenPipeError, OSError,
"Broken pipe.");
MiddlingExtendsException(PyExc_ConnectionError, ConnectionAbortedError, OSError,
"Connection aborted.");
MiddlingExtendsException(PyExc_ConnectionError, ConnectionRefusedError, OSError,
"Connection refused.");
MiddlingExtendsException(PyExc_ConnectionError, ConnectionResetError, OSError,
"Connection reset.");
MiddlingExtendsException(PyExc_OSError, FileExistsError, OSError,
"File already exists.");
MiddlingExtendsException(PyExc_OSError, FileNotFoundError, OSError,
"File not found.");
MiddlingExtendsException(PyExc_OSError, IsADirectoryError, OSError,
"Operation doesn't work on directories.");
MiddlingExtendsException(PyExc_OSError, NotADirectoryError, OSError,
"Operation only works on directories.");
MiddlingExtendsException(PyExc_OSError, InterruptedError, OSError,
"Interrupted by signal.");
MiddlingExtendsException(PyExc_OSError, PermissionError, OSError,
"Not enough permissions.");
MiddlingExtendsException(PyExc_OSError, ProcessLookupError, OSError,
"Process not found.");
MiddlingExtendsException(PyExc_OSError, TimeoutError, OSError,
"Timeout expired.");
/*
* EOFError extends Exception
*/
SimpleExtendsException(PyExc_Exception, EOFError,
"Read beyond end of file.");
/*
* RuntimeError extends Exception
*/
SimpleExtendsException(PyExc_Exception, RuntimeError,
"Unspecified run-time error.");
/*
* RecursionError extends RuntimeError
*/
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
"Recursion limit exceeded.");
/*
* NotImplementedError extends RuntimeError
*/
SimpleExtendsException(PyExc_RuntimeError, NotImplementedError,
"Method or function hasn't been implemented yet.");
/*
* NameError extends Exception
*/
static int
NameError_init(PyNameErrorObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"name", NULL};
PyObject *name = NULL;
if (BaseException_init((PyBaseExceptionObject *)self, args, NULL) == -1) {
return -1;
}
PyObject *empty_tuple = PyTuple_New(0);
if (!empty_tuple) {
return -1;
}
if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$O:NameError", kwlist,
&name)) {
Py_DECREF(empty_tuple);
return -1;
}
Py_DECREF(empty_tuple);
Py_XSETREF(self->name, Py_XNewRef(name));
return 0;
}
static int
NameError_clear(PyNameErrorObject *self)
{
Py_CLEAR(self->name);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
NameError_dealloc(PyNameErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
NameError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
NameError_traverse(PyNameErrorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->name);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyMemberDef NameError_members[] = {
{"name", T_OBJECT, offsetof(PyNameErrorObject, name), 0, PyDoc_STR("name")},
{NULL} /* Sentinel */
};
static PyMethodDef NameError_methods[] = {
{NULL} /* Sentinel */
};
ComplexExtendsException(PyExc_Exception, NameError,
NameError, 0,
NameError_methods, NameError_members,
0, BaseException_str, "Name not found globally.");
/*
* UnboundLocalError extends NameError
*/
MiddlingExtendsException(PyExc_NameError, UnboundLocalError, NameError,
"Local name referenced but not bound to a value.");
/*
* AttributeError extends Exception
*/
static int
AttributeError_init(PyAttributeErrorObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"name", "obj", NULL};
PyObject *name = NULL;
PyObject *obj = NULL;
if (BaseException_init((PyBaseExceptionObject *)self, args, NULL) == -1) {
return -1;
}
PyObject *empty_tuple = PyTuple_New(0);
if (!empty_tuple) {
return -1;
}
if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$OO:AttributeError", kwlist,
&name, &obj)) {
Py_DECREF(empty_tuple);
return -1;
}
Py_DECREF(empty_tuple);
Py_XSETREF(self->name, Py_XNewRef(name));
Py_XSETREF(self->obj, Py_XNewRef(obj));
return 0;
}
static int
AttributeError_clear(PyAttributeErrorObject *self)
{
Py_CLEAR(self->obj);
Py_CLEAR(self->name);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
AttributeError_dealloc(PyAttributeErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
AttributeError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
AttributeError_traverse(PyAttributeErrorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->obj);
Py_VISIT(self->name);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
/* Pickling support */
static PyObject *
AttributeError_getstate(PyAttributeErrorObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *dict = ((PyAttributeErrorObject *)self)->dict;
if (self->name || self->args) {
dict = dict ? PyDict_Copy(dict) : PyDict_New();
if (dict == NULL) {
return NULL;
}
if (self->name && PyDict_SetItemString(dict, "name", self->name) < 0) {
Py_DECREF(dict);
return NULL;
}
/* We specifically are not pickling the obj attribute since there are many
cases where it is unlikely to be picklable. See GH-103352.
*/
if (self->args && PyDict_SetItemString(dict, "args", self->args) < 0) {
Py_DECREF(dict);
return NULL;
}
return dict;
}
else if (dict) {
return Py_NewRef(dict);
}
Py_RETURN_NONE;
}
static PyObject *
AttributeError_reduce(PyAttributeErrorObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *state = AttributeError_getstate(self, NULL);
if (state == NULL) {
return NULL;
}
PyObject *return_value = PyTuple_Pack(3, Py_TYPE(self), self->args, state);
Py_DECREF(state);
return return_value;
}
static PyMemberDef AttributeError_members[] = {
{"name", T_OBJECT, offsetof(PyAttributeErrorObject, name), 0, PyDoc_STR("attribute name")},
{"obj", T_OBJECT, offsetof(PyAttributeErrorObject, obj), 0, PyDoc_STR("object")},
{NULL} /* Sentinel */
};
static PyMethodDef AttributeError_methods[] = {
{"__getstate__", (PyCFunction)AttributeError_getstate, METH_NOARGS},
{"__reduce__", (PyCFunction)AttributeError_reduce, METH_NOARGS },
{NULL}
};
ComplexExtendsException(PyExc_Exception, AttributeError,
AttributeError, 0,
AttributeError_methods, AttributeError_members,
0, BaseException_str, "Attribute not found.");
/*
* SyntaxError extends Exception
*/
static int
SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
{
PyObject *info = NULL;
Py_ssize_t lenargs = PyTuple_GET_SIZE(args);
if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
return -1;
if (lenargs >= 1) {
Py_XSETREF(self->msg, Py_NewRef(PyTuple_GET_ITEM(args, 0)));
}
if (lenargs == 2) {
info = PyTuple_GET_ITEM(args, 1);
info = PySequence_Tuple(info);
if (!info) {
return -1;
}
self->end_lineno = NULL;
self->end_offset = NULL;
if (!PyArg_ParseTuple(info, "OOOO|OO",
&self->filename, &self->lineno,
&self->offset, &self->text,
&self->end_lineno, &self->end_offset)) {
Py_DECREF(info);
return -1;
}
Py_INCREF(self->filename);
Py_INCREF(self->lineno);
Py_INCREF(self->offset);
Py_INCREF(self->text);
Py_XINCREF(self->end_lineno);
Py_XINCREF(self->end_offset);
Py_DECREF(info);
if (self->end_lineno != NULL && self->end_offset == NULL) {
PyErr_SetString(PyExc_TypeError, "end_offset must be provided when end_lineno is provided");
return -1;
}
}
return 0;
}
static int
SyntaxError_clear(PySyntaxErrorObject *self)
{
Py_CLEAR(self->msg);
Py_CLEAR(self->filename);
Py_CLEAR(self->lineno);
Py_CLEAR(self->offset);
Py_CLEAR(self->end_lineno);
Py_CLEAR(self->end_offset);
Py_CLEAR(self->text);
Py_CLEAR(self->print_file_and_line);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
SyntaxError_dealloc(PySyntaxErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
SyntaxError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
SyntaxError_traverse(PySyntaxErrorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->msg);
Py_VISIT(self->filename);
Py_VISIT(self->lineno);
Py_VISIT(self->offset);
Py_VISIT(self->end_lineno);
Py_VISIT(self->end_offset);
Py_VISIT(self->text);
Py_VISIT(self->print_file_and_line);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
/* This is called "my_basename" instead of just "basename" to avoid name
conflicts with glibc; basename is already prototyped if _GNU_SOURCE is
defined, and Python does define that. */
static PyObject*
my_basename(PyObject *name)
{
Py_ssize_t i, size, offset;
int kind;
const void *data;
if (PyUnicode_READY(name))
return NULL;
kind = PyUnicode_KIND(name);
data = PyUnicode_DATA(name);
size = PyUnicode_GET_LENGTH(name);
offset = 0;
for(i=0; i < size; i++) {
if (PyUnicode_READ(kind, data, i) == SEP) {
offset = i + 1;
}
}
if (offset != 0) {
return PyUnicode_Substring(name, offset, size);
}
else {
return Py_NewRef(name);
}
}
static PyObject *
SyntaxError_str(PySyntaxErrorObject *self)
{
int have_lineno = 0;
PyObject *filename;
PyObject *result;
/* Below, we always ignore overflow errors, just printing -1.
Still, we cannot allow an OverflowError to be raised, so
we need to call PyLong_AsLongAndOverflow. */
int overflow;
/* XXX -- do all the additional formatting with filename and
lineno here */
if (self->filename && PyUnicode_Check(self->filename)) {
filename = my_basename(self->filename);
if (filename == NULL)
return NULL;
} else {
filename = NULL;
}
have_lineno = (self->lineno != NULL) && PyLong_CheckExact(self->lineno);
if (!filename && !have_lineno)
return PyObject_Str(self->msg ? self->msg : Py_None);
if (filename && have_lineno)
result = PyUnicode_FromFormat("%S (%U, line %ld)",
self->msg ? self->msg : Py_None,
filename,
PyLong_AsLongAndOverflow(self->lineno, &overflow));
else if (filename)
result = PyUnicode_FromFormat("%S (%U)",
self->msg ? self->msg : Py_None,
filename);
else /* only have_lineno */
result = PyUnicode_FromFormat("%S (line %ld)",
self->msg ? self->msg : Py_None,
PyLong_AsLongAndOverflow(self->lineno, &overflow));
Py_XDECREF(filename);
return result;
}
static PyMemberDef SyntaxError_members[] = {
{"msg", T_OBJECT, offsetof(PySyntaxErrorObject, msg), 0,
PyDoc_STR("exception msg")},
{"filename", T_OBJECT, offsetof(PySyntaxErrorObject, filename), 0,
PyDoc_STR("exception filename")},
{"lineno", T_OBJECT, offsetof(PySyntaxErrorObject, lineno), 0,
PyDoc_STR("exception lineno")},
{"offset", T_OBJECT, offsetof(PySyntaxErrorObject, offset), 0,
PyDoc_STR("exception offset")},
{"text", T_OBJECT, offsetof(PySyntaxErrorObject, text), 0,
PyDoc_STR("exception text")},
{"end_lineno", T_OBJECT, offsetof(PySyntaxErrorObject, end_lineno), 0,
PyDoc_STR("exception end lineno")},
{"end_offset", T_OBJECT, offsetof(PySyntaxErrorObject, end_offset), 0,
PyDoc_STR("exception end offset")},
{"print_file_and_line", T_OBJECT,
offsetof(PySyntaxErrorObject, print_file_and_line), 0,
PyDoc_STR("exception print_file_and_line")},
{NULL} /* Sentinel */
};
ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError,
0, 0, SyntaxError_members, 0,
SyntaxError_str, "Invalid syntax.");
/*
* IndentationError extends SyntaxError
*/
MiddlingExtendsException(PyExc_SyntaxError, IndentationError, SyntaxError,
"Improper indentation.");
/*
* TabError extends IndentationError
*/
MiddlingExtendsException(PyExc_IndentationError, TabError, SyntaxError,
"Improper mixture of spaces and tabs.");
/*
* LookupError extends Exception
*/
SimpleExtendsException(PyExc_Exception, LookupError,
"Base class for lookup errors.");
/*
* IndexError extends LookupError
*/
SimpleExtendsException(PyExc_LookupError, IndexError,
"Sequence index out of range.");
/*
* KeyError extends LookupError
*/
static PyObject *
KeyError_str(PyBaseExceptionObject *self)
{
/* If args is a tuple of exactly one item, apply repr to args[0].
This is done so that e.g. the exception raised by {}[''] prints
KeyError: ''
rather than the confusing
KeyError
alone. The downside is that if KeyError is raised with an explanatory
string, that string will be displayed in quotes. Too bad.
If args is anything else, use the default BaseException__str__().
*/
if (PyTuple_GET_SIZE(self->args) == 1) {
return PyObject_Repr(PyTuple_GET_ITEM(self->args, 0));
}
return BaseException_str(self);
}
ComplexExtendsException(PyExc_LookupError, KeyError, BaseException,
0, 0, 0, 0, KeyError_str, "Mapping key not found.");
/*
* ValueError extends Exception
*/
SimpleExtendsException(PyExc_Exception, ValueError,
"Inappropriate argument value (of correct type).");
/*
* UnicodeError extends ValueError
*/
SimpleExtendsException(PyExc_ValueError, UnicodeError,
"Unicode related error.");
static PyObject *
get_string(PyObject *attr, const char *name)
{
if (!attr) {
PyErr_Format(PyExc_TypeError, "%.200s attribute not set", name);
return NULL;
}
if (!PyBytes_Check(attr)) {
PyErr_Format(PyExc_TypeError, "%.200s attribute must be bytes", name);
return NULL;
}
return Py_NewRef(attr);
}
static PyObject *
get_unicode(PyObject *attr, const char *name)
{
if (!attr) {
PyErr_Format(PyExc_TypeError, "%.200s attribute not set", name);
return NULL;
}
if (!PyUnicode_Check(attr)) {
PyErr_Format(PyExc_TypeError,
"%.200s attribute must be unicode", name);
return NULL;
}
return Py_NewRef(attr);
}
static int
set_unicodefromstring(PyObject **attr, const char *value)
{
PyObject *obj = PyUnicode_FromString(value);
if (!obj)
return -1;
Py_XSETREF(*attr, obj);
return 0;
}
PyObject *
PyUnicodeEncodeError_GetEncoding(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->encoding, "encoding");
}
PyObject *
PyUnicodeDecodeError_GetEncoding(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->encoding, "encoding");
}
PyObject *
PyUnicodeEncodeError_GetObject(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->object, "object");
}
PyObject *
PyUnicodeDecodeError_GetObject(PyObject *exc)
{
return get_string(((PyUnicodeErrorObject *)exc)->object, "object");
}
PyObject *
PyUnicodeTranslateError_GetObject(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->object, "object");
}
int
PyUnicodeEncodeError_GetStart(PyObject *exc, Py_ssize_t *start)
{
Py_ssize_t size;
PyObject *obj = get_unicode(((PyUnicodeErrorObject *)exc)->object,
"object");
if (!obj)
return -1;
*start = ((PyUnicodeErrorObject *)exc)->start;
size = PyUnicode_GET_LENGTH(obj);
if (*start<0)
*start = 0; /*XXX check for values <0*/
if (*start>=size)
*start = size-1;
Py_DECREF(obj);
return 0;
}
int
PyUnicodeDecodeError_GetStart(PyObject *exc, Py_ssize_t *start)
{
Py_ssize_t size;
PyObject *obj = get_string(((PyUnicodeErrorObject *)exc)->object, "object");
if (!obj)
return -1;
size = PyBytes_GET_SIZE(obj);
*start = ((PyUnicodeErrorObject *)exc)->start;
if (*start<0)
*start = 0;
if (*start>=size)
*start = size-1;
Py_DECREF(obj);
return 0;
}
int
PyUnicodeTranslateError_GetStart(PyObject *exc, Py_ssize_t *start)
{
return PyUnicodeEncodeError_GetStart(exc, start);
}
int
PyUnicodeEncodeError_SetStart(PyObject *exc, Py_ssize_t start)
{
((PyUnicodeErrorObject *)exc)->start = start;
return 0;
}
int
PyUnicodeDecodeError_SetStart(PyObject *exc, Py_ssize_t start)
{
((PyUnicodeErrorObject *)exc)->start = start;
return 0;
}
int
PyUnicodeTranslateError_SetStart(PyObject *exc, Py_ssize_t start)
{
((PyUnicodeErrorObject *)exc)->start = start;
return 0;
}
int
PyUnicodeEncodeError_GetEnd(PyObject *exc, Py_ssize_t *end)
{
Py_ssize_t size;
PyObject *obj = get_unicode(((PyUnicodeErrorObject *)exc)->object,
"object");
if (!obj)
return -1;
*end = ((PyUnicodeErrorObject *)exc)->end;
size = PyUnicode_GET_LENGTH(obj);
if (*end<1)
*end = 1;
if (*end>size)
*end = size;
Py_DECREF(obj);
return 0;
}
int
PyUnicodeDecodeError_GetEnd(PyObject *exc, Py_ssize_t *end)
{
Py_ssize_t size;
PyObject *obj = get_string(((PyUnicodeErrorObject *)exc)->object, "object");
if (!obj)
return -1;
size = PyBytes_GET_SIZE(obj);
*end = ((PyUnicodeErrorObject *)exc)->end;
if (*end<1)
*end = 1;
if (*end>size)
*end = size;
Py_DECREF(obj);
return 0;
}
int
PyUnicodeTranslateError_GetEnd(PyObject *exc, Py_ssize_t *end)
{
return PyUnicodeEncodeError_GetEnd(exc, end);
}
int
PyUnicodeEncodeError_SetEnd(PyObject *exc, Py_ssize_t end)
{
((PyUnicodeErrorObject *)exc)->end = end;
return 0;
}
int
PyUnicodeDecodeError_SetEnd(PyObject *exc, Py_ssize_t end)
{
((PyUnicodeErrorObject *)exc)->end = end;
return 0;
}
int
PyUnicodeTranslateError_SetEnd(PyObject *exc, Py_ssize_t end)
{
((PyUnicodeErrorObject *)exc)->end = end;
return 0;
}
PyObject *
PyUnicodeEncodeError_GetReason(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason");
}
PyObject *
PyUnicodeDecodeError_GetReason(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason");
}
PyObject *
PyUnicodeTranslateError_GetReason(PyObject *exc)
{
return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason");
}
int
PyUnicodeEncodeError_SetReason(PyObject *exc, const char *reason)
{
return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason,
reason);
}
int
PyUnicodeDecodeError_SetReason(PyObject *exc, const char *reason)
{
return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason,
reason);
}
int
PyUnicodeTranslateError_SetReason(PyObject *exc, const char *reason)
{
return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason,
reason);
}
static int
UnicodeError_clear(PyUnicodeErrorObject *self)
{
Py_CLEAR(self->encoding);
Py_CLEAR(self->object);
Py_CLEAR(self->reason);
return BaseException_clear((PyBaseExceptionObject *)self);
}
static void
UnicodeError_dealloc(PyUnicodeErrorObject *self)
{
_PyObject_GC_UNTRACK(self);
UnicodeError_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
UnicodeError_traverse(PyUnicodeErrorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->encoding);
Py_VISIT(self->object);
Py_VISIT(self->reason);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
static PyMemberDef UnicodeError_members[] = {
{"encoding", T_OBJECT, offsetof(PyUnicodeErrorObject, encoding), 0,
PyDoc_STR("exception encoding")},
{"object", T_OBJECT, offsetof(PyUnicodeErrorObject, object), 0,
PyDoc_STR("exception object")},
{"start", T_PYSSIZET, offsetof(PyUnicodeErrorObject, start), 0,
PyDoc_STR("exception start")},
{"end", T_PYSSIZET, offsetof(PyUnicodeErrorObject, end), 0,
PyDoc_STR("exception end")},
{"reason", T_OBJECT, offsetof(PyUnicodeErrorObject, reason), 0,
PyDoc_STR("exception reason")},
{NULL} /* Sentinel */
};
/*
* UnicodeEncodeError extends UnicodeError
*/
static int
UnicodeEncodeError_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyUnicodeErrorObject *err;
if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
return -1;
err = (PyUnicodeErrorObject *)self;
Py_CLEAR(err->encoding);
Py_CLEAR(err->object);
Py_CLEAR(err->reason);
if (!PyArg_ParseTuple(args, "UUnnU",
&err->encoding, &err->object,
&err->start, &err->end, &err->reason)) {
err->encoding = err->object = err->reason = NULL;
return -1;
}
Py_INCREF(err->encoding);
Py_INCREF(err->object);
Py_INCREF(err->reason);
return 0;
}
static PyObject *
UnicodeEncodeError_str(PyObject *self)
{
PyUnicodeErrorObject *uself = (PyUnicodeErrorObject *)self;
PyObject *result = NULL;
PyObject *reason_str = NULL;
PyObject *encoding_str = NULL;
if (!uself->object)
/* Not properly initialized. */
return PyUnicode_FromString("");
/* Get reason and encoding as strings, which they might not be if
they've been modified after we were constructed. */
reason_str = PyObject_Str(uself->reason);
if (reason_str == NULL)
goto done;
encoding_str = PyObject_Str(uself->encoding);
if (encoding_str == NULL)
goto done;
if (uself->start < PyUnicode_GET_LENGTH(uself->object) && uself->end == uself->start+1) {
Py_UCS4 badchar = PyUnicode_ReadChar(uself->object, uself->start);
const char *fmt;
if (badchar <= 0xff)
fmt = "'%U' codec can't encode character '\\x%02x' in position %zd: %U";
else if (badchar <= 0xffff)
fmt = "'%U' codec can't encode character '\\u%04x' in position %zd: %U";
else
fmt = "'%U' codec can't encode character '\\U%08x' in position %zd: %U";
result = PyUnicode_FromFormat(
fmt,
encoding_str,
(int)badchar,
uself->start,
reason_str);
}
else {
result = PyUnicode_FromFormat(
"'%U' codec can't encode characters in position %zd-%zd: %U",
encoding_str,
uself->start,
uself->end-1,
reason_str);
}
done:
Py_XDECREF(reason_str);
Py_XDECREF(encoding_str);
return result;
}
static PyTypeObject _PyExc_UnicodeEncodeError = {
PyVarObject_HEAD_INIT(NULL, 0)
"UnicodeEncodeError",
sizeof(PyUnicodeErrorObject), 0,
(destructor)UnicodeError_dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(reprfunc)UnicodeEncodeError_str, 0, 0, 0,