fix `AsRef` and `Deref` impls on `Bound<T>` (#3879)
* fix `AsRef` and `Deref` of `Bound<T>` to `Bound<PyAny>` * cleanup unnessesary `.as_any()` calls * remove trait bound on `AsRef` impl * add comment for `Deref` trait bound * rename marker trait
This commit is contained in:
parent
9e74c858c2
commit
4f8ee96881
|
@ -217,8 +217,8 @@ impl Number {
|
|||
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let x = &Bound::new(py, Number(4))?.into_any();
|
||||
# let y = &Bound::new(py, Number(4))?.into_any();
|
||||
# let x = &Bound::new(py, Number(4))?;
|
||||
# let y = &Bound::new(py, Number(4))?;
|
||||
# assert!(x.eq(y)?);
|
||||
# assert!(!x.ne(y)?);
|
||||
# Ok(())
|
||||
|
|
|
@ -365,6 +365,8 @@ fn impl_class(
|
|||
const _: () = {
|
||||
use #krate as _pyo3;
|
||||
|
||||
impl _pyo3::types::DerefToPyAny for #cls {}
|
||||
|
||||
#pytypeinfo_impl
|
||||
|
||||
#py_class_impl
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
|
|||
use crate::pyclass::boolean_struct::{False, True};
|
||||
use crate::type_object::HasPyGilRef;
|
||||
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
|
||||
use crate::types::{PyDict, PyString, PyTuple};
|
||||
use crate::types::{DerefToPyAny, PyDict, PyString, PyTuple};
|
||||
use crate::{
|
||||
ffi, AsPyPointer, DowncastError, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer,
|
||||
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
|
||||
|
@ -366,9 +366,12 @@ fn python_format(
|
|||
}
|
||||
}
|
||||
|
||||
// The trait bound is needed to avoid running into the auto-deref recursion
|
||||
// limit (error[E0055]), because `Bound<PyAny>` would deref into itself. See:
|
||||
// https://github.com/rust-lang/rust/issues/19509
|
||||
impl<'py, T> Deref for Bound<'py, T>
|
||||
where
|
||||
T: AsRef<PyAny>,
|
||||
T: DerefToPyAny,
|
||||
{
|
||||
type Target = Bound<'py, PyAny>;
|
||||
|
||||
|
@ -378,10 +381,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'py, T> AsRef<Bound<'py, PyAny>> for Bound<'py, T>
|
||||
where
|
||||
T: AsRef<PyAny>,
|
||||
{
|
||||
impl<'py, T> AsRef<Bound<'py, PyAny>> for Bound<'py, T> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Bound<'py, PyAny> {
|
||||
self.as_any()
|
||||
|
|
|
@ -168,7 +168,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
assert!(PyBool::new_bound(py, true).is_true());
|
||||
let t = PyBool::new_bound(py, true);
|
||||
assert!(t.as_any().extract::<bool>().unwrap());
|
||||
assert!(t.extract::<bool>().unwrap());
|
||||
assert!(true.to_object(py).is(&*PyBool::new_bound(py, true)));
|
||||
});
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
assert!(!PyBool::new_bound(py, false).is_true());
|
||||
let t = PyBool::new_bound(py, false);
|
||||
assert!(!t.as_any().extract::<bool>().unwrap());
|
||||
assert!(!t.extract::<bool>().unwrap());
|
||||
assert!(false.to_object(py).is(&*PyBool::new_bound(py, false)));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -86,6 +86,31 @@ pub mod iter {
|
|||
pub use super::tuple::{BorrowedTupleIterator, BoundTupleIterator, PyTupleIterator};
|
||||
}
|
||||
|
||||
/// Python objects that have a base type.
|
||||
///
|
||||
/// This marks types that can be upcast into a [`PyAny`] and used in its place.
|
||||
/// This essentially includes every Python object except [`PyAny`] itself.
|
||||
///
|
||||
/// This is used to provide the [`Deref<Target = Bound<'_, PyAny>>`](std::ops::Deref)
|
||||
/// implementations for [`Bound<'_, T>`](crate::Bound).
|
||||
///
|
||||
/// Users should not need to implement this trait directly. It's implementation
|
||||
/// is provided by the [`#[pyclass]`](macro@crate::pyclass) attribute.
|
||||
///
|
||||
/// ## Note
|
||||
/// This is needed because the compiler currently tries to figure out all the
|
||||
/// types in a deref-chain before starting to look for applicable method calls.
|
||||
/// So we need to prevent [`Bound<'_, PyAny`](crate::Bound) dereferencing to
|
||||
/// itself in order to avoid running into the recursion limit. This trait is
|
||||
/// used to exclude this from our blanket implementation. See [this Rust
|
||||
/// issue][1] for more details. If the compiler limitation gets resolved, this
|
||||
/// trait will be removed.
|
||||
///
|
||||
/// [1]: https://github.com/rust-lang/rust/issues/19509
|
||||
pub trait DerefToPyAny {
|
||||
// Empty.
|
||||
}
|
||||
|
||||
// Implementations core to all native types
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -183,6 +208,8 @@ macro_rules! pyobject_native_type_named (
|
|||
unsafe{&*(ob as *const $name as *const $crate::PyAny)}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::types::DerefToPyAny for $name {}
|
||||
};
|
||||
);
|
||||
|
||||
|
|
|
@ -96,11 +96,11 @@ fn test_get_buffer_errors() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(PyBuffer::<u32>::get_bound(instance.bind(py).as_any()).is_ok());
|
||||
assert!(PyBuffer::<u32>::get_bound(instance.bind(py)).is_ok());
|
||||
|
||||
instance.borrow_mut(py).error = Some(TestGetBufferError::NullShape);
|
||||
assert_eq!(
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py).as_any())
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py))
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"BufferError: shape is null"
|
||||
|
@ -108,7 +108,7 @@ fn test_get_buffer_errors() {
|
|||
|
||||
instance.borrow_mut(py).error = Some(TestGetBufferError::NullStrides);
|
||||
assert_eq!(
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py).as_any())
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py))
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"BufferError: strides is null"
|
||||
|
@ -116,7 +116,7 @@ fn test_get_buffer_errors() {
|
|||
|
||||
instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectItemSize);
|
||||
assert_eq!(
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py).as_any())
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py))
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"BufferError: buffer contents are not compatible with u32"
|
||||
|
@ -124,7 +124,7 @@ fn test_get_buffer_errors() {
|
|||
|
||||
instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectFormat);
|
||||
assert_eq!(
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py).as_any())
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py))
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"BufferError: buffer contents are not compatible with u32"
|
||||
|
@ -132,7 +132,7 @@ fn test_get_buffer_errors() {
|
|||
|
||||
instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectAlignment);
|
||||
assert_eq!(
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py).as_any())
|
||||
PyBuffer::<u32>::get_bound(instance.bind(py))
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"BufferError: buffer contents are insufficiently aligned for u32"
|
||||
|
|
|
@ -146,13 +146,11 @@ fn test_pyref_as_base() {
|
|||
#[test]
|
||||
fn test_pycell_deref() {
|
||||
Python::with_gil(|py| {
|
||||
let cell = Bound::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap();
|
||||
let obj = Bound::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap();
|
||||
|
||||
// Should be able to deref as PyAny
|
||||
// FIXME: This deref does _not_ work
|
||||
assert_eq!(
|
||||
cell.as_any()
|
||||
.call_method0("foo")
|
||||
obj.call_method0("foo")
|
||||
.and_then(|e| e.extract::<&str>())
|
||||
.unwrap(),
|
||||
"SubClass"
|
||||
|
|
|
@ -22,14 +22,8 @@ fn test_cfg() {
|
|||
Python::with_gil(|py| {
|
||||
let cfg = CfgClass { b: 3 };
|
||||
let py_cfg = Py::new(py, cfg).unwrap();
|
||||
assert!(py_cfg.bind(py).as_any().getattr("a").is_err());
|
||||
let b: u32 = py_cfg
|
||||
.bind(py)
|
||||
.as_any()
|
||||
.getattr("b")
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(py_cfg.bind(py).getattr("a").is_err());
|
||||
let b: u32 = py_cfg.bind(py).getattr("b").unwrap().extract().unwrap();
|
||||
assert_eq!(b, 3);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ mod inheriting_native_type {
|
|||
let item = &py.eval_bound("object()", None, None).unwrap();
|
||||
assert_eq!(item.get_refcnt(), 1);
|
||||
|
||||
dict_sub.bind(py).as_any().set_item("foo", item).unwrap();
|
||||
dict_sub.bind(py).set_item("foo", item).unwrap();
|
||||
assert_eq!(item.get_refcnt(), 2);
|
||||
|
||||
drop(dict_sub);
|
||||
|
|
|
@ -123,7 +123,7 @@ fn mapping_is_not_sequence() {
|
|||
|
||||
PyMapping::register::<Mapping>(py).unwrap();
|
||||
|
||||
assert!(m.bind(py).as_any().downcast::<PyMapping>().is_ok());
|
||||
assert!(m.bind(py).as_any().downcast::<PySequence>().is_err());
|
||||
assert!(m.bind(py).downcast::<PyMapping>().is_ok());
|
||||
assert!(m.bind(py).downcast::<PySequence>().is_err());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -81,7 +81,6 @@ fn test_getattr() {
|
|||
let example_py = make_example(py);
|
||||
assert_eq!(
|
||||
example_py
|
||||
.as_any()
|
||||
.getattr("value")
|
||||
.unwrap()
|
||||
.extract::<i32>()
|
||||
|
@ -90,7 +89,6 @@ fn test_getattr() {
|
|||
);
|
||||
assert_eq!(
|
||||
example_py
|
||||
.as_any()
|
||||
.getattr("special_custom_attr")
|
||||
.unwrap()
|
||||
.extract::<i32>()
|
||||
|
@ -98,7 +96,6 @@ fn test_getattr() {
|
|||
20,
|
||||
);
|
||||
assert!(example_py
|
||||
.as_any()
|
||||
.getattr("other_attr")
|
||||
.unwrap_err()
|
||||
.is_instance_of::<PyAttributeError>(py));
|
||||
|
@ -109,13 +106,9 @@ fn test_getattr() {
|
|||
fn test_setattr() {
|
||||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
example_py
|
||||
.as_any()
|
||||
.setattr("special_custom_attr", 15)
|
||||
.unwrap();
|
||||
example_py.setattr("special_custom_attr", 15).unwrap();
|
||||
assert_eq!(
|
||||
example_py
|
||||
.as_any()
|
||||
.getattr("special_custom_attr")
|
||||
.unwrap()
|
||||
.extract::<i32>()
|
||||
|
@ -129,12 +122,8 @@ fn test_setattr() {
|
|||
fn test_delattr() {
|
||||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
example_py.as_any().delattr("special_custom_attr").unwrap();
|
||||
assert!(example_py
|
||||
.as_any()
|
||||
.getattr("special_custom_attr")
|
||||
.unwrap()
|
||||
.is_none());
|
||||
example_py.delattr("special_custom_attr").unwrap();
|
||||
assert!(example_py.getattr("special_custom_attr").unwrap().is_none());
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -142,7 +131,7 @@ fn test_delattr() {
|
|||
fn test_str() {
|
||||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
assert_eq!(example_py.as_any().str().unwrap().to_cow().unwrap(), "5");
|
||||
assert_eq!(example_py.str().unwrap().to_cow().unwrap(), "5");
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -151,7 +140,7 @@ fn test_repr() {
|
|||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
assert_eq!(
|
||||
example_py.as_any().repr().unwrap().to_cow().unwrap(),
|
||||
example_py.repr().unwrap().to_cow().unwrap(),
|
||||
"ExampleClass(value=5)"
|
||||
);
|
||||
})
|
||||
|
@ -161,7 +150,7 @@ fn test_repr() {
|
|||
fn test_hash() {
|
||||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
assert_eq!(example_py.as_any().hash().unwrap(), 5);
|
||||
assert_eq!(example_py.hash().unwrap(), 5);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -169,9 +158,9 @@ fn test_hash() {
|
|||
fn test_bool() {
|
||||
Python::with_gil(|py| {
|
||||
let example_py = make_example(py);
|
||||
assert!(example_py.as_any().is_truthy().unwrap());
|
||||
assert!(example_py.is_truthy().unwrap());
|
||||
example_py.borrow_mut().value = 0;
|
||||
assert!(!example_py.as_any().is_truthy().unwrap());
|
||||
assert!(!example_py.is_truthy().unwrap());
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -231,7 +220,7 @@ fn mapping() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let mapping: &Bound<'_, PyMapping> = inst.bind(py).as_any().downcast().unwrap();
|
||||
let mapping: &Bound<'_, PyMapping> = inst.bind(py).downcast().unwrap();
|
||||
|
||||
py_assert!(py, inst, "len(inst) == 0");
|
||||
|
||||
|
@ -333,7 +322,7 @@ fn sequence() {
|
|||
|
||||
let inst = Py::new(py, Sequence { values: vec![] }).unwrap();
|
||||
|
||||
let sequence: &Bound<'_, PySequence> = inst.bind(py).as_any().downcast().unwrap();
|
||||
let sequence: &Bound<'_, PySequence> = inst.bind(py).downcast().unwrap();
|
||||
|
||||
py_assert!(py, inst, "len(inst) == 0");
|
||||
|
||||
|
@ -360,16 +349,16 @@ fn sequence() {
|
|||
// indices.
|
||||
assert!(sequence.len().is_err());
|
||||
// however regular python len() works thanks to mp_len slot
|
||||
assert_eq!(inst.bind(py).as_any().len().unwrap(), 0);
|
||||
assert_eq!(inst.bind(py).len().unwrap(), 0);
|
||||
|
||||
py_run!(py, inst, "inst.append(0)");
|
||||
sequence.set_item(0, 5).unwrap();
|
||||
assert_eq!(inst.bind(py).as_any().len().unwrap(), 1);
|
||||
assert_eq!(inst.bind(py).len().unwrap(), 1);
|
||||
|
||||
assert_eq!(sequence.get_item(0).unwrap().extract::<u8>().unwrap(), 5);
|
||||
sequence.del_item(0).unwrap();
|
||||
|
||||
assert_eq!(inst.bind(py).as_any().len().unwrap(), 0);
|
||||
assert_eq!(inst.bind(py).len().unwrap(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue