add `bound` method variants for `PyTypeInfo`

This commit is contained in:
David Hewitt 2024-01-30 10:31:34 +00:00
parent 9bb001108b
commit 367eeaeeab
20 changed files with 144 additions and 75 deletions

View File

@ -388,7 +388,7 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict(); # 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)?; # py.run(SCRIPT, Some(globals), None)?;
# Ok(()) # Ok(())

View File

@ -210,6 +210,7 @@ To minimise breakage of code using the GIL-Refs API, the `Bound<T>` smart pointe
For example, the following APIs have gained updated variants: 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. - `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) - `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<T>`.
Because the new `Bound<T>` 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: Because the new `Bound<T>` 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<T>` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count) - Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound<T>` 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. 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 ## from 0.19.* to 0.20
### Drop support for older technologies ### Drop support for older technologies
@ -656,7 +659,7 @@ To migrate, update trait bounds and imports from `PyTypeObject` to `PyTypeInfo`.
Before: Before:
```rust,compile_fail ```rust,ignore
use pyo3::Python; use pyo3::Python;
use pyo3::type_object::PyTypeObject; use pyo3::type_object::PyTypeObject;
use pyo3::types::PyType; use pyo3::types::PyType;
@ -668,7 +671,7 @@ fn get_type_object<T: PyTypeObject>(py: Python<'_>) -> &PyType {
After After
```rust ```rust,ignore
use pyo3::{Python, PyTypeInfo}; use pyo3::{Python, PyTypeInfo};
use pyo3::types::PyType; 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: 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"); let err: PyErr = TypeError::py_err("error message");
``` ```
After: After:
```rust,compile_fail ```rust,ignore
# use pyo3::{PyErr, PyResult, Python, type_object::PyTypeObject}; # use pyo3::{PyErr, PyResult, Python, type_object::PyTypeObject};
# use pyo3::exceptions::{PyBaseException, PyTypeError}; # use pyo3::exceptions::{PyBaseException, PyTypeError};
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {

View File

@ -35,8 +35,7 @@
//! ``` //! ```
use crate::exceptions::PyValueError; use crate::exceptions::PyValueError;
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::types::any::PyAnyMethods; use crate::types::{any::PyAnyMethods, PyType};
use crate::types::PyType;
use crate::{ use crate::{
intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
}; };
@ -51,7 +50,7 @@ impl ToPyObject for Tz {
.unwrap() .unwrap()
.call1((self.name(),)) .call1((self.name(),))
.unwrap() .unwrap()
.into() .unbind()
} }
} }

View File

@ -36,7 +36,7 @@ impl ToPyObject for Ipv4Addr {
.expect("failed to load ipaddress.IPv4Address") .expect("failed to load ipaddress.IPv4Address")
.call1((u32::from_be_bytes(self.octets()),)) .call1((u32::from_be_bytes(self.octets()),))
.expect("failed to construct ipaddress.IPv4Address") .expect("failed to construct ipaddress.IPv4Address")
.to_object(py) .unbind()
} }
} }
@ -48,7 +48,7 @@ impl ToPyObject for Ipv6Addr {
.expect("failed to load ipaddress.IPv6Address") .expect("failed to load ipaddress.IPv6Address")
.call1((u128::from_be_bytes(self.octets()),)) .call1((u128::from_be_bytes(self.octets()),))
.expect("failed to construct ipaddress.IPv6Address") .expect("failed to construct ipaddress.IPv6Address")
.to_object(py) .unbind()
} }
} }

View File

