diff options
author | shadchin <shadchin@yandex-team.com> | 2024-02-12 07:53:52 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@ydb.tech> | 2024-02-14 14:26:16 +0000 |
commit | 31f2a419764a8ba77c2a970cfc80056c6cd06756 (patch) | |
tree | c1995d239eba8571cefc640f6648e1d5dd4ce9e2 /contrib/tools/python3/src/Objects/exceptions.c | |
parent | fe2ef02b38d9c85d80060963b265a1df9f38c3bb (diff) | |
download | ydb-31f2a419764a8ba77c2a970cfc80056c6cd06756.tar.gz |
Update Python from 3.11.8 to 3.12.2
Diffstat (limited to 'contrib/tools/python3/src/Objects/exceptions.c')
-rw-r--r-- | contrib/tools/python3/src/Objects/exceptions.c | 458 |
1 files changed, 210 insertions, 248 deletions
diff --git a/contrib/tools/python3/src/Objects/exceptions.c b/contrib/tools/python3/src/Objects/exceptions.c index 86cabbf3f1..e3217c922e 100644 --- a/contrib/tools/python3/src/Objects/exceptions.c +++ b/contrib/tools/python3/src/Objects/exceptions.c @@ -8,6 +8,7 @@ #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" @@ -53,8 +54,7 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->suppress_context = 0; if (args) { - self->args = args; - Py_INCREF(args); + self->args = Py_NewRef(args); return (PyObject *)self; } @@ -73,9 +73,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds) if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds)) return -1; - Py_INCREF(args); - Py_XSETREF(self->args, args); - + Py_XSETREF(self->args, Py_NewRef(args)); return 0; } @@ -185,8 +183,7 @@ BaseException_with_traceback(PyObject *self, PyObject *tb) { if (PyException_SetTraceback(self, tb)) return NULL; - Py_INCREF(self); - return self; + return Py_NewRef(self); } PyDoc_STRVAR(with_traceback_doc, @@ -257,8 +254,7 @@ BaseException_get_args(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) if (self->args == NULL) { Py_RETURN_NONE; } - Py_INCREF(self->args); - return self->args; + return Py_NewRef(self->args); } static int @@ -282,8 +278,7 @@ BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) if (self->traceback == NULL) { Py_RETURN_NONE; } - Py_INCREF(self->traceback); - return self->traceback; + return Py_NewRef(self->traceback); } static int @@ -293,14 +288,17 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED( PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); return -1; } - else if (!(tb == Py_None || PyTraceBack_Check(tb))) { + 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; } - - Py_INCREF(tb); - Py_XSETREF(self->traceback, tb); return 0; } @@ -379,8 +377,7 @@ PyObject * PyException_GetTraceback(PyObject *self) { PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self); - Py_XINCREF(base_self->traceback); - return base_self->traceback; + return Py_XNewRef(base_self->traceback); } @@ -394,8 +391,7 @@ PyObject * PyException_GetCause(PyObject *self) { PyObject *cause = _PyBaseExceptionObject_cast(self)->cause; - Py_XINCREF(cause); - return cause; + return Py_XNewRef(cause); } /* Steals a reference to cause */ @@ -411,8 +407,7 @@ PyObject * PyException_GetContext(PyObject *self) { PyObject *context = _PyBaseExceptionObject_cast(self)->context; - Py_XINCREF(context); - return context; + return Py_XNewRef(context); } /* Steals a reference to context */ @@ -422,6 +417,20 @@ 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) { @@ -578,8 +587,7 @@ StopIteration_init(PyStopIterationObject *self, PyObject *args, PyObject *kwds) value = PyTuple_GET_ITEM(args, 0); else value = Py_None; - Py_INCREF(value); - self->value = value; + self->value = Py_NewRef(value); return 0; } @@ -605,17 +613,9 @@ StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg) return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg); } -ComplexExtendsException( - PyExc_Exception, /* base */ - StopIteration, /* name */ - StopIteration, /* prefix for *_init, etc */ - 0, /* new */ - 0, /* methods */ - StopIteration_members, /* members */ - 0, /* getset */ - 0, /* str */ - "Signal the end from iterator.__next__()." -); +ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration, + 0, 0, StopIteration_members, 0, 0, + "Signal the end from iterator.__next__()."); /* @@ -640,12 +640,10 @@ SystemExit_init(PySystemExitObject *self, PyObject *args, PyObject *kwds) if (size == 0) return 0; if (size == 1) { - Py_INCREF(PyTuple_GET_ITEM(args, 0)); - Py_XSETREF(self->code, PyTuple_GET_ITEM(args, 0)); + Py_XSETREF(self->code, Py_NewRef(PyTuple_GET_ITEM(args, 0))); } else { /* size > 1 */ - Py_INCREF(args); - Py_XSETREF(self->code, args); + Py_XSETREF(self->code, Py_NewRef(args)); } return 0; } @@ -1352,7 +1350,10 @@ is_same_exception_metadata(PyObject *exc1, PyObject *exc2) PyObject * _PyExc_PrepReraiseStar(PyObject *orig, PyObject *excs) { + /* orig must be a raised & caught exception, so it has a traceback */ assert(PyExceptionInstance_Check(orig)); + assert(_PyBaseExceptionObject_cast(orig)->traceback != NULL); + assert(PyList_Check(excs)); Py_ssize_t numexcs = PyList_GET_SIZE(excs); @@ -1439,6 +1440,42 @@ done: return result; } +PyObject * +PyUnstable_Exc_PrepReraiseStar(PyObject *orig, PyObject *excs) +{ + if (orig == NULL || !PyExceptionInstance_Check(orig)) { + PyErr_SetString(PyExc_TypeError, "orig must be an exception instance"); + return NULL; + } + if (excs == NULL || !PyList_Check(excs)) { + PyErr_SetString(PyExc_TypeError, + "excs must be a list of exception instances"); + return NULL; + } + Py_ssize_t numexcs = PyList_GET_SIZE(excs); + for (Py_ssize_t i = 0; i < numexcs; i++) { + PyObject *exc = PyList_GET_ITEM(excs, i); + if (exc == NULL || !(PyExceptionInstance_Check(exc) || Py_IsNone(exc))) { + PyErr_Format(PyExc_TypeError, + "item %d of excs is not an exception", i); + return NULL; + } + } + + /* Make sure that orig has something as traceback, in the interpreter + * it always does becuase it's a raised exception. + */ + PyObject *tb = PyException_GetTraceback(orig); + + if (tb == NULL) { + PyErr_Format(PyExc_ValueError, "orig must be a raised exception"); + return NULL; + } + Py_DECREF(tb); + + return _PyExc_PrepReraiseStar(orig, excs); +} + static PyMemberDef BaseExceptionGroup_members[] = { {"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY, PyDoc_STR("exception message")}, @@ -1497,11 +1534,12 @@ SimpleExtendsException(PyExc_BaseException, KeyboardInterrupt, static int ImportError_init(PyImportErrorObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"name", "path", 0}; + 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; @@ -1509,22 +1547,19 @@ ImportError_init(PyImportErrorObject *self, PyObject *args, PyObject *kwds) empty_tuple = PyTuple_New(0); if (!empty_tuple) return -1; - if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$OO:ImportError", kwlist, - &name, &path)) { + if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$OOO:ImportError", kwlist, + &name, &path, &name_from)) { Py_DECREF(empty_tuple); return -1; } Py_DECREF(empty_tuple); - Py_XINCREF(name); - Py_XSETREF(self->name, name); - - Py_XINCREF(path); - Py_XSETREF(self->path, path); + 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 = PyTuple_GET_ITEM(args, 0); - Py_INCREF(msg); + msg = Py_NewRef(PyTuple_GET_ITEM(args, 0)); } Py_XSETREF(self->msg, msg); @@ -1537,6 +1572,7 @@ 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); } @@ -1554,6 +1590,7 @@ 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); } @@ -1561,8 +1598,7 @@ static PyObject * ImportError_str(PyImportErrorObject *self) { if (self->msg && PyUnicode_CheckExact(self->msg)) { - Py_INCREF(self->msg); - return self->msg; + return Py_NewRef(self->msg); } else { return BaseException_str((PyBaseExceptionObject *)self); @@ -1573,7 +1609,7 @@ static PyObject * ImportError_getstate(PyImportErrorObject *self) { PyObject *dict = ((PyBaseExceptionObject *)self)->dict; - if (self->name || self->path) { + if (self->name || self->path || self->name_from) { dict = dict ? PyDict_Copy(dict) : PyDict_New(); if (dict == NULL) return NULL; @@ -1585,11 +1621,14 @@ ImportError_getstate(PyImportErrorObject *self) 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) { - Py_INCREF(dict); - return dict; + return Py_NewRef(dict); } else { Py_RETURN_NONE; @@ -1621,6 +1660,8 @@ static PyMemberDef ImportError_members[] = { 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 */ }; @@ -1714,8 +1755,7 @@ oserror_parse_args(PyObject **p_args, PyTuple_SET_ITEM(newargs, 0, *myerrno); for (i = 1; i < nargs; i++) { PyObject *val = PyTuple_GET_ITEM(args, i); - Py_INCREF(val); - PyTuple_SET_ITEM(newargs, i, val); + PyTuple_SET_ITEM(newargs, i, Py_NewRef(val)); } Py_DECREF(args); args = *p_args = newargs; @@ -1750,12 +1790,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, return -1; } else { - Py_INCREF(filename); - self->filename = filename; + self->filename = Py_NewRef(filename); if (filename2 && filename2 != Py_None) { - Py_INCREF(filename2); - self->filename2 = filename2; + self->filename2 = Py_NewRef(filename2); } if (nargs >= 2 && nargs <= 5) { @@ -1770,15 +1808,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, } } } - Py_XINCREF(myerrno); - self->myerrno = myerrno; - - Py_XINCREF(strerror); - self->strerror = strerror; - + self->myerrno = Py_XNewRef(myerrno); + self->strerror = Py_XNewRef(strerror); #ifdef MS_WINDOWS - Py_XINCREF(winerror); - self->winerror = winerror; + self->winerror = Py_XNewRef(winerror); #endif /* Steals the reference to args */ @@ -2004,7 +2037,7 @@ static PyObject * OSError_reduce(PyOSErrorObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *args = self->args; - PyObject *res = NULL, *tmp; + PyObject *res = NULL; /* self->args is only the first two real arguments if there was a * file name given to OSError. */ @@ -2014,16 +2047,9 @@ OSError_reduce(PyOSErrorObject *self, PyObject *Py_UNUSED(ignored)) if (!args) return NULL; - tmp = PyTuple_GET_ITEM(self->args, 0); - Py_INCREF(tmp); - PyTuple_SET_ITEM(args, 0, tmp); - - tmp = PyTuple_GET_ITEM(self->args, 1); - Py_INCREF(tmp); - PyTuple_SET_ITEM(args, 1, tmp); - - Py_INCREF(self->filename); - PyTuple_SET_ITEM(args, 2, self->filename); + 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) { /* @@ -2031,12 +2057,10 @@ OSError_reduce(PyOSErrorObject *self, PyObject *Py_UNUSED(ignored)) * So, to recreate filename2, we need to pass in * winerror as well. */ - Py_INCREF(Py_None); - PyTuple_SET_ITEM(args, 3, Py_None); + PyTuple_SET_ITEM(args, 3, Py_NewRef(Py_None)); /* filename2 */ - Py_INCREF(self->filename2); - PyTuple_SET_ITEM(args, 4, self->filename2); + PyTuple_SET_ITEM(args, 4, Py_NewRef(self->filename2)); } } else Py_INCREF(args); @@ -2197,8 +2221,7 @@ NameError_init(PyNameErrorObject *self, PyObject *args, PyObject *kwds) } Py_DECREF(empty_tuple); - Py_XINCREF(name); - Py_XSETREF(self->name, name); + Py_XSETREF(self->name, Py_XNewRef(name)); return 0; } @@ -2272,11 +2295,8 @@ AttributeError_init(PyAttributeErrorObject *self, PyObject *args, PyObject *kwds } Py_DECREF(empty_tuple); - Py_XINCREF(name); - Py_XSETREF(self->name, name); - - Py_XINCREF(obj); - Py_XSETREF(self->obj, obj); + Py_XSETREF(self->name, Py_XNewRef(name)); + Py_XSETREF(self->obj, Py_XNewRef(obj)); return 0; } @@ -2305,6 +2325,48 @@ AttributeError_traverse(PyAttributeErrorObject *self, visitproc visit, void *arg 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")}, @@ -2312,7 +2374,9 @@ static PyMemberDef AttributeError_members[] = { }; static PyMethodDef AttributeError_methods[] = { - {NULL} /* Sentinel */ + {"__getstate__", (PyCFunction)AttributeError_getstate, METH_NOARGS}, + {"__reduce__", (PyCFunction)AttributeError_reduce, METH_NOARGS }, + {NULL} }; ComplexExtendsException(PyExc_Exception, AttributeError, @@ -2334,8 +2398,7 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) return -1; if (lenargs >= 1) { - Py_INCREF(PyTuple_GET_ITEM(args, 0)); - Py_XSETREF(self->msg, PyTuple_GET_ITEM(args, 0)); + Py_XSETREF(self->msg, Py_NewRef(PyTuple_GET_ITEM(args, 0))); } if (lenargs == 2) { info = PyTuple_GET_ITEM(args, 1); @@ -2431,8 +2494,7 @@ my_basename(PyObject *name) return PyUnicode_Substring(name, offset, size); } else { - Py_INCREF(name); - return name; + return Py_NewRef(name); } } @@ -2584,8 +2646,7 @@ get_string(PyObject *attr, const char *name) PyErr_Format(PyExc_TypeError, "%.200s attribute must be bytes", name); return NULL; } - Py_INCREF(attr); - return attr; + return Py_NewRef(attr); } static PyObject * @@ -2601,8 +2662,7 @@ get_unicode(PyObject *attr, const char *name) "%.200s attribute must be unicode", name); return NULL; } - Py_INCREF(attr); - return attr; + return Py_NewRef(attr); } static int @@ -3235,19 +3295,18 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, #define MEMERRORS_SAVE 16 static PyObject * -MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds) { PyBaseExceptionObject *self; - - /* If this is a subclass of MemoryError, don't use the freelist - * and just return a fresh object */ - if (type != (PyTypeObject *) PyExc_MemoryError) { - return BaseException_new(type, args, kwds); - } - struct _Py_exc_state *state = get_exc_state(); if (state->memerrors_freelist == NULL) { - return BaseException_new(type, args, kwds); + if (!allow_allocation) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + return Py_NewRef( + &_Py_INTERP_SINGLETON(interp, last_resort_memory_error)); + } + PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds); + return result; } /* Fetch object from freelist and revive it */ @@ -3267,6 +3326,33 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } +static PyObject * +MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + /* If this is a subclass of MemoryError, don't use the freelist + * and just return a fresh object */ + if (type != (PyTypeObject *) PyExc_MemoryError) { + return BaseException_new(type, args, kwds); + } + return get_memory_error(1, args, kwds); +} + +PyObject * +_PyErr_NoMemory(PyThreadState *tstate) +{ + if (Py_IS_TYPE(PyExc_MemoryError, NULL)) { + /* PyErr_NoMemory() has been called before PyExc_MemoryError has been + initialized by _PyExc_Init() */ + Py_FatalError("Out of memory and PyExc_MemoryError is not " + "initialized yet"); + } + PyObject *err = get_memory_error(0, NULL, NULL); + if (err != NULL) { + _PyErr_SetRaisedException(tstate, err); + } + return NULL; +} + static void MemoryError_dealloc(PyBaseExceptionObject *self) { @@ -3298,6 +3384,7 @@ preallocate_memerrors(void) /* We create enough MemoryErrors and then decref them, which will fill up the freelist. */ int i; + PyObject *errors[MEMERRORS_SAVE]; for (i = 0; i < MEMERRORS_SAVE; i++) { errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError, @@ -3323,7 +3410,7 @@ free_preallocated_memerrors(struct _Py_exc_state *state) } -static PyTypeObject _PyExc_MemoryError = { +PyTypeObject _PyExc_MemoryError = { PyVarObject_HEAD_INIT(NULL, 0) "MemoryError", sizeof(PyBaseExceptionObject), @@ -3591,14 +3678,9 @@ static struct static_exception static_exceptions[] = { int _PyExc_InitTypes(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return 0; - } - for (size_t i=0; i < Py_ARRAY_LENGTH(static_exceptions); i++) { PyTypeObject *exc = static_exceptions[i].exc; - - if (PyType_Ready(exc) < 0) { + if (_PyStaticType_InitBuiltin(interp, exc) < 0) { return -1; } } @@ -3609,13 +3691,9 @@ _PyExc_InitTypes(PyInterpreterState *interp) static void _PyExc_FiniTypes(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return; - } - for (Py_ssize_t i=Py_ARRAY_LENGTH(static_exceptions) - 1; i >= 0; i--) { PyTypeObject *exc = static_exceptions[i].exc; - _PyStaticType_Dealloc(exc); + _PyStaticType_Dealloc(interp, exc); } } @@ -3623,10 +3701,6 @@ _PyExc_FiniTypes(PyInterpreterState *interp) PyStatus _PyExc_InitGlobalObjects(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return _PyStatus_OK(); - } - if (preallocate_memerrors() < 0) { return _PyStatus_NO_MEMORY(); } @@ -3750,130 +3824,18 @@ _PyExc_Fini(PyInterpreterState *interp) _PyExc_FiniTypes(interp); } -/* Helper to do the equivalent of "raise X from Y" in C, but always using - * the current exception rather than passing one in. - * - * We currently limit this to *only* exceptions that use the BaseException - * tp_init and tp_new methods, since we can be reasonably sure we can wrap - * those correctly without losing data and without losing backwards - * compatibility. - * - * We also aim to rule out *all* exceptions that might be storing additional - * state, whether by having a size difference relative to BaseException, - * additional arguments passed in during construction or by having a - * non-empty instance dict. - * - * We need to be very careful with what we wrap, since changing types to - * a broader exception type would be backwards incompatible for - * existing codecs, and with different init or new method implementations - * may either not support instantiation with PyErr_Format or lose - * information when instantiated that way. - * - * XXX (ncoghlan): This could be made more comprehensive by exploiting the - * fact that exceptions are expected to support pickling. If more builtin - * exceptions (e.g. AttributeError) start to be converted to rich - * exceptions with additional attributes, that's probably a better approach - * to pursue over adding special cases for particular stateful subclasses. - * - * Returns a borrowed reference to the new exception (if any), NULL if the - * existing exception was left in place. - */ -PyObject * -_PyErr_TrySetFromCause(const char *format, ...) -{ - PyObject* msg_prefix; - PyObject *exc, *val, *tb; - PyTypeObject *caught_type; - PyObject *instance_args; - Py_ssize_t num_args, caught_type_size, base_exc_size; - PyObject *new_exc, *new_val, *new_tb; - va_list vargs; - int same_basic_size; - - PyErr_Fetch(&exc, &val, &tb); - caught_type = (PyTypeObject *)exc; - /* Ensure type info indicates no extra state is stored at the C level - * and that the type can be reinstantiated using PyErr_Format - */ - caught_type_size = caught_type->tp_basicsize; - base_exc_size = _PyExc_BaseException.tp_basicsize; - same_basic_size = ( - caught_type_size == base_exc_size || - (_PyType_SUPPORTS_WEAKREFS(caught_type) && - (caught_type_size == base_exc_size + (Py_ssize_t)sizeof(PyObject *)) - ) - ); - if (caught_type->tp_init != (initproc)BaseException_init || - caught_type->tp_new != BaseException_new || - !same_basic_size || - caught_type->tp_itemsize != _PyExc_BaseException.tp_itemsize) { - /* We can't be sure we can wrap this safely, since it may contain - * more state than just the exception type. Accordingly, we just - * leave it alone. - */ - PyErr_Restore(exc, val, tb); - return NULL; - } - - /* Check the args are empty or contain a single string */ - PyErr_NormalizeException(&exc, &val, &tb); - instance_args = ((PyBaseExceptionObject *)val)->args; - num_args = PyTuple_GET_SIZE(instance_args); - if (num_args > 1 || - (num_args == 1 && - !PyUnicode_CheckExact(PyTuple_GET_ITEM(instance_args, 0)))) { - /* More than 1 arg, or the one arg we do have isn't a string - */ - PyErr_Restore(exc, val, tb); - return NULL; - } - - /* Ensure the instance dict is also empty */ - if (!_PyObject_IsInstanceDictEmpty(val)) { - /* While we could potentially copy a non-empty instance dictionary - * to the replacement exception, for now we take the more - * conservative path of leaving exceptions with attributes set - * alone. - */ - PyErr_Restore(exc, val, tb); - return NULL; - } - - /* For exceptions that we can wrap safely, we chain the original - * exception to a new one of the exact same type with an - * error message that mentions the additional details and the - * original exception. - * - * It would be nice to wrap OSError and various other exception - * types as well, but that's quite a bit trickier due to the extra - * state potentially stored on OSError instances. - */ - /* Ensure the traceback is set correctly on the existing exception */ - if (tb != NULL) { - PyException_SetTraceback(val, tb); - Py_DECREF(tb); - } - -#ifdef HAVE_STDARG_PROTOTYPES - va_start(vargs, format); -#else - va_start(vargs); -#endif - msg_prefix = PyUnicode_FromFormatV(format, vargs); - va_end(vargs); - if (msg_prefix == NULL) { - Py_DECREF(exc); - Py_DECREF(val); - return NULL; +int +_PyException_AddNote(PyObject *exc, PyObject *note) +{ + if (!PyExceptionInstance_Check(exc)) { + PyErr_Format(PyExc_TypeError, + "exc must be an exception, not '%s'", + Py_TYPE(exc)->tp_name); + return -1; } - - PyErr_Format(exc, "%U (%s: %S)", - msg_prefix, Py_TYPE(val)->tp_name, val); - Py_DECREF(exc); - Py_DECREF(msg_prefix); - PyErr_Fetch(&new_exc, &new_val, &new_tb); - PyErr_NormalizeException(&new_exc, &new_val, &new_tb); - PyException_SetCause(new_val, val); - PyErr_Restore(new_exc, new_val, new_tb); - return new_val; + PyObject *r = BaseException_add_note(exc, note); + int res = r == NULL ? -1 : 0; + Py_XDECREF(r); + return res; } + |