| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,14 +30,6 @@ typedef struct _frame { | |||
| 30 | 30 | char f_trace_lines; /* Emit per-line trace events? */ | |
| 31 | 31 | char f_trace_opcodes; /* Emit per-opcode trace events? */ | |
| 32 | 32 | ||
| 33 | - /* In a generator, we need to be able to swap between the exception | ||
| 34 | - state inside the generator and the exception state of the calling | ||
| 35 | - frame (which shouldn't be impacted when the generator "yields" | ||
| 36 | - from an except handler). | ||
| 37 | - These three fields exist exactly for that, and are unused for | ||
| 38 | - non-generator frames. See the save_exc_state and swap_exc_state | ||
| 39 | - functions in ceval.c for details of their use. */ | ||
| 40 | - PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; | ||
| 41 | 33 | /* Borrowed reference to a generator, or NULL */ | |
| 42 | 34 | PyObject *f_gen; | |
| 43 | 35 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,7 +25,8 @@ struct _frame; /* Avoid including frameobject.h */ | |||
| 25 | 25 | /* Name of the generator. */ \ | |
| 26 | 26 | PyObject *prefix##_name; \ | |
| 27 | 27 | /* Qualified name of the generator. */ \ | |
| 28 | - PyObject *prefix##_qualname; | ||
| 28 | + PyObject *prefix##_qualname; \ | ||
| 29 | + _PyErr_StackItem prefix##_exc_state; | ||
| 29 | 30 | ||
| 30 | 31 | typedef struct { | |
| 31 | 32 | /* The gi_ prefix is intended to remind of generator-iterator. */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -78,6 +78,7 @@ PyAPI_FUNC(void) PyErr_SetNone(PyObject *); | |||
| 78 | 78 | PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *); | |
| 79 | 79 | #ifndef Py_LIMITED_API | |
| 80 | 80 | PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *); | |
| 81 | + _PyErr_StackItem *_PyErr_GetTopmostException(PyThreadState *tstate); | ||
| 81 | 82 | #endif | |
| 82 | 83 | PyAPI_FUNC(void) PyErr_SetString( | |
| 83 | 84 | PyObject *exception, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -123,6 +123,21 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); | |||
| 123 | 123 | #ifdef Py_LIMITED_API | |
| 124 | 124 | typedef struct _ts PyThreadState; | |
| 125 | 125 | #else | |
| 126 | + | ||
| 127 | + typedef struct _err_stackitem { | ||
| 128 | + /* This struct represents an entry on the exception stack, which is a | ||
| 129 | + * per-coroutine state. (Coroutine in the computer science sense, | ||
| 130 | + * including the thread and generators). | ||
| 131 | + * This ensures that the exception state is not impacted by "yields" | ||
| 132 | + * from an except handler. | ||
| 133 | + */ | ||
| 134 | + PyObject *exc_type, *exc_value, *exc_traceback; | ||
| 135 | + | ||
| 136 | + struct _err_stackitem *previous_item; | ||
| 137 | + | ||
| 138 | + } _PyErr_StackItem; | ||
| 139 | + | ||
| 140 | + | ||
| 126 | 141 | typedef struct _ts { | |
| 127 | 142 | /* See Python/ceval.c for comments explaining most fields */ | |
| 128 | 143 | ||
@@ -147,13 +162,19 @@ typedef struct _ts { | |||
| 147 | 162 | PyObject *c_profileobj; | |
| 148 | 163 | PyObject *c_traceobj; | |
| 149 | 164 | ||
| 165 | + /* The exception currently being raised */ | ||
| 150 | 166 | PyObject *curexc_type; | |
| 151 | 167 | PyObject *curexc_value; | |
| 152 | 168 | PyObject *curexc_traceback; | |
| 153 | 169 | ||
| 154 | - PyObject *exc_type; | ||
| 155 | - PyObject *exc_value; | ||
| 156 | - PyObject *exc_traceback; | ||
| 170 | + /* The exception currently being handled, if no coroutines/generators | ||
| 171 | + * are present. Always last element on the stack referred to be exc_info. | ||
| 172 | + */ | ||
| 173 | + _PyErr_StackItem exc_state; | ||
| 174 | + | ||
| 175 | + /* Pointer to the top of the stack of the exceptions currently | ||
| 176 | + * being handled */ | ||
| 177 | + _PyErr_StackItem *exc_info; | ||
| 157 | 178 | ||
| 158 | 179 | PyObject *dict; /* Stores per-thread state */ | |
| 159 | 180 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1097,6 +1097,62 @@ def test_unhandled(self): | |||
| 1097 | 1097 | self.assertIn("test message", report) | |
| 1098 | 1098 | self.assertTrue(report.endswith("\n")) | |
| 1099 | 1099 | ||
| 1100 | + def test_yield_in_nested_try_excepts(self): | ||
| 1101 | + #Issue #25612 | ||
| 1102 | + class MainError(Exception): | ||
| 1103 | + pass | ||
| 1104 | + | ||
| 1105 | + class SubError(Exception): | ||
| 1106 | + pass | ||
| 1107 | + | ||
| 1108 | + def main(): | ||
| 1109 | + try: | ||
| 1110 | + raise MainError() | ||
| 1111 | + except MainError: | ||
| 1112 | + try: | ||
| 1113 | + yield | ||
| 1114 | + except SubError: | ||
| 1115 | + pass | ||
| 1116 | + raise | ||
| 1117 | + | ||
| 1118 | + coro = main() | ||
| 1119 | + coro.send(None) | ||
| 1120 | + with self.assertRaises(MainError): | ||
| 1121 | + coro.throw(SubError()) | ||
| 1122 | + | ||
| 1123 | + def test_generator_doesnt_retain_old_exc2(self): | ||
| 1124 | + #Issue 28884#msg282532 | ||
| 1125 | + def g(): | ||
| 1126 | + try: | ||
| 1127 | + raise ValueError | ||
| 1128 | + except ValueError: | ||
| 1129 | + yield 1 | ||
| 1130 | + self.assertEqual(sys.exc_info(), (None, None, None)) | ||
| 1131 | + yield 2 | ||
| 1132 | + | ||
| 1133 | + gen = g() | ||
| 1134 | + | ||
| 1135 | + try: | ||
| 1136 | + raise IndexError | ||
| 1137 | + except IndexError: | ||
| 1138 | + self.assertEqual(next(gen), 1) | ||
| 1139 | + self.assertEqual(next(gen), 2) | ||
| 1140 | + | ||
| 1141 | + def test_raise_in_generator(self): | ||
| 1142 | + #Issue 25612#msg304117 | ||
| 1143 | + def g(): | ||
| 1144 | + yield 1 | ||
| 1145 | + raise | ||
| 1146 | + yield 2 | ||
| 1147 | + | ||
| 1148 | + with self.assertRaises(ZeroDivisionError): | ||
| 1149 | + i = g() | ||
| 1150 | + try: | ||
| 1151 | + 1/0 | ||
| 1152 | + except: | ||
| 1153 | + next(i) | ||
| 1154 | + next(i) | ||
| 1155 | + | ||
| 1100 | 1156 | ||
| 1101 | 1157 | class ImportErrorTests(unittest.TestCase): | |
| 1102 | 1158 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -971,7 +971,7 @@ class C(object): pass | |||
| 971 | 971 | nfrees = len(x.f_code.co_freevars) | |
| 972 | 972 | extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ | |
| 973 | 973 | ncells + nfrees - 1 | |
| 974 | - check(x, vsize('8P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) | ||
| 974 | + check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) | ||
| 975 | 975 | # function | |
| 976 | 976 | def func(): pass | |
| 977 | 977 | check(func, size('12P')) | |
@@ -988,7 +988,7 @@ def bar(cls): | |||
| 988 | 988 | check(bar, size('PP')) | |
| 989 | 989 | # generator | |
| 990 | 990 | def get_gen(): yield 1 | |
| 991 | - check(get_gen(), size('Pb2PPP')) | ||
| 991 | + check(get_gen(), size('Pb2PPP4P')) | ||
| 992 | 992 | # iterator | |
| 993 | 993 | check(iter('abc'), size('lP')) | |
| 994 | 994 | # callable-iterator | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Move the current exception state from the frame object to the co-routine. | ||
| 2 | + This simplifies the interpreter and fixes a couple of obscure bugs caused by | ||
| 3 | + having swap exception state when entering or exiting a generator. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -379,8 +379,7 @@ static PyGetSetDef frame_getsetlist[] = { | |||
| 379 | 379 | ||
| 380 | 380 | * ob_type, ob_size, f_code, f_valuestack; | |
| 381 | 381 | ||
| 382 | - * f_locals, f_trace, | ||
| 383 | - f_exc_type, f_exc_value, f_exc_traceback are NULL; | ||
| 382 | + * f_locals, f_trace are NULL; | ||
| 384 | 383 | ||
| 385 | 384 | * f_localsplus does not require re-allocation and | |
| 386 | 385 | the local variables in f_localsplus are NULL. | |
@@ -438,9 +437,6 @@ frame_dealloc(PyFrameObject *f) | |||
| 438 | 437 | Py_DECREF(f->f_globals); | |
| 439 | 438 | Py_CLEAR(f->f_locals); | |
| 440 | 439 | Py_CLEAR(f->f_trace); | |
| 441 | - Py_CLEAR(f->f_exc_type); | ||
| 442 | - Py_CLEAR(f->f_exc_value); | ||
| 443 | - Py_CLEAR(f->f_exc_traceback); | ||
| 444 | 440 | ||
| 445 | 441 | co = f->f_code; | |
| 446 | 442 | if (co->co_zombieframe == NULL) | |
@@ -469,9 +465,6 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) | |||
| 469 | 465 | Py_VISIT(f->f_globals); | |
| 470 | 466 | Py_VISIT(f->f_locals); | |
| 471 | 467 | Py_VISIT(f->f_trace); | |
| 472 | - Py_VISIT(f->f_exc_type); | ||
| 473 | - Py_VISIT(f->f_exc_value); | ||
| 474 | - Py_VISIT(f->f_exc_traceback); | ||
| 475 | 468 | ||
| 476 | 469 | /* locals */ | |
| 477 | 470 | slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); | |
@@ -502,9 +495,6 @@ frame_tp_clear(PyFrameObject *f) | |||
| 502 | 495 | f->f_stacktop = NULL; | |
| 503 | 496 | f->f_executing = 0; | |
| 504 | 497 | ||
| 505 | - Py_CLEAR(f->f_exc_type); | ||
| 506 | - Py_CLEAR(f->f_exc_value); | ||
| 507 | - Py_CLEAR(f->f_exc_traceback); | ||
| 508 | 498 | Py_CLEAR(f->f_trace); | |
| 509 | 499 | ||
| 510 | 500 | /* locals */ | |
@@ -698,7 +688,6 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, | |||
| 698 | 688 | f->f_localsplus[i] = NULL; | |
| 699 | 689 | f->f_locals = NULL; | |
| 700 | 690 | f->f_trace = NULL; | |
| 701 | - f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; | ||
| 702 | 691 | } | |
| 703 | 692 | f->f_stacktop = f->f_valuestack; | |
| 704 | 693 | f->f_builtins = builtins; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,14 +16,23 @@ static char *NON_INIT_CORO_MSG = "can't send non-None value to a " | |||
| 16 | 16 | static char *ASYNC_GEN_IGNORED_EXIT_MSG = | |
| 17 | 17 | "async generator ignored GeneratorExit"; | |
| 18 | 18 | ||
| 19 | + static inline int | ||
| 20 | + exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) | ||
| 21 | + { | ||
| 22 | + Py_VISIT(exc_state->exc_type); | ||
| 23 | + Py_VISIT(exc_state->exc_value); | ||
| 24 | + Py_VISIT(exc_state->exc_traceback); | ||
| 25 | + return 0; | ||
| 26 | + } | ||
| 27 | + | ||
| 19 | 28 | static int | |
| 20 | 29 | gen_traverse(PyGenObject *gen, visitproc visit, void *arg) | |
| 21 | 30 | { | |
| 22 | 31 | Py_VISIT((PyObject *)gen->gi_frame); | |
| 23 | 32 | Py_VISIT(gen->gi_code); | |
| 24 | 33 | Py_VISIT(gen->gi_name); | |
| 25 | 34 | Py_VISIT(gen->gi_qualname); | |
| 26 | - return 0; | ||
| 35 | + return exc_state_traverse(&gen->gi_exc_state, visit, arg); | ||
| 27 | 36 | } | |
| 28 | 37 | ||
| 29 | 38 | void | |
@@ -87,6 +96,21 @@ _PyGen_Finalize(PyObject *self) | |||
| 87 | 96 | PyErr_Restore(error_type, error_value, error_traceback); | |
| 88 | 97 | } | |
| 89 | 98 | ||
| 99 | + static inline void | ||
| 100 | + exc_state_clear(_PyErr_StackItem *exc_state) | ||
| 101 | + { | ||
| 102 | + PyObject *t, *v, *tb; | ||
| 103 | + t = exc_state->exc_type; | ||
| 104 | + v = exc_state->exc_value; | ||
| 105 | + tb = exc_state->exc_traceback; | ||
| 106 | + exc_state->exc_type = NULL; | ||
| 107 | + exc_state->exc_value = NULL; | ||
| 108 | + exc_state->exc_traceback = NULL; | ||
| 109 | + Py_XDECREF(t); | ||
| 110 | + Py_XDECREF(v); | ||
| 111 | + Py_XDECREF(tb); | ||
| 112 | + } | ||
| 113 | + | ||
| 90 | 114 | static void | |
| 91 | 115 | gen_dealloc(PyGenObject *gen) | |
| 92 | 116 | { | |
@@ -116,6 +140,7 @@ gen_dealloc(PyGenObject *gen) | |||
| 116 | 140 | Py_CLEAR(gen->gi_code); | |
| 117 | 141 | Py_CLEAR(gen->gi_name); | |
| 118 | 142 | Py_CLEAR(gen->gi_qualname); | |
| 143 | + exc_state_clear(&gen->gi_exc_state); | ||
| 119 | 144 | PyObject_GC_Del(gen); | |
| 120 | 145 | } | |
| 121 | 146 | ||
@@ -187,7 +212,11 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) | |||
| 187 | 212 | f->f_back = tstate->frame; | |
| 188 | 213 | ||
| 189 | 214 | gen->gi_running = 1; | |
| 215 | + gen->gi_exc_state.previous_item = tstate->exc_info; | ||
| 216 | + tstate->exc_info = &gen->gi_exc_state; | ||
| 190 | 217 | result = PyEval_EvalFrameEx(f, exc); | |
| 218 | + tstate->exc_info = gen->gi_exc_state.previous_item; | ||
| 219 | + gen->gi_exc_state.previous_item = NULL; | ||
| 191 | 220 | gen->gi_running = 0; | |
| 192 | 221 | ||
| 193 | 222 | /* Don't keep the reference to f_back any longer than necessary. It | |
@@ -281,16 +310,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) | |||
| 281 | 310 | if (!result || f->f_stacktop == NULL) { | |
| 282 | 311 | /* generator can't be rerun, so release the frame */ | |
| 283 | 312 | /* first clean reference cycle through stored exception traceback */ | |
| 284 | - PyObject *t, *v, *tb; | ||
| 285 | - t = f->f_exc_type; | ||
| 286 | - v = f->f_exc_value; | ||
| 287 | - tb = f->f_exc_traceback; | ||
| 288 | - f->f_exc_type = NULL; | ||
| 289 | - f->f_exc_value = NULL; | ||
| 290 | - f->f_exc_traceback = NULL; | ||
| 291 | - Py_XDECREF(t); | ||
| 292 | - Py_XDECREF(v); | ||
| 293 | - Py_XDECREF(tb); | ||
| 313 | + exc_state_clear(&gen->gi_exc_state); | ||
| 294 | 314 | gen->gi_frame->f_gen = NULL; | |
| 295 | 315 | gen->gi_frame = NULL; | |
| 296 | 316 | Py_DECREF(f); | |
@@ -810,6 +830,10 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, | |||
| 810 | 830 | gen->gi_code = (PyObject *)(f->f_code); | |
| 811 | 831 | gen->gi_running = 0; | |
| 812 | 832 | gen->gi_weakreflist = NULL; | |
| 833 | + gen->gi_exc_state.exc_type = NULL; | ||
| 834 | + gen->gi_exc_state.exc_value = NULL; | ||
| 835 | + gen->gi_exc_state.exc_traceback = NULL; | ||
| 836 | + gen->gi_exc_state.previous_item = NULL; | ||
| 813 | 837 | if (name != NULL) | |
| 814 | 838 | gen->gi_name = name; | |
| 815 | 839 | else | |
| Back | FazBrowse Home | New Git URL |
0 commit comments