Skip to content

Commit

Permalink
add PyErr::display
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jul 21, 2023
1 parent e75d773 commit cde9141
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 23 deletions.
1 change: 1 addition & 0 deletions newsfragments/3334.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `PyErr_Display` for all Python versions, and FFI symbol `PyErr_DisplayException` for Python 3.12.
3 changes: 3 additions & 0 deletions pyo3-ffi/src/pythonrun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ extern "C" {
pub fn PyErr_PrintEx(arg1: c_int);
#[cfg_attr(PyPy, link_name = "PyPyErr_Display")]
pub fn PyErr_Display(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject);

#[cfg(Py_3_12)]
pub fn PyErr_DisplayException(exc: *mut PyObject);
}

// skipped PyOS_InputHook
Expand Down
25 changes: 22 additions & 3 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,18 +431,37 @@ impl PyErr {
}

/// Prints a standard traceback to `sys.stderr`.
#[cfg(Py_3_12)]
pub fn display(&self, py: Python<'_>) {
unsafe { ffi::PyErr_DisplayException(self.value(py).as_ptr()) }
}

/// Prints a standard traceback to `sys.stderr`.
#[cfg(not(Py_3_12))]
pub fn display(&self, py: Python<'_>) {
unsafe {
ffi::PyErr_Display(
self.get_type(py).as_ptr(),
self.value(py).as_ptr(),
self.traceback(py)
.map_or(std::ptr::null_mut(), PyTraceback::as_ptr),
)
}
}

/// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
pub fn print(&self, py: Python<'_>) {
self.clone_ref(py).restore(py);
unsafe { ffi::PyErr_PrintEx(0) }
}

/// Prints a standard traceback to `sys.stderr`, and sets
/// `sys.last_{type,value,traceback}` attributes to this exception's data.
/// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
///
/// Additionally sets `sys.last_{type,value,traceback,exc}` attributes to this exception.
pub fn print_and_set_sys_last_vars(&self, py: Python<'_>) {
self.clone_ref(py).restore(py);
unsafe { ffi::PyErr_PrintEx(1) }
}

/// Returns true if the current exception matches the exception in `exc`.
///
/// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass.
Expand Down
16 changes: 8 additions & 8 deletions src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,20 +813,20 @@ mod tests {
let err: PyErr = gaierror::new_err(());
let socket = py
.import("socket")
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not import socket");

let d = PyDict::new(py);
d.set_item("socket", socket)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not setitem");

d.set_item("exc", err)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not setitem");

py.run("assert isinstance(exc, socket.gaierror)", None, Some(d))
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("assertion failed");
});
}
Expand All @@ -837,23 +837,23 @@ mod tests {
let err: PyErr = MessageError::new_err(());
let email = py
.import("email")
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not import email");

let d = PyDict::new(py);
d.set_item("email", email)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not setitem");
d.set_item("exc", err)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("could not setitem");

py.run(
"assert isinstance(exc, email.errors.MessageError)",
None,
Some(d),
)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("assertion failed");
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,7 @@ a = A()
Python::with_gil(|py| {
let v = py
.eval("...", None, None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap()
.to_object(py);

Expand Down
7 changes: 5 additions & 2 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ mod tests {
// Make sure builtin names are accessible
let v: i32 = py
.eval("min(1, 2)", None, None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap()
.extract()
.unwrap();
Expand Down Expand Up @@ -1158,7 +1158,10 @@ mod tests {
Python::with_gil(|py| {
assert_eq!(py.Ellipsis().to_string(), "Ellipsis");

let v = py.eval("...", None, None).map_err(|e| e.print(py)).unwrap();
let v = py
.eval("...", None, None)
.map_err(|e| e.display(py))
.unwrap();

assert!(v.eq(py.Ellipsis()).unwrap());
});
Expand Down
5 changes: 4 additions & 1 deletion src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,10 @@ class SimpleClass:
#[test]
fn test_is_ellipsis() {
Python::with_gil(|py| {
let v = py.eval("...", None, None).map_err(|e| e.print(py)).unwrap();
let v = py
.eval("...", None, None)
.map_err(|e| e.display(py))
.unwrap();

assert!(v.is_ellipsis());

Expand Down
2 changes: 1 addition & 1 deletion tests/test_append_to_inittab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ assert module_with_functions.foo() == 123
None,
None,
)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
})
}
4 changes: 2 additions & 2 deletions tests/test_class_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ fn new_with_two_args() {
let typeobj = py.get_type::<NewWithTwoArgs>();
let wrp = typeobj
.call((10, 20), None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
let obj = wrp.downcast::<PyCell<NewWithTwoArgs>>().unwrap();
let obj_ref = obj.borrow();
Expand Down Expand Up @@ -172,7 +172,7 @@ assert c.from_rust is False
let globals = PyModule::import(py, "__main__").unwrap().dict();
globals.set_item("SuperClass", super_cls).unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fn test_time_check() {
fn test_datetime_check() {
Python::with_gil(|py| {
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(py, "datetime", "2018, 1, 1, 13, 30, 15")
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
unsafe { PyDateTime_IMPORT() }

Expand Down
2 changes: 1 addition & 1 deletion tests/test_inheritance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn subclass() {
None,
Some(d),
)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}
Expand Down
6 changes: 3 additions & 3 deletions tests/test_proto_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ asyncio.run(main())
let globals = PyModule::import(py, "__main__").unwrap().dict();
globals.set_item("Once", once).unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}
Expand Down Expand Up @@ -746,7 +746,7 @@ asyncio.run(main())
.set_item("AsyncIterator", py.get_type::<AsyncIterator>())
.unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}
Expand Down Expand Up @@ -815,7 +815,7 @@ assert c.counter.count == 1
let globals = PyModule::import(py, "__main__").unwrap().dict();
globals.set_item("Counter", counter).unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}
Expand Down

0 comments on commit cde9141

Please sign in to comment.