1 | use crate::{exceptions::PyTypeError, FromPyObject, PyAny, PyErr, PyResult, Python}; |
2 | |
3 | #[cold ] |
4 | pub fn failed_to_extract_enum( |
5 | py: Python<'_>, |
6 | type_name: &str, |
7 | variant_names: &[&str], |
8 | error_names: &[&str], |
9 | errors: &[PyErr], |
10 | ) -> PyErr { |
11 | // TODO maybe use ExceptionGroup on Python 3.11+ ? |
12 | let mut err_msg: String = format!( |
13 | "failed to extract enum {} (' {}')" , |
14 | type_name, |
15 | error_names.join(" | " ) |
16 | ); |
17 | for ((variant_name: &&str, error_name: &&str), error: &PyErr) in variant_names.iter().zip(error_names).zip(errors) { |
18 | use std::fmt::Write; |
19 | writeResult<(), Error>!( |
20 | &mut err_msg, |
21 | " \n- variant {variant_name} ( {error_name}): {error_msg}" , |
22 | variant_name = variant_name, |
23 | error_name = error_name, |
24 | error_msg = extract_traceback(py, error.clone_ref(py)), |
25 | ) |
26 | .unwrap(); |
27 | } |
28 | PyTypeError::new_err(args:err_msg) |
29 | } |
30 | |
31 | /// Flattens a chain of errors into a single string. |
32 | fn extract_traceback(py: Python<'_>, mut error: PyErr) -> String { |
33 | use std::fmt::Write; |
34 | |
35 | let mut error_msg: String = error.to_string(); |
36 | while let Some(cause: PyErr) = error.cause(py) { |
37 | write!(&mut error_msg, ", caused by {}" , cause).unwrap(); |
38 | error = cause |
39 | } |
40 | error_msg |
41 | } |
42 | |
43 | pub fn extract_struct_field<'py, T>( |
44 | obj: &'py PyAny, |
45 | struct_name: &str, |
46 | field_name: &str, |
47 | ) -> PyResult<T> |
48 | where |
49 | T: FromPyObject<'py>, |
50 | { |
51 | match obj.extract() { |
52 | Ok(value: T) => Ok(value), |
53 | Err(err: PyErr) => Err(failed_to_extract_struct_field( |
54 | obj.py(), |
55 | inner_err:err, |
56 | struct_name, |
57 | field_name, |
58 | )), |
59 | } |
60 | } |
61 | |
62 | pub fn extract_struct_field_with<'py, T>( |
63 | extractor: impl FnOnce(&'py PyAny) -> PyResult<T>, |
64 | obj: &'py PyAny, |
65 | struct_name: &str, |
66 | field_name: &str, |
67 | ) -> PyResult<T> { |
68 | match extractor(obj) { |
69 | Ok(value: T) => Ok(value), |
70 | Err(err: PyErr) => Err(failed_to_extract_struct_field( |
71 | obj.py(), |
72 | inner_err:err, |
73 | struct_name, |
74 | field_name, |
75 | )), |
76 | } |
77 | } |
78 | |
79 | #[cold ] |
80 | fn failed_to_extract_struct_field( |
81 | py: Python<'_>, |
82 | inner_err: PyErr, |
83 | struct_name: &str, |
84 | field_name: &str, |
85 | ) -> PyErr { |
86 | let new_err: PyErr = PyTypeError::new_err(args:format!( |
87 | "failed to extract field {}. {}" , |
88 | struct_name, field_name |
89 | )); |
90 | new_err.set_cause(py, ::std::option::Option::Some(inner_err)); |
91 | new_err |
92 | } |
93 | |
94 | pub fn extract_tuple_struct_field<'py, T>( |
95 | obj: &'py PyAny, |
96 | struct_name: &str, |
97 | index: usize, |
98 | ) -> PyResult<T> |
99 | where |
100 | T: FromPyObject<'py>, |
101 | { |
102 | match obj.extract() { |
103 | Ok(value: T) => Ok(value), |
104 | Err(err: PyErr) => Err(failed_to_extract_tuple_struct_field( |
105 | obj.py(), |
106 | inner_err:err, |
107 | struct_name, |
108 | index, |
109 | )), |
110 | } |
111 | } |
112 | |
113 | pub fn extract_tuple_struct_field_with<'py, T>( |
114 | extractor: impl FnOnce(&'py PyAny) -> PyResult<T>, |
115 | obj: &'py PyAny, |
116 | struct_name: &str, |
117 | index: usize, |
118 | ) -> PyResult<T> { |
119 | match extractor(obj) { |
120 | Ok(value: T) => Ok(value), |
121 | Err(err: PyErr) => Err(failed_to_extract_tuple_struct_field( |
122 | obj.py(), |
123 | inner_err:err, |
124 | struct_name, |
125 | index, |
126 | )), |
127 | } |
128 | } |
129 | |
130 | #[cold ] |
131 | fn failed_to_extract_tuple_struct_field( |
132 | py: Python<'_>, |
133 | inner_err: PyErr, |
134 | struct_name: &str, |
135 | index: usize, |
136 | ) -> PyErr { |
137 | let new_err: PyErr = |
138 | PyTypeError::new_err(args:format!("failed to extract field {}. {}" , struct_name, index)); |
139 | new_err.set_cause(py, ::std::option::Option::Some(inner_err)); |
140 | new_err |
141 | } |
142 | |