Skip to content

Commit b727b3d

Browse files
use PyErr_SetRaisedException in raise_lazy
1 parent 42a35d9 commit b727b3d

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

pyo3-ffi/src/compat/py_3_9.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@ compat_function!(
1818
crate::PyObject_CallMethodObjArgs(obj, name, std::ptr::null_mut::<crate::PyObject>())
1919
}
2020
);
21+
22+
compat_function!(
23+
// Added to python in 3.9 but available in 3.8 using private vectorcall api
24+
originally_defined_for(all(Py_3_8, not(any(Py_LIMITED_API, PyPy))));
25+
26+
#[inline]
27+
pub unsafe fn PyObject_CallOneArg(func: *mut crate::PyObject, arg: *mut crate::PyObject) -> *mut crate::PyObject {
28+
crate::PyObject_CallFunctionObjArgs(func, arg, std::ptr::null_mut::<crate::PyObject>())
29+
}
30+
);

src/err/err_state.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,22 +383,42 @@ fn lazy_into_normalized_ffi_tuple(
383383
}
384384

385385
/// Raises a "lazy" exception state into the Python interpreter.
386-
///
387-
/// In principle this could be split in two; first a function to create an exception
388-
/// in a normalized state, and then a call to `PyErr_SetRaisedException` to raise it.
389-
///
390-
/// This would require either moving some logic from C to Rust, or requesting a new
391-
/// API in CPython.
392386
fn raise_lazy(py: Python<'_>, lazy: Box<PyErrStateLazyFn>) {
393387
let PyErrStateLazyFnOutput { ptype, pvalue } = lazy(py);
388+
394389
unsafe {
395390
if ffi::PyExceptionClass_Check(ptype.as_ptr()) == 0 {
396391
ffi::PyErr_SetString(
397392
PyTypeError::type_object_raw(py).cast(),
398393
c"exceptions must derive from BaseException".as_ptr(),
399-
)
394+
);
400395
} else {
401-
ffi::PyErr_SetObject(ptype.as_ptr(), pvalue.as_ptr())
396+
#[cfg(not(Py_3_12))]
397+
ffi::PyErr_SetObject(ptype.as_ptr(), pvalue.as_ptr());
398+
399+
#[cfg(Py_3_12)]
400+
{
401+
let exc = if ffi::PyExceptionInstance_Check(pvalue.as_ptr()) != 0 {
402+
// If it's already an exception instance, keep it as-is.
403+
ffi::Py_NewRef(pvalue.as_ptr())
404+
} else if pvalue.as_ptr() == ffi::Py_None() {
405+
// If the value is None, call the type with no arguments.
406+
ffi::PyObject_CallNoArgs(ptype.as_ptr())
407+
} else if ffi::PyTuple_Check(pvalue.as_ptr()) != 0 {
408+
// If the value is a tuple, unpack it as arguments to the type.
409+
ffi::PyObject_Call(ptype.as_ptr(), pvalue.as_ptr(), std::ptr::null_mut())
410+
} else {
411+
// Fallback: type(value)
412+
ffi::compat::PyObject_CallOneArg(ptype.as_ptr(), pvalue.as_ptr())
413+
};
414+
415+
if exc.is_null() {
416+
// Exception constructor raised an exception, so propagate that instead of the original one.
417+
return;
418+
}
419+
420+
ffi::PyErr_SetRaisedException(exc);
421+
}
402422
}
403423
}
404424
}

0 commit comments

Comments
 (0)