Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow downstream overwrite of the Bound<T> deref target #3956

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,9 @@ struct MyClass {
num: i32,
}

impl pyo3::types::DerefToPyAny for MyClass {}
impl pyo3::types::DerefToTarget for MyClass {
type Target = pyo3::PyAny;
}

# #[allow(deprecated)]
unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
Expand Down
9 changes: 8 additions & 1 deletion pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,15 @@ fn impl_class(
.doc(doc)
.impl_all(ctx)?;

let base = match &args.options.extends {
Some(extends_attr) => extends_attr.value.clone(),
None => parse_quote! { #pyo3_path::PyAny },
};

Ok(quote! {
impl #pyo3_path::types::DerefToPyAny for #cls {}
impl #pyo3_path::types::DerefToTarget for #cls {
type Target = #base;
}

#pytypeinfo_impl

Expand Down
10 changes: 5 additions & 5 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::{False, True};
use crate::type_object::HasPyGilRef;
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
use crate::types::{DerefToPyAny, PyDict, PyString, PyTuple};
use crate::types::{DerefToTarget, PyDict, PyString, PyTuple};
use crate::{
ffi, AsPyPointer, DowncastError, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer,
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
Expand Down Expand Up @@ -368,13 +368,13 @@ fn python_format(
// https://github.com/rust-lang/rust/issues/19509
impl<'py, T> Deref for Bound<'py, T>
where
T: DerefToPyAny,
T: DerefToTarget,
{
type Target = Bound<'py, PyAny>;
type Target = Bound<'py, T::Target>;

#[inline]
fn deref(&self) -> &Bound<'py, PyAny> {
self.as_any()
fn deref(&self) -> &Self::Target {
unsafe { self.as_any().downcast_unchecked() }
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/types/frozenset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ pub(crate) fn new_from_iter<T: ToPyObject>(
// We create the `Py` pointer because its Drop cleans up the set if user code panics.
ffi::PyFrozenSet_New(std::ptr::null_mut())
.assume_owned_or_err(py)?
.downcast_into_unchecked()
.downcast_into_unchecked::<PyFrozenSet>()
};
let ptr = set.as_ptr();

Expand Down
23 changes: 16 additions & 7 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,17 @@ pub mod iter {

/// 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 marks types that can be upcast into the `Target` and used in its place.
/// This essentially includes every Python object except [`PyAny`]. Most of the
/// time the `Target` is [`PyAny`].
///
/// This is used to provide the [`Deref<Target = Bound<'_, PyAny>>`](std::ops::Deref)
/// This is used to provide the [`Deref<Target = Bound<'_, Target>>`](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.
/// is provided by the [`#[pyclass]`](macro@crate::pyclass) attribute. The macro
/// takes inheritance via `#[pyclass(extends = ...)]` into account, and sets the
/// extended type as the `Target`.
///
/// ## Note
/// This is needed because the compiler currently tries to figure out all the
Expand All @@ -107,8 +110,9 @@ pub mod iter {
/// trait will be removed.
///
/// [1]: https://github.com/rust-lang/rust/issues/19509
pub trait DerefToPyAny {
// Empty.
pub trait DerefToTarget {
/// Target Type
type Target;
}

// Implementations core to all native types
Expand Down Expand Up @@ -162,6 +166,9 @@ macro_rules! pyobject_native_type_base(
#[macro_export]
macro_rules! pyobject_native_type_named (
($name:ty $(;$generics:ident)*) => {
$crate::pyobject_native_type_named!($name $(;$generics)*, $crate::PyAny);
};
($name:ty $(;$generics:ident)*, $target:ty) => {
$crate::pyobject_native_type_base!($name $(;$generics)*);

impl<$($generics,)*> ::std::convert::AsRef<$crate::PyAny> for $name {
Expand Down Expand Up @@ -215,7 +222,9 @@ macro_rules! pyobject_native_type_named (
}
}

impl $crate::types::DerefToPyAny for $name {}
impl $crate::types::DerefToTarget for $name {
type Target = $target;
}
};
);

Expand Down
2 changes: 1 addition & 1 deletion src/types/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ pub(crate) fn new_from_iter<T: ToPyObject>(
// We create the `Py` pointer because its Drop cleans up the set if user code panics.
ffi::PySet_New(std::ptr::null_mut())
.assume_owned_or_err(py)?
.downcast_into_unchecked()
.downcast_into_unchecked::<PySet>()
};
let ptr = set.as_ptr();

Expand Down
2 changes: 1 addition & 1 deletion src/types/typeobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl PyType {
p: *mut ffi::PyTypeObject,
) -> Bound<'_, PyType> {
Borrowed::from_ptr_unchecked(py, p.cast())
.downcast_unchecked()
.downcast_unchecked::<PyType>()
.to_owned()
}

Expand Down
Loading