1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use pyo3::ffi;
use pyo3::prelude::*;
// This test mucks around with sys.modules, so run it separately to prevent it
// from potentially corrupting the state of the python interpreter used in other
// tests.
#[test]
fn err_debug_unformattable() {
// Debug representation should be like the following (without the newlines):
// PyErr {
// type: <class 'Exception'>,
// value: Exception('banana'),
// traceback: Some(\"<unformattable <traceback object at 0x...>>\")
// }
Python::attach(|py| {
// PyTracebackMethods::format uses io.StringIO. Mock it out to trigger a
// formatting failure:
// TypeError: 'Mock' object cannot be cast as 'str'
let err = py
.run(
ffi::c_str!(
r#"
import io, sys, unittest.mock
sys.modules['orig_io'] = sys.modules['io']
sys.modules['io'] = unittest.mock.Mock()
raise Exception('banana')"#
),
None,
None,
)
.expect_err("raising should have given us an error");
let debug_str = format!("{err:?}");
assert!(debug_str.starts_with("PyErr { "));
assert!(debug_str.ends_with(" }"));
// Strip "PyErr { " and " }". Split into 3 substrings to separate type,
// value, and traceback while not splitting the string within traceback.
let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].splitn(3, ", ");
assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
assert_eq!(fields.next().unwrap(), "value: Exception('banana')");
let traceback = fields.next().unwrap();
assert!(
traceback.starts_with("traceback: Some(\"<unformattable <traceback object at 0x"),
"assertion failed, actual traceback str: {traceback:?}"
);
assert!(fields.next().is_none());
py.run(
ffi::c_str!(
r#"
import io, sys, unittest.mock
sys.modules['io'] = sys.modules['orig_io']
del sys.modules['orig_io']
"#
),
None,
None,
)
.unwrap();
});
}