python_ast/ast/tree/
raise_stmt.rs1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 CodeGen, CodeGenContext, ExprType, Node, PythonOptions, SymbolTableScopes,
8};
9
10#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12pub struct Raise {
13 pub exc: Option<ExprType>,
15 pub cause: Option<ExprType>,
17 pub lineno: Option<usize>,
19 pub col_offset: Option<usize>,
20 pub end_lineno: Option<usize>,
21 pub end_col_offset: Option<usize>,
22}
23
24impl<'a, 'py> FromPyObject<'a, 'py> for Raise {
25 type Error = pyo3::PyErr;
26 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
27 let exc: Option<ExprType> = if let Ok(exc_attr) = ob.getattr("exc") {
29 if exc_attr.is_none() {
30 None
31 } else {
32 Some(exc_attr.extract()?)
33 }
34 } else {
35 None
36 };
37
38 let cause: Option<ExprType> = if let Ok(cause_attr) = ob.getattr("cause") {
40 if cause_attr.is_none() {
41 None
42 } else {
43 Some(cause_attr.extract()?)
44 }
45 } else {
46 None
47 };
48
49 Ok(Raise {
50 exc,
51 cause,
52 lineno: ob.lineno(),
53 col_offset: ob.col_offset(),
54 end_lineno: ob.end_lineno(),
55 end_col_offset: ob.end_col_offset(),
56 })
57 }
58}
59
60impl Node for Raise {
61 fn lineno(&self) -> Option<usize> { self.lineno }
62 fn col_offset(&self) -> Option<usize> { self.col_offset }
63 fn end_lineno(&self) -> Option<usize> { self.end_lineno }
64 fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
65}
66
67impl CodeGen for Raise {
68 type Context = CodeGenContext;
69 type Options = PythonOptions;
70 type SymbolTable = SymbolTableScopes;
71
72 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
73 let symbols = if let Some(exc) = self.exc {
74 exc.find_symbols(symbols)
75 } else {
76 symbols
77 };
78
79 if let Some(cause) = self.cause {
80 cause.find_symbols(symbols)
81 } else {
82 symbols
83 }
84 }
85
86 fn to_rust(
87 self,
88 ctx: Self::Context,
89 options: Self::Options,
90 symbols: Self::SymbolTable,
91 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
92 let exc_tokens = match self.exc {
93 Some(exc) => {
94 let mut tokens =
95 exception_value(&exc, ctx.clone(), options.clone(), symbols.clone())?;
96 if let Some(cause) = self.cause {
97 let cause_tokens = cause.to_rust(ctx.clone(), options, symbols)?;
100 tokens = quote! {
101 {
102 let mut __rython_raised = #tokens;
103 __rython_raised.message =
104 format!("{} [from {}]", __rython_raised.message, #cause_tokens);
105 __rython_raised
106 }
107 };
108 }
109 tokens
110 }
111 None => {
112 if !ctx.in_except_handler() {
116 return Err(
117 "bare `raise` outside an except handler has no exception to re-raise"
118 .to_string()
119 .into(),
120 );
121 }
122 quote!(__rython_exc.clone())
123 }
124 };
125
126 Ok(quote!(return Err(#exc_tokens)))
131 }
132}
133
134fn is_exception_class_name(name: &str) -> bool {
139 matches!(
140 name,
141 "Exception"
142 | "BaseException"
143 | "ArithmeticError"
144 | "AssertionError"
145 | "AttributeError"
146 | "BufferError"
147 | "EOFError"
148 | "FileExistsError"
149 | "FileNotFoundError"
150 | "FloatingPointError"
151 | "ImportError"
152 | "IndentationError"
153 | "IndexError"
154 | "InterruptedError"
155 | "IsADirectoryError"
156 | "KeyError"
157 | "KeyboardInterrupt"
158 | "LookupError"
159 | "MemoryError"
160 | "ModuleNotFoundError"
161 | "NameError"
162 | "NotADirectoryError"
163 | "NotImplementedError"
164 | "OSError"
165 | "OverflowError"
166 | "PermissionError"
167 | "RecursionError"
168 | "ReferenceError"
169 | "RuntimeError"
170 | "StopAsyncIteration"
171 | "StopIteration"
172 | "SyntaxError"
173 | "SystemError"
174 | "SystemExit"
175 | "TabError"
176 | "TimeoutError"
177 | "TypeError"
178 | "UnboundLocalError"
179 | "UnicodeDecodeError"
180 | "UnicodeEncodeError"
181 | "UnicodeError"
182 | "ValueError"
183 | "ZeroDivisionError"
184 ) || name.ends_with("Error")
185 || name.ends_with("Exception")
186 || name.ends_with("Warning")
187}
188
189fn exception_value(
194 exc: &ExprType,
195 ctx: CodeGenContext,
196 options: PythonOptions,
197 symbols: SymbolTableScopes,
198) -> Result<TokenStream, Box<dyn std::error::Error>> {
199 match exc {
200 ExprType::Call(call) => {
201 if let ExprType::Name(name) = call.func.as_ref() {
202 if is_exception_class_name(&name.id) {
203 let kind = &name.id;
204 let msg = match call.args.len() {
205 0 => quote!(String::new()),
206 1 => {
207 let arg = call.args[0].clone().to_rust(ctx, options, symbols)?;
208 quote!(format!("{}", #arg))
209 }
210 _ => {
211 let args: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = call
212 .args
213 .iter()
214 .map(|a| {
215 a.clone().to_rust(
216 ctx.clone(),
217 options.clone(),
218 symbols.clone(),
219 )
220 })
221 .collect();
222 let args = args?;
223 let fmt = vec!["{}"; args.len()].join(", ");
224 quote!(format!(#fmt, #(#args),*))
225 }
226 };
227 return Ok(quote!(PyException::new(#kind, #msg)));
228 }
229 }
230 let tokens = exc.clone().to_rust(ctx, options, symbols)?;
231 Ok(quote!(#tokens))
232 }
233 ExprType::Name(name) if is_exception_class_name(&name.id) => {
234 let kind = &name.id;
235 Ok(quote!(PyException::new(#kind, String::new())))
236 }
237 other => {
238 let tokens = other.clone().to_rust(ctx, options, symbols)?;
239 Ok(quote!(#tokens))
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 }