Skip to content

Commit b065394

Browse files
committed
Plumb into exception subtypes; including pickling.
1 parent bcc720b commit b065394

1 file changed

Lines changed: 79 additions & 60 deletions

File tree

Objects/exceptions.c

Lines changed: 79 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
7777
return (PyObject *)self;
7878
}
7979

80+
static inline void BaseException_init_timestamp(PyBaseExceptionObject *self)
81+
{
82+
PyTime_TimeRaw(&self->timestamp_ns); /* fills in 0 on failure. */
83+
}
84+
8085
static int
8186
BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
8287
{
@@ -88,7 +93,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
8893
Py_IS_TYPE(self, (PyTypeObject *)PyExc_StopAsyncIteration)) {
8994
self->timestamp_ns = 0; /* fast; frequent non-error control flow. */
9095
} else {
91-
PyTime_TimeRaw(&self->timestamp_ns); /* fills in 0 on failure. */
96+
BaseException_init_timestamp(self);
9297
}
9398
return 0;
9499
}
@@ -112,7 +117,7 @@ BaseException_vectorcall(PyObject *type_obj, PyObject * const*args,
112117
// The dict is created on the fly in PyObject_GenericSetAttr()
113118
self->dict = NULL;
114119
self->notes = NULL;
115-
PyTime_TimeRaw(&self->timestamp_ns); /* fills in 0 on failure. */
120+
BaseException_init_timestamp(self);
116121
self->traceback = NULL;
117122
self->cause = NULL;
118123
self->context = NULL;
@@ -204,6 +209,23 @@ BaseException_repr(PyBaseExceptionObject *self)
204209

205210
/* Pickling support */
206211

212+
/* Returns dict on success, after having added a __timestamp_ns__ key; NULL
213+
otherwise. dict does not have to be self->dict as the getstate use case
214+
often uses a copy. */
215+
static PyObject* BaseException_add_timestamp_to_dict(PyBaseExceptionObject *self, PyObject *dict)
216+
{
217+
assert(dict != NULL);
218+
PyObject *ts = PyLong_FromLongLong(self->timestamp_ns);
219+
if (!ts)
220+
return NULL;
221+
if (PyDict_SetItemString(dict, "__timestamp_ns__", ts) == -1) {
222+
Py_DECREF(ts);
223+
return NULL;
224+
}
225+
Py_DECREF(ts);
226+
return dict;
227+
}
228+
207229
/*[clinic input]
208230
@critical_section
209231
BaseException.__reduce__
@@ -215,16 +237,14 @@ BaseException___reduce___impl(PyBaseExceptionObject *self)
215237
{
216238
if (!self->dict) {
217239
self->dict = PyDict_New();
218-
}
219-
if (self->args && self->dict) {
220-
PyObject *ts = PyLong_FromLongLong(self->timestamp_ns);
221-
if (!ts)
222-
return NULL;
223-
if (PyDict_SetItemString(self->dict, "__timestamp_ns__", ts) == -1) {
224-
Py_DECREF(ts);
240+
if (self->dict == NULL) {
225241
return NULL;
226242
}
227-
Py_DECREF(ts);
243+
}
244+
if (!BaseException_add_timestamp_to_dict(self, self->dict)) {
245+
return NULL;
246+
}
247+
if (self->args && self->dict) {
228248
return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict);
229249
} else {
230250
return PyTuple_Pack(2, Py_TYPE(self), self->args);
@@ -1804,30 +1824,26 @@ static PyObject *
18041824
ImportError_getstate(PyImportErrorObject *self)
18051825
{
18061826
PyObject *dict = ((PyBaseExceptionObject *)self)->dict;
1807-
if (self->name || self->path || self->name_from) {
1808-
dict = dict ? PyDict_Copy(dict) : PyDict_New();
1809-
if (dict == NULL)
1810-
return NULL;
1811-
if (self->name && PyDict_SetItem(dict, &_Py_ID(name), self->name) < 0) {
1812-
Py_DECREF(dict);
1813-
return NULL;
1814-
}
1815-
if (self->path && PyDict_SetItem(dict, &_Py_ID(path), self->path) < 0) {
1816-
Py_DECREF(dict);
1817-
return NULL;
1818-
}
1819-
if (self->name_from && PyDict_SetItem(dict, &_Py_ID(name_from), self->name_from) < 0) {
1820-
Py_DECREF(dict);
1821-
return NULL;
1822-
}
1823-
return dict;
1827+
dict = dict ? PyDict_Copy(dict) : PyDict_New();
1828+
if (dict == NULL) {
1829+
return NULL;
18241830
}
1825-
else if (dict) {
1826-
return Py_NewRef(dict);
1831+
if (!BaseException_add_timestamp_to_dict((PyBaseExceptionObject *)self, dict)) {
1832+
return NULL;
18271833
}
1828-
else {
1829-
Py_RETURN_NONE;
1834+
if (self->name && PyDict_SetItem(dict, &_Py_ID(name), self->name) < 0) {
1835+
Py_DECREF(dict);
1836+
return NULL;
1837+
}
1838+
if (self->path && PyDict_SetItem(dict, &_Py_ID(path), self->path) < 0) {
1839+
Py_DECREF(dict);
1840+
return NULL;
18301841
}
1842+
if (self->name_from && PyDict_SetItem(dict, &_Py_ID(name_from), self->name_from) < 0) {
1843+
Py_DECREF(dict);
1844+
return NULL;
1845+
}
1846+
return dict;
18311847
}
18321848

18331849
/* Pickling support */
@@ -1840,10 +1856,8 @@ ImportError_reduce(PyImportErrorObject *self, PyObject *Py_UNUSED(ignored))
18401856
if (state == NULL)
18411857
return NULL;
18421858
args = ((PyBaseExceptionObject *)self)->args;
1843-
if (state == Py_None)
1844-
res = PyTuple_Pack(2, Py_TYPE(self), args);
1845-
else
1846-
res = PyTuple_Pack(3, Py_TYPE(self), args, state);
1859+
assert(state != Py_None);
1860+
res = PyTuple_Pack(3, Py_TYPE(self), args, state);
18471861
Py_DECREF(state);
18481862
return res;
18491863
}
@@ -2119,6 +2133,8 @@ OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds)
21192133
PyObject *winerror = NULL;
21202134
#endif
21212135

