From 4584a6900f9e77f62760f03453ab6ddc117b1335 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Thu, 9 May 2024 21:14:31 +0200 Subject: [PATCH] feature gate APIs using `into_gil_ref` (Part 2) --- guide/src/conversions/traits.md | 6 ++--- guide/src/exception.md | 4 +-- guide/src/memory.md | 8 ++++++ guide/src/migration.md | 2 +- guide/src/types.md | 2 ++ pytests/src/pyclasses.rs | 26 +----------------- pytests/src/sequence.rs | 2 +- pytests/tests/test_pyclasses.py | 11 -------- src/conversion.rs | 1 + src/conversions/chrono.rs | 2 +- src/conversions/std/osstr.rs | 5 ++-- src/conversions/std/path.rs | 5 ++-- src/derive_utils.rs | 2 +- src/err/mod.rs | 1 + src/exceptions.rs | 6 ++++- src/impl_/deprecations.rs | 32 +++++++--------------- src/impl_/extract_argument.rs | 8 +++--- src/instance.rs | 4 +-- src/pycell.rs | 4 +-- src/tests/hygiene/pyfunction.rs | 1 + src/types/any.rs | 15 ++++++----- src/types/bytearray.rs | 4 ++- src/types/float.rs | 2 +- src/types/iterator.rs | 4 +-- src/types/memoryview.rs | 4 +-- src/types/mod.rs | 14 +++++++--- src/types/num.rs | 2 +- tests/test_class_basics.rs | 15 +---------- tests/test_compile_error.rs | 10 +++++-- tests/test_methods.rs | 7 ----- tests/test_module.rs | 33 ----------------------- tests/test_proto_methods.rs | 4 +-- tests/ui/deprecations.stderr | 6 ----- tests/ui/invalid_result_conversion.stderr | 2 +- 34 files changed, 92 insertions(+), 162 deletions(-) diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index 65a5d150e79..95d16faaaa6 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -265,7 +265,7 @@ use pyo3::prelude::*; #[derive(FromPyObject)] # #[derive(Debug)] -enum RustyEnum<'a> { +enum RustyEnum<'py> { Int(usize), // input is a positive int String(String), // input is a string IntTuple(usize, usize), // input is a 2-tuple with positive ints @@ -284,7 +284,7 @@ enum RustyEnum<'a> { b: usize, }, #[pyo3(transparent)] - CatchAll(&'a PyAny), // This extraction never fails + CatchAll(Bound<'py, PyAny>), // This extraction never fails } # # use pyo3::types::{PyBytes, PyString}; @@ -394,7 +394,7 @@ enum RustyEnum<'a> { # assert_eq!( # b"text", # match rust_thing { -# RustyEnum::CatchAll(i) => i.downcast::()?.as_bytes(), +# RustyEnum::CatchAll(ref i) => i.downcast::()?.as_bytes(), # other => unreachable!("Error extracting: {:?}", other), # } # ); diff --git a/guide/src/exception.md b/guide/src/exception.md index 3e2f5034897..1a68e24086f 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -128,5 +128,5 @@ defines exceptions for several standard library modules. [`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html [`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html [`PyErr::from_value`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_value -[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance -[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance_of +[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance +[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance_of diff --git a/guide/src/memory.md b/guide/src/memory.md index a6640e65cf3..67a78d3be68 100644 --- a/guide/src/memory.md +++ b/guide/src/memory.md @@ -34,9 +34,11 @@ held. (If PyO3 could not assume this, every PyO3 API would need to take a very simple and easy-to-understand programs like this: ```rust +# #![allow(unused_imports)] # use pyo3::prelude::*; # use pyo3::types::PyString; # fn main() -> PyResult<()> { +# #[cfg(feature = "gil-refs")] Python::with_gil(|py| -> PyResult<()> { #[allow(deprecated)] // py.eval() is part of the GIL Refs API let hello = py @@ -57,9 +59,11 @@ it owns are decreased, releasing them to the Python garbage collector. Most of the time we don't have to think about this, but consider the following: ```rust +# #![allow(unused_imports)] # use pyo3::prelude::*; # use pyo3::types::PyString; # fn main() -> PyResult<()> { +# #[cfg(feature = "gil-refs")] Python::with_gil(|py| -> PyResult<()> { for _ in 0..10 { #[allow(deprecated)] // py.eval() is part of the GIL Refs API @@ -96,9 +100,11 @@ In general we don't want unbounded memory growth during loops! One workaround is to acquire and release the GIL with each iteration of the loop. ```rust +# #![allow(unused_imports)] # use pyo3::prelude::*; # use pyo3::types::PyString; # fn main() -> PyResult<()> { +# #[cfg(feature = "gil-refs")] for _ in 0..10 { Python::with_gil(|py| -> PyResult<()> { #[allow(deprecated)] // py.eval() is part of the GIL Refs API @@ -118,9 +124,11 @@ times. Another workaround is to work with the `GILPool` object directly, but this is unsafe. ```rust +# #![allow(unused_imports)] # use pyo3::prelude::*; # use pyo3::types::PyString; # fn main() -> PyResult<()> { +# #[cfg(feature = "gil-refs")] Python::with_gil(|py| -> PyResult<()> { for _ in 0..10 { #[allow(deprecated)] // `new_pool` is not needed in code not using the GIL Refs API diff --git a/guide/src/migration.md b/guide/src/migration.md index 0a048bf02bc..2317f85185e 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -54,7 +54,7 @@ pyo3 = { version = "0.21", features = ["gil-refs"] } The `PyTryFrom` trait has aged poorly, its `try_from` method now conflicts with `TryFrom::try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`. -To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods [`PyAny::downcast`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast) and [`PyAny::downcast_exact`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast_exact) no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively. +To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods `PyAny::downcast` and `PyAny::downcast_exact` no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively. To migrate, switch all type casts to use `obj.downcast()` instead of `try_from(obj)` (and similar for `downcast_exact`). diff --git a/guide/src/types.md b/guide/src/types.md index d28fb7e15d6..20e5e76a330 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -467,8 +467,10 @@ let _: &mut MyClass = &mut *py_ref_mut; `PyCell` was also accessed like a Python-native type. ```rust +#![allow(unused_imports)] # use pyo3::prelude::*; # #[pyclass] struct MyClass { } +# #[cfg(feature = "gil-refs")] # Python::with_gil(|py| -> PyResult<()> { #[allow(deprecated)] // &PyCell is part of the deprecate GIL Refs API let cell: &PyCell = PyCell::new(py, MyClass {})?; diff --git a/pytests/src/pyclasses.rs b/pytests/src/pyclasses.rs index ac817627cfe..6338596b481 100644 --- a/pytests/src/pyclasses.rs +++ b/pytests/src/pyclasses.rs @@ -63,30 +63,6 @@ impl AssertingBaseClass { } } -#[allow(deprecated)] -mod deprecated { - use super::*; - - #[pyclass(subclass)] - #[derive(Clone, Debug)] - pub struct AssertingBaseClassGilRef; - - #[pymethods] - impl AssertingBaseClassGilRef { - #[new] - #[classmethod] - fn new(cls: &PyType, expected_type: &PyType) -> PyResult { - if !cls.is(expected_type) { - return Err(PyValueError::new_err(format!( - "{:?} != {:?}", - cls, expected_type - ))); - } - Ok(Self) - } - } -} - #[pyclass] struct ClassWithoutConstructor; @@ -95,7 +71,7 @@ pub fn pyclasses(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - m.add_class::()?; m.add_class::()?; + Ok(()) } diff --git a/pytests/src/sequence.rs b/pytests/src/sequence.rs index 0e48a161bd3..f552b4048b8 100644 --- a/pytests/src/sequence.rs +++ b/pytests/src/sequence.rs @@ -12,7 +12,7 @@ fn array_to_array_i32(arr: [i32; 3]) -> [i32; 3] { } #[pyfunction] -fn vec_to_vec_pystring(vec: Vec<&PyString>) -> Vec<&PyString> { +fn vec_to_vec_pystring(vec: Vec>) -> Vec> { vec } diff --git a/pytests/tests/test_pyclasses.py b/pytests/tests/test_pyclasses.py index 74f883e3808..efef178d489 100644 --- a/pytests/tests/test_pyclasses.py +++ b/pytests/tests/test_pyclasses.py @@ -65,17 +65,6 @@ def test_new_classmethod(): _ = AssertingSubClass(expected_type=str) -def test_new_classmethod_gil_ref(): - class AssertingSubClass(pyclasses.AssertingBaseClassGilRef): - pass - - # The `AssertingBaseClass` constructor errors if it is not passed the - # relevant subclass. - _ = AssertingSubClass(expected_type=AssertingSubClass) - with pytest.raises(ValueError): - _ = AssertingSubClass(expected_type=str) - - class ClassWithoutConstructorPy: def __new__(cls): raise TypeError("No constructor defined") diff --git a/src/conversion.rs b/src/conversion.rs index 8644db84289..6e116af7303 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -345,6 +345,7 @@ where } #[allow(deprecated)] +#[cfg(feature = "gil-refs")] impl<'py, T> FromPyObject<'py> for &'py crate::PyCell where T: PyClass, diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 544d1cf2663..2e220681951 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -347,7 +347,7 @@ impl FromPyObject<'_> for FixedOffset { /// does not supports microseconds. fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] - let ob: &PyTzInfo = ob.extract()?; + let ob = ob.downcast::()?; #[cfg(Py_LIMITED_API)] check_type(ob, &DatetimeTypes::get(ob.py()).tzinfo, "PyTzInfo")?; diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index b9382688589..4565c3fbd94 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -147,6 +147,7 @@ impl<'a> IntoPy for &'a OsString { #[cfg(test)] mod tests { + use crate::types::{PyAnyMethods, PyStringMethods}; use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject}; use std::fmt::Debug; use std::{ @@ -179,7 +180,7 @@ mod tests { Python::with_gil(|py| { fn test_roundtrip + Debug>(py: Python<'_>, obj: T) { let pyobject = obj.to_object(py); - let pystring: &PyString = pyobject.extract(py).unwrap(); + let pystring = pyobject.downcast_bound::(py).unwrap(); assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy()); let roundtripped_obj: OsString = pystring.extract().unwrap(); assert_eq!(obj.as_ref(), roundtripped_obj.as_os_str()); @@ -200,7 +201,7 @@ mod tests { obj: T, ) { let pyobject = obj.clone().into_py(py); - let pystring: &PyString = pyobject.extract(py).unwrap(); + let pystring = pyobject.downcast_bound::(py).unwrap(); assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy()); let roundtripped_obj: OsString = pystring.extract().unwrap(); assert!(obj.as_ref() == roundtripped_obj.as_os_str()); diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 5d832e89575..d7f3121ea10 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -64,6 +64,7 @@ impl<'a> IntoPy for &'a PathBuf { #[cfg(test)] mod tests { + use crate::types::{PyAnyMethods, PyStringMethods}; use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject}; use std::borrow::Cow; use std::fmt::Debug; @@ -95,7 +96,7 @@ mod tests { Python::with_gil(|py| { fn test_roundtrip + Debug>(py: Python<'_>, obj: T) { let pyobject = obj.to_object(py); - let pystring: &PyString = pyobject.extract(py).unwrap(); + let pystring = pyobject.downcast_bound::(py).unwrap(); assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy()); let roundtripped_obj: PathBuf = pystring.extract().unwrap(); assert_eq!(obj.as_ref(), roundtripped_obj.as_path()); @@ -116,7 +117,7 @@ mod tests { obj: T, ) { let pyobject = obj.clone().into_py(py); - let pystring: &PyString = pyobject.extract(py).unwrap(); + let pystring = pyobject.downcast_bound::(py).unwrap(); assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy()); let roundtripped_obj: PathBuf = pystring.extract().unwrap(); assert_eq!(obj.as_ref(), roundtripped_obj.as_path()); diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 4ccb38f901b..a47f489ceb8 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -13,7 +13,7 @@ impl<'a> PyFunctionArguments<'a> { match self { PyFunctionArguments::Python(py) => (py, None), PyFunctionArguments::PyModule(module) => { - let py = module.py(); + let py = crate::PyNativeType::py(module); (py, Some(module)) } } diff --git a/src/err/mod.rs b/src/err/mod.rs index d923761af1d..200c180e9b0 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -1000,6 +1000,7 @@ where } /// Convert `PyDowncastError` to Python `TypeError`. +#[cfg(feature = "gil-refs")] impl<'a> std::convert::From> for PyErr { fn from(err: PyDowncastError<'_>) -> PyErr { let args = PyDowncastErrorArguments { diff --git a/src/exceptions.rs b/src/exceptions.rs index 367022927c5..b44a5c5a3fe 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -32,6 +32,7 @@ macro_rules! impl_exception_boilerplate { $crate::impl_exception_boilerplate_bound!($name); + #[cfg(feature = "gil-refs")] impl ::std::error::Error for $name { fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> { unsafe { @@ -58,6 +59,7 @@ macro_rules! impl_exception_boilerplate_bound { /// /// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3" #[inline] + #[allow(dead_code)] pub fn new_err(args: A) -> $crate::PyErr where A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static, @@ -881,7 +883,9 @@ mod tests { use super::*; use crate::types::any::PyAnyMethods; use crate::types::{IntoPyDict, PyDict}; - use crate::{PyErr, PyNativeType}; + use crate::PyErr; + #[cfg(feature = "gil-refs")] + use crate::PyNativeType; import_exception_bound!(socket, gaierror); import_exception_bound!(email.errors, MessageError); diff --git a/src/impl_/deprecations.rs b/src/impl_/deprecations.rs index 9eb1da05c92..650e01ce729 100644 --- a/src/impl_/deprecations.rs +++ b/src/impl_/deprecations.rs @@ -29,39 +29,27 @@ impl GilRefs { } impl GilRefs> { - #[cfg_attr( - not(feature = "gil-refs"), - deprecated(since = "0.21.0", note = "use `wrap_pyfunction_bound!` instead") - )] + #[deprecated(since = "0.21.0", note = "use `wrap_pyfunction_bound!` instead")] pub fn is_python(&self) {} } impl GilRefs { - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "use `&Bound<'_, T>` instead for this function argument" - ) + #[deprecated( + since = "0.21.0", + note = "use `&Bound<'_, T>` instead for this function argument" )] pub fn function_arg(&self) {} - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor" - ) + #[deprecated( + since = "0.21.0", + note = "use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor" )] pub fn from_py_with_arg(&self) {} } impl OptionGilRefs> { - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "use `Option<&Bound<'_, T>>` instead for this function argument" - ) + #[deprecated( + since = "0.21.0", + note = "use `Option<&Bound<'_, T>>` instead for this function argument" )] pub fn function_arg(&self) {} } diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index 485b8645086..24f60ffa56b 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -792,7 +792,7 @@ fn push_parameter_list(msg: &mut String, parameter_names: &[&str]) { mod tests { use crate::{ types::{IntoPyDict, PyTuple}, - PyAny, Python, + Bound, PyAny, Python, }; use super::{push_parameter_list, FunctionDescription, NoVarargs, NoVarkeywords}; @@ -809,7 +809,7 @@ mod tests { }; Python::with_gil(|py| { - let args = PyTuple::new_bound(py, Vec::<&PyAny>::new()); + let args = PyTuple::new_bound(py, Vec::>::new()); let kwargs = [("foo", 0u8)].into_py_dict_bound(py); let err = unsafe { function_description @@ -840,7 +840,7 @@ mod tests { }; Python::with_gil(|py| { - let args = PyTuple::new_bound(py, Vec::<&PyAny>::new()); + let args = PyTuple::new_bound(py, Vec::>::new()); let kwargs = [(1u8, 1u8)].into_py_dict_bound(py); let err = unsafe { function_description @@ -871,7 +871,7 @@ mod tests { }; Python::with_gil(|py| { - let args = PyTuple::new_bound(py, Vec::<&PyAny>::new()); + let args = PyTuple::new_bound(py, Vec::>::new()); let mut output = [None, None]; let err = unsafe { function_description.extract_arguments_tuple_dict::( diff --git a/src/instance.rs b/src/instance.rs index 81ceaa95546..e160de3a314 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1283,7 +1283,7 @@ impl Py { } /// Returns whether `self` and `other` point to the same object. To compare - /// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq). + /// the equality of two objects (the `==` operator), use [`eq`](PyAnyMethods::eq). /// /// This is equivalent to the Python expression `self is other`. #[inline] @@ -2142,7 +2142,7 @@ a = A() fn test_is_ellipsis() { Python::with_gil(|py| { let v = py - .eval("...", None, None) + .eval_bound("...", None, None) .map_err(|e| e.display(py)) .unwrap() .to_object(py); diff --git a/src/pycell.rs b/src/pycell.rs index 80ccff0a030..e0088d9b523 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -41,7 +41,7 @@ //! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more), //! using [`PyCell`] under the hood: //! -//! ```rust +//! ```rust,ignore //! # use pyo3::prelude::*; //! # #[pyclass] //! # struct Number { @@ -148,7 +148,7 @@ //! ``` //! //! It is better to write that function like this: -//! ```rust +//! ```rust,ignore //! # #![allow(deprecated)] //! # use pyo3::prelude::*; //! # #[pyclass] diff --git a/src/tests/hygiene/pyfunction.rs b/src/tests/hygiene/pyfunction.rs index edc8b6e35d3..c1bca213933 100644 --- a/src/tests/hygiene/pyfunction.rs +++ b/src/tests/hygiene/pyfunction.rs @@ -8,6 +8,7 @@ fn do_something(x: i32) -> crate::PyResult { } #[test] +#[cfg(feature = "gil-refs")] fn invoke_wrap_pyfunction() { crate::Python::with_gil(|py| { #[allow(deprecated)] diff --git a/src/types/any.rs b/src/types/any.rs index 1854308ae7f..17837835be1 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -1,15 +1,17 @@ use crate::class::basic::CompareOp; use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPy, ToPyObject}; -use crate::err::{DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyResult}; +use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult}; use crate::exceptions::{PyAttributeError, PyTypeError}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Bound; use crate::py_result_ext::PyResultExt; -use crate::type_object::{HasPyGilRef, PyTypeCheck, PyTypeInfo}; +use crate::type_object::{PyTypeCheck, PyTypeInfo}; #[cfg(not(any(PyPy, GraalPy)))] use crate::types::PySuper; use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType}; -use crate::{err, ffi, Py, PyNativeType, Python}; +use crate::{err, ffi, Py, Python}; +#[cfg(feature = "gil-refs")] +use crate::{err::PyDowncastError, type_object::HasPyGilRef, PyNativeType}; use std::cell::UnsafeCell; use std::cmp::Ordering; use std::os::raw::c_int; @@ -66,6 +68,7 @@ pyobject_native_type_extract!(PyAny); pyobject_native_type_sized!(PyAny, ffi::PyObject); +#[cfg(feature = "gil-refs")] impl PyAny { /// Returns whether `self` and `other` point to the same object. To compare /// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq). @@ -942,7 +945,7 @@ impl PyAny { #[doc(alias = "PyAny")] pub trait PyAnyMethods<'py>: crate::sealed::Sealed { /// Returns whether `self` and `other` point to the same object. To compare - /// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq). + /// the equality of two objects (the `==` operator), use [`eq`](PyAnyMethods::eq). /// /// This is equivalent to the Python expression `self is other`. fn is(&self, other: &T) -> bool; @@ -1589,10 +1592,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed { /// Downcast this `PyAny` to a concrete Python type or pyclass (but not a subclass of it). /// - /// It is almost always better to use [`PyAny::downcast`] because it accounts for Python + /// It is almost always better to use [`PyAnyMethods::downcast`] because it accounts for Python /// subtyping. Use this method only when you do not want to allow subtypes. /// - /// The advantage of this method over [`PyAny::downcast`] is that it is faster. The implementation + /// The advantage of this method over [`PyAnyMethods::downcast`] is that it is faster. The implementation /// of `downcast_exact` uses the equivalent of the Python expression `type(self) is T`, whereas /// `downcast` uses `isinstance(self, T)`. /// diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index bdf677c9019..ef20509e629 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -3,7 +3,9 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::{Borrowed, Bound}; use crate::py_result_ext::PyResultExt; use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, PyAny, PyNativeType, Python}; +#[cfg(feature = "gil-refs")] +use crate::AsPyPointer; +use crate::{ffi, PyAny, PyNativeType, Python}; use std::os::raw::c_char; use std::slice; diff --git a/src/types/float.rs b/src/types/float.rs index 3a64694a624..2ed3d2921b9 100644 --- a/src/types/float.rs +++ b/src/types/float.rs @@ -11,7 +11,7 @@ use super::any::PyAnyMethods; /// Represents a Python `float` object. /// /// You can usually avoid directly working with this type -/// by using [`ToPyObject`] and [`extract`](PyAny::extract) +/// by using [`ToPyObject`] and [`extract`](PyAnyMethods::extract) /// with `f32`/`f64`. #[repr(transparent)] pub struct PyFloat(PyAny); diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 6131033af7d..4562efde3f8 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,9 +1,9 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Borrowed; use crate::py_result_ext::PyResultExt; -use crate::{ffi, AsPyPointer, Bound, PyAny, PyErr, PyResult, PyTypeCheck}; +use crate::{ffi, Bound, PyAny, PyErr, PyResult, PyTypeCheck}; #[cfg(feature = "gil-refs")] -use crate::{PyDowncastError, PyNativeType}; +use crate::{AsPyPointer, PyDowncastError, PyNativeType}; /// A Python iterator object. /// diff --git a/src/types/memoryview.rs b/src/types/memoryview.rs index 31afb372d7f..320b3f9f70b 100644 --- a/src/types/memoryview.rs +++ b/src/types/memoryview.rs @@ -1,9 +1,9 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; +use crate::{ffi, Bound, PyAny}; #[cfg(feature = "gil-refs")] -use crate::PyNativeType; -use crate::{ffi, AsPyPointer, Bound, PyAny}; +use crate::{AsPyPointer, PyNativeType}; /// Represents a Python `memoryview`. #[repr(transparent)] diff --git a/src/types/mod.rs b/src/types/mod.rs index 38c9238961d..2203ccdf2dc 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -130,7 +130,8 @@ macro_rules! pyobject_native_type_base( fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { - let s = self.repr().or(::std::result::Result::Err(::std::fmt::Error))?; + use $crate::{PyNativeType, types::{PyAnyMethods, PyStringMethods}}; + let s = self.as_borrowed().repr().or(::std::result::Result::Err(::std::fmt::Error))?; f.write_str(&s.to_string_lossy()) } } @@ -139,19 +140,20 @@ macro_rules! pyobject_native_type_base( fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { - use $crate::PyNativeType; - match self.str() { + use $crate::{PyNativeType, types::{PyAnyMethods, PyStringMethods, PyTypeMethods}}; + match self.as_borrowed().str() { ::std::result::Result::Ok(s) => return f.write_str(&s.to_string_lossy()), ::std::result::Result::Err(err) => err.write_unraisable_bound(self.py(), ::std::option::Option::Some(&self.as_borrowed())), } - match self.get_type().name() { + match self.as_borrowed().get_type().name() { ::std::result::Result::Ok(name) => ::std::write!(f, "", name), ::std::result::Result::Err(_err) => f.write_str(""), } } } + #[cfg(feature = "gil-refs")] impl<$($generics,)*> $crate::ToPyObject for $name { #[inline] @@ -196,6 +198,7 @@ macro_rules! pyobject_native_type_named ( // FIXME https://github.com/PyO3/pyo3/issues/3903 #[allow(unknown_lints, non_local_definitions)] + #[cfg(feature = "gil-refs")] impl<$($generics,)*> $crate::IntoPy<$crate::Py<$name>> for &'_ $name { #[inline] fn into_py(self, py: $crate::Python<'_>) -> $crate::Py<$name> { @@ -205,6 +208,7 @@ macro_rules! pyobject_native_type_named ( // FIXME https://github.com/PyO3/pyo3/issues/3903 #[allow(unknown_lints, non_local_definitions)] + #[cfg(feature = "gil-refs")] impl<$($generics,)*> ::std::convert::From<&'_ $name> for $crate::Py<$name> { #[inline] fn from(other: &$name) -> Self { @@ -215,6 +219,7 @@ macro_rules! pyobject_native_type_named ( // FIXME https://github.com/PyO3/pyo3/issues/3903 #[allow(unknown_lints, non_local_definitions)] + #[cfg(feature = "gil-refs")] impl<'a, $($generics,)*> ::std::convert::From<&'a $name> for &'a $crate::PyAny { fn from(ob: &'a $name) -> Self { unsafe{&*(ob as *const $name as *const $crate::PyAny)} @@ -271,6 +276,7 @@ macro_rules! pyobject_native_type_extract { ($name:ty $(;$generics:ident)*) => { // FIXME https://github.com/PyO3/pyo3/issues/3903 #[allow(unknown_lints, non_local_definitions)] + #[cfg(feature = "gil-refs")] impl<'py, $($generics,)*> $crate::FromPyObject<'py> for &'py $name { #[inline] fn extract_bound(obj: &$crate::Bound<'py, $crate::PyAny>) -> $crate::PyResult { diff --git a/src/types/num.rs b/src/types/num.rs index 26748f7d1c7..924d4b2c593 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -4,7 +4,7 @@ use crate::{ffi, PyAny}; /// /// You can usually avoid directly working with this type /// by using [`ToPyObject`](crate::conversion::ToPyObject) -/// and [`extract`](PyAny::extract) +/// and [`extract`](super::PyAnyMethods::extract) /// with the primitive Rust integer types. #[repr(transparent)] pub struct PyLong(PyAny); diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index 8ff61bd2d6b..b7bee2638ca 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -310,15 +310,6 @@ impl ClassWithFromPyWithMethods { argument } - #[classmethod] - #[cfg(feature = "gil-refs")] - fn classmethod_gil_ref( - _cls: &PyType, - #[pyo3(from_py_with = "PyAny::len")] argument: usize, - ) -> usize { - argument - } - #[staticmethod] fn staticmethod(#[pyo3(from_py_with = "get_length")] argument: usize) -> usize { argument @@ -333,19 +324,15 @@ impl ClassWithFromPyWithMethods { fn test_pymethods_from_py_with() { Python::with_gil(|py| { let instance = Py::new(py, ClassWithFromPyWithMethods {}).unwrap(); - let has_gil_refs = cfg!(feature = "gil-refs"); py_run!( py, - instance - has_gil_refs, + instance, r#" arg = {1: 1, 2: 3} assert instance.instance_method(arg) == 2 assert instance.classmethod(arg) == 2 - if has_gil_refs: - assert instance.classmethod_gil_ref(arg) == 2 assert instance.staticmethod(arg) == 2 assert 42 in instance diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 30e77888cfd..975d26009a5 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -20,7 +20,7 @@ fn test_compile_errors() { t.compile_fail("tests/ui/invalid_pymethod_names.rs"); t.compile_fail("tests/ui/invalid_pymodule_args.rs"); t.compile_fail("tests/ui/reject_generics.rs"); - #[cfg(not(feature = "gil-refs"))] + #[cfg(feature = "gil-refs")] t.compile_fail("tests/ui/deprecations.rs"); t.compile_fail("tests/ui/invalid_closure.rs"); t.compile_fail("tests/ui/pyclass_send.rs"); @@ -38,7 +38,13 @@ fn test_compile_errors() { t.compile_fail("tests/ui/invalid_pymethod_receiver.rs"); t.compile_fail("tests/ui/missing_intopy.rs"); // adding extra error conversion impls changes the output - #[cfg(not(any(windows, feature = "eyre", feature = "anyhow", Py_LIMITED_API)))] + #[cfg(not(any( + windows, + feature = "eyre", + feature = "anyhow", + feature = "gil-refs", + Py_LIMITED_API + )))] t.compile_fail("tests/ui/invalid_result_conversion.rs"); t.compile_fail("tests/ui/not_send.rs"); t.compile_fail("tests/ui/not_send2.rs"); diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 2b5396e9ee4..c1610be3dd6 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -77,13 +77,6 @@ impl ClassMethod { Ok(format!("{}.method()!", cls.qualname()?)) } - #[classmethod] - /// Test class method. - #[cfg(feature = "gil-refs")] - fn method_gil_ref(cls: &PyType) -> PyResult { - Ok(format!("{}.method()!", cls.qualname()?)) - } - #[classmethod] fn method_owned(cls: Py) -> PyResult { let qualname = Python::with_gil(|gil| cls.bind(gil).qualname())?; diff --git a/tests/test_module.rs b/tests/test_module.rs index 5760c3ebaf3..b2487cfd8b3 100644 --- a/tests/test_module.rs +++ b/tests/test_module.rs @@ -371,13 +371,6 @@ fn pyfunction_with_module<'py>(module: &Bound<'py, PyModule>) -> PyResult PyResult<&str> { - module.name() -} - #[pyfunction] #[pyo3(pass_module)] fn pyfunction_with_module_owned( @@ -426,28 +419,14 @@ fn pyfunction_with_module_and_args_kwargs<'py>( .map(|s| (s, args.len(), kwargs.map(|d| d.len()))) } -#[pyfunction] -#[pyo3(pass_module)] -#[cfg(feature = "gil-refs")] -fn pyfunction_with_pass_module_in_attribute(module: &PyModule) -> PyResult<&str> { - module.name() -} - #[pymodule] fn module_with_functions_with_module(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(pyfunction_with_module, m)?)?; - #[cfg(feature = "gil-refs")] - m.add_function(wrap_pyfunction!(pyfunction_with_module_gil_ref, m)?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module_owned, m)?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module_and_py, m)?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module_and_arg, m)?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module_and_default_arg, m)?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module_and_args_kwargs, m)?)?; - #[cfg(feature = "gil-refs")] - m.add_function(wrap_pyfunction!( - pyfunction_with_pass_module_in_attribute, - m - )?)?; m.add_function(wrap_pyfunction!(pyfunction_with_module, m)?)?; Ok(()) } @@ -461,12 +440,6 @@ fn test_module_functions_with_module() { m, "m.pyfunction_with_module() == 'module_with_functions_with_module'" ); - #[cfg(feature = "gil-refs")] - py_assert!( - py, - m, - "m.pyfunction_with_module_gil_ref() == 'module_with_functions_with_module'" - ); py_assert!( py, m, @@ -489,12 +462,6 @@ fn test_module_functions_with_module() { "m.pyfunction_with_module_and_args_kwargs(1, x=1, y=2) \ == ('module_with_functions_with_module', 1, 2)" ); - #[cfg(feature = "gil-refs")] - py_assert!( - py, - m, - "m.pyfunction_with_pass_module_in_attribute() == 'module_with_functions_with_module'" - ); }); } diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index c5d7306086d..5f0fa105e1f 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -247,9 +247,9 @@ fn mapping() { } #[derive(FromPyObject)] -enum SequenceIndex<'a> { +enum SequenceIndex<'py> { Integer(isize), - Slice(&'a PySlice), + Slice(Bound<'py, PySlice>), } #[pyclass] diff --git a/tests/ui/deprecations.stderr b/tests/ui/deprecations.stderr index 9c61c26581e..dc21b595743 100644 --- a/tests/ui/deprecations.stderr +++ b/tests/ui/deprecations.stderr @@ -10,12 +10,6 @@ note: the lint level is defined here 1 | #![deny(deprecated)] | ^^^^^^^^^^ -error: use of deprecated struct `pyo3::PyCell`: `PyCell` was merged into `Bound`, use that instead; see the migration guide for more info - --> tests/ui/deprecations.rs:23:30 - | -23 | fn method_gil_ref(_slf: &PyCell) {} - | ^^^^^^ - error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor --> tests/ui/deprecations.rs:42:44 | diff --git a/tests/ui/invalid_result_conversion.stderr b/tests/ui/invalid_result_conversion.stderr index 9695c5e6a19..8da8f49fac3 100644 --- a/tests/ui/invalid_result_conversion.stderr +++ b/tests/ui/invalid_result_conversion.stderr @@ -9,10 +9,10 @@ error[E0277]: the trait bound `PyErr: From` is not satisfied > > > - >> >> >> > + > and $N others = note: required for `MyError` to implement `Into` = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)