39
39
40
40
import sys
41
41
import warnings
42
- from . import CPyExtTestCase , CPyExtFunction , CPyExtFunctionOutVars , CPyExtFunctionVoid , unhandled_error_compare , GRAALPYTHON
42
+
43
+ from . import CPyExtTestCase , CPyExtFunction , CPyExtFunctionVoid , unhandled_error_compare , \
44
+ CPyExtType
45
+
43
46
__dir__ = __file__ .rpartition ("/" )[0 ]
44
47
45
48
@@ -73,25 +76,27 @@ def _reference_restore(args):
73
76
74
77
def compare_restore_result (x , y ):
75
78
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 )
79
+ isinstance (x , BaseException ) and
80
+ type (x ) == type (y ) and
81
+ # Compare str because different exceptions are not equal
82
+ str (x .args ) == str (y .args ) and
83
+ (x .__traceback__ is example_traceback ) == (y .__traceback__ is example_traceback )
81
84
)
82
85
83
86
84
87
def _new_ex_result_check (x , y ):
85
88
name = y [0 ]
86
89
base = y [1 ]
87
90
return name in str (x ) and issubclass (x , base )
88
-
91
+
92
+
89
93
def _new_ex_with_doc_result_check (x , y ):
90
94
name = y [0 ]
91
95
doc = y [1 ]
92
96
base = y [2 ]
93
97
return name in str (x ) and issubclass (x , base ) and x .__doc__ == doc
94
98
99
+
95
100
def _reference_setnone (args ):
96
101
raise args [0 ]()
97
102
@@ -145,8 +150,10 @@ def _reference_fetch_tb_f_back(args):
145
150
def _raise_exception ():
146
151
def inner ():
147
152
raise OSError
153
+
148
154
def reraise (e ):
149
155
raise e
156
+
150
157
try :
151
158
inner ()
152
159
except Exception as e :
@@ -193,6 +200,16 @@ def raise_erorr():
193
200
194
201
assert example_traceback
195
202
203
+ ExceptionSubclass = CPyExtType (
204
+ "ExceptionSubclass" ,
205
+ '' ,
206
+ struct_base = 'PyBaseExceptionObject base' ,
207
+ tp_new = '0' ,
208
+ tp_alloc = '0' ,
209
+ tp_free = '0' ,
210
+ ready_code = 'ExceptionSubclassType.tp_base = (PyTypeObject*)PyExc_Exception;'
211
+ )
212
+
196
213
197
214
class TestPyErr (CPyExtTestCase ):
198
215
@@ -206,14 +223,15 @@ def compile_module(self, name):
206
223
(ValueError , "hello" ),
207
224
(TypeError , "world" ),
208
225
(KeyError , "key" ),
226
+ (ExceptionSubclass , "hello" )
209
227
),
210
228
resultspec = "O" ,
211
229
argspec = 'Os' ,
212
230
arguments = ["PyObject* v" , "char* msg" ],
213
231
resultval = "NULL" ,
214
232
cmpfunc = unhandled_error_compare
215
233
)
216
-
234
+
217
235
test_PyErr_NewException = CPyExtFunction (
218
236
lambda args : args ,
219
237
lambda : (
@@ -224,7 +242,7 @@ def compile_module(self, name):
224
242
arguments = ["char* name" , "PyObject* base" , "PyObject* dict" ],
225
243
cmpfunc = _new_ex_result_check
226
244
)
227
-
245
+
228
246
test_PyErr_NewExceptionWithDoc = CPyExtFunction (
229
247
lambda args : args ,
230
248
lambda : (
@@ -235,7 +253,7 @@ def compile_module(self, name):
235
253
arguments = ["char* name" , "char* doc" , "PyObject* base" , "PyObject* dict" ],
236
254
cmpfunc = _new_ex_with_doc_result_check
237
255
)
238
-
256
+
239
257
test_PyErr_SetObject = CPyExtFunctionVoid (
240
258
_reference_setobject ,
241
259
lambda : (
@@ -246,6 +264,9 @@ def compile_module(self, name):
246
264
(KeyError , "key" ),
247
265
(RuntimeError , ValueError ()),
248
266
(OSError , (2 , "error" )),
267
+ (ExceptionSubclass , None ),
268
+ (ExceptionSubclass , "hello" ),
269
+ (ExceptionSubclass , (1 , 2 )),
249
270
),
250
271
resultspec = "O" ,
251
272
argspec = 'OO' ,
@@ -260,6 +281,7 @@ def compile_module(self, name):
260
281
(ValueError ,),
261
282
(TypeError ,),
262
283
(KeyError ,),
284
+ (ExceptionSubclass ,),
263
285
),
264
286
resultspec = "O" ,
265
287
argspec = 'O' ,
@@ -275,6 +297,7 @@ def compile_module(self, name):
275
297
(TypeError , "world %S %S" , "" , "" ),
276
298
(KeyError , "key %S %S" , "" , "" ),
277
299
(KeyError , "unknown key: %S %S" , "some_key" , "" ),
300
+ (ExceptionSubclass , "hello %S %S" , "beautiful" , "world" ),
278
301
),
279
302
resultspec = "O" ,
280
303
argspec = 'OsOO' ,
@@ -319,15 +342,17 @@ def compile_module(self, name):
319
342
test_PyErr_GivenExceptionMatches = CPyExtFunction (
320
343
_reference_givenexceptionmatches ,
321
344
lambda : (
322
- (ValueError (), ValueError ),
323
- (ValueError (), BaseException ),
324
- (ValueError (), KeyError ),
325
- (ValueError (), (KeyError , SystemError , OverflowError )),
326
- (ValueError (), Dummy ),
327
- (ValueError (), Dummy ()),
328
- (Dummy (), Dummy ()),
329
- (Dummy (), Dummy ),
330
- (Dummy (), KeyError ),
345
+ (ValueError , ValueError ),
346
+ (ValueError , BaseException ),
347
+ (ValueError , KeyError ),
348
+ (ValueError , (KeyError , SystemError , OverflowError )),
349
+ (ValueError , (KeyError , SystemError , ValueError )),
350
+ (ValueError , Dummy ),
351
+ (ValueError , Dummy ()),
352
+ (ValueError , ExceptionSubclass ),
353
+ (ExceptionSubclass , ExceptionSubclass ),
354
+ (ExceptionSubclass , Exception ),
355
+ (ExceptionSubclass , ValueError ),
331
356
),
332
357
resultspec = "i" ,
333
358
argspec = 'OO' ,
@@ -394,8 +419,14 @@ def compile_module(self, name):
394
419
(ValueError , BaseException ),
395
420
(ValueError , KeyError ),
396
421
(ValueError , (KeyError , SystemError , OverflowError )),
422
+ (ValueError , (KeyError , SystemError , ValueError )),
397
423
(ValueError , Dummy ),
398
424
(ValueError , Dummy ()),
425
+ (ValueError , ExceptionSubclass ),
426
+ (ExceptionSubclass , ExceptionSubclass ),
427
+ (ExceptionSubclass , Exception ),
428
+ (ExceptionSubclass , ValueError ),
429
+
399
430
),
400
431
code = """int wrap_PyErr_ExceptionMatches(PyObject* err, PyObject* exc) {
401
432
int res;
@@ -431,7 +462,8 @@ def compile_module(self, name):
431
462
),
432
463
resultspec = "O" ,
433
464
argspec = 'OOOiOO' ,
434
- arguments = ["PyObject* category" , "PyObject* text" , "PyObject* filename_str" , "int lineno" , "PyObject* module_str" , "PyObject* registry" ],
465
+ arguments = ["PyObject* category" , "PyObject* text" , "PyObject* filename_str" , "int lineno" ,
466
+ "PyObject* module_str" , "PyObject* registry" ],
435
467
stderr_validator = lambda args , stderr : "UserWarning: custom warning" in stderr ,
436
468
cmpfunc = unhandled_error_compare
437
469
)
@@ -443,7 +475,8 @@ def compile_module(self, name):
443
475
),
444
476
resultspec = "O" ,
445
477
argspec = 'OssisO' ,
446
- arguments = ["PyObject* category" , "const char* text" , "const char* filename_str" , "int lineno" , "const char* module_str" , "PyObject* registry" ],
478
+ arguments = ["PyObject* category" , "const char* text" , "const char* filename_str" , "int lineno" ,
479
+ "const char* module_str" , "PyObject* registry" ],
447
480
stderr_validator = lambda args , stderr : "UserWarning: custom warning" in stderr ,
448
481
cmpfunc = unhandled_error_compare
449
482
)
@@ -496,7 +529,8 @@ def compile_module(self, name):
496
529
argspec = 'O' ,
497
530
arguments = ["PyObject* obj" ],
498
531
callfunction = "wrap_PyErr_WriteUnraisableMsg" ,
499
- stderr_validator = lambda args , stderr : "RuntimeError: unraisable_exception" in stderr and "Exception ignored in my function:" in stderr ,
532
+ stderr_validator = lambda args ,
533
+ stderr : "RuntimeError: unraisable_exception" in stderr and "Exception ignored in my function:" in stderr ,
500
534
cmpfunc = unhandled_error_compare
501
535
)
502
536
@@ -511,6 +545,7 @@ def compile_module(self, name):
511
545
(RuntimeError , ValueError (), None ),
512
546
(OSError , (2 , "error" ), None ),
513
547
(NameError , None , example_traceback ),
548
+ (ExceptionSubclass , "error" , None ),
514
549
),
515
550
# Note on CPython all the exception creation happens not in PyErr_Restore, but when leaving the function and
516
551
# normalizing the exception in the caller. So this really test both of these mechanisms together.
@@ -618,3 +653,119 @@ def compile_module(self, name):
618
653
# callfunction="wrap_PyErr_Fetch_tb_f_back",
619
654
# cmpfunc=compare_frame_f_back_chain,
620
655
# )
656
+
657
+
658
+ def raise_native_exception ():
659
+ raise ExceptionSubclass (1 )
660
+
661
+
662
+ class TestNativeExceptionSubclass :
663
+ def test_raise_type (self ):
664
+ try :
665
+ raise ExceptionSubclass
666
+ except ExceptionSubclass as e :
667
+ assert e .args == ()
668
+ else :
669
+ assert False
670
+
671
+ def test_raise_instance (self ):
672
+ try :
673
+ raise ExceptionSubclass (1 )
674
+ except ExceptionSubclass as e :
675
+ assert e .args == (1 ,)
676
+ else :
677
+ assert False
678
+
679
+ def test_traceback (self ):
680
+ try :
681
+ try :
682
+ raise_native_exception ()
683
+ finally :
684
+ assert True # no-op, just to test implicit reraising
685
+ except ExceptionSubclass as e :
686
+ tb = e .__traceback__
687
+ else :
688
+ assert False
689
+ assert tb
690
+ assert tb .tb_frame .f_code is TestNativeExceptionSubclass .test_traceback .__code__
691
+ assert tb .tb_next
692
+ assert tb .tb_next .tb_frame .f_code is raise_native_exception .__code__
693
+ assert tb .tb_next .tb_lineno == raise_native_exception .__code__ .co_firstlineno + 1
694
+ e2 = ExceptionSubclass ()
695
+ assert e2 .__traceback__ is None
696
+ e2 .__traceback__ = tb
697
+ assert e2 .__traceback__ is tb
698
+ e2 = ExceptionSubclass ()
699
+ e2 = e2 .with_traceback (tb )
700
+ assert e2 .__traceback__ is tb
701
+
702
+ def test_chaining (self ):
703
+ inner_e = ExceptionSubclass ()
704
+ outer_e = ExceptionSubclass ()
705
+ try :
706
+ try :
707
+ raise inner_e
708
+ except Exception :
709
+ raise outer_e
710
+ except Exception :
711
+ pass
712
+ assert outer_e .__context__ is inner_e
713
+ assert outer_e .__suppress_context__ is False
714
+
715
+ def test_raise_from (self ):
716
+ inner_e = ExceptionSubclass ()
717
+ outer_e = ExceptionSubclass ()
718
+ try :
719
+ raise outer_e from inner_e
720
+ except Exception :
721
+ pass
722
+ assert outer_e .__cause__ is inner_e
723
+ assert outer_e .__suppress_context__ is True
724
+
725
+ def test_cause (self ):
726
+ e = ExceptionSubclass ()
727
+ e2 = ExceptionSubclass ()
728
+ assert e .__suppress_context__ is False
729
+ e .__cause__ = e2
730
+ assert e .__cause__ is e2
731
+ assert e .__suppress_context__ is True
732
+ e .__suppress_context__ = False
733
+ assert e .__suppress_context__ is False
734
+
735
+ def test_context (self ):
736
+ e = ExceptionSubclass ()
737
+ e2 = ExceptionSubclass ()
738
+ e .__context__ = e2
739
+ assert e .__context__ is e2
740
+
741
+ def test_args (self ):
742
+ e = ExceptionSubclass (1 , 2 , 3 )
743
+ assert e .args == (1 , 2 , 3 )
744
+ e .args = ("foo" ,)
745
+ assert e .args == ("foo" ,)
746
+
747
+ def test_dict (self ):
748
+ e = ExceptionSubclass ()
749
+ e .asdf = 1
750
+ assert e .asdf == 1
751
+ assert e .__dict__ == {'asdf' : 1 }
752
+ e .__dict__ = {'foo' : 'bar' }
753
+ assert e .__dict__ == {'foo' : 'bar' }
754
+
755
+ def test_reduce (self ):
756
+ e = ExceptionSubclass (1 )
757
+ e .asdf = 1
758
+ assert e .__reduce__ () == (ExceptionSubclass , (1 ,), {'asdf' : 1 })
759
+
760
+ def test_repr_str (self ):
761
+ assert repr (ExceptionSubclass ()) == "ExceptionSubclass()"
762
+ assert repr (ExceptionSubclass (1 )) == "ExceptionSubclass(1)"
763
+ assert repr (ExceptionSubclass (1 , 2 )) == "ExceptionSubclass(1, 2)"
764
+ assert str (ExceptionSubclass ()) == ""
765
+ assert str (ExceptionSubclass (1 )) == "1"
766
+ assert str (ExceptionSubclass (1 , 2 )) == "(1, 2)"
767
+
768
+ def test_setstate (self ):
769
+ e = ExceptionSubclass ()
770
+ e .__setstate__ ({'foo' : 'bar' })
771
+ assert e .foo == 'bar'
0 commit comments