From 50659b6b02f9417b6e938f2d94ad6d66aa3bccdf Mon Sep 17 00:00:00 2001 From: Aviram Hassan Date: Fri, 31 Dec 2021 15:11:02 +0200 Subject: [PATCH 1/4] `__ipow__` now supports modulo argument on Python 3.8+. `pyo3-macros-backend` is now compiled with PyO3 cfgs to enable different magic method definitions based on version. Add check for correct number of arguments on magic methods. --- CHANGELOG.md | 3 + guide/src/class/protocols.md | 4 +- pyo3-macros-backend/Cargo.toml | 3 + pyo3-macros-backend/build.rs | 14 + pyo3-macros-backend/src/defs.rs | 6 + pyo3-macros-backend/src/pymethod.rs | 16 +- src/class/number.rs | 44 ++- src/test_hygiene/pymethods.rs | 394 ++++++++++++++++++++ tests/test_arithmetics.rs | 118 +++++- tests/test_arithmetics_protos.rs | 103 +++++ tests/test_compile_error.rs | 1 + tests/ui/invalid_pymethod_proto_args.rs | 13 + tests/ui/invalid_pymethod_proto_args.stderr | 5 + 13 files changed, 716 insertions(+), 8 deletions(-) create mode 100644 pyo3-macros-backend/build.rs create mode 100644 tests/ui/invalid_pymethod_proto_args.rs create mode 100644 tests/ui/invalid_pymethod_proto_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 693ea4f6d87..eea51e237b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 where to find the `pyo3` crate, in case it has been renamed or is re-exported and not found at the crate root. [#2022](https://github.com/PyO3/pyo3/pull/2022) - Expose `pyo3-build-config` APIs for cross-compiling and Python configuration discovery for use in other projects. [#1996](https://github.com/PyO3/pyo3/pull/1996) - Accept paths in `wrap_pyfunction` and `wrap_pymodule`. [#2081](https://github.com/PyO3/pyo3/pull/2081) +- Add check for correct number of arguments on magic methods. [#2083](https://github.com/PyO3/pyo3/pull/2083) ### Changed @@ -45,6 +46,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - internal `handle_panic` helper [#2074](https://github.com/PyO3/pyo3/pull/2074) - `#[pyfunction]` and `#[pymethods]` argument extraction [#2075](https://github.com/PyO3/pyo3/pull/2075) - `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) [#2081](https://github.com/PyO3/pyo3/pull/2081) +- `__ipow__` now supports modulo argument on Python 3.8+. [#2083](https://github.com/PyO3/pyo3/pull/2083) +- `pyo3-macros-backend` is now compiled with PyO3 cfgs to enable different magic method definitions based on version. [#2083](https://github.com/PyO3/pyo3/pull/2083) ### Removed diff --git a/guide/src/class/protocols.md b/guide/src/class/protocols.md index 4bdcaa471ce..3d456dd1b2a 100644 --- a/guide/src/class/protocols.md +++ b/guide/src/class/protocols.md @@ -288,13 +288,15 @@ This trait also has support the augmented arithmetic assignments (`+=`, `-=`, * `fn __itruediv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __ifloordiv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __imod__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` - * `fn __ipow__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` + * `fn __ipow__(&'p mut self, other: impl FromPyObject, modulo: impl FromPyObject) -> PyResult<()>` on Python 3.8^ + * `fn __ipow__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` on Python 3.7 see https://bugs.python.org/issue36379 * `fn __ilshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __irshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __iand__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __ior__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` * `fn __ixor__(&'p mut self, other: impl FromPyObject) -> PyResult<()>` + The following methods implement the unary arithmetic operations (`-`, `+`, `abs()` and `~`): * `fn __neg__(&'p self) -> PyResult` diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index 5abd99afdd1..8ed07db286c 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -18,6 +18,9 @@ quote = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false } pyo3-build-config = { path = "../pyo3-build-config", version = "0.15.1", features = ["resolve-config"] } +[build-dependencies] +pyo3-build-config = { path = "../pyo3-build-config", version = "0.15.1", features = ["resolve-config"] } + [dependencies.syn] version = "1" default-features = false diff --git a/pyo3-macros-backend/build.rs b/pyo3-macros-backend/build.rs new file mode 100644 index 00000000000..afcf26de3ca --- /dev/null +++ b/pyo3-macros-backend/build.rs @@ -0,0 +1,14 @@ +use pyo3_build_config::pyo3_build_script_impl::{errors::Result, resolve_interpreter_config}; + +fn configure_pyo3() -> Result<()> { + let interpreter_config = resolve_interpreter_config()?; + interpreter_config.emit_pyo3_cfgs(); + Ok(()) +} + +fn main() { + if let Err(e) = configure_pyo3() { + eprintln!("error: {}", e.report()); + std::process::exit(1) + } +} diff --git a/pyo3-macros-backend/src/defs.rs b/pyo3-macros-backend/src/defs.rs index be90f137974..938a91c51f1 100644 --- a/pyo3-macros-backend/src/defs.rs +++ b/pyo3-macros-backend/src/defs.rs @@ -420,6 +420,12 @@ pub const NUM: Proto = Proto { MethodProto::new("__imod__", "PyNumberIModProtocol") .args(&["Other"]) .has_self(), + // See https://bugs.python.org/issue36379 + #[cfg(Py_3_8)] + MethodProto::new("__ipow__", "PyNumberIPowProtocol") + .args(&["Other", "Modulo"]) + .has_self(), + #[cfg(not(Py_3_8))] MethodProto::new("__ipow__", "PyNumberIPowProtocol") .args(&["Other"]) .has_self(), diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index c8b4d607e5b..8c52369cdd8 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -532,10 +532,16 @@ const __IMOD__: SlotDef = SlotDef::new("Py_nb_inplace_remainder", "binaryfunc") .arguments(&[Ty::Object]) .extract_error_mode(ExtractErrorMode::NotImplemented) .return_self(); +#[cfg(Py_3_8)] const __IPOW__: SlotDef = SlotDef::new("Py_nb_inplace_power", "ternaryfunc") .arguments(&[Ty::Object, Ty::Object]) .extract_error_mode(ExtractErrorMode::NotImplemented) .return_self(); +#[cfg(not(Py_3_8))] +const __IPOW__: SlotDef = SlotDef::new("Py_nb_inplace_power", "binaryfunc") + .arguments(&[Ty::Object]) + .extract_error_mode(ExtractErrorMode::NotImplemented) + .return_self(); const __ILSHIFT__: SlotDef = SlotDef::new("Py_nb_inplace_lshift", "binaryfunc") .arguments(&[Ty::Object]) .extract_error_mode(ExtractErrorMode::NotImplemented) @@ -856,8 +862,11 @@ fn generate_method_body( ) -> Result { let self_conversion = spec.tp.self_conversion(Some(cls), extract_error_mode); let rust_name = spec.name; - let (arg_idents, conversions) = + let (arg_idents, arg_count, conversions) = extract_proto_arguments(cls, py, &spec.args, arguments, extract_error_mode)?; + if arg_count != arguments.len() { + bail_spanned!(spec.name.span() => format!("Expected {} arguments, got {}", arguments.len(), arg_count)); + } let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*)) }; let body = if let Some(return_mode) = return_mode { return_mode.return_call_output(py, call) @@ -1026,7 +1035,7 @@ fn extract_proto_arguments( method_args: &[FnArg], proto_args: &[Ty], extract_error_mode: ExtractErrorMode, -) -> Result<(Vec, TokenStream)> { +) -> Result<(Vec, usize, TokenStream)> { let mut arg_idents = Vec::with_capacity(method_args.len()); let mut non_python_args = 0; @@ -1045,9 +1054,8 @@ fn extract_proto_arguments( arg_idents.push(ident); } } - let conversions = quote!(#(#args_conversions)*); - Ok((arg_idents, conversions)) + Ok((arg_idents, non_python_args, conversions)) } struct StaticIdent(&'static str); diff --git a/src/class/number.rs b/src/class/number.rs index 2b0c2ade799..2dbe5403793 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -221,6 +221,14 @@ pub trait PyNumberProtocol<'p>: PyClass { { unimplemented!() } + #[cfg(Py_3_8)] + fn __ipow__(&'p mut self, other: Self::Other, modulo: Option) -> Self::Result + where + Self: PyNumberIPowProtocol<'p>, + { + unimplemented!() + } + #[cfg(not(Py_3_8))] fn __ipow__(&'p mut self, other: Self::Other) -> Self::Result where Self: PyNumberIPowProtocol<'p>, @@ -504,6 +512,8 @@ pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> { pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; + #[cfg(Py_3_8)] + type Modulo: FromPyObject<'p>; } #[allow(clippy::upper_case_acronyms)] @@ -714,11 +724,13 @@ py_binary_self_func!(isub, PyNumberISubProtocol, T::__isub__); py_binary_self_func!(imul, PyNumberIMulProtocol, T::__imul__); py_binary_self_func!(imod, PyNumberIModProtocol, T::__imod__); +// See https://bugs.python.org/issue36379 #[doc(hidden)] +#[cfg(Py_3_8)] pub unsafe extern "C" fn ipow( slf: *mut ffi::PyObject, other: *mut ffi::PyObject, - _modulo: *mut ffi::PyObject, + modulo: *mut ffi::PyObject, ) -> *mut ffi::PyObject where T: for<'p> PyNumberIPowProtocol<'p>, @@ -728,7 +740,35 @@ where crate::callback_body!(py, { let slf_cell = py.from_borrowed_ptr::>(slf); let other = py.from_borrowed_ptr::(other); - call_operator_mut!(py, slf_cell, __ipow__, other).convert(py)?; + let modulo = py.from_borrowed_ptr::(modulo); + slf_cell + .try_borrow_mut()? + .__ipow__( + extract_or_return_not_implemented!(other), + extract_or_return_not_implemented!(modulo), + ) + .convert(py)?; + ffi::Py_INCREF(slf); + Ok::<_, PyErr>(slf) + }) +} + +#[doc(hidden)] +#[cfg(not(Py_3_8))] +pub unsafe extern "C" fn ipow( + slf: *mut ffi::PyObject, + other: *mut ffi::PyObject, +) -> *mut ffi::PyObject +where + T: for<'p> PyNumberIPowProtocol<'p>, +{ + crate::callback_body!(py, { + let slf_cell = py.from_borrowed_ptr::>(slf); + let other = py.from_borrowed_ptr::(other); + slf_cell + .try_borrow_mut()? + .__ipow__(extract_or_return_not_implemented!(other)) + .convert(py)?; ffi::Py_INCREF(slf); Ok::<_, PyErr>(slf) }) diff --git a/src/test_hygiene/pymethods.rs b/src/test_hygiene/pymethods.rs index 832e72396d3..fdd2f2b8223 100644 --- a/src/test_hygiene/pymethods.rs +++ b/src/test_hygiene/pymethods.rs @@ -9,6 +9,7 @@ pub struct Dummy; #[pyo3(crate = "crate")] pub struct DummyIter; +#[cfg(Py_3_8)] #[crate::pymethods] #[pyo3(crate = "crate")] impl Dummy { @@ -401,3 +402,396 @@ impl Dummy { // PyGcProtocol // Buffer protocol? } + +#[cfg(not(Py_3_8))] +#[crate::pymethods] +#[pyo3(crate = "crate")] +impl Dummy { + ////////////////////// + // Basic customization + ////////////////////// + fn __repr__(&self) -> &'static str { + "Dummy" + } + + fn __str__(&self) -> &'static str { + "Dummy" + } + + fn __bytes__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyBytes { + crate::types::PyBytes::new(py, &[0]) + } + + fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String { + ::std::panic!("unimplemented isn't hygienic before 1.50") + } + + fn __lt__(&self, other: &Self) -> bool { + false + } + + fn __le__(&self, other: &Self) -> bool { + false + } + fn __eq__(&self, other: &Self) -> bool { + false + } + fn __ne__(&self, other: &Self) -> bool { + false + } + fn __gt__(&self, other: &Self) -> bool { + false + } + fn __ge__(&self, other: &Self) -> bool { + false + } + + fn __hash__(&self) -> u64 { + 42 + } + + fn __bool__(&self) -> bool { + true + } + + ////////////////////// + // Customizing attribute access + ////////////////////// + + fn __getattr__(&self, name: ::std::string::String) -> &crate::PyAny { + ::std::panic!("unimplemented isn't hygienic before 1.50") + } + + fn __getattribute__(&self, name: ::std::string::String) -> &crate::PyAny { + ::std::panic!("unimplemented isn't hygienic before 1.50") + } + + fn __setattr__(&mut self, name: ::std::string::String, value: ::std::string::String) {} + + fn __delattr__(&mut self, name: ::std::string::String) {} + + fn __dir__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyList { + crate::types::PyList::new(py, ::std::vec![0_u8]) + } + + ////////////////////// + // Implementing Descriptors + ////////////////////// + + fn __get__( + &self, + instance: &crate::PyAny, + owner: &crate::PyAny, + ) -> crate::PyResult<&crate::PyAny> { + ::std::panic!("unimplemented isn't hygienic before 1.50") + } + + fn __set__(&self, instance: &crate::PyAny, owner: &crate::PyAny) {} + + fn __delete__(&self, instance: &crate::PyAny) {} + + fn __set_name__(&self, owner: &crate::PyAny, name: &crate::PyAny) {} + + ////////////////////// + // Implementing Descriptors + ////////////////////// + + fn __len__(&self) -> usize { + 0 + } + + fn __getitem__(&self, key: u32) -> crate::PyResult { + ::std::result::Result::Err(crate::exceptions::PyKeyError::new_err("boo")) + } + + fn __setitem__(&self, key: u32, value: u32) {} + + fn __delitem__(&self, key: u32) {} + + fn __iter__(_: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() + } + + fn __next__(&mut self) -> ::std::option::Option<()> { + ::std::option::Option::None + } + + fn __reversed__(slf: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() + } + + fn __contains__(&self, item: u32) -> bool { + false + } + + ////////////////////// + // Emulating numeric types + ////////////////////// + + fn __add__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __sub__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __mul__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __truediv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) + } + + fn __floordiv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) + } + + fn __mod__(&self, _other: &Self) -> u32 { + 0 + } + + fn __divmod__(&self, _other: &Self) -> (u32, u32) { + (0, 0) + } + + fn __pow__(&self, _other: &Self, modulo: ::std::option::Option) -> Dummy { + Dummy {} + } + + fn __lshift__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rshift__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __and__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __xor__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __or__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __radd__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rrsub__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rmul__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rtruediv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) + } + + fn __rfloordiv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) + } + + fn __rmod__(&self, _other: &Self) -> u32 { + 0 + } + + fn __rdivmod__(&self, _other: &Self) -> (u32, u32) { + (0, 0) + } + + fn __rpow__(&self, _other: &Self, modulo: ::std::option::Option) -> Dummy { + Dummy {} + } + + fn __rlshift__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rrshift__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rand__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __rxor__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __ror__(&self, other: &Self) -> Dummy { + Dummy {} + } + + fn __iadd__(&mut self, other: &Self) {} + + fn __irsub__(&mut self, other: &Self) {} + + fn __imul__(&mut self, other: &Self) {} + + fn __itruediv__(&mut self, _other: &Self) {} + + fn __ifloordiv__(&mut self, _other: &Self) {} + + fn __imod__(&mut self, _other: &Self) {} + + fn __ipow__(&mut self, _other: &Self) {} + fn __ilshift__(&mut self, other: &Self) {} + + fn __irshift__(&mut self, other: &Self) {} + + fn __iand__(&mut self, other: &Self) {} + + fn __ixor__(&mut self, other: &Self) {} + + fn __ior__(&mut self, other: &Self) {} + + fn __neg__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { + slf + } + + fn __pos__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { + slf + } + + fn __abs__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { + slf + } + + fn __invert__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { + slf + } + + fn __complex__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyComplex { + crate::types::PyComplex::from_doubles(py, 0.0, 0.0) + } + + fn __int__(&self) -> u32 { + 0 + } + + fn __float__(&self) -> f64 { + 0.0 + } + + fn __index__(&self) -> u32 { + 0 + } + + fn __round__(&self, ndigits: ::std::option::Option) -> u32 { + 0 + } + + fn __trunc__(&self) -> u32 { + 0 + } + + fn __floor__(&self) -> u32 { + 0 + } + + fn __ceil__(&self) -> u32 { + 0 + } + + ////////////////////// + // With Statement Context Managers + ////////////////////// + + fn __enter__(&mut self) {} + + fn __exit__( + &mut self, + exc_type: &crate::PyAny, + exc_value: &crate::PyAny, + traceback: &crate::PyAny, + ) { + } + + ////////////////////// + // Awaitable Objects + ////////////////////// + + fn __await__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { + slf + } + + ////////////////////// + + // Asynchronous Iterators + ////////////////////// + + fn __aiter__(slf: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() + } + + fn __anext__(&mut self) -> ::std::option::Option<()> { + ::std::option::Option::None + } + + ////////////////////// + // Asynchronous Context Managers + ////////////////////// + + fn __aenter__(&mut self) {} + + fn __aexit__( + &mut self, + exc_type: &crate::PyAny, + exc_value: &crate::PyAny, + traceback: &crate::PyAny, + ) { + } + + // Things with attributes + + #[args(x = "1", "*", _z = "2")] + fn test(&self, _y: &Dummy, _z: i32) {} + #[staticmethod] + fn staticmethod() {} + #[classmethod] + fn clsmethod(_: &crate::types::PyType) {} + #[args(args = "*", kwds = "**")] + fn __call__( + &self, + _args: &crate::types::PyTuple, + _kwds: ::std::option::Option<&crate::types::PyDict>, + ) -> crate::PyResult { + ::std::panic!("unimplemented isn't hygienic before 1.50") + } + #[new] + fn new(a: u8) -> Self { + Dummy {} + } + #[getter] + fn get(&self) -> i32 { + 0 + } + #[setter] + fn set(&mut self, _v: i32) {} + #[classattr] + fn class_attr() -> i32 { + 0 + } + + // Dunder methods invented for protocols + + fn __richcmp__( + &self, + other: &Self, + op: crate::class::basic::CompareOp, + ) -> crate::PyResult { + ::std::result::Result::Ok(false) + } + // PyGcProtocol + // Buffer protocol? +} diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 5d9e2b1a316..7f317407a64 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -57,6 +57,51 @@ struct InPlaceOperations { value: u32, } +#[cfg(Py_3_8)] +#[pymethods] +impl InPlaceOperations { + fn __repr__(&self) -> String { + format!("IPO({:?})", self.value) + } + + fn __iadd__(&mut self, other: u32) { + self.value += other; + } + + fn __isub__(&mut self, other: u32) { + self.value -= other; + } + + fn __imul__(&mut self, other: u32) { + self.value *= other; + } + + fn __ilshift__(&mut self, other: u32) { + self.value <<= other; + } + + fn __irshift__(&mut self, other: u32) { + self.value >>= other; + } + + fn __iand__(&mut self, other: u32) { + self.value &= other; + } + + fn __ixor__(&mut self, other: u32) { + self.value ^= other; + } + + fn __ior__(&mut self, other: u32) { + self.value |= other; + } + + fn __ipow__(&mut self, other: u32, _modulo: Option) { + self.value = self.value.pow(other); + } +} + +#[cfg(not(Py_3_8))] #[pymethods] impl InPlaceOperations { fn __repr__(&self) -> String { @@ -505,6 +550,77 @@ mod return_not_implemented { #[pyclass] struct RichComparisonToSelf {} + #[cfg(Py_3_8)] + #[pymethods] + impl RichComparisonToSelf { + fn __repr__(&self) -> &'static str { + "RC_Self" + } + + fn __richcmp__(&self, other: PyRef, _op: CompareOp) -> PyObject { + other.py().None() + } + + fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __sub__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __mul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __matmul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __truediv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __floordiv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __mod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __pow__(slf: PyRef, _other: u8, _modulo: Option) -> PyRef { + slf + } + fn __lshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __rshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __divmod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __and__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __or__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + fn __xor__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + slf + } + + // Inplace assignments + fn __iadd__(&mut self, _other: PyRef) {} + fn __isub__(&mut self, _other: PyRef) {} + fn __imul__(&mut self, _other: PyRef) {} + fn __imatmul__(&mut self, _other: PyRef) {} + fn __itruediv__(&mut self, _other: PyRef) {} + fn __ifloordiv__(&mut self, _other: PyRef) {} + fn __imod__(&mut self, _other: PyRef) {} + fn __ilshift__(&mut self, _other: PyRef) {} + fn __irshift__(&mut self, _other: PyRef) {} + fn __iand__(&mut self, _other: PyRef) {} + fn __ior__(&mut self, _other: PyRef) {} + fn __ixor__(&mut self, _other: PyRef) {} + fn __ipow__(&mut self, _other: PyRef, _modulo: Option) {} + } + + #[cfg(not(Py_3_8))] #[pymethods] impl RichComparisonToSelf { fn __repr__(&self) -> &'static str { @@ -566,12 +682,12 @@ mod return_not_implemented { fn __itruediv__(&mut self, _other: PyRef) {} fn __ifloordiv__(&mut self, _other: PyRef) {} fn __imod__(&mut self, _other: PyRef) {} - fn __ipow__(&mut self, _other: PyRef) {} fn __ilshift__(&mut self, _other: PyRef) {} fn __irshift__(&mut self, _other: PyRef) {} fn __iand__(&mut self, _other: PyRef) {} fn __ior__(&mut self, _other: PyRef) {} fn __ixor__(&mut self, _other: PyRef) {} + fn __ipow__(&mut self, _other: PyRef) {} } fn _test_binary_dunder(dunder: &str) { diff --git a/tests/test_arithmetics_protos.rs b/tests/test_arithmetics_protos.rs index b57c5f49268..02ff0173516 100644 --- a/tests/test_arithmetics_protos.rs +++ b/tests/test_arithmetics_protos.rs @@ -74,6 +74,7 @@ impl PyObjectProtocol for InPlaceOperations { } } +#[cfg(not(Py_3_8))] #[pyproto] impl PyNumberProtocol for InPlaceOperations { fn __iadd__(&mut self, other: u32) { @@ -113,6 +114,45 @@ impl PyNumberProtocol for InPlaceOperations { } } +#[cfg(Py_3_8)] +#[pyproto] +impl PyNumberProtocol for InPlaceOperations { + fn __iadd__(&mut self, other: u32) { + self.value += other; + } + + fn __isub__(&mut self, other: u32) { + self.value -= other; + } + + fn __imul__(&mut self, other: u32) { + self.value *= other; + } + + fn __ilshift__(&mut self, other: u32) { + self.value <<= other; + } + + fn __irshift__(&mut self, other: u32) { + self.value >>= other; + } + + fn __iand__(&mut self, other: u32) { + self.value &= other; + } + + fn __ixor__(&mut self, other: u32) { + self.value ^= other; + } + + fn __ior__(&mut self, other: u32) { + self.value |= other; + } + + fn __ipow__(&mut self, other: u32, _modulo: Option) { + self.value = self.value.pow(other); + } +} #[test] fn inplace_operations() { let gil = Python::acquire_gil(); @@ -536,6 +576,69 @@ mod return_not_implemented { } } + #[cfg(Py_3_8)] + #[pyproto] + impl<'p> PyNumberProtocol<'p> for RichComparisonToSelf { + fn __add__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __sub__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __mul__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __matmul__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __truediv__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __floordiv__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __mod__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __pow__(lhs: &'p PyAny, _other: u8, _modulo: Option) -> &'p PyAny { + lhs + } + fn __lshift__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __rshift__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __divmod__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __and__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __or__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + fn __xor__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { + lhs + } + + // Inplace assignments + fn __iadd__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __isub__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __imul__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __imatmul__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __itruediv__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __ifloordiv__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __imod__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __ipow__(&'p mut self, _other: PyRef<'p, Self>, _modulo: Option) {} + fn __ilshift__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __irshift__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __iand__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __ior__(&'p mut self, _other: PyRef<'p, Self>) {} + fn __ixor__(&'p mut self, _other: PyRef<'p, Self>) {} + } + + #[cfg(not(Py_3_8))] #[pyproto] impl<'p> PyNumberProtocol<'p> for RichComparisonToSelf { fn __add__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index daade52d8c6..9801f97a1a3 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -30,6 +30,7 @@ fn _test_compile_errors() { t.compile_fail("tests/ui/missing_clone.rs"); t.compile_fail("tests/ui/reject_generics.rs"); t.compile_fail("tests/ui/not_send.rs"); + t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs"); tests_rust_1_49(&t); tests_rust_1_55(&t); diff --git a/tests/ui/invalid_pymethod_proto_args.rs b/tests/ui/invalid_pymethod_proto_args.rs new file mode 100644 index 00000000000..3c3e96ab3b6 --- /dev/null +++ b/tests/ui/invalid_pymethod_proto_args.rs @@ -0,0 +1,13 @@ +use pyo3::prelude::*; + +#[pyclass] +struct MyClass {} + +#[pymethods] +impl MyClass { + fn __truediv__(&self) -> PyResult<()> { + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/invalid_pymethod_proto_args.stderr b/tests/ui/invalid_pymethod_proto_args.stderr new file mode 100644 index 00000000000..04d77c76308 --- /dev/null +++ b/tests/ui/invalid_pymethod_proto_args.stderr @@ -0,0 +1,5 @@ +error: Expected 1 arguments, got 0 + --> tests/ui/invalid_pymethod_proto_args.rs:8:8 + | +8 | fn __truediv__(&self) -> PyResult<()> { + | ^^^^^^^^^^^ From a9b98b74332adb792f9859869fe15ac43f7230ca Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Tue, 4 Jan 2022 22:58:33 +0000 Subject: [PATCH 2/4] pymethods: __ipow__ always receive None on Python 3.7 --- pyo3-macros-backend/Cargo.toml | 3 - pyo3-macros-backend/build.rs | 14 ---- pyo3-macros-backend/src/defs.rs | 6 -- pyo3-macros-backend/src/pymethod.rs | 22 ++++-- src/class/number.rs | 47 +++-------- src/ffi/mod.rs | 4 + src/impl_.rs | 2 + src/impl_/pymethods.rs | 33 ++++++++ tests/test_arithmetics.rs | 116 ---------------------------- tests/test_arithmetics_protos.rs | 104 ------------------------- 10 files changed, 63 insertions(+), 288 deletions(-) delete mode 100644 pyo3-macros-backend/build.rs create mode 100644 src/impl_/pymethods.rs diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index 8ed07db286c..5abd99afdd1 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -18,9 +18,6 @@ quote = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false } pyo3-build-config = { path = "../pyo3-build-config", version = "0.15.1", features = ["resolve-config"] } -[build-dependencies] -pyo3-build-config = { path = "../pyo3-build-config", version = "0.15.1", features = ["resolve-config"] } - [dependencies.syn] version = "1" default-features = false diff --git a/pyo3-macros-backend/build.rs b/pyo3-macros-backend/build.rs deleted file mode 100644 index afcf26de3ca..00000000000 --- a/pyo3-macros-backend/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -use pyo3_build_config::pyo3_build_script_impl::{errors::Result, resolve_interpreter_config}; - -fn configure_pyo3() -> Result<()> { - let interpreter_config = resolve_interpreter_config()?; - interpreter_config.emit_pyo3_cfgs(); - Ok(()) -} - -fn main() { - if let Err(e) = configure_pyo3() { - eprintln!("error: {}", e.report()); - std::process::exit(1) - } -} diff --git a/pyo3-macros-backend/src/defs.rs b/pyo3-macros-backend/src/defs.rs index 938a91c51f1..258029d63f7 100644 --- a/pyo3-macros-backend/src/defs.rs +++ b/pyo3-macros-backend/src/defs.rs @@ -420,15 +420,9 @@ pub const NUM: Proto = Proto { MethodProto::new("__imod__", "PyNumberIModProtocol") .args(&["Other"]) .has_self(), - // See https://bugs.python.org/issue36379 - #[cfg(Py_3_8)] MethodProto::new("__ipow__", "PyNumberIPowProtocol") .args(&["Other", "Modulo"]) .has_self(), - #[cfg(not(Py_3_8))] - MethodProto::new("__ipow__", "PyNumberIPowProtocol") - .args(&["Other"]) - .has_self(), MethodProto::new("__ilshift__", "PyNumberILShiftProtocol") .args(&["Other"]) .has_self(), diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 8c52369cdd8..dcc51e9f2b6 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -532,14 +532,8 @@ const __IMOD__: SlotDef = SlotDef::new("Py_nb_inplace_remainder", "binaryfunc") .arguments(&[Ty::Object]) .extract_error_mode(ExtractErrorMode::NotImplemented) .return_self(); -#[cfg(Py_3_8)] -const __IPOW__: SlotDef = SlotDef::new("Py_nb_inplace_power", "ternaryfunc") - .arguments(&[Ty::Object, Ty::Object]) - .extract_error_mode(ExtractErrorMode::NotImplemented) - .return_self(); -#[cfg(not(Py_3_8))] -const __IPOW__: SlotDef = SlotDef::new("Py_nb_inplace_power", "binaryfunc") - .arguments(&[Ty::Object]) +const __IPOW__: SlotDef = SlotDef::new("Py_nb_inplace_power", "ipowfunc") + .arguments(&[Ty::Object, Ty::IPowModulo]) .extract_error_mode(ExtractErrorMode::NotImplemented) .return_self(); const __ILSHIFT__: SlotDef = SlotDef::new("Py_nb_inplace_lshift", "binaryfunc") @@ -609,6 +603,7 @@ enum Ty { Object, MaybeNullObject, NonNullObject, + IPowModulo, CompareOp, Int, PyHashT, @@ -621,6 +616,7 @@ impl Ty { match self { Ty::Object | Ty::MaybeNullObject => quote! { *mut _pyo3::ffi::PyObject }, Ty::NonNullObject => quote! { ::std::ptr::NonNull<_pyo3::ffi::PyObject> }, + Ty::IPowModulo => quote! { _pyo3::impl_::pymethods::IPowModulo }, Ty::Int | Ty::CompareOp => quote! { ::std::os::raw::c_int }, Ty::PyHashT => quote! { _pyo3::ffi::Py_hash_t }, Ty::PySsizeT => quote! { _pyo3::ffi::Py_ssize_t }, @@ -673,6 +669,16 @@ impl Ty { ); extract_object(cls, arg.ty, ident, extract) } + Ty::IPowModulo => { + let extract = handle_error( + extract_error_mode, + py, + quote! { + #ident.extract(#py) + }, + ); + extract_object(cls, arg.ty, ident, extract) + } Ty::CompareOp => { let extract = handle_error( extract_error_mode, diff --git a/src/class/number.rs b/src/class/number.rs index 2dbe5403793..b320f0c1bcd 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -221,20 +221,12 @@ pub trait PyNumberProtocol<'p>: PyClass { { unimplemented!() } - #[cfg(Py_3_8)] fn __ipow__(&'p mut self, other: Self::Other, modulo: Option) -> Self::Result where Self: PyNumberIPowProtocol<'p>, { unimplemented!() } - #[cfg(not(Py_3_8))] - fn __ipow__(&'p mut self, other: Self::Other) -> Self::Result - where - Self: PyNumberIPowProtocol<'p>, - { - unimplemented!() - } fn __ilshift__(&'p mut self, other: Self::Other) -> Self::Result where Self: PyNumberILShiftProtocol<'p>, @@ -512,7 +504,7 @@ pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> { pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; - #[cfg(Py_3_8)] + // See https://bugs.python.org/issue36379 type Modulo: FromPyObject<'p>; } @@ -724,28 +716,30 @@ py_binary_self_func!(isub, PyNumberISubProtocol, T::__isub__); py_binary_self_func!(imul, PyNumberIMulProtocol, T::__imul__); py_binary_self_func!(imod, PyNumberIModProtocol, T::__imod__); -// See https://bugs.python.org/issue36379 #[doc(hidden)] -#[cfg(Py_3_8)] pub unsafe extern "C" fn ipow( slf: *mut ffi::PyObject, other: *mut ffi::PyObject, - modulo: *mut ffi::PyObject, + modulo: crate::impl_::pymethods::IPowModulo, ) -> *mut ffi::PyObject where T: for<'p> PyNumberIPowProtocol<'p>, { - // NOTE: Somehow __ipow__ causes SIGSEGV in Python < 3.8 when we extract, - // so we ignore it. It's the same as what CPython does. crate::callback_body!(py, { let slf_cell = py.from_borrowed_ptr::>(slf); let other = py.from_borrowed_ptr::(other); - let modulo = py.from_borrowed_ptr::(modulo); slf_cell .try_borrow_mut()? .__ipow__( extract_or_return_not_implemented!(other), - extract_or_return_not_implemented!(modulo), + match modulo.extract(py) { + Ok(value) => value, + Err(_) => { + let res = crate::ffi::Py_NotImplemented(); + crate::ffi::Py_INCREF(res); + return Ok(res); + } + }, ) .convert(py)?; ffi::Py_INCREF(slf); @@ -753,27 +747,6 @@ where }) } -#[doc(hidden)] -#[cfg(not(Py_3_8))] -pub unsafe extern "C" fn ipow( - slf: *mut ffi::PyObject, - other: *mut ffi::PyObject, -) -> *mut ffi::PyObject -where - T: for<'p> PyNumberIPowProtocol<'p>, -{ - crate::callback_body!(py, { - let slf_cell = py.from_borrowed_ptr::>(slf); - let other = py.from_borrowed_ptr::(other); - slf_cell - .try_borrow_mut()? - .__ipow__(extract_or_return_not_implemented!(other)) - .convert(py)?; - ffi::Py_INCREF(slf); - Ok::<_, PyErr>(slf) - }) -} - py_binary_self_func!(ilshift, PyNumberILShiftProtocol, T::__ilshift__); py_binary_self_func!(irshift, PyNumberIRShiftProtocol, T::__irshift__); py_binary_self_func!(iand, PyNumberIAndProtocol, T::__iand__); diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 428c71c6694..8d34e3d9a0f 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -204,3 +204,7 @@ mod cpython; #[cfg(not(Py_LIMITED_API))] pub use self::cpython::*; + +/// Helper to enable #[pymethods] to see the workaround for __ipow__ on Python 3.7 +#[doc(hidden)] +pub use crate::impl_::pymethods::ipowfunc; diff --git a/src/impl_.rs b/src/impl_.rs index d591886f901..3a35a8788b8 100644 --- a/src/impl_.rs +++ b/src/impl_.rs @@ -12,4 +12,6 @@ pub mod frompyobject; pub(crate) mod not_send; #[doc(hidden)] pub mod pyclass; +#[doc(hidden)] +pub mod pymethods; pub mod pymodule; diff --git a/src/impl_/pymethods.rs b/src/impl_/pymethods.rs new file mode 100644 index 00000000000..2a8d743a9c0 --- /dev/null +++ b/src/impl_/pymethods.rs @@ -0,0 +1,33 @@ +use crate::{ffi, FromPyObject, PyAny, PyResult, Python}; + +/// Python 3.8 and up - __ipow__ has modulo argument correctly populated. +#[cfg(Py_3_8)] +#[repr(transparent)] +pub struct IPowModulo(*mut ffi::PyObject); + +/// Python 3.7 and older - __ipow__ does not have modulo argument correctly populated. +#[cfg(not(Py_3_8))] +#[repr(transparent)] +pub struct IPowModulo(std::mem::MaybeUninit<*mut ffi::PyObject>); + +/// Helper to use as pymethod ffi definition +#[allow(non_camel_case_types)] +pub type ipowfunc = unsafe extern "C" fn( + arg1: *mut ffi::PyObject, + arg2: *mut ffi::PyObject, + arg3: IPowModulo, +) -> *mut ffi::PyObject; + +impl IPowModulo { + #[cfg(Py_3_8)] + #[inline] + pub fn extract<'a, T: FromPyObject<'a>>(self, py: Python<'a>) -> PyResult { + unsafe { py.from_borrowed_ptr::(self.0) }.extract() + } + + #[cfg(not(Py_3_8))] + #[inline] + pub fn extract<'a, T: FromPyObject<'a>>(self, py: Python<'a>) -> PyResult { + unsafe { py.from_borrowed_ptr::(ffi::Py_None()) }.extract() + } +} diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 7f317407a64..ed1870bbcbe 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -57,7 +57,6 @@ struct InPlaceOperations { value: u32, } -#[cfg(Py_3_8)] #[pymethods] impl InPlaceOperations { fn __repr__(&self) -> String { @@ -101,50 +100,6 @@ impl InPlaceOperations { } } -#[cfg(not(Py_3_8))] -#[pymethods] -impl InPlaceOperations { - fn __repr__(&self) -> String { - format!("IPO({:?})", self.value) - } - - fn __iadd__(&mut self, other: u32) { - self.value += other; - } - - fn __isub__(&mut self, other: u32) { - self.value -= other; - } - - fn __imul__(&mut self, other: u32) { - self.value *= other; - } - - fn __ilshift__(&mut self, other: u32) { - self.value <<= other; - } - - fn __irshift__(&mut self, other: u32) { - self.value >>= other; - } - - fn __iand__(&mut self, other: u32) { - self.value &= other; - } - - fn __ixor__(&mut self, other: u32) { - self.value ^= other; - } - - fn __ior__(&mut self, other: u32) { - self.value |= other; - } - - fn __ipow__(&mut self, other: u32) { - self.value = self.value.pow(other); - } -} - #[test] fn inplace_operations() { let gil = Python::acquire_gil(); @@ -550,7 +505,6 @@ mod return_not_implemented { #[pyclass] struct RichComparisonToSelf {} - #[cfg(Py_3_8)] #[pymethods] impl RichComparisonToSelf { fn __repr__(&self) -> &'static str { @@ -620,76 +574,6 @@ mod return_not_implemented { fn __ipow__(&mut self, _other: PyRef, _modulo: Option) {} } - #[cfg(not(Py_3_8))] - #[pymethods] - impl RichComparisonToSelf { - fn __repr__(&self) -> &'static str { - "RC_Self" - } - - fn __richcmp__(&self, other: PyRef, _op: CompareOp) -> PyObject { - other.py().None() - } - - fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __sub__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __mul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __matmul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __truediv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __floordiv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __mod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __pow__(slf: PyRef, _other: u8, _modulo: Option) -> PyRef { - slf - } - fn __lshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __rshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __divmod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __and__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __or__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - fn __xor__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { - slf - } - - // Inplace assignments - fn __iadd__(&mut self, _other: PyRef) {} - fn __isub__(&mut self, _other: PyRef) {} - fn __imul__(&mut self, _other: PyRef) {} - fn __imatmul__(&mut self, _other: PyRef) {} - fn __itruediv__(&mut self, _other: PyRef) {} - fn __ifloordiv__(&mut self, _other: PyRef) {} - fn __imod__(&mut self, _other: PyRef) {} - fn __ilshift__(&mut self, _other: PyRef) {} - fn __irshift__(&mut self, _other: PyRef) {} - fn __iand__(&mut self, _other: PyRef) {} - fn __ior__(&mut self, _other: PyRef) {} - fn __ixor__(&mut self, _other: PyRef) {} - fn __ipow__(&mut self, _other: PyRef) {} - } - fn _test_binary_dunder(dunder: &str) { let gil = Python::acquire_gil(); let py = gil.python(); diff --git a/tests/test_arithmetics_protos.rs b/tests/test_arithmetics_protos.rs index 02ff0173516..86b30c1385e 100644 --- a/tests/test_arithmetics_protos.rs +++ b/tests/test_arithmetics_protos.rs @@ -74,47 +74,6 @@ impl PyObjectProtocol for InPlaceOperations { } } -#[cfg(not(Py_3_8))] -#[pyproto] -impl PyNumberProtocol for InPlaceOperations { - fn __iadd__(&mut self, other: u32) { - self.value += other; - } - - fn __isub__(&mut self, other: u32) { - self.value -= other; - } - - fn __imul__(&mut self, other: u32) { - self.value *= other; - } - - fn __ilshift__(&mut self, other: u32) { - self.value <<= other; - } - - fn __irshift__(&mut self, other: u32) { - self.value >>= other; - } - - fn __iand__(&mut self, other: u32) { - self.value &= other; - } - - fn __ixor__(&mut self, other: u32) { - self.value ^= other; - } - - fn __ior__(&mut self, other: u32) { - self.value |= other; - } - - fn __ipow__(&mut self, other: u32) { - self.value = self.value.pow(other); - } -} - -#[cfg(Py_3_8)] #[pyproto] impl PyNumberProtocol for InPlaceOperations { fn __iadd__(&mut self, other: u32) { @@ -576,7 +535,6 @@ mod return_not_implemented { } } - #[cfg(Py_3_8)] #[pyproto] impl<'p> PyNumberProtocol<'p> for RichComparisonToSelf { fn __add__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { @@ -638,68 +596,6 @@ mod return_not_implemented { fn __ixor__(&'p mut self, _other: PyRef<'p, Self>) {} } - #[cfg(not(Py_3_8))] - #[pyproto] - impl<'p> PyNumberProtocol<'p> for RichComparisonToSelf { - fn __add__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __sub__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __mul__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __matmul__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __truediv__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __floordiv__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __mod__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __pow__(lhs: &'p PyAny, _other: u8, _modulo: Option) -> &'p PyAny { - lhs - } - fn __lshift__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __rshift__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __divmod__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __and__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __or__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - fn __xor__(lhs: &'p PyAny, _other: PyRef<'p, Self>) -> &'p PyAny { - lhs - } - - // Inplace assignments - fn __iadd__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __isub__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __imul__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __imatmul__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __itruediv__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __ifloordiv__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __imod__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __ipow__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __ilshift__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __irshift__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __iand__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __ior__(&'p mut self, _other: PyRef<'p, Self>) {} - fn __ixor__(&'p mut self, _other: PyRef<'p, Self>) {} - } - fn _test_binary_dunder(dunder: &str) { let gil = Python::acquire_gil(); let py = gil.python(); From 9ae31f2b8776e317dcc0fb1a2e0fcc30295c063d Mon Sep 17 00:00:00 2001 From: Aviram Hassan Date: Thu, 6 Jan 2022 09:59:10 +0200 Subject: [PATCH 3/4] fix tests --- src/test_hygiene/pymethods.rs | 2 +- tests/test_compile_error.rs | 1 + tests/ui/invalid_pymethod_proto_args_py.rs | 13 +++++++++++++ tests/ui/invalid_pymethod_proto_args_py.stderr | 5 +++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/ui/invalid_pymethod_proto_args_py.rs create mode 100644 tests/ui/invalid_pymethod_proto_args_py.stderr diff --git a/src/test_hygiene/pymethods.rs b/src/test_hygiene/pymethods.rs index fdd2f2b8223..e46112288b2 100644 --- a/src/test_hygiene/pymethods.rs +++ b/src/test_hygiene/pymethods.rs @@ -644,7 +644,7 @@ impl Dummy { fn __imod__(&mut self, _other: &Self) {} - fn __ipow__(&mut self, _other: &Self) {} + fn __ipow__(&mut self, _other: &Self, _modulo: ::std::option::Option) {} fn __ilshift__(&mut self, other: &Self) {} fn __irshift__(&mut self, other: &Self) {} diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 9801f97a1a3..e1af60bd4b5 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -31,6 +31,7 @@ fn _test_compile_errors() { t.compile_fail("tests/ui/reject_generics.rs"); t.compile_fail("tests/ui/not_send.rs"); t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs"); + t.compile_fail("tests/ui/invalid_pymethod_proto_args_py.rs"); tests_rust_1_49(&t); tests_rust_1_55(&t); diff --git a/tests/ui/invalid_pymethod_proto_args_py.rs b/tests/ui/invalid_pymethod_proto_args_py.rs new file mode 100644 index 00000000000..e5f9345f0b9 --- /dev/null +++ b/tests/ui/invalid_pymethod_proto_args_py.rs @@ -0,0 +1,13 @@ +use pyo3::prelude::*; + +#[pyclass] +struct MyClass {} + +#[pymethods] +impl MyClass { + fn __truediv__(&self, _py: Python) -> PyResult<()> { + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/invalid_pymethod_proto_args_py.stderr b/tests/ui/invalid_pymethod_proto_args_py.stderr new file mode 100644 index 00000000000..5661ed450d7 --- /dev/null +++ b/tests/ui/invalid_pymethod_proto_args_py.stderr @@ -0,0 +1,5 @@ +error: Expected 1 arguments, got 0 + --> tests/ui/invalid_pymethod_proto_args_py.rs:8:8 + | +8 | fn __truediv__(&self, _py: Python) -> PyResult<()> { + | ^^^^^^^^^^^ From cbfb5acc4327ee6bb516152684c6568e4b9cabb9 Mon Sep 17 00:00:00 2001 From: Aviram Hassan Date: Thu, 6 Jan 2022 10:02:53 +0200 Subject: [PATCH 4/4] fix guide --- src/ffi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 8d34e3d9a0f..82e59e9920f 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -205,6 +205,6 @@ mod cpython; #[cfg(not(Py_LIMITED_API))] pub use self::cpython::*; -/// Helper to enable #[pymethods] to see the workaround for __ipow__ on Python 3.7 +/// Helper to enable #\[pymethods\] to see the workaround for __ipow__ on Python 3.7 #[doc(hidden)] pub use crate::impl_::pymethods::ipowfunc;