1use crate::types::any::PyAnyMethods;
2use crate::Bound;
3use crate::{exceptions::PyTypeError, FromPyObject, PyAny, PyErr, PyResult, Python};
4
5#[cold]
6pub 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 let mut err_msg = format!(
15 "failed to extract enum {} ('{}')",
16 type_name,
17 error_names.join(" | ")
18 );
19 for ((variant_name, error_name), error) in variant_names.iter().zip(error_names).zip(errors) {
20 use std::fmt::Write;
21 write!(
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(err_msg)
31}
32
33fn extract_traceback(py: Python<'_>, mut error: PyErr) -> String {
35 use std::fmt::Write;
36
37 let mut error_msg = error.to_string();
38 while let Some(cause) = error.cause(py) {
39 write!(&mut error_msg, ", caused by {}", cause).unwrap();
40 error = cause
41 }
42 error_msg
43}
44
45pub fn extract_struct_field<'py, T>(
46 obj: &Bound<'py, PyAny>,
47 struct_name: &str,
48 field_name: &str,
49) -> PyResult<T>
50where
51 T: FromPyObject<'py>,
52{
53 match obj.extract() {
54 Ok(value) => Ok(value),
55 Err(err) => Err(failed_to_extract_struct_field(
56 obj.py(),
57 err,
58 struct_name,
59 field_name,
60 )),
61 }
62}
63
64pub 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) => Ok(value),
72 Err(err) => Err(failed_to_extract_struct_field(
73 obj.py(),
74 err,
75 struct_name,
76 field_name,
77 )),
78 }
79}
80
81#[cold]
82fn 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 = PyTypeError::new_err(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
96pub fn extract_tuple_struct_field<'py, T>(
97 obj: &Bound<'py, PyAny>,
98 struct_name: &str,
99 index: usize,
100) -> PyResult<T>
101where
102 T: FromPyObject<'py>,
103{
104 match obj.extract() {
105 Ok(value) => Ok(value),
106 Err(err) => Err(failed_to_extract_tuple_struct_field(
107 obj.py(),
108 err,
109 struct_name,
110 index,
111 )),
112 }
113}
114
115pub 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) => Ok(value),
123 Err(err) => Err(failed_to_extract_tuple_struct_field(
124 obj.py(),
125 err,
126 struct_name,
127 index,
128 )),
129 }
130}
131
132#[cold]
133fn 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 =
140 PyTypeError::new_err(format!("failed to extract field {}.{}", struct_name, index));
141 new_err.set_cause(py, ::std::option::Option::Some(inner_err));
142 new_err
143}