Skip to content

Commit 416914c

Browse files
Add PyErr::set_context, PyErr::context
1 parent 68381ae commit 416914c

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

newsfragments/5887.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `PyErr::set_context`, `PyErr::context`, `ffi::PyErr_GetHandledException` and `ffi::PyErr_SetHandledException`.

pyo3-ffi/src/pyerrors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ extern_libpython! {
5353
pub fn PyErr_GetRaisedException() -> *mut PyObject;
5454
#[cfg(Py_3_12)]
5555
pub fn PyErr_SetRaisedException(exc: *mut PyObject);
56+
#[cfg(Py_3_11)]
57+
#[cfg_attr(PyPy, link_name = "PyPyErr_GetHandledException")]
58+
pub fn PyErr_GetHandledException() -> *mut PyObject;
59+
#[cfg(Py_3_11)]
60+
#[cfg_attr(PyPy, link_name = "PyPyErr_SetHandledException")]
61+
pub fn PyErr_SetHandledException(exc: *mut PyObject);
5662
#[cfg_attr(PyPy, link_name = "PyPyException_SetTraceback")]
5763
pub fn PyException_SetTraceback(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int;
5864
#[cfg_attr(PyPy, link_name = "PyPyException_GetTraceback")]

src/err/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,29 @@ impl PyErr {
604604
}
605605
}
606606

607+
/// Return the context (either an exception instance, or None, set by an implicit exception
608+
/// during handling of another exception) associated with the exception, as accessible from
609+
/// Python through `__context__`.
610+
pub fn context(&self, py: Python<'_>) -> Option<PyErr> {
611+
unsafe {
612+
ffi::PyException_GetContext(self.value(py).as_ptr())
613+
.assume_owned_or_opt(py)
614+
.map(Self::from_value)
615+
}
616+
}
617+
618+
/// Set the context associated with the exception, pass `None` to clear it.
619+
pub fn set_context(&self, py: Python<'_>, context: Option<PyErr>) {
620+
let value = self.value(py);
621+
let context = context.map(|err| err.into_value(py));
622+
unsafe {
623+
ffi::PyException_SetContext(
624+
value.as_ptr(),
625+
context.map_or(std::ptr::null_mut(), Py::into_ptr),
626+
);
627+
}
628+
}
629+
607630
/// Equivalent to calling `add_note` on the exception in Python.
608631
#[cfg(Py_3_11)]
609632
pub fn add_note<N: for<'py> IntoPyObject<'py, Target = PyString>>(
@@ -1027,4 +1050,19 @@ mod tests {
10271050
);
10281051
});
10291052
}
1053+
1054+
#[test]
1055+
fn test_set_context() {
1056+
Python::attach(|py| {
1057+
let err = PyErr::new::<PyValueError, _>("original error");
1058+
assert!(err.context(py).is_none());
1059+
1060+
let context = PyErr::new::<PyTypeError, _>("context error");
1061+
err.set_context(py, Some(context));
1062+
assert!(err.context(py).unwrap().is_instance_of::<PyTypeError>(py));
1063+
1064+
err.set_context(py, None);
1065+
assert!(err.context(py).is_none());
1066+
})
1067+
}
10301068
}

0 commit comments

Comments
 (0)