Skip to content

Commit 11a2c6c

Browse files
authored
gh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives (in Objects/) (#102218)
1 parent b097925 commit 11a2c6c

File tree

8 files changed

+51
-106
lines changed

8 files changed

+51
-106
lines changed

Objects/dictobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ As a consequence of this, split keys have a maximum size of 16.
119119
#include "pycore_dict.h" // PyDictKeysObject
120120
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
121121
#include "pycore_object.h" // _PyObject_GC_TRACK()
122-
#include "pycore_pyerrors.h" // _PyErr_Fetch()
122+
#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
123123
#include "pycore_pystate.h" // _PyThreadState_GET()
124124
#include "stringlib/eq.h" // unicode_eq()
125125

Objects/exceptions.c

+13-26
Original file line numberDiff line numberDiff line change
@@ -3781,16 +3781,13 @@ PyObject *
37813781
_PyErr_TrySetFromCause(const char *format, ...)
37823782
{
37833783
PyObject* msg_prefix;
3784-
PyObject *exc, *val, *tb;
3785-
PyTypeObject *caught_type;
37863784
PyObject *instance_args;
37873785
Py_ssize_t num_args, caught_type_size, base_exc_size;
3788-
PyObject *new_exc, *new_val, *new_tb;
37893786
va_list vargs;
37903787
int same_basic_size;
37913788

3792-
PyErr_Fetch(&exc, &val, &tb);
3793-
caught_type = (PyTypeObject *)exc;
3789+
PyObject *exc = PyErr_GetRaisedException();
3790+
PyTypeObject *caught_type = Py_TYPE(exc);
37943791
/* Ensure type info indicates no extra state is stored at the C level
37953792
* and that the type can be reinstantiated using PyErr_Format
37963793
*/
@@ -3810,31 +3807,30 @@ _PyErr_TrySetFromCause(const char *format, ...)
38103807
* more state than just the exception type. Accordingly, we just
38113808
* leave it alone.
38123809
*/
3813-
PyErr_Restore(exc, val, tb);
3810+
PyErr_SetRaisedException(exc);
38143811
return NULL;
38153812
}
38163813

38173814
/* Check the args are empty or contain a single string */
3818-
PyErr_NormalizeException(&exc, &val, &tb);
3819-
instance_args = ((PyBaseExceptionObject *)val)->args;
3815+
instance_args = ((PyBaseExceptionObject *)exc)->args;
38203816
num_args = PyTuple_GET_SIZE(instance_args);
38213817
if (num_args > 1 ||
38223818
(num_args == 1 &&
38233819
!PyUnicode_CheckExact(PyTuple_GET_ITEM(instance_args, 0)))) {
38243820
/* More than 1 arg, or the one arg we do have isn't a string
38253821
*/
3826-
PyErr_Restore(exc, val, tb);
3822+
PyErr_SetRaisedException(exc);
38273823
return NULL;
38283824
}
38293825

38303826
/* Ensure the instance dict is also empty */
3831-
if (!_PyObject_IsInstanceDictEmpty(val)) {
3827+
if (!_PyObject_IsInstanceDictEmpty(exc)) {
38323828
/* While we could potentially copy a non-empty instance dictionary
38333829
* to the replacement exception, for now we take the more
38343830
* conservative path of leaving exceptions with attributes set
38353831
* alone.
38363832
*/
3837-
PyErr_Restore(exc, val, tb);
3833+
PyErr_SetRaisedException(exc);
38383834
return NULL;
38393835
}
38403836

@@ -3847,28 +3843,19 @@ _PyErr_TrySetFromCause(const char *format, ...)
38473843
* types as well, but that's quite a bit trickier due to the extra
38483844
* state potentially stored on OSError instances.
38493845
*/
3850-
/* Ensure the traceback is set correctly on the existing exception */
3851-
if (tb != NULL) {
3852-
PyException_SetTraceback(val, tb);
3853-
Py_DECREF(tb);
3854-
}
3855-
38563846
va_start(vargs, format);
38573847
msg_prefix = PyUnicode_FromFormatV(format, vargs);
38583848
va_end(vargs);
38593849
if (msg_prefix == NULL) {
38603850
Py_DECREF(exc);
3861-
Py_DECREF(val);
38623851
return NULL;
38633852
}
38643853

3865-
PyErr_Format(exc, "%U (%s: %S)",
3866-
msg_prefix, Py_TYPE(val)->tp_name, val);
3867-
Py_DECREF(exc);
3854+
PyErr_Format((PyObject*)Py_TYPE(exc), "%U (%s: %S)",
3855+
msg_prefix, Py_TYPE(exc)->tp_name, exc);
38683856
Py_DECREF(msg_prefix);
3869-
PyErr_Fetch(&new_exc, &new_val, &new_tb);
3870-
PyErr_NormalizeException(&new_exc, &new_val, &new_tb);
3871-
PyException_SetCause(new_val, val);
3872-
PyErr_Restore(new_exc, new_val, new_tb);
3873-
return new_val;
3857+
PyObject *new_exc = PyErr_GetRaisedException();
3858+
PyException_SetCause(new_exc, exc);
3859+
PyErr_SetRaisedException(new_exc);
3860+
return new_exc;
38743861
}

Objects/frameobject.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,6 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
13081308
/* Merge locals into fast locals */
13091309
PyObject *locals;
13101310
PyObject **fast;
1311-
PyObject *error_type, *error_value, *error_traceback;
13121311
PyCodeObject *co;
13131312
locals = frame->f_locals;
13141313
if (locals == NULL) {
@@ -1317,7 +1316,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
13171316
fast = _PyFrame_GetLocalsArray(frame);
13181317
co = frame->f_code;
13191318

1320-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
1319+
PyObject *exc = PyErr_GetRaisedException();
13211320
for (int i = 0; i < co->co_nlocalsplus; i++) {
13221321
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
13231322

@@ -1374,7 +1373,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
13741373
}
13751374
Py_XDECREF(value);
13761375
}
1377-
PyErr_Restore(error_type, error_value, error_traceback);
1376+
PyErr_SetRaisedException(exc);
13781377
}
13791378

