From bcdb17db5bb0cc4316b85138ab353008df9bc53e Mon Sep 17 00:00:00 2001 From: Will Stott Date: Sat, 18 Mar 2023 11:04:05 +0000 Subject: [PATCH 1/2] Fix compile error when trying to use static slot methods --- newsfragments/3055.fixed.md | 1 + pyo3-macros-backend/src/pymethod.rs | 3 +- tests/test_static_slots.rs | 57 +++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 newsfragments/3055.fixed.md create mode 100644 tests/test_static_slots.rs diff --git a/newsfragments/3055.fixed.md b/newsfragments/3055.fixed.md new file mode 100644 index 00000000000..0a070e53603 --- /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 a93fe70f859..82cd1fd28e4 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 00000000000..12192eabbb0 --- /dev/null +++ b/tests/test_static_slots.rs @@ -0,0 +1,57 @@ +#![cfg(feature = "macros")] + +use pyo3::exceptions::PyIndexError; +use pyo3::prelude::*; +use pyo3::types::IntoPyDict; + +use pyo3::py_run; + +mod common; + +#[pyclass] +struct Vector3 { + elements: [f64; 3], +} + +#[pymethods] +impl Vector3 { + #[new] + fn new(x: f64, y: f64, z: f64) -> Self { + Self { + elements: [x, y, z], + } + } + + #[staticmethod] + fn __len__() -> usize { + 3 + } + + fn __getitem__(&self, idx: isize) -> PyResult { + self.elements + .get(idx as usize) + .copied() + .ok_or_else(|| PyIndexError::new_err("list index out of range")) + } + + fn __setitem__(&mut self, idx: isize, value: f64) { + self.elements[idx as usize] = value; + } +} + +/// Return a dict with `s = Vector3(1, 2, 3)`. +fn seq_dict(py: Python<'_>) -> &pyo3::types::PyDict { + let d = [("Vector3", py.get_type::())].into_py_dict(py); + // Though we can construct `s` in Rust, let's test `__new__` works. + py_run!(py, *d, "s = Vector3(1, 2, 3)"); + d +} + +#[test] +fn test_len() { + Python::with_gil(|py| { + let d = seq_dict(py); + + py_assert!(py, *d, "len(s) == 3"); + }); +} From b05795307c9a2c3a6b5d0fcf5302f001331eb38e Mon Sep 17 00:00:00 2001 From: Will Stott Date: Sat, 18 Mar 2023 13:53:31 +0000 Subject: [PATCH 2/2] Test static __getitem__ as well as __len__ --- tests/test_static_slots.rs | 62 +++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/tests/test_static_slots.rs b/tests/test_static_slots.rs index 12192eabbb0..0092f5e5003 100644 --- a/tests/test_static_slots.rs +++ b/tests/test_static_slots.rs @@ -9,49 +9,63 @@ use pyo3::py_run; mod common; #[pyclass] -struct Vector3 { - elements: [f64; 3], -} +struct Count5(); #[pymethods] -impl Vector3 { +impl Count5 { #[new] - fn new(x: f64, y: f64, z: f64) -> Self { - Self { - elements: [x, y, z], - } + fn new() -> Self { + Self() } #[staticmethod] fn __len__() -> usize { - 3 + 5 } - fn __getitem__(&self, idx: isize) -> PyResult { - self.elements - .get(idx as usize) - .copied() - .ok_or_else(|| PyIndexError::new_err("list index out of range")) - } - - fn __setitem__(&mut self, idx: isize, value: f64) { - self.elements[idx as usize] = value; + #[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 = Vector3(1, 2, 3)`. -fn seq_dict(py: Python<'_>) -> &pyo3::types::PyDict { - let d = [("Vector3", py.get_type::())].into_py_dict(py); +/// 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 = Vector3(1, 2, 3)"); + py_run!(py, *d, "s = Count5()"); d } #[test] fn test_len() { Python::with_gil(|py| { - let d = seq_dict(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, "len(s) == 3"); + py_assert!(py, *d, "list(s) == [1.0, 2.0, 3.0, 4.0, 5.0]"); }); }