Skip to content
Merged
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
1 change: 1 addition & 0 deletions newsfragments/5413.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`PyClassGuard(Mut)` and `PyRef(Mut)` extraction now returns an opaque Rust error
36 changes: 22 additions & 14 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ use crate::err::PyResult;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::pyclass::boolean_struct::False;
use crate::pyclass::{PyClassGuardError, PyClassGuardMutError};
use crate::types::PyTuple;
use crate::{Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyRef, PyRefMut, Python};
use crate::{
Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyRef, PyRefMut, Python,
};
use std::convert::Infallible;

/// Defines a conversion from a Rust type to a Python object, which may fail.
Expand Down Expand Up @@ -460,46 +463,51 @@ pub trait FromPyObject<'a, 'py>: Sized {
pub trait FromPyObjectOwned<'py>: for<'a> FromPyObject<'a, 'py> {}
impl<'py, T> FromPyObjectOwned<'py> for T where T: for<'a> FromPyObject<'a, 'py> {}

impl<T> FromPyObject<'_, '_> for T
impl<'a, 'py, T> FromPyObject<'a, 'py> for T
where
T: PyClass + Clone,
{
type Error = PyErr;
type Error = PyClassGuardError<'a, 'py>;

#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: &'static str = <T as crate::impl_::pyclass::PyClassImpl>::TYPE_NAME;

fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
let bound = obj.cast::<Self>()?;
Ok(bound.try_borrow()?.clone())
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
Ok(obj.extract::<PyClassGuard<'_, T>>()?.clone())
}
}

impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T>
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'py, T>
where
T: PyClass,
{
type Error = PyErr;
type Error = PyClassGuardError<'a, 'py>;

#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: &'static str = <T as crate::impl_::pyclass::PyClassImpl>::TYPE_NAME;

fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()?.try_borrow().map_err(Into::into)
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()
.map_err(|e| PyClassGuardError(Some(e)))?
.try_borrow()
.map_err(|_| PyClassGuardError(None))
}
}

impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T>
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
{
type Error = PyErr;
type Error = PyClassGuardMutError<'a, 'py>;

#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: &'static str = <T as crate::impl_::pyclass::PyClassImpl>::TYPE_NAME;

fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()?.try_borrow_mut().map_err(Into::into)
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()
.map_err(|e| PyClassGuardMutError(Some(e)))?
.try_borrow_mut()
.map_err(|_| PyClassGuardMutError(None))
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,12 @@ pub struct PyBorrowError {
_private: (),
}

impl PyBorrowError {
pub(crate) fn new() -> Self {
Self { _private: () }
}
}

impl fmt::Debug for PyBorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PyBorrowError").finish()
Expand All @@ -712,6 +718,12 @@ pub struct PyBorrowMutError {
_private: (),
}

impl PyBorrowMutError {
pub(crate) fn new() -> Self {
Self { _private: () }
}
}

impl fmt::Debug for PyBorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PyBorrowMutError").finish()
Expand Down
4 changes: 3 additions & 1 deletion src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ mod guard;
pub(crate) use self::create_type_object::{create_type_object, PyClassTypeObject};

pub use self::gc::{PyTraverseError, PyVisit};
pub use self::guard::{PyClassGuard, PyClassGuardMap, PyClassGuardMut};
pub use self::guard::{
PyClassGuard, PyClassGuardError, PyClassGuardMap, PyClassGuardMut, PyClassGuardMutError,
};

/// Types that can be used as Python classes.
///
Expand Down
87 changes: 82 additions & 5 deletions src/pyclass/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use crate::impl_::pycell::{PyClassObject, PyClassObjectLayout as _};
use crate::pycell::PyBorrowMutError;
use crate::pycell::{impl_::PyClassBorrowChecker, PyBorrowError};
use crate::pyclass::boolean_struct::False;
use crate::{ffi, Borrowed, FromPyObject, IntoPyObject, Py, PyClass, PyErr};
use crate::{ffi, Borrowed, DowncastError, FromPyObject, IntoPyObject, Py, PyClass, PyErr};
use std::convert::Infallible;
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
Expand Down Expand Up @@ -277,10 +278,15 @@ impl<T: PyClass> Deref for PyClassGuard<'_, T> {
}

