@@ -47,16 +47,38 @@ def _reference_setstring(args):
47
47
raise args [0 ](args [1 ])
48
48
49
49
50
- def _reference_setobject (args ):
51
- exc_type , value = args
50
+ def _normalize_exception (exc_type , value ):
52
51
if issubclass (exc_type , BaseException ):
53
52
if isinstance (value , exc_type ):
54
- raise value
53
+ return value
55
54
if value is None :
56
- raise exc_type
55
+ return exc_type ()
57
56
if isinstance (value , tuple ):
58
- raise exc_type (* value )
59
- raise exc_type (value )
57
+ return exc_type (* value )
58
+ return exc_type (value )
59
+
60
+
61
+ def _reference_setobject (args ):
62
+ exc_type , value = args
63
+ raise _normalize_exception (exc_type , value )
64
+
65
+
66
+ def _reference_restore (args ):
67
+ exc_type , value , tb = args
68
+ exc = _normalize_exception (exc_type , value )
69
+ if tb :
70
+ exc .__traceback__ = tb
71
+ raise exc
72
+
73
+
74
+ def compare_restore_result (x , y ):
75
+ return (
76
+ isinstance (x , BaseException ) and
77
+ type (x ) == type (y ) and
78
+ # Compare str because different exceptions are not equal
79
+ str (x .args ) == str (y .args ) and
80
+ (x .__traceback__ is example_traceback ) == (y .__traceback__ is example_traceback )
81
+ )
60
82
61
83
62
84
def _new_ex_result_check (x , y ):
@@ -158,6 +180,20 @@ class Dummy:
158
180
pass
159
181
160
182
183
+ def raise_erorr ():
184
+ raise NameError
185
+
186
+
187
+ try :
188
+ raise_erorr ()
189
+ except NameError as e :
190
+ example_traceback = e .__traceback__
191
+ else :
192
+ assert False
193
+
194
+ assert example_traceback
195
+
196
+
161
197
class TestPyErr (CPyExtTestCase ):
162
198
163
199
def compile_module (self , name ):
@@ -464,6 +500,39 @@ def compile_module(self, name):
464
500
cmpfunc = unhandled_error_compare
465
501
)
466
502
503
+ test_PyErr_Restore = CPyExtFunctionVoid (
504
+ _reference_restore ,
505
+ lambda : (
506
+ (RuntimeError , None , None ),
507
+ (RuntimeError , RuntimeError ("error" ), None ),
508
+ (ValueError , "hello" , None ),
509
+ (TypeError , "world" , None ),
510
+ (KeyError , "key" , None ),
511
+ (RuntimeError , ValueError (), None ),
512
+ (OSError , (2 , "error" ), None ),
513
+ (NameError , None , example_traceback ),
514
+ ),
515
+ # Note on CPython all the exception creation happens not in PyErr_Restore, but when leaving the function and
516
+ # normalizing the exception in the caller. So this really test both of these mechanisms together.
517
+ code = """PyObject* wrap_PyErr_Restore(PyObject* typ, PyObject* val, PyObject* tb) {
518
+ if (typ == Py_None) typ = NULL;
519
+ if (val == Py_None) val = NULL;
520
+ if (tb == Py_None) tb = NULL;
521
+ Py_XINCREF(typ);
522
+ Py_XINCREF(val);
523
+ Py_XINCREF(tb);
524
+ PyErr_Restore(typ, val, tb);
525
+ return NULL;
526
+ }
527
+ """ ,
528
+ resultspec = "O" ,
529
+ argspec = 'OOO' ,
530
+ arguments = ["PyObject* typ" , "PyObject* val" , "PyObject* tb" ],
531
+ resultval = "NULL" ,
532
+ callfunction = "wrap_PyErr_Restore" ,
533
+ cmpfunc = compare_restore_result
534
+ )
535
+
467
536
test_PyErr_Fetch = CPyExtFunction (
468
537
_reference_fetch ,
469
538
lambda : (
0 commit comments