diff --git a/newsfragments/4814.fixed.md b/newsfragments/4814.fixed.md new file mode 100644 index 00000000000..6634efc2b9f --- /dev/null +++ b/newsfragments/4814.fixed.md @@ -0,0 +1 @@ +`derive(FromPyObject)` support raw identifiers like `r#box`. \ No newline at end of file diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 14c8755e9be..565c54da1f3 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -3,6 +3,7 @@ use crate::utils::Ctx; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{ + ext::IdentExt, parenthesized, parse::{Parse, ParseStream}, parse_quote, @@ -323,11 +324,11 @@ impl<'a> Container<'a> { fn build_struct(&self, struct_fields: &[NamedStructField<'_>], ctx: &Ctx) -> TokenStream { let Ctx { pyo3_path, .. } = ctx; let self_ty = &self.path; - let struct_name = &self.name(); - let mut fields: Punctuated = Punctuated::new(); + let struct_name = self.name(); + let mut fields: Punctuated = Punctuated::new(); for field in struct_fields { - let ident = &field.ident; - let field_name = ident.to_string(); + let ident = field.ident; + let field_name = ident.unraw().to_string(); let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) { FieldGetter::GetAttr(Some(name)) => { quote!(#pyo3_path::types::PyAnyMethods::getattr(obj, #pyo3_path::intern!(obj.py(), #name))) diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index 344a47acf72..2192caf1f7c 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -648,3 +648,41 @@ fn test_transparent_from_py_with() { assert_eq!(result, expected); }); } + +#[derive(Debug, FromPyObject, PartialEq, Eq)] +pub struct WithKeywordAttr { + r#box: usize, +} + +#[pyclass] +pub struct WithKeywordAttrC { + #[pyo3(get)] + r#box: usize, +} + +#[test] +fn test_with_keyword_attr() { + Python::with_gil(|py| { + let cls = WithKeywordAttrC { r#box: 3 }.into_pyobject(py).unwrap(); + let result = cls.extract::().unwrap(); + let expected = WithKeywordAttr { r#box: 3 }; + assert_eq!(result, expected); + }); +} + +#[derive(Debug, FromPyObject, PartialEq, Eq)] +pub struct WithKeywordItem { + #[pyo3(item)] + r#box: usize, +} + +#[test] +fn test_with_keyword_item() { + Python::with_gil(|py| { + let dict = PyDict::new(py); + dict.set_item("box", 3).unwrap(); + let result = dict.extract::().unwrap(); + let expected = WithKeywordItem { r#box: 3 }; + assert_eq!(result, expected); + }); +}