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