13801379
void

Objects/genobject.c

+16-48
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ void
6969
_PyGen_Finalize(PyObject *self)
7070
{
7171
PyGenObject *gen = (PyGenObject *)self;
72-
PyObject *res = NULL;
73-
PyObject *error_type, *error_value, *error_traceback;
7472

7573
if (gen->gi_frame_state >= FRAME_COMPLETED) {
7674
/* Generator isn't paused, so no need to close */
@@ -82,23 +80,22 @@ _PyGen_Finalize(PyObject *self)
8280
PyObject *finalizer = agen->ag_origin_or_finalizer;
8381
if (finalizer && !agen->ag_closed) {
8482
/* Save the current exception, if any. */
85-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
86-
87-
res = PyObject_CallOneArg(finalizer, self);
83+
PyObject *exc = PyErr_GetRaisedException();
8884

85+
PyObject *res = PyObject_CallOneArg(finalizer, self);
8986
if (res == NULL) {
9087
PyErr_WriteUnraisable(self);
9188
} else {
9289
Py_DECREF(res);
9390
}
9491
/* Restore the saved exception. */
95-
PyErr_Restore(error_type, error_value, error_traceback);
92+
PyErr_SetRaisedException(exc);
9693
return;
9794
}
9895
}
9996

10097
/* Save the current exception, if any. */
101-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
98+
PyObject *exc = PyErr_GetRaisedException();
10299

103100
/* If `gen` is a coroutine, and if it was never awaited on,
104101
issue a RuntimeWarning. */
@@ -109,20 +106,19 @@ _PyGen_Finalize(PyObject *self)
109106
_PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
110107
}
111108
else {
112-
res = gen_close(gen, NULL);
113-
}
114-
115-
if (res == NULL) {
116-
if (PyErr_Occurred()) {
117-
PyErr_WriteUnraisable(self);
109+
PyObject *res = gen_close(gen, NULL);
110+
if (res == NULL) {
111+
if (PyErr_Occurred()) {
112+
PyErr_WriteUnraisable(self);
113+
}
114+
}
115+
else {
116+
Py_DECREF(res);
118117
}
119-
}
120-
else {
121-
Py_DECREF(res);
122118
}
123119

124120
/* Restore the saved exception. */
125-
PyErr_Restore(error_type, error_value, error_traceback);
121+
PyErr_SetRaisedException(exc);
126122
}
127123

