diff --git a/newsfragments/2796.changed.md b/newsfragments/2796.changed.md new file mode 100644 index 00000000000..6d50956d179 --- /dev/null +++ b/newsfragments/2796.changed.md @@ -0,0 +1 @@ +Mixing cfgs and pyo3 attributes on struct fields will now work \ No newline at end of file diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 7feafc99dcb..fa81c6d495d 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -519,7 +519,15 @@ pub fn impl_py_setter_def( } }; + let mut cfg_attrs = TokenStream::new(); + if let PropertyType::Descriptor { field, .. } = &property_type { + for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) { + attr.to_tokens(&mut cfg_attrs); + } + } + let associated_method = quote! { + #cfg_attrs unsafe fn #wrapper_ident( _py: _pyo3::Python<'_>, _slf: *mut _pyo3::ffi::PyObject, @@ -538,6 +546,7 @@ pub fn impl_py_setter_def( }; let method_def = quote! { + #cfg_attrs _pyo3::class::PyMethodDefType::Setter({ #deprecations _pyo3::class::PySetterDef::new( @@ -653,7 +662,15 @@ pub fn impl_py_getter_def( } }; + let mut cfg_attrs = TokenStream::new(); + if let PropertyType::Descriptor { field, .. } = &property_type { + for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) { + attr.to_tokens(&mut cfg_attrs); + } + } + let associated_method = quote! { + #cfg_attrs unsafe fn #wrapper_ident( _py: _pyo3::Python<'_>, _slf: *mut _pyo3::ffi::PyObject @@ -665,6 +682,7 @@ pub fn impl_py_getter_def( }; let method_def = quote! { + #cfg_attrs _pyo3::class::PyMethodDefType::Getter({ #deprecations _pyo3::class::PyGetterDef::new( diff --git a/src/test_hygiene/pyclass.rs b/src/test_hygiene/pyclass.rs index d86ea09c507..0b535abe860 100644 --- a/src/test_hygiene/pyclass.rs +++ b/src/test_hygiene/pyclass.rs @@ -34,3 +34,28 @@ pub struct Bar { pub enum Enum { Var0, } + +#[crate::pyclass] +#[pyo3(crate = "crate")] +pub struct Foo3 { + #[pyo3(get, set)] + #[cfg(FALSE)] + field: i32, + + #[pyo3(get, set)] + #[cfg(not(FALSE))] + field: u32, +} + +#[crate::pyclass] +#[pyo3(crate = "crate")] +pub struct Foo4 { + #[pyo3(get, set)] + #[cfg(FALSE)] + #[cfg(not(FALSE))] + field: i32, + + #[pyo3(get, set)] + #[cfg(any(not(FALSE)))] + field: u32, +} diff --git a/tests/test_field_cfg.rs b/tests/test_field_cfg.rs new file mode 100644 index 00000000000..dc84701c2b3 --- /dev/null +++ b/tests/test_field_cfg.rs @@ -0,0 +1,24 @@ +#![cfg(feature = "macros")] + +use pyo3::prelude::*; + +#[pyclass] +struct CfgClass { + #[pyo3(get, set)] + #[cfg(any())] + pub a: u32, + #[pyo3(get, set)] + #[cfg(all())] + pub b: u32, +} + +#[test] +fn test_cfg() { + Python::with_gil(|py| { + let cfg = CfgClass { b: 3 }; + let py_cfg = Py::new(py, cfg).unwrap(); + assert!(py_cfg.as_ref(py).getattr("a").is_err()); + let b: u32 = py_cfg.as_ref(py).getattr("b").unwrap().extract().unwrap(); + assert_eq!(b, 3); + }); +}