python_ast/ast/tree/
attribute.rs1use proc_macro2::TokenStream;
2use pyo3::{Borrowed, PyAny, FromPyObject, PyResult, prelude::PyAnyMethods, types::PyTypeMethods};
3use quote::quote;
4
5use crate::{extraction_failure, CodeGen, CodeGenContext, ExprType, PythonOptions, SymbolTableScopes};
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
10pub struct Attribute {
12 pub value: Box<ExprType>,
13 pub attr: String,
14 ctx: String,
15}
16
17impl<'a, 'py> FromPyObject<'a, 'py> for Attribute {
18 type Error = pyo3::PyErr;
19 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
20 let value = ob.getattr("value").map_err(|e| extraction_failure("Attribute.value", &ob, e))?;
21 let attr = ob.getattr("attr").map_err(|e| extraction_failure("Attribute.attr", &ob, e))?;
22 let ctx = ob
23 .getattr("ctx")
24 .map_err(|e| extraction_failure("attribute context", &ob, e))?
25 .get_type()
26 .name()
27 .map_err(|e| extraction_failure("attribute context type", &ob, e))?;
28 Ok(Attribute {
29 value: Box::new(value.extract().map_err(|e| extraction_failure("Attribute.value", &ob, e))?),
30 attr: attr.extract().map_err(|e| extraction_failure("Attribute.attr", &ob, e))?,
31 ctx: ctx.to_string(),
32 })
33 }
34}
35
36impl<'a> CodeGen for Attribute {
37 type Context = CodeGenContext;
38 type Options = PythonOptions;
39 type SymbolTable = SymbolTableScopes;
40
41 fn to_rust(
42 self,
43 ctx: Self::Context,
44 options: Self::Options,
45 symbols: Self::SymbolTable,
46 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
47 let value_tokens = self.value.to_rust(ctx, options, symbols)?;
48 let value_str = value_tokens.to_string();
49 let attr = crate::safe_ident(&self.attr);
50
51 let is_module_access = matches!(value_str.as_str(),
54 "sys" | "os" | "subprocess" | "json" | "urllib" | "xml" | "asyncio" |
55 "time" | "math" | "random" | "heapq" | "functools" | "textwrap" | "itertools" | "re" | "hashlib" | "csv" | "io" |
56 "datetime" |
61 "os :: path" | "os::path" );
63
64 if is_module_access {
65 let needs_deref = matches!((value_str.as_str(), self.attr.as_str()),
70 ("sys", "executable") | ("sys", "argv")
71 );
72
73 if needs_deref {
74 Ok(quote!((*#value_tokens::#attr)))
77 } else {
78 Ok(quote!(#value_tokens::#attr))
79 }
80 } else {
81 Ok(quote!(#value_tokens.#attr))
83 }
84 }
85}