128124
static void
@@ -648,39 +644,11 @@ _PyGen_SetStopIterationValue(PyObject *value)
648644
int
649645
_PyGen_FetchStopIterationValue(PyObject **pvalue)
650646
{
651-
PyObject *et, *ev, *tb;
652647
PyObject *value = NULL;
653-
654648
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
655-
PyErr_Fetch(&et, &ev, &tb);
656-
if (ev) {
657-
/* exception will usually be normalised already */
658-
if (PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
659-
value = Py_NewRef(((PyStopIterationObject *)ev)->value);
660-
Py_DECREF(ev);
661-
} else if (et == PyExc_StopIteration && !PyTuple_Check(ev)) {
662-
/* Avoid normalisation and take ev as value.
663-
*
664-
* Normalization is required if the value is a tuple, in
665-
* that case the value of StopIteration would be set to
666-
* the first element of the tuple.
667-
*
668-
* (See _PyErr_CreateException code for details.)
669-
*/
670-
value = ev;
671-
} else {
672-
/* normalisation required */
673-
PyErr_NormalizeException(&et, &ev, &tb);
674-
if (!PyObject_TypeCheck(ev, (PyTypeObject *)PyExc_StopIteration)) {
675-
PyErr_Restore(et, ev, tb);
676-
return -1;
677-
}
678-
value = Py_NewRef(((PyStopIterationObject *)ev)->value);
679-
Py_DECREF(ev);
680-
}
681-
}
682-
Py_XDECREF(et);
683-
Py_XDECREF(tb);
649+
PyObject *exc = PyErr_GetRaisedException();
650+
value = Py_NewRef(((PyStopIterationObject *)exc)->value);
651+
Py_DECREF(exc);
684652
} else if (PyErr_Occurred()) {
685653
return -1;
686654
}

Objects/object.c

+10-15
Original file line numberDiff line numberDiff line change
@@ -370,13 +370,12 @@ _PyObject_Dump(PyObject* op)
370370
fflush(stderr);
371371

372372
PyGILState_STATE gil = PyGILState_Ensure();
373-
PyObject *error_type, *error_value, *error_traceback;
374-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
373+
PyObject *exc = PyErr_GetRaisedException();
375374

376375
(void)PyObject_Print(op, stderr, 0);
377376
fflush(stderr);
378377

379-
PyErr_Restore(error_type, error_value, error_traceback);
378+
PyErr_SetRaisedException(exc);
380379
PyGILState_Release(gil);
381380

382381
fprintf(stderr, "\n");
@@ -860,25 +859,22 @@ set_attribute_error_context(PyObject* v, PyObject* name)
860859
return 0;
861860
}
862861
// Intercept AttributeError exceptions and augment them to offer suggestions later.
863-
PyObject *type, *value, *traceback;
864-
PyErr_Fetch(&type, &value, &traceback);
865-
PyErr_NormalizeException(&type, &value, &traceback);
866-
// Check if the normalized exception is indeed an AttributeError
867-
if (!PyErr_GivenExceptionMatches(value, PyExc_AttributeError)) {
862+
PyObject *exc = PyErr_GetRaisedException();
863+
if (!PyErr_GivenExceptionMatches(exc, PyExc_AttributeError)) {
868864
goto restore;
869865
}
870-
PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) value;
866+
PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) exc;
871867
// Check if this exception was already augmented
872868
if (the_exc->name || the_exc->obj) {
873869
goto restore;
874870
}
875871
// Augment the exception with the name and object
876-
if (PyObject_SetAttr(value, &_Py_ID(name), name) ||
877-
PyObject_SetAttr(value, &_Py_ID(obj), v)) {
872+
if (PyObject_SetAttr(exc, &_Py_ID(name), name) ||
873+
PyObject_SetAttr(exc, &_Py_ID(obj), v)) {
878874
return 1;
879875
}
880876
restore:
881-
PyErr_Restore(type, value, traceback);
877+
PyErr_SetRaisedException(exc);
882878
return 0;
883879
}
884880

