add `bound` method variants for `PyTypeInfo`
This commit is contained in:
parent
9bb001108b
commit
367eeaeeab
|
@ -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(())
|
||||||
|
|
|
@ -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<()> {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue