1use crate::platform::prelude::*;
2use crate::types::any::PyAnyMethods;
3use crate::Bound;
4use crate::{exceptions::PyTypeError, FromPyObject, PyAny, PyErr, PyResult, Python};
5
6#[cold]
7pub fn failed_to_extract_enum(
8 py: Python<'_>,
9 type_name: &str,
10 variant_names: &[&str],
11 error_names: &[&str],
12 errors: &[PyErr],
13) -> PyErr {
14 let mut err_msg = format!(
16 "failed to extract enum {} ('{}')",
17 type_name,
18 error_names.join(" | ")
19 );
20 for ((variant_name, error_name), error) in variant_names.iter().zip(error_names).zip(errors) {
21 use core::fmt::Write;
22 write!(
23 &mut err_msg,
24 "\n- variant {variant_name} ({error_name}): {error_msg}",
25 variant_name = variant_name,
26 error_name = error_name,
27 error_msg = extract_traceback(py, error.clone_ref(py)),
28 )
29 .unwrap();
30 }
31 PyTypeError::new_err(err_msg)
32}
33
34fn extract_traceback(py: Python<'_>, mut error: PyErr) -> String {
36 use core::fmt::Write;
37
38 let mut error_msg = error.to_string();
39 while let Some(cause) = error.cause(py) {
40 write!(&mut error_msg, ", caused by {cause}").unwrap();
41 error = cause
42 }
43 error_msg
44}
45
46pub fn extract_struct_field<'a, 'py, T>(
47 obj: &'a Bound<'py, PyAny>,
48 struct_name: &str,
49 field_name: &str,
50) -> PyResult<T>
51where
52 T: FromPyObject<'a, 'py>,
53{
54 match obj.extract() {
55 Ok(value) => Ok(value),
56 Err(err) => Err(failed_to_extract_struct_field(
57 obj.py(),
58 err.into(),
59 struct_name,
60 field_name,
61 )),
62 }
63}
64
65pub fn extract_struct_field_with<'a, 'py, T>(
66 extractor: fn(&'a Bound<'py, PyAny>) -> PyResult<T>,
67 obj: &'a Bound<'py, PyAny>,
68 struct_name: &str,
69 field_name: &str,
70) -> PyResult<T> {
71 match extractor(obj) {
72 Ok(value) => Ok(value),
73 Err(err) => Err(failed_to_extract_struct_field(
74 obj.py(),
75 err,
76 struct_name,
77 field_name,
78 )),
79 }
80}
81
82#[cold]
83fn failed_to_extract_struct_field(
84 py: Python<'_>,
85 inner_err: PyErr,
86 struct_name: &str,
87 field_name: &str,
88) -> PyErr {
89 let new_err = PyTypeError::new_err(format!(
90 "failed to extract field {struct_name}.{field_name}"
91 ));
92 new_err.set_cause(py, ::core::option::Option::Some(inner_err));
93 new_err
94}
95
96pub fn extract_tuple_struct_field<'a, 'py, T>(
97 obj: &'a Bound<'py, PyAny>,
98 struct_name: &str,
99 index: usize,
100) -> PyResult<T>
101where
102 T: FromPyObject<'a, '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.into(),
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 = PyTypeError::new_err(format!("failed to extract field {struct_name}.{index}"));
140 new_err.set_cause(py, ::core::option::Option::Some(inner_err));
141 new_err
142}