From 367eeaeeaba7f9422f3e4af005b759b840b7f7ec Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 30 Jan 2024 10:31:34 +0000 Subject: [PATCH] add `bound` method variants for `PyTypeInfo` --- guide/src/class/numeric.md | 2 +- guide/src/migration.md | 11 ++++--- src/conversions/chrono_tz.rs | 5 ++- src/conversions/std/ipaddr.rs | 4 +-- src/err/mod.rs | 20 +++++++---- src/marker.rs | 2 +- src/pycell.rs | 4 +-- src/sync.rs | 6 ++-- src/tests/common.rs | 4 +-- src/type_object.rs | 62 ++++++++++++++++++++++++++++++++--- src/types/any.rs | 12 +++---- src/types/ellipsis.rs | 15 +++++---- src/types/iterator.rs | 2 +- src/types/mapping.rs | 10 +++--- src/types/mod.rs | 4 +-- src/types/none.rs | 18 ++++++---- src/types/notimplemented.rs | 15 +++++---- src/types/pysuper.rs | 3 +- src/types/sequence.rs | 16 ++++----- src/types/typeobject.rs | 4 +-- 20 files changed, 144 insertions(+), 75 deletions(-) diff --git a/guide/src/class/numeric.md b/guide/src/class/numeric.md index ee17ea10bd9..6f2e6e18f86 100644 --- a/guide/src/class/numeric.md +++ b/guide/src/class/numeric.md @@ -388,7 +388,7 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> { # fn main() -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> { # let globals = PyModule::import(py, "__main__")?.dict(); -# globals.set_item("Number", Number::type_object(py))?; +# globals.set_item("Number", Number::type_object_bound(py))?; # # py.run(SCRIPT, Some(globals), None)?; # Ok(()) diff --git a/guide/src/migration.md b/guide/src/migration.md index 6a151b99164..d88a765a16a 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -210,6 +210,7 @@ To minimise breakage of code using the GIL-Refs API, the `Bound` smart pointe For example, the following APIs have gained updated variants: - `PyList::new`, `PyTyple::new` and similar constructors have replacements `PyList::new_bound`, `PyTuple::new_bound` etc. - `FromPyObject::extract` has a new `FromPyObject::extract_bound` (see the section below) +- The `PyTypeInfo` trait has had new `_bound` methods added to accept / return `Bound`. Because the new `Bound` API brings ownership out of the PyO3 framework and into user code, there are a few places where user code is expected to need to adjust while switching to the new API: - Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count) @@ -245,6 +246,8 @@ impl<'py> FromPyObject<'py> for MyType { The expectation is that in 0.22 `extract_bound` will have the default implementation removed and in 0.23 `extract` will be removed. + + ## from 0.19.* to 0.20 ### Drop support for older technologies @@ -656,7 +659,7 @@ To migrate, update trait bounds and imports from `PyTypeObject` to `PyTypeInfo`. Before: -```rust,compile_fail +```rust,ignore use pyo3::Python; use pyo3::type_object::PyTypeObject; use pyo3::types::PyType; @@ -668,7 +671,7 @@ fn get_type_object(py: Python<'_>) -> &PyType { After -```rust +```rust,ignore use pyo3::{Python, PyTypeInfo}; use pyo3::types::PyType; @@ -995,13 +998,13 @@ makes it possible to interact with Python exception objects. The new types also have names starting with the "Py" prefix. For example, before: -```rust,compile_fail +```rust,ignore let err: PyErr = TypeError::py_err("error message"); ``` After: -```rust,compile_fail +```rust,ignore # use pyo3::{PyErr, PyResult, Python, type_object::PyTypeObject}; # use pyo3::exceptions::{PyBaseException, PyTypeError}; # Python::with_gil(|py| -> PyResult<()> { diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index 108dae253e3..791be15c96d 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -35,8 +35,7 @@ //! ``` use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; -use crate::types::any::PyAnyMethods; -use crate::types::PyType; +use crate::types::{any::PyAnyMethods, PyType}; use crate::{ intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, }; @@ -51,7 +50,7 @@ impl ToPyObject for Tz { .unwrap() .call1((self.name(),)) .unwrap() - .into() + .unbind() } } diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 5e313cd7cdd..84aacab43a2 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -36,7 +36,7 @@ impl ToPyObject for Ipv4Addr { .expect("failed to load ipaddress.IPv4Address") .call1((u32::from_be_bytes(self.octets()),)) .expect("failed to construct ipaddress.IPv4Address") - .to_object(py) + .unbind() } } @@ -48,7 +48,7 @@ impl ToPyObject for Ipv6Addr { .expect("failed to load ipaddress.IPv6Address") .call1((u128::from_be_bytes(self.octets()),)) .expect("failed to construct ipaddress.IPv6Address") - .to_object(py) + .unbind() } } diff --git a/src/err/mod.rs b/src/err/mod.rs index c50e377409e..8568545664b 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -159,7 +159,7 @@ impl PyErr { { PyErr::from_state(PyErrState::Lazy(Box::new(move |py| { PyErrStateLazyFnOutput { - ptype: T::type_object(py).into(), + ptype: T::type_object_bound(py).into(), pvalue: args.arguments(py), } }))) @@ -540,7 +540,7 @@ impl PyErr { where T: PyTypeInfo, { - self.is_instance(py, T::type_object(py)) + self.is_instance(py, T::type_object_bound(py).as_gil_ref()) } /// Writes the error back to the Python interpreter's global state. @@ -1077,18 +1077,24 @@ mod tests { fn test_pyerr_matches() { Python::with_gil(|py| { let err = PyErr::new::("foo"); - assert!(err.matches(py, PyValueError::type_object(py))); + assert!(err.matches(py, PyValueError::type_object_bound(py))); assert!(err.matches( py, - (PyValueError::type_object(py), PyTypeError::type_object(py)) + ( + PyValueError::type_object_bound(py), + PyTypeError::type_object_bound(py) + ) )); - assert!(!err.matches(py, PyTypeError::type_object(py))); + assert!(!err.matches(py, PyTypeError::type_object_bound(py))); // String is not a valid exception class, so we should get a TypeError - let err: PyErr = PyErr::from_type(crate::types::PyString::type_object(py), "foo"); - assert!(err.matches(py, PyTypeError::type_object(py))); + let err: PyErr = PyErr::from_type( + crate::types::PyString::type_object_bound(py).as_gil_ref(), + "foo", + ); + assert!(err.matches(py, PyTypeError::type_object_bound(py))); }) } diff --git a/src/marker.rs b/src/marker.rs index 1987ffaed13..448496de508 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -685,7 +685,7 @@ impl<'py> Python<'py> { where T: PyTypeInfo, { - T::type_object(self) + T::type_object_bound(self).into_gil_ref() } /// Imports the Python module with the specified name. diff --git a/src/pycell.rs b/src/pycell.rs index bde95ad8313..3744c55f67d 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -207,7 +207,7 @@ use crate::{ type_object::get_tp_free, PyTypeInfo, }; -use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, PyTypeCheck, Python}; +use crate::{ffi, Bound, IntoPy, PyErr, PyNativeType, PyObject, PyResult, PyTypeCheck, Python}; use std::cell::UnsafeCell; use std::fmt; use std::mem::ManuallyDrop; @@ -553,7 +553,7 @@ where { const NAME: &'static str = ::NAME; - fn type_check(object: &PyAny) -> bool { + fn type_check(object: &Bound<'_, PyAny>) -> bool { ::type_check(object) } } diff --git a/src/sync.rs b/src/sync.rs index a88a0f4b485..76c81026884 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,5 +1,5 @@ //! Synchronization mechanisms based on the Python GIL. -use crate::{instance::Bound, types::PyString, types::PyType, Py, PyResult, PyVisit, Python}; +use crate::{types::PyString, types::PyType, Bound, Py, PyResult, PyVisit, Python}; use std::cell::UnsafeCell; /// Value with concurrent access protected by the GIL. @@ -196,9 +196,9 @@ impl GILOnceCell> { py: Python<'py>, module_name: &str, attr_name: &str, - ) -> PyResult<&'py PyType> { + ) -> PyResult<&Bound<'py, PyType>> { self.get_or_try_init(py, || py.import(module_name)?.getattr(attr_name)?.extract()) - .map(|ty| ty.as_ref(py)) + .map(|ty| ty.bind(py)) } } diff --git a/src/tests/common.rs b/src/tests/common.rs index f2082437693..efab3ccb4b3 100644 --- a/src/tests/common.rs +++ b/src/tests/common.rs @@ -138,11 +138,11 @@ mod inner { ($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{ $crate::tests::common::CatchWarnings::enter($py, |w| { $body; - let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object($py), $message)),+]; + let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object_bound($py), $message)),+]; assert_eq!(w.len(), expected_warnings.len()); for (warning, (category, message)) in w.iter().zip(expected_warnings) { - assert!(warning.getattr("category").unwrap().is(category)); + assert!(warning.getattr("category").unwrap().is(&category)); assert_eq!( warning.getattr("message").unwrap().str().unwrap().to_string_lossy(), message diff --git a/src/type_object.rs b/src/type_object.rs index e7d88f76c18..994781b3fc0 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -1,7 +1,9 @@ //! Python type object information +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::types::any::PyAnyMethods; use crate::types::{PyAny, PyType}; -use crate::{ffi, PyNativeType, Python}; +use crate::{ffi, Bound, PyNativeType, Python}; /// `T: PyLayout` represents that `T` is a concrete representation of `U` in the Python heap. /// E.g., `PyCell` is a concrete representation of all `pyclass`es, and `ffi::PyObject` @@ -64,19 +66,71 @@ pub unsafe trait PyTypeInfo: Sized + HasPyGilRef { /// Returns the safe abstraction over the type object. #[inline] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyTypeInfo::type_object` will be replaced by `PyTypeInfo::type_object_bound` in a future PyO3 version" + ) + )] fn type_object(py: Python<'_>) -> &PyType { + // This isn't implemented in terms of `type_object_bound` because this just borrowed the + // object, for legacy reasons. unsafe { py.from_borrowed_ptr(Self::type_object_raw(py) as _) } } + /// Returns the safe abstraction over the type object. + #[inline] + fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> { + // Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme + // edge case, but arbitrary Python code _could_ change the __class__ of an object and cause + // the type object to be freed. + // + // By making `Bound` we assume ownership which is then safe against races. + unsafe { + Self::type_object_raw(py) + .cast::() + .assume_borrowed_unchecked(py) + .to_owned() + .downcast_into_unchecked() + } + } + /// Checks if `object` is an instance of this type or a subclass of this type. #[inline] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyTypeInfo::is_type_of` will be replaced by `PyTypeInfo::is_type_of_bound` in a future PyO3 version" + ) + )] fn is_type_of(object: &PyAny) -> bool { + Self::is_type_of_bound(&object.as_borrowed()) + } + + /// Checks if `object` is an instance of this type or a subclass of this type. + #[inline] + fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 } } /// Checks if `object` is an instance of this type. #[inline] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyTypeInfo::is_exact_type_of` will be replaced by `PyTypeInfo::is_exact_type_of_bound` in a future PyO3 version" + ) + )] fn is_exact_type_of(object: &PyAny) -> bool { + Self::is_exact_type_of_bound(&object.as_borrowed()) + } + + /// Checks if `object` is an instance of this type. + #[inline] + fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) } } } @@ -89,7 +143,7 @@ pub trait PyTypeCheck: HasPyGilRef { /// Checks if `object` is an instance of `Self`, which may include a subtype. /// /// This should be equivalent to the Python expression `isinstance(object, Self)`. - fn type_check(object: &PyAny) -> bool; + fn type_check(object: &Bound<'_, PyAny>) -> bool; } impl PyTypeCheck for T @@ -99,8 +153,8 @@ where const NAME: &'static str = ::NAME; #[inline] - fn type_check(object: &PyAny) -> bool { - ::is_type_of(object) + fn type_check(object: &Bound<'_, PyAny>) -> bool { + T::is_type_of_bound(object) } } diff --git a/src/types/any.rs b/src/types/any.rs index d79a9227cd7..a19985ae8bb 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -733,7 +733,7 @@ impl PyAny { where T: PyTypeCheck, { - if T::type_check(self) { + if T::type_check(&self.as_borrowed()) { // Safety: type_check is responsible for ensuring that the type is correct Ok(unsafe { self.downcast_unchecked() }) } else { @@ -776,7 +776,7 @@ impl PyAny { where T: PyTypeInfo, { - if T::is_exact_type_of(self) { + if T::is_exact_type_of_bound(&self.as_borrowed()) { // Safety: type_check is responsible for ensuring that the type is correct Ok(unsafe { self.downcast_unchecked() }) } else { @@ -2100,7 +2100,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { where T: PyTypeCheck, { - if T::type_check(self.as_gil_ref()) { + if T::type_check(self) { // Safety: type_check is responsible for ensuring that the type is correct Ok(unsafe { self.downcast_unchecked() }) } else { @@ -2113,7 +2113,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { where T: PyTypeCheck, { - if T::type_check(self.as_gil_ref()) { + if T::type_check(&self) { // Safety: type_check is responsible for ensuring that the type is correct Ok(unsafe { self.downcast_into_unchecked() }) } else { @@ -2218,12 +2218,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { #[inline] fn is_instance_of(&self) -> bool { - T::is_type_of(self.as_gil_ref()) + T::is_type_of_bound(self) } #[inline] fn is_exact_instance_of(&self) -> bool { - T::is_exact_type_of(self.as_gil_ref()) + T::is_exact_type_of_bound(self) } fn contains(&self, value: V) -> PyResult diff --git a/src/types/ellipsis.rs b/src/types/ellipsis.rs index 9151e89df97..b39b74b3832 100644 --- a/src/types/ellipsis.rs +++ b/src/types/ellipsis.rs @@ -1,4 +1,7 @@ -use crate::{ffi, ffi_ptr_ext::FfiPtrExt, Borrowed, PyAny, PyTypeInfo, Python}; +use crate::{ + ffi, ffi_ptr_ext::FfiPtrExt, types::any::PyAnyMethods, Borrowed, Bound, PyAny, PyTypeInfo, + Python, +}; /// Represents the Python `Ellipsis` object. #[repr(transparent)] @@ -38,14 +41,14 @@ unsafe impl PyTypeInfo for PyEllipsis { } #[inline] - fn is_type_of(object: &PyAny) -> bool { + fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { // ellipsis is not usable as a base type - Self::is_exact_type_of(object) + Self::is_exact_type_of_bound(object) } #[inline] - fn is_exact_type_of(object: &PyAny) -> bool { - object.is(Self::get_bound(object.py()).as_ref()) + fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { + object.is(&**Self::get_bound(object.py())) } } @@ -68,7 +71,7 @@ mod tests { Python::with_gil(|py| { assert!(PyEllipsis::get_bound(py) .get_type() - .is(PyEllipsis::type_object(py))); + .is(&PyEllipsis::type_object_bound(py))); }) } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index e27032867bc..cd5cd4ab9fe 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -114,7 +114,7 @@ impl<'py> Borrowed<'_, 'py, PyIterator> { impl PyTypeCheck for PyIterator { const NAME: &'static str = "Iterator"; - fn type_check(object: &PyAny) -> bool { + fn type_check(object: &Bound<'_, PyAny>) -> bool { unsafe { ffi::PyIter_Check(object.as_ptr()) != 0 } } } diff --git a/src/types/mapping.rs b/src/types/mapping.rs index 4c9677cea8b..81bb2e63912 100644 --- a/src/types/mapping.rs +++ b/src/types/mapping.rs @@ -97,7 +97,7 @@ impl PyMapping { /// library). This is equvalent to `collections.abc.Mapping.register(T)` in Python. /// This registration is required for a pyclass to be downcastable from `PyAny` to `PyMapping`. pub fn register(py: Python<'_>) -> PyResult<()> { - let ty = T::type_object(py); + let ty = T::type_object_bound(py); get_mapping_abc(py)?.call_method1("register", (ty,))?; Ok(()) } @@ -232,7 +232,7 @@ impl<'py> PyMappingMethods<'py> for Bound<'py, PyMapping> { } } -fn get_mapping_abc(py: Python<'_>) -> PyResult<&PyType> { +fn get_mapping_abc(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { static MAPPING_ABC: GILOnceCell> = GILOnceCell::new(); MAPPING_ABC.get_or_try_init_type_ref(py, "collections.abc", "Mapping") @@ -242,10 +242,10 @@ impl PyTypeCheck for PyMapping { const NAME: &'static str = "Mapping"; #[inline] - fn type_check(object: &PyAny) -> bool { + fn type_check(object: &Bound<'_, PyAny>) -> bool { // Using `is_instance` for `collections.abc.Mapping` is slow, so provide // optimized case dict as a well-known mapping - PyDict::is_type_of(object) + PyDict::is_type_of_bound(object) || get_mapping_abc(object.py()) .and_then(|abc| object.is_instance(abc)) .unwrap_or_else(|err| { @@ -263,7 +263,7 @@ impl<'v> crate::PyTryFrom<'v> for PyMapping { fn try_from>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> { let value = value.into(); - if PyMapping::type_check(value) { + if PyMapping::type_check(&value.as_borrowed()) { unsafe { return Ok(value.downcast_unchecked()) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index f802348f466..82e025b2d24 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -207,9 +207,9 @@ macro_rules! pyobject_native_type_info( $( #[inline] - fn is_type_of(ptr: &$crate::PyAny) -> bool { + fn is_type_of_bound(obj: &$crate::Bound<'_, $crate::PyAny>) -> bool { #[allow(unused_unsafe)] - unsafe { $checkfunction(ptr.as_ptr()) > 0 } + unsafe { $checkfunction(obj.as_ptr()) > 0 } } )? } diff --git a/src/types/none.rs b/src/types/none.rs index 3c3b171668d..ee650f12f67 100644 --- a/src/types/none.rs +++ b/src/types/none.rs @@ -1,5 +1,8 @@ use crate::ffi_ptr_ext::FfiPtrExt; -use crate::{ffi, Borrowed, IntoPy, PyAny, PyObject, PyTypeInfo, Python, ToPyObject}; +use crate::{ + ffi, types::any::PyAnyMethods, Borrowed, Bound, IntoPy, PyAny, PyObject, PyTypeInfo, Python, + ToPyObject, +}; /// Represents the Python `None` object. #[repr(transparent)] @@ -40,15 +43,14 @@ unsafe impl PyTypeInfo for PyNone { } #[inline] - fn is_type_of(object: &PyAny) -> bool { + fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { // NoneType is not usable as a base type - Self::is_exact_type_of(object) + Self::is_exact_type_of_bound(object) } #[inline] - fn is_exact_type_of(object: &PyAny) -> bool { - let none = Self::get_bound(object.py()); - object.is(none.as_ref()) + fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { + object.is(&**Self::get_bound(object.py())) } } @@ -82,7 +84,9 @@ mod tests { #[test] fn test_none_type_object_consistent() { Python::with_gil(|py| { - assert!(PyNone::get_bound(py).get_type().is(PyNone::type_object(py))); + assert!(PyNone::get_bound(py) + .get_type() + .is(&PyNone::type_object_bound(py))); }) } diff --git a/src/types/notimplemented.rs b/src/types/notimplemented.rs index 7560dff37d1..5832c6a9803 100644 --- a/src/types/notimplemented.rs +++ b/src/types/notimplemented.rs @@ -1,4 +1,7 @@ -use crate::{ffi, ffi_ptr_ext::FfiPtrExt, Borrowed, PyAny, PyTypeInfo, Python}; +use crate::{ + ffi, ffi_ptr_ext::FfiPtrExt, types::any::PyAnyMethods, Borrowed, Bound, PyAny, PyTypeInfo, + Python, +}; /// Represents the Python `NotImplemented` object. #[repr(transparent)] @@ -41,14 +44,14 @@ unsafe impl PyTypeInfo for PyNotImplemented { } #[inline] - fn is_type_of(object: &PyAny) -> bool { + fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { // NotImplementedType is not usable as a base type - Self::is_exact_type_of(object) + Self::is_exact_type_of_bound(object) } #[inline] - fn is_exact_type_of(object: &PyAny) -> bool { - object.is(Self::get_bound(object.py()).as_ref()) + fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { + object.is(&**Self::get_bound(object.py())) } } @@ -71,7 +74,7 @@ mod tests { Python::with_gil(|py| { assert!(PyNotImplemented::get_bound(py) .get_type() - .is(PyNotImplemented::type_object(py))); + .is(&PyNotImplemented::type_object_bound(py))); }) } diff --git a/src/types/pysuper.rs b/src/types/pysuper.rs index 25f5e1ceb1c..261a91f289b 100644 --- a/src/types/pysuper.rs +++ b/src/types/pysuper.rs @@ -72,8 +72,7 @@ impl PySuper { ty: &Bound<'py, PyType>, obj: &Bound<'py, PyAny>, ) -> PyResult> { - PySuper::type_object(ty.py()) - .as_borrowed() + PySuper::type_object_bound(ty.py()) .call1((ty, obj)) .map(|any| { // Safety: super() always returns instance of super diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 19f6846b4d1..80306cbf44b 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -8,11 +8,9 @@ use crate::internal_tricks::get_ssize_index; use crate::py_result_ext::PyResultExt; use crate::sync::GILOnceCell; use crate::type_object::PyTypeInfo; -use crate::types::{PyAny, PyList, PyString, PyTuple, PyType}; +use crate::types::{any::PyAnyMethods, PyAny, PyList, PyString, PyTuple, PyType}; use crate::{ffi, FromPyObject, Py, PyNativeType, PyTypeCheck, Python, ToPyObject}; -use super::any::PyAnyMethods; - /// Represents a reference to a Python object supporting the sequence protocol. #[repr(transparent)] pub struct PySequence(PyAny); @@ -182,7 +180,7 @@ impl PySequence { /// library). This is equvalent to `collections.abc.Sequence.register(T)` in Python. /// This registration is required for a pyclass to be downcastable from `PyAny` to `PySequence`. pub fn register(py: Python<'_>) -> PyResult<()> { - let ty = T::type_object(py); + let ty = T::type_object_bound(py); get_sequence_abc(py)?.call_method1("register", (ty,))?; Ok(()) } @@ -517,7 +515,7 @@ where Ok(v) } -fn get_sequence_abc(py: Python<'_>) -> PyResult<&PyType> { +fn get_sequence_abc(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { static SEQUENCE_ABC: GILOnceCell> = GILOnceCell::new(); SEQUENCE_ABC.get_or_try_init_type_ref(py, "collections.abc", "Sequence") @@ -527,11 +525,11 @@ impl PyTypeCheck for PySequence { const NAME: &'static str = "Sequence"; #[inline] - fn type_check(object: &PyAny) -> bool { + fn type_check(object: &Bound<'_, PyAny>) -> bool { // Using `is_instance` for `collections.abc.Sequence` is slow, so provide // optimized cases for list and tuples as common well-known sequences - PyList::is_type_of(object) - || PyTuple::is_type_of(object) + PyList::is_type_of_bound(object) + || PyTuple::is_type_of_bound(object) || get_sequence_abc(object.py()) .and_then(|abc| object.is_instance(abc)) .unwrap_or_else(|err| { @@ -549,7 +547,7 @@ impl<'v> crate::PyTryFrom<'v> for PySequence { fn try_from>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { let value = value.into(); - if PySequence::type_check(value) { + if PySequence::type_check(&value.as_borrowed()) { unsafe { return Ok(value.downcast_unchecked::()) } } diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index a1a053f93a4..50eeaa7153d 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -14,7 +14,7 @@ impl PyType { /// Creates a new type object. #[inline] pub fn new(py: Python<'_>) -> &PyType { - T::type_object(py) + T::type_object_bound(py).into_gil_ref() } /// Retrieves the underlying FFI pointer associated with this Python object. @@ -104,7 +104,7 @@ impl PyType { where T: PyTypeInfo, { - self.is_subclass(T::type_object(self.py())) + self.is_subclass(T::type_object_bound(self.py()).as_gil_ref()) } }