@@ -2190,9 +2186,8 @@ Py_ReprLeave(PyObject *obj)
21902186
PyObject *dict;
21912187
PyObject *list;
21922188
Py_ssize_t i;
2193-
PyObject *error_type, *error_value, *error_traceback;
21942189

2195-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
2190+
PyObject *exc = PyErr_GetRaisedException();
21962191

21972192
dict = PyThreadState_GetDict();
21982193
if (dict == NULL)
@@ -2213,7 +2208,7 @@ Py_ReprLeave(PyObject *obj)
22132208

22142209
finally:
22152210
/* ignore exceptions because there is no way to report them. */
2216-
PyErr_Restore(error_type, error_value, error_traceback);
2211+
PyErr_SetRaisedException(exc);
22172212
}
22182213

22192214
/* Trashcan support. */

Objects/odictobject.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -1556,10 +1556,9 @@ _PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value,
15561556
res = _odict_add_new_node((PyODictObject *)od, key, hash);
15571557
if (res < 0) {
15581558
/* Revert setting the value on the dict */
1559-
PyObject *exc, *val, *tb;
1560-
PyErr_Fetch(&exc, &val, &tb);
1559+
PyObject *exc = PyErr_GetRaisedException();
15611560
(void) _PyDict_DelItem_KnownHash(od, key, hash);
1562-
_PyErr_ChainExceptions(exc, val, tb);
1561+
_PyErr_ChainExceptions1(exc);
15631562
}
15641563
}
15651564
return res;

Objects/typeobject.c

+4-6
Original file line numberDiff line numberDiff line change
@@ -4397,10 +4397,9 @@ static void
43974397
type_dealloc_common(PyTypeObject *type)
43984398
{
43994399
if (type->tp_bases != NULL) {
4400-
PyObject *tp, *val, *tb;
4401-
PyErr_Fetch(&tp, &val, &tb);
4400+
PyObject *exc = PyErr_GetRaisedException();
44024401
remove_all_subclasses(type, type->tp_bases);
4403-
PyErr_Restore(tp, val, tb);
4402+
PyErr_SetRaisedException(exc);
44044403
}
44054404
}
44064405

@@ -8445,10 +8444,9 @@ slot_tp_finalize(PyObject *self)
84458444
{
84468445
int unbound;
84478446
PyObject *del, *res;
8448-
PyObject *error_type, *error_value, *error_traceback;
84498447

84508448
/* Save the current exception, if any. */
8451-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
8449+
PyObject *exc = PyErr_GetRaisedException();
84528450

84538451
/* Execute __del__ method, if any. */
84548452
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound);
@@ -8462,7 +8460,7 @@ slot_tp_finalize(PyObject *self)
84628460
}
84638461

84648462
/* Restore the saved exception. */
8465-
PyErr_Restore(error_type, error_value, error_traceback);
8463+
PyErr_SetRaisedException(exc);
84668464
}
84678465

84688466
static PyObject *

Objects/weakrefobject.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -959,9 +959,8 @@ PyObject_ClearWeakRefs(PyObject *object)
959959
if (*list != NULL) {
960960
PyWeakReference *current = *list;
961961
Py_ssize_t count = _PyWeakref_GetWeakrefCount(current);
962-
PyObject *err_type, *err_value, *err_tb;
962+
PyObject *exc = PyErr_GetRaisedException();
963963

964-
PyErr_Fetch(&err_type, &err_value, &err_tb);
965964
if (count == 1) {
966965
PyObject *callback = current->wr_callback;
967966

@@ -980,7 +979,7 @@ PyObject_ClearWeakRefs(PyObject *object)
980979

981980
tuple = PyTuple_New(count * 2);
982981
if (tuple == NULL) {
983-
_PyErr_ChainExceptions(err_type, err_value, err_tb);
982+
_PyErr_ChainExceptions1(exc);
984983
return;
985984
}
986985

@@ -1010,7 +1009,7 @@ PyObject_ClearWeakRefs(PyObject *object)
10101009
Py_DECREF(tuple);
10111010
}
10121011
assert(!PyErr_Occurred());
1013-
PyErr_Restore(err_type, err_value, err_tb);
1012+
PyErr_SetRaisedException(exc);
10141013
}
10151014
}
10161015

0 commit comments

Comments
 (0)