Merge pull request #3334 from davidhewitt/pyerr-display

add PyErr::display
This commit is contained in:
Adam Reichold 2023-07-25 05:56:21 +00:00 committed by GitHub
commit 8ce6c26c81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 51 additions and 22 deletions

View File

@ -0,0 +1 @@
Add `PyErr::Display` for all Python versions, and FFI symbol `PyErr_DisplayException` for Python 3.12.

View File

@ -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

View File

@ -431,13 +431,32 @@ impl PyErr {
}
/// Prints a standard traceback to `sys.stderr`.
pub fn display(&self, py: Python<'_>) {
#[cfg(Py_3_12)]
unsafe {
ffi::PyErr_DisplayException(self.value(py).as_ptr())
}
#[cfg(not(Py_3_12))]
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) }

View File

@ -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");
});
}
@ -837,15 +837,15 @@ 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(
@ -853,7 +853,7 @@ mod tests {
None,
Some(d),
)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.expect("assertion failed");
});
}

View File

@ -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);

View File

@ -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();
@ -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());
});

View File

@ -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());

View File

@ -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();
})
}

View File

@ -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();
@ -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();
});
}

View File

@ -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() }

View File

@ -26,7 +26,7 @@ fn subclass() {
None,
Some(d),
)
.map_err(|e| e.print(py))
.map_err(|e| e.display(py))
.unwrap();
});
}

View File

@ -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();
});
}
@ -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();
});
}
@ -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();
});
}