mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives (in Objects/) (#102218)
This commit is contained in:
		
							parent
							
								
									b097925858
								
							
						
					
					
						commit
						11a2c6ce51
					
				
					 8 changed files with 51 additions and 106 deletions
				
			
		|  | @ -119,7 +119,7 @@ As a consequence of this, split keys have a maximum size of 16. | |||
| #include "pycore_dict.h"          // PyDictKeysObject | ||||
| #include "pycore_gc.h"            // _PyObject_GC_IS_TRACKED() | ||||
| #include "pycore_object.h"        // _PyObject_GC_TRACK() | ||||
| #include "pycore_pyerrors.h"      // _PyErr_Fetch() | ||||
| #include "pycore_pyerrors.h"      // _PyErr_GetRaisedException() | ||||
| #include "pycore_pystate.h"       // _PyThreadState_GET() | ||||
| #include "stringlib/eq.h"         // unicode_eq() | ||||
| 
 | ||||
|  |  | |||
|  | @ -3781,16 +3781,13 @@ 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; | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
|     PyTypeObject *caught_type = Py_TYPE(exc); | ||||
|     /* Ensure type info indicates no extra state is stored at the C level
 | ||||
|      * and that the type can be reinstantiated using PyErr_Format | ||||
|      */ | ||||
|  | @ -3810,31 +3807,30 @@ _PyErr_TrySetFromCause(const char *format, ...) | |||
|          * more state than just the exception type. Accordingly, we just | ||||
|          * leave it alone. | ||||
|          */ | ||||
|         PyErr_Restore(exc, val, tb); | ||||
|         PyErr_SetRaisedException(exc); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     /* Check the args are empty or contain a single string */ | ||||
|     PyErr_NormalizeException(&exc, &val, &tb); | ||||
|     instance_args = ((PyBaseExceptionObject *)val)->args; | ||||
|     instance_args = ((PyBaseExceptionObject *)exc)->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); | ||||
|         PyErr_SetRaisedException(exc); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     /* Ensure the instance dict is also empty */ | ||||
|     if (!_PyObject_IsInstanceDictEmpty(val)) { | ||||
|     if (!_PyObject_IsInstanceDictEmpty(exc)) { | ||||
|         /* 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); | ||||
|         PyErr_SetRaisedException(exc); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -3847,28 +3843,19 @@ _PyErr_TrySetFromCause(const char *format, ...) | |||
|      * 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); | ||||
|     } | ||||
| 
 | ||||
|     va_start(vargs, format); | ||||
|     msg_prefix = PyUnicode_FromFormatV(format, vargs); | ||||
|     va_end(vargs); | ||||
|     if (msg_prefix == NULL) { | ||||
|         Py_DECREF(exc); | ||||
|         Py_DECREF(val); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     PyErr_Format(exc, "%U (%s: %S)", | ||||
|                  msg_prefix, Py_TYPE(val)->tp_name, val); | ||||
|     Py_DECREF(exc); | ||||
|     PyErr_Format((PyObject*)Py_TYPE(exc), "%U (%s: %S)", | ||||
|                  msg_prefix, Py_TYPE(exc)->tp_name, 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 *new_exc = PyErr_GetRaisedException(); | ||||
|     PyException_SetCause(new_exc, exc); | ||||
|     PyErr_SetRaisedException(new_exc); | ||||
|     return new_exc; | ||||
| } | ||||
|  |  | |||
|  | @ -1308,7 +1308,6 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) | |||
|     /* Merge locals into fast locals */ | ||||
|     PyObject *locals; | ||||
|     PyObject **fast; | ||||
|     PyObject *error_type, *error_value, *error_traceback; | ||||
|     PyCodeObject *co; | ||||
|     locals = frame->f_locals; | ||||
|     if (locals == NULL) { | ||||
|  | @ -1317,7 +1316,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) | |||
|     fast = _PyFrame_GetLocalsArray(frame); | ||||
|     co = frame->f_code; | ||||
| 
 | ||||
|     PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
|     for (int i = 0; i < co->co_nlocalsplus; i++) { | ||||
|         _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); | ||||
| 
 | ||||
|  | @ -1374,7 +1373,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) | |||
|         } | ||||
|         Py_XDECREF(value); | ||||
|     } | ||||
|     PyErr_Restore(error_type, error_value, error_traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  |  | |||
|  | @ -69,8 +69,6 @@ void | |||
| _PyGen_Finalize(PyObject *self) | ||||
| { | ||||
|     PyGenObject *gen = (PyGenObject *)self; | ||||
|     PyObject *res = NULL; | ||||
|     PyObject *error_type, *error_value, *error_traceback; | ||||
| 
 | ||||
|     if (gen->gi_frame_state >= FRAME_COMPLETED) { | ||||
|         /* Generator isn't paused, so no need to close */ | ||||
|  | @ -82,23 +80,22 @@ _PyGen_Finalize(PyObject *self) | |||
|         PyObject *finalizer = agen->ag_origin_or_finalizer; | ||||
|         if (finalizer && !agen->ag_closed) { | ||||
|             /* Save the current exception, if any. */ | ||||
|             PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
| 
 | ||||
|             res = PyObject_CallOneArg(finalizer, self); | ||||
|             PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|             PyObject *res = PyObject_CallOneArg(finalizer, self); | ||||
|             if (res == NULL) { | ||||
|                 PyErr_WriteUnraisable(self); | ||||
|             } else { | ||||
|                 Py_DECREF(res); | ||||
|             } | ||||
|             /* Restore the saved exception. */ | ||||
|             PyErr_Restore(error_type, error_value, error_traceback); | ||||
|             PyErr_SetRaisedException(exc); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Save the current exception, if any. */ | ||||
|     PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|     /* If `gen` is a coroutine, and if it was never awaited on,
 | ||||
|        issue a RuntimeWarning. */ | ||||
|  | @ -109,20 +106,19 @@ _PyGen_Finalize(PyObject *self) | |||
|         _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); | ||||
|     } | ||||
|     else { | ||||
|         res = gen_close(gen, NULL); | ||||
|     } | ||||
| 
 | ||||
|     if (res == NULL) { | ||||
|         if (PyErr_Occurred()) { | ||||
|             PyErr_WriteUnraisable(self); | ||||
|         PyObject *res = gen_close(gen, NULL); | ||||
|         if (res == NULL) { | ||||
|             if (PyErr_Occurred()) { | ||||
|                 PyErr_WriteUnraisable(self); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             Py_DECREF(res); | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         Py_DECREF(res); | ||||
|     } | ||||
| 
 | ||||
|     /* Restore the saved exception. */ | ||||
|     PyErr_Restore(error_type, error_value, error_traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -648,39 +644,11 @@ _PyGen_SetStopIterationValue(PyObject *value) | |||
| int | ||||
| _PyGen_FetchStopIterationValue(PyObject **pvalue) | ||||
| { | ||||
|     PyObject *et, *ev, *tb; | ||||
|     PyObject *value = NULL; | ||||
| 
 | ||||
|     if (PyErr_ExceptionMatches(PyExc_StopIteration)) { | ||||
|         PyErr_Fetch(&et, &ev, &tb); | ||||
|         if (ev) { | ||||
|             /* exception will usually be normalised already */ | ||||
|             if (PyObject_TypeCheck(ev, (PyTypeObject *) et)) { | ||||
|                 value = Py_NewRef(((PyStopIterationObject *)ev)->value); | ||||
|                 Py_DECREF(ev); | ||||
|             } else if (et == PyExc_StopIteration && !PyTuple_Check(ev)) { | ||||
|                 /* Avoid normalisation and take ev as value.
 | ||||
|                  * | ||||
|                  * Normalization is required if the value is a tuple, in | ||||
|                  * that case the value of StopIteration would be set to | ||||
|                  * the first element of the tuple. | ||||
|                  * | ||||
|                  * (See _PyErr_CreateException code for details.) | ||||
|                  */ | ||||
|                 value = ev; | ||||
|             } else { | ||||
|                 /* normalisation required */ | ||||
|                 PyErr_NormalizeException(&et, &ev, &tb); | ||||
|                 if (!PyObject_TypeCheck(ev, (PyTypeObject *)PyExc_StopIteration)) { | ||||
|                     PyErr_Restore(et, ev, tb); | ||||
|                     return -1; | ||||
|                 } | ||||
|                 value = Py_NewRef(((PyStopIterationObject *)ev)->value); | ||||
|                 Py_DECREF(ev); | ||||
|             } | ||||
|         } | ||||
|         Py_XDECREF(et); | ||||
|         Py_XDECREF(tb); | ||||
|         PyObject *exc = PyErr_GetRaisedException(); | ||||
|         value = Py_NewRef(((PyStopIterationObject *)exc)->value); | ||||
|         Py_DECREF(exc); | ||||
|     } else if (PyErr_Occurred()) { | ||||
|         return -1; | ||||
|     } | ||||
|  |  | |||
|  | @ -370,13 +370,12 @@ _PyObject_Dump(PyObject* op) | |||
|     fflush(stderr); | ||||
| 
 | ||||
|     PyGILState_STATE gil = PyGILState_Ensure(); | ||||
|     PyObject *error_type, *error_value, *error_traceback; | ||||
|     PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|     (void)PyObject_Print(op, stderr, 0); | ||||
|     fflush(stderr); | ||||
| 
 | ||||
|     PyErr_Restore(error_type, error_value, error_traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
|     PyGILState_Release(gil); | ||||
| 
 | ||||
|     fprintf(stderr, "\n"); | ||||
|  | @ -860,25 +859,22 @@ set_attribute_error_context(PyObject* v, PyObject* name) | |||
|         return 0; | ||||
|     } | ||||
|     // Intercept AttributeError exceptions and augment them to offer suggestions later.
 | ||||
|     PyObject *type, *value, *traceback; | ||||
|     PyErr_Fetch(&type, &value, &traceback); | ||||
|     PyErr_NormalizeException(&type, &value, &traceback); | ||||
|     // Check if the normalized exception is indeed an AttributeError
 | ||||
|     if (!PyErr_GivenExceptionMatches(value, PyExc_AttributeError)) { | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
|     if (!PyErr_GivenExceptionMatches(exc, PyExc_AttributeError)) { | ||||
|         goto restore; | ||||
|     } | ||||
|     PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) value; | ||||
|     PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) exc; | ||||
|     // Check if this exception was already augmented
 | ||||
|     if (the_exc->name || the_exc->obj) { | ||||
|         goto restore; | ||||
|     } | ||||
|     // Augment the exception with the name and object
 | ||||
|     if (PyObject_SetAttr(value, &_Py_ID(name), name) || | ||||
|         PyObject_SetAttr(value, &_Py_ID(obj), v)) { | ||||
|     if (PyObject_SetAttr(exc, &_Py_ID(name), name) || | ||||
|         PyObject_SetAttr(exc, &_Py_ID(obj), v)) { | ||||
|         return 1; | ||||
|     } | ||||
| restore: | ||||
|     PyErr_Restore(type, value, traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2190,9 +2186,8 @@ Py_ReprLeave(PyObject *obj) | |||
|     PyObject *dict; | ||||
|     PyObject *list; | ||||
|     Py_ssize_t i; | ||||
|     PyObject *error_type, *error_value, *error_traceback; | ||||
| 
 | ||||
|     PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|     dict = PyThreadState_GetDict(); | ||||
|     if (dict == NULL) | ||||
|  | @ -2213,7 +2208,7 @@ Py_ReprLeave(PyObject *obj) | |||
| 
 | ||||
| finally: | ||||
|     /* ignore exceptions because there is no way to report them. */ | ||||
|     PyErr_Restore(error_type, error_value, error_traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
| } | ||||
| 
 | ||||
| /* Trashcan support. */ | ||||
|  |  | |||
|  | @ -1556,10 +1556,9 @@ _PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value, | |||
|         res = _odict_add_new_node((PyODictObject *)od, key, hash); | ||||
|         if (res < 0) { | ||||
|             /* Revert setting the value on the dict */ | ||||
|             PyObject *exc, *val, *tb; | ||||
|             PyErr_Fetch(&exc, &val, &tb); | ||||
|             PyObject *exc = PyErr_GetRaisedException(); | ||||
|             (void) _PyDict_DelItem_KnownHash(od, key, hash); | ||||
|             _PyErr_ChainExceptions(exc, val, tb); | ||||
|             _PyErr_ChainExceptions1(exc); | ||||
|         } | ||||
|     } | ||||
|     return res; | ||||
|  |  | |||
|  | @ -4397,10 +4397,9 @@ static void | |||
| type_dealloc_common(PyTypeObject *type) | ||||
| { | ||||
|     if (type->tp_bases != NULL) { | ||||
|         PyObject *tp, *val, *tb; | ||||
|         PyErr_Fetch(&tp, &val, &tb); | ||||
|         PyObject *exc = PyErr_GetRaisedException(); | ||||
|         remove_all_subclasses(type, type->tp_bases); | ||||
|         PyErr_Restore(tp, val, tb); | ||||
|         PyErr_SetRaisedException(exc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -8445,10 +8444,9 @@ slot_tp_finalize(PyObject *self) | |||
| { | ||||
|     int unbound; | ||||
|     PyObject *del, *res; | ||||
|     PyObject *error_type, *error_value, *error_traceback; | ||||
| 
 | ||||
|     /* Save the current exception, if any. */ | ||||
|     PyErr_Fetch(&error_type, &error_value, &error_traceback); | ||||
|     PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|     /* Execute __del__ method, if any. */ | ||||
|     del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound); | ||||
|  | @ -8462,7 +8460,7 @@ slot_tp_finalize(PyObject *self) | |||
|     } | ||||
| 
 | ||||
|     /* Restore the saved exception. */ | ||||
|     PyErr_Restore(error_type, error_value, error_traceback); | ||||
|     PyErr_SetRaisedException(exc); | ||||
| } | ||||
| 
 | ||||
| static PyObject * | ||||
|  |  | |||
|  | @ -959,9 +959,8 @@ PyObject_ClearWeakRefs(PyObject *object) | |||
|     if (*list != NULL) { | ||||
|         PyWeakReference *current = *list; | ||||
|         Py_ssize_t count = _PyWeakref_GetWeakrefCount(current); | ||||
|         PyObject *err_type, *err_value, *err_tb; | ||||
|         PyObject *exc = PyErr_GetRaisedException(); | ||||
| 
 | ||||
|         PyErr_Fetch(&err_type, &err_value, &err_tb); | ||||
|         if (count == 1) { | ||||
|             PyObject *callback = current->wr_callback; | ||||
| 
 | ||||
|  | @ -980,7 +979,7 @@ PyObject_ClearWeakRefs(PyObject *object) | |||
| 
 | ||||
|             tuple = PyTuple_New(count * 2); | ||||
|             if (tuple == NULL) { | ||||
|                 _PyErr_ChainExceptions(err_type, err_value, err_tb); | ||||
|                 _PyErr_ChainExceptions1(exc); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|  | @ -1010,7 +1009,7 @@ PyObject_ClearWeakRefs(PyObject *object) | |||
|             Py_DECREF(tuple); | ||||
|         } | ||||
|         assert(!PyErr_Occurred()); | ||||
|         PyErr_Restore(err_type, err_value, err_tb); | ||||
|         PyErr_SetRaisedException(exc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Irit Katriel
						Irit Katriel