impl<'a, 'py, T: PyClass> FromPyObject<'a, 'py> for PyClassGuard<'a, T> {
type Error = PyErr;
type Error = PyClassGuardError<'a, 'py>;

fn extract(obj: Borrowed<'a, 'py, crate::PyAny>) -> Result<Self, Self::Error> {
Self::try_from_class_object(obj.cast()?.get_class_object()).map_err(Into::into)
Self::try_from_class_object(
obj.cast()
.map_err(|e| PyClassGuardError(Some(e)))?
.get_class_object(),
)
.map_err(|_| PyClassGuardError(None))
}
}

Expand Down Expand Up @@ -327,6 +333,39 @@ unsafe impl<T: PyClass> crate::marker::Ungil for PyClassGuard<'_, T> {}
unsafe impl<T: PyClass + Sync> Send for PyClassGuard<'_, T> {}
unsafe impl<T: PyClass + Sync> Sync for PyClassGuard<'_, T> {}

/// Custom error type for extracting a [PyClassGuard]
pub struct PyClassGuardError<'a, 'py>(pub(crate) Option<DowncastError<'a, 'py>>);

impl fmt::Debug for PyClassGuardError<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(e) = &self.0 {
write!(f, "{e:?}")
} else {
write!(f, "{:?}", PyBorrowError::new())
}
}
}

impl fmt::Display for PyClassGuardError<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(e) = &self.0 {
write!(f, "{e}")
} else {
write!(f, "{}", PyBorrowError::new())
}
}
}

impl From<PyClassGuardError<'_, '_>> for PyErr {
fn from(value: PyClassGuardError<'_, '_>) -> Self {
if let Some(e) = value.0 {
e.into()
} else {
PyBorrowError::new().into()
}
}
}

/// A wrapper type for a mutably borrowed value from a `PyClass`
///
/// # When *not* to use [`PyClassGuardMut`]
Expand Down Expand Up @@ -642,10 +681,15 @@ impl<T: PyClass<Frozen = False>> DerefMut for PyClassGuardMut<'_, T> {
}

impl<'a, 'py, T: PyClass<Frozen = False>> FromPyObject<'a, 'py> for PyClassGuardMut<'a, T> {
type Error = PyErr;
type Error = PyClassGuardMutError<'a, 'py>;

fn extract(obj: Borrowed<'a, 'py, crate::PyAny>) -> Result<Self, Self::Error> {
Self::try_from_class_object(obj.cast()?.get_class_object()).map_err(Into::into)
Self::try_from_class_object(
obj.cast()
.map_err(|e| PyClassGuardMutError(Some(e)))?
.get_class_object(),
)
.map_err(|_| PyClassGuardMutError(None))
}
}

Expand Down Expand Up @@ -690,6 +734,39 @@ unsafe impl<T: PyClass<Frozen = False>> crate::marker::Ungil for PyClassGuardMut
unsafe impl<T: PyClass<Frozen = False> + Send + Sync> Send for PyClassGuardMut<'_, T> {}
unsafe impl<T: PyClass<Frozen = False> + Sync> Sync for PyClassGuardMut<'_, T> {}

/// Custom error type for extracting a [PyClassGuardMut]
pub struct PyClassGuardMutError<'a, 'py>(pub(crate) Option<DowncastError<'a, 'py>>);

impl fmt::Debug for PyClassGuardMutError<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(e) = &self.0 {
write!(f, "{e:?}")
} else {
write!(f, "{:?}", PyBorrowMutError::new())
}
}
}

impl fmt::Display for PyClassGuardMutError<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(e) = &self.0 {
write!(f, "{e}")
} else {
write!(f, "{}", PyBorrowMutError::new())
}
}
}

impl From<PyClassGuardMutError<'_, '_>> for PyErr {
fn from(value: PyClassGuardMutError<'_, '_>) -> Self {
if let Some(e) = value.0 {
e.into()
} else {
PyBorrowMutError::new().into()
}
}
}

/// Wraps a borrowed reference `U` to a value stored inside of a pyclass `T`
///
/// See [`PyClassGuard::map`] and [`PyClassGuardMut::map`]
Expand Down
Loading