@ -159,7 +159,7 @@ impl PyErr {
{ {
PyErr::from_state(PyErrState::Lazy(Box::new(move |py| { PyErr::from_state(PyErrState::Lazy(Box::new(move |py| {
PyErrStateLazyFnOutput { PyErrStateLazyFnOutput {
ptype: T::type_object(py).into(), ptype: T::type_object_bound(py).into(),
pvalue: args.arguments(py), pvalue: args.arguments(py),
} }
}))) })))
@ -540,7 +540,7 @@ impl PyErr {
where where
T: PyTypeInfo, 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. /// Writes the error back to the Python interpreter's global state.
@ -1077,18 +1077,24 @@ mod tests {
fn test_pyerr_matches() { fn test_pyerr_matches() {
Python::with_gil(|py| { Python::with_gil(|py| {
let err = PyErr::new::<PyValueError, _>("foo"); let err = PyErr::new::<PyValueError, _>("foo");
assert!(err.matches(py, PyValueError::type_object(py))); assert!(err.matches(py, PyValueError::type_object_bound(py)));
assert!(err.matches( assert!(err.matches(
py, 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 // 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"); let err: PyErr = PyErr::from_type(
assert!(err.matches(py, PyTypeError::type_object(py))); crate::types::PyString::type_object_bound(py).as_gil_ref(),
"foo",
);
assert!(err.matches(py, PyTypeError::type_object_bound(py)));
}) })
} }

View File

@ -685,7 +685,7 @@ impl<'py> Python<'py> {
where where
T: PyTypeInfo, T: PyTypeInfo,
{ {
T::type_object(self) T::type_object_bound(self).into_gil_ref()
} }
/// Imports the Python module with the specified name. /// Imports the Python module with the specified name.

View File

@ -207,7 +207,7 @@ use crate::{
type_object::get_tp_free, type_object::get_tp_free,
PyTypeInfo, 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::cell::UnsafeCell;
use std::fmt; use std::fmt;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
@ -553,7 +553,7 @@ where
{ {
const NAME: &'static str = <T as PyTypeCheck>::NAME; const NAME: &'static str = <T as PyTypeCheck>::NAME;
fn type_check(object: &PyAny) -> bool { fn type_check(object: &Bound<'_, PyAny>) -> bool {
<T as PyTypeCheck>::type_check(object) <T as PyTypeCheck>::type_check(object)
} }
} }

View File

@ -1,5 +1,5 @@
//! Synchronization mechanisms based on the Python GIL. //! 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; use std::cell::UnsafeCell;
/// Value with concurrent access protected by the GIL. /// Value with concurrent access protected by the GIL.
@ -196,9 +196,9 @@ impl GILOnceCell<Py<PyType>> {
py: Python<'py>, py: Python<'py>,
module_name: &str, module_name: &str,
attr_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()) 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))
} }
} }

View File

@ -138,11 +138,11 @@ mod inner {
($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{ ($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{
$crate::tests::common::CatchWarnings::enter($py, |w| { $crate::tests::common::CatchWarnings::enter($py, |w| {
$body; $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()); assert_eq!(w.len(), expected_warnings.len());
for (warning, (category, message)) in w.iter().zip(expected_warnings) { 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!( assert_eq!(
warning.getattr("message").unwrap().str().unwrap().to_string_lossy(), warning.getattr("message").unwrap().str().unwrap().to_string_lossy(),
message message

View File

@ -1,7 +1,9 @@
//! Python type object information //! Python type object information
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyType}; use crate::types::{PyAny, PyType};
use crate::{ffi, PyNativeType, Python}; use crate::{ffi, Bound, PyNativeType, Python};
/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap. /// `T: PyLayout<U>` 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` /// 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. /// Returns the safe abstraction over the type object.
#[inline] #[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 { 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 _) } 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::<ffi::PyObject>()
.assume_borrowed_unchecked(py)
.to_owned()
.downcast_into_unchecked()
}
}
/// Checks if `object` is an instance of this type or a subclass of this type. /// Checks if `object` is an instance of this type or a subclass of this type.
#[inline] #[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 { 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 } unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 }
} }
/// Checks if `object` is an instance of this type. /// Checks if `object` is an instance of this type.
#[inline] #[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 { 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()) } 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. /// Checks if `object` is an instance of `Self`, which may include a subtype.
/// ///
/// This should be equivalent to the Python expression `isinstance(object, Self)`. /// 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<T> PyTypeCheck for T impl<T> PyTypeCheck for T
@ -99,8 +153,8 @@ where
const NAME: &'static str = <T as PyTypeInfo>::NAME; const NAME: &'static str = <T as PyTypeInfo>::NAME;
#[inline] #[inline]
fn type_check(object: &PyAny) -> bool { fn type_check(object: &Bound<'_, PyAny>) -> bool {
<T as PyTypeInfo>::is_type_of(object) T::is_type_of_bound(object)
} }
} }

View File

@ -733,7 +733,7 @@ impl PyAny {
where where
T: PyTypeCheck<AsRefTarget = T>, T: PyTypeCheck<AsRefTarget = T>,
{ {
if T::type_check(self) { if T::type_check(&self.as_borrowed()) {
// Safety: type_check is responsible for ensuring that the type is correct // Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() }) Ok(unsafe { self.downcast_unchecked() })
} else { } else {
@ -776,7 +776,7 @@ impl PyAny {
where where
T: PyTypeInfo<AsRefTarget = T>, T: PyTypeInfo<AsRefTarget = T>,
{ {
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 // Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() }) Ok(unsafe { self.downcast_unchecked() })
} else { } else {
@ -2100,7 +2100,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
where where
T: PyTypeCheck, 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 // Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() }) Ok(unsafe { self.downcast_unchecked() })
} else { } else {
@ -2113,7 +2113,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
where where
T: PyTypeCheck, 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 // Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_into_unchecked() }) Ok(unsafe { self.downcast_into_unchecked() })
} else { } else {
@ -2218,12 +2218,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
#[inline] #[inline]
fn is_instance_of<T: PyTypeInfo>(&self) -> bool { fn is_instance_of<T: PyTypeInfo>(&self) -> bool {
T::is_type_of(self.as_gil_ref()) T::is_type_of_bound(self)
} }
#[inline] #[inline]
fn is_exact_instance_of<T: PyTypeInfo>(&self) -> bool { fn is_exact_instance_of<T: PyTypeInfo>(&self) -> bool {
T::is_exact_type_of(self.as_gil_ref()) T::is_exact_type_of_bound(self)
} }
fn contains<V>(&self, value: V) -> PyResult<bool> fn contains<V>(&self, value: V) -> PyResult<bool>

View File

@ -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. /// Represents the Python `Ellipsis` object.
#[repr(transparent)] #[repr(transparent)]
@ -38,14 +41,14 @@ unsafe impl PyTypeInfo for PyEllipsis {
} }
#[inline] #[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 // ellipsis is not usable as a base type
Self::is_exact_type_of(object) Self::is_exact_type_of_bound(object)
} }
#[inline] #[inline]
fn is_exact_type_of(object: &PyAny) -> bool { fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
object.is(Self::get_bound(object.py()).as_ref()) object.is(&**Self::get_bound(object.py()))
} }
} }
@ -68,7 +71,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
assert!(PyEllipsis::get_bound(py) assert!(PyEllipsis::get_bound(py)
.get_type() .get_type()
.is(PyEllipsis::type_object(py))); .is(&PyEllipsis::type_object_bound(py)));
}) })
} }

View File

@ -114,7 +114,7 @@ impl<'py> Borrowed<'_, 'py, PyIterator> {
impl PyTypeCheck for PyIterator { impl PyTypeCheck for PyIterator {
const NAME: &'static str = "Iterator"; 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 } unsafe { ffi::PyIter_Check(object.as_ptr()) != 0 }
} }
} }

View File

@ -97,7 +97,7 @@ impl PyMapping {
/// library). This is equvalent to `collections.abc.Mapping.register(T)` in Python. /// 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`. /// This registration is required for a pyclass to be downcastable from `PyAny` to `PyMapping`.
pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> { pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> {
let ty = T::type_object(py); let ty = T::type_object_bound(py);
get_mapping_abc(py)?.call_method1("register", (ty,))?; get_mapping_abc(py)?.call_method1("register", (ty,))?;
Ok(()) 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<Py<PyType>> = GILOnceCell::new(); static MAPPING_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new();
MAPPING_ABC.get_or_try_init_type_ref(py, "collections.abc", "Mapping") 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"; const NAME: &'static str = "Mapping";
#[inline] #[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 // Using `is_instance` for `collections.abc.Mapping` is slow, so provide
// optimized case dict as a well-known mapping // optimized case dict as a well-known mapping
PyDict::is_type_of(object) PyDict::is_type_of_bound(object)
|| get_mapping_abc(object.py()) || get_mapping_abc(object.py())
.and_then(|abc| object.is_instance(abc)) .and_then(|abc| object.is_instance(abc))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
@ -263,7 +263,7 @@ impl<'v> crate::PyTryFrom<'v> for PyMapping {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> { fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> {
let value = value.into(); let value = value.into();
if PyMapping::type_check(value) { if PyMapping::type_check(&value.as_borrowed()) {
unsafe { return Ok(value.downcast_unchecked()) } unsafe { return Ok(value.downcast_unchecked()) }
} }

View File

@ -207,9 +207,9 @@ macro_rules! pyobject_native_type_info(
$( $(
#[inline] #[inline]
fn is_type_of(ptr: &$crate::PyAny) -> bool { fn is_type_of_bound(obj: &$crate::Bound<'_, $crate::PyAny>) -> bool {
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
unsafe { $checkfunction(ptr.as_ptr()) > 0 } unsafe { $checkfunction(obj.as_ptr()) > 0 }
} }
)? )?
} }

View File

@ -1,5 +1,8 @@
use crate::ffi_ptr_ext::FfiPtrExt; 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. /// Represents the Python `None` object.
#[repr(transparent)] #[repr(transparent)]
@ -40,15 +43,14 @@ unsafe impl PyTypeInfo for PyNone {
} }
#[inline] #[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 // NoneType is not usable as a base type
Self::is_exact_type_of(object) Self::is_exact_type_of_bound(object)
} }
#[inline] #[inline]
fn is_exact_type_of(object: &PyAny) -> bool { fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
let none = Self::get_bound(object.py()); object.is(&**Self::get_bound(object.py()))
object.is(none.as_ref())
} }
} }
@ -82,7 +84,9 @@ mod tests {
#[test] #[test]
fn test_none_type_object_consistent() { fn test_none_type_object_consistent() {
Python::with_gil(|py| { 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)));
}) })
} }