2136+
BaseException_init_timestamp((PyBaseExceptionObject *)self);
2137+
21222138
if (!oserror_use_init(Py_TYPE(self)))
21232139
/* Everything already done in OSError_new */
21242140
return 0;
@@ -2260,10 +2276,16 @@ OSError_reduce(PyOSErrorObject *self, PyObject *Py_UNUSED(ignored))
22602276
} else
22612277
Py_INCREF(args);
22622278

2263-
if (self->dict)
2264-
res = PyTuple_Pack(3, Py_TYPE(self), args, self->dict);
2265-
else
2266-
res = PyTuple_Pack(2, Py_TYPE(self), args);
2279+
if (!self->dict) {
2280+
self->dict = PyDict_New();
2281+
if (!self->dict) {
2282+
return NULL;
2283+
}
2284+
}
2285+
if (!BaseException_add_timestamp_to_dict((PyBaseExceptionObject*)self, self->dict)) {
2286+
return NULL;
2287+
}
2288+
res = PyTuple_Pack(3, Py_TYPE(self), args, self->dict);
22672289
Py_DECREF(args);
22682290
return res;
22692291
}
@@ -2528,29 +2550,26 @@ AttributeError_traverse(PyAttributeErrorObject *self, visitproc visit, void *arg
25282550
static PyObject *
25292551
AttributeError_getstate(PyAttributeErrorObject *self, PyObject *Py_UNUSED(ignored))
25302552
{
2531-
PyObject *dict = ((PyAttributeErrorObject *)self)->dict;
2532-
if (self->name || self->args) {
2533-
dict = dict ? PyDict_Copy(dict) : PyDict_New();
2534-
if (dict == NULL) {
2535-
return NULL;
2536-
}
2537-
if (self->name && PyDict_SetItemString(dict, "name", self->name) < 0) {
2538-
Py_DECREF(dict);
2539-
return NULL;
2540-
}
2541-
/* We specifically are not pickling the obj attribute since there are many
2542-
cases where it is unlikely to be picklable. See GH-103352.
2543-
*/
2544-
if (self->args && PyDict_SetItemString(dict, "args", self->args) < 0) {
2545-
Py_DECREF(dict);
2546-
return NULL;
2547-
}
2548-
return dict;
2553+
PyObject *dict = self->dict;
2554+
dict = dict ? PyDict_Copy(dict) : PyDict_New();
2555+
if (dict == NULL) {
2556+
return NULL;
25492557
}
2550-
else if (dict) {
2551-
return Py_NewRef(dict);
2558+
if (!BaseException_add_timestamp_to_dict((PyBaseExceptionObject*)self, dict)) {
2559+
return NULL;
25522560
}
2553-
Py_RETURN_NONE;
2561+
if (self->name && PyDict_SetItemString(dict, "name", self->name) < 0) {
2562+
Py_DECREF(dict);
2563+
return NULL;
2564+
}
2565+
/* We specifically are not pickling the obj attribute since there are many
2566+
cases where it is unlikely to be picklable. See GH-103352.
2567+
*/
2568+
if (self->args && PyDict_SetItemString(dict, "args", self->args) < 0) {
2569+
Py_DECREF(dict);
2570+
return NULL;
2571+
}
2572+
return dict;
25542573
}
25552574

25562575
static PyObject *

0 commit comments

Comments
 (0)