1
1
// Copyright (c) 2017-present PyO3 Project and Contributors
2
2
3
+ use crate :: panic:: PanicException ;
3
4
use crate :: type_object:: PyTypeObject ;
4
5
use crate :: types:: PyType ;
5
6
use crate :: { exceptions, ffi} ;
6
7
use crate :: {
7
- AsPyPointer , FromPy , IntoPy , IntoPyPointer , Py , PyAny , PyObject , Python , ToBorrowedObject ,
8
- ToPyObject ,
8
+ AsPyPointer , FromPy , FromPyPointer , IntoPy , IntoPyPointer , ObjectProtocol , Py , PyAny , PyObject ,
9
+ Python , ToBorrowedObject , ToPyObject ,
9
10
} ;
10
11
use libc:: c_int;
11
12
use std:: ffi:: CString ;
@@ -168,13 +169,33 @@ impl PyErr {
168
169
///
169
170
/// The error is cleared from the Python interpreter.
170
171
/// If no error is set, returns a `SystemError`.
171
- pub fn fetch ( _: Python ) -> PyErr {
172
+ ///
173
+ /// If the error fetched is a `PanicException` (which would have originated from a panic in a
174
+ /// pyo3 callback) then this function will resume the panic.
175
+ pub fn fetch ( py : Python ) -> PyErr {
172
176
unsafe {
173
177
let mut ptype: * mut ffi:: PyObject = std:: ptr:: null_mut ( ) ;
174
178
let mut pvalue: * mut ffi:: PyObject = std:: ptr:: null_mut ( ) ;
175
179
let mut ptraceback: * mut ffi:: PyObject = std:: ptr:: null_mut ( ) ;
176
180
ffi:: PyErr_Fetch ( & mut ptype, & mut pvalue, & mut ptraceback) ;
177
- PyErr :: new_from_ffi_tuple ( ptype, pvalue, ptraceback)
181
+
182
+ let err = PyErr :: new_from_ffi_tuple ( ptype, pvalue, ptraceback) ;
183
+
184
+ if ptype == PanicException :: type_object ( ) . as_ptr ( ) {
185
+ let msg: String = PyAny :: from_borrowed_ptr_or_opt ( py, pvalue)
186
+ . and_then ( |obj| obj. extract ( ) . ok ( ) )
187
+ . unwrap_or_else ( || String :: from ( "Unwrapped panic from Python code" ) ) ;
188
+
189
+ eprintln ! (
190
+ "--- PyO3 is resuming a panic after fetching a PanicException from Python. ---"
191
+ ) ;
192
+ eprintln ! ( "Python stack trace below:" ) ;
193
+ err. print ( py) ;
194
+
195
+ std:: panic:: resume_unwind ( Box :: new ( msg) )
196
+ }
197
+
198
+ err
178
199
}
179
200
}
180
201
@@ -564,6 +585,7 @@ pub fn error_on_minusone(py: Python, result: c_int) -> PyResult<()> {
564
585
#[ cfg( test) ]
565
586
mod tests {
566
587
use crate :: exceptions;
588
+ use crate :: panic:: PanicException ;
567
589
use crate :: { PyErr , Python } ;
568
590
569
591
#[ test]
@@ -575,4 +597,16 @@ mod tests {
575
597
assert ! ( PyErr :: occurred( py) ) ;
576
598
drop ( PyErr :: fetch ( py) ) ;
577
599
}
600
+
601
+ #[ test]
602
+ fn fetching_panic_exception_panics ( ) {
603
+ let gil = Python :: acquire_gil ( ) ;
604
+ let py = gil. python ( ) ;
605
+ let err: PyErr = PanicException :: py_err ( "new panic" ) ;
606
+ err. restore ( py) ;
607
+ assert ! ( PyErr :: occurred( py) ) ;
608
+ let started_unwind =
609
+ std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || PyErr :: fetch ( py) ) ) . is_err ( ) ;
610
+ assert ! ( started_unwind) ;
611
+ }
578
612
}
0 commit comments