View File

@ -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. /// Represents the Python `NotImplemented` object.
#[repr(transparent)] #[repr(transparent)]
@ -41,14 +44,14 @@ unsafe impl PyTypeInfo for PyNotImplemented {
} }
#[inline] #[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 // NotImplementedType is not usable as a base type
Self::is_exact_type_of(object) Self::is_exact_type_of_bound(object)
} }
#[inline] #[inline]
fn is_exact_type_of(object: &PyAny) -> bool { fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
object.is(Self::get_bound(object.py()).as_ref()) object.is(&**Self::get_bound(object.py()))
} }
} }
@ -71,7 +74,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
assert!(PyNotImplemented::get_bound(py) assert!(PyNotImplemented::get_bound(py)
.get_type() .get_type()
.is(PyNotImplemented::type_object(py))); .is(&PyNotImplemented::type_object_bound(py)));
}) })
} }

View File

@ -72,8 +72,7 @@ impl PySuper {
ty: &Bound<'py, PyType>, ty: &Bound<'py, PyType>,
obj: &Bound<'py, PyAny>, obj: &Bound<'py, PyAny>,
) -> PyResult<Bound<'py, PySuper>> { ) -> PyResult<Bound<'py, PySuper>> {
PySuper::type_object(ty.py()) PySuper::type_object_bound(ty.py())
.as_borrowed()
.call1((ty, obj)) .call1((ty, obj))
.map(|any| { .map(|any| {
// Safety: super() always returns instance of super // Safety: super() always returns instance of super

View File

@ -8,11 +8,9 @@ use crate::internal_tricks::get_ssize_index;
use crate::py_result_ext::PyResultExt; use crate::py_result_ext::PyResultExt;
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::type_object::PyTypeInfo; 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 crate::{ffi, FromPyObject, Py, PyNativeType, PyTypeCheck, Python, ToPyObject};
use super::any::PyAnyMethods;
/// Represents a reference to a Python object supporting the sequence protocol. /// Represents a reference to a Python object supporting the sequence protocol.
#[repr(transparent)] #[repr(transparent)]
pub struct PySequence(PyAny); pub struct PySequence(PyAny);
@ -182,7 +180,7 @@ impl PySequence {
/// library). This is equvalent to `collections.abc.Sequence.register(T)` in Python. /// 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`. /// This registration is required for a pyclass to be downcastable from `PyAny` to `PySequence`.
pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> { pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> {
let ty = T::type_object(py); let ty = T::type_object_bound(py);
get_sequence_abc(py)?.call_method1("register", (ty,))?; get_sequence_abc(py)?.call_method1("register", (ty,))?;
Ok(()) Ok(())
} }
@ -517,7 +515,7 @@ where
Ok(v) Ok(v)
} }
fn get_sequence_abc(py: Python<'_>) -> PyResult<&PyType> { fn get_sequence_abc(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
static SEQUENCE_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new(); static SEQUENCE_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new();
SEQUENCE_ABC.get_or_try_init_type_ref(py, "collections.abc", "Sequence") 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"; const NAME: &'static str = "Sequence";
#[inline] #[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 // Using `is_instance` for `collections.abc.Sequence` is slow, so provide
// optimized cases for list and tuples as common well-known sequences // optimized cases for list and tuples as common well-known sequences
PyList::is_type_of(object) PyList::is_type_of_bound(object)
|| PyTuple::is_type_of(object) || PyTuple::is_type_of_bound(object)
|| get_sequence_abc(object.py()) || get_sequence_abc(object.py())
.and_then(|abc| object.is_instance(abc)) .and_then(|abc| object.is_instance(abc))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
@ -549,7 +547,7 @@ impl<'v> crate::PyTryFrom<'v> for PySequence {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
let value = value.into(); let value = value.into();
if PySequence::type_check(value) { if PySequence::type_check(&value.as_borrowed()) {
unsafe { return Ok(value.downcast_unchecked::<PySequence>()) } unsafe { return Ok(value.downcast_unchecked::<PySequence>()) }
} }

View File

@ -14,7 +14,7 @@ impl PyType {
/// Creates a new type object. /// Creates a new type object.
#[inline] #[inline]
pub fn new<T: PyTypeInfo>(py: Python<'_>) -> &PyType { pub fn new<T: PyTypeInfo>(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. /// Retrieves the underlying FFI pointer associated with this Python object.
@ -104,7 +104,7 @@ impl PyType {
where where
T: PyTypeInfo, T: PyTypeInfo,
{ {
self.is_subclass(T::type_object(self.py())) self.is_subclass(T::type_object_bound(self.py()).as_gil_ref())
} }
} }