diff --git a/newsfragments/3055.fixed.md b/newsfragments/3055.fixed.md new file mode 100644 index 00000000..0a070e53 --- /dev/null +++ b/newsfragments/3055.fixed.md @@ -0,0 +1 @@ +compile error in generated code for a `#[staticmethod]` occupying a slot diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index a93fe70f..82cd1fd2 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -1195,9 +1195,10 @@ fn generate_method_body( return_mode: Option<&ReturnMode>, ) -> Result { let self_conversion = spec.tp.self_conversion(Some(cls), extract_error_mode); + let self_arg = spec.tp.self_arg(); let rust_name = spec.name; let args = extract_proto_arguments(py, spec, arguments, extract_error_mode)?; - let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#args),*)) }; + let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(#self_arg #(#args),*)) }; let body = if let Some(return_mode) = return_mode { return_mode.return_call_output(py, call) } else { diff --git a/tests/test_static_slots.rs b/tests/test_static_slots.rs new file mode 100644 index 00000000..0092f5e5 --- /dev/null +++ b/tests/test_static_slots.rs @@ -0,0 +1,71 @@ +#![cfg(feature = "macros")] + +use pyo3::exceptions::PyIndexError; +use pyo3::prelude::*; +use pyo3::types::IntoPyDict; + +use pyo3::py_run; + +mod common; + +#[pyclass] +struct Count5(); + +#[pymethods] +impl Count5 { + #[new] + fn new() -> Self { + Self() + } + + #[staticmethod] + fn __len__() -> usize { + 5 + } + + #[staticmethod] + fn __getitem__(idx: isize) -> PyResult { + if idx < 0 { + Err(PyIndexError::new_err("Count5 cannot count backwards")) + } else if idx > 4 { + Err(PyIndexError::new_err("Count5 cannot count higher than 5")) + } else { + Ok(idx as f64 + 1.0) + } + } +} + +/// Return a dict with `s = Count5()`. +fn test_dict(py: Python<'_>) -> &pyo3::types::PyDict { + let d = [("Count5", py.get_type::())].into_py_dict(py); + // Though we can construct `s` in Rust, let's test `__new__` works. + py_run!(py, *d, "s = Count5()"); + d +} + +#[test] +fn test_len() { + Python::with_gil(|py| { + let d = test_dict(py); + + py_assert!(py, *d, "len(s) == 5"); + }); +} + +#[test] +fn test_getitem() { + Python::with_gil(|py| { + let d = test_dict(py); + + py_assert!(py, *d, "s[4] == 5.0"); + }); +} + +#[test] +fn test_list() { + Python::with_gil(|py| { + let d = test_dict(py); + + py_assert!(py, *d, "list(s) == [1.0, 2.0, 3.0, 4.0, 5.0]"); + }); +}