Skip to content

Commit

Permalink
replace PyTryFrom by splitting PyTypeInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Nov 27, 2023
1 parent 0f34fcd commit 7ecd127
Show file tree
Hide file tree
Showing 19 changed files with 213 additions and 214 deletions.
4 changes: 3 additions & 1 deletion guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -1108,8 +1108,10 @@ struct MyClass {
# #[allow(dead_code)]
num: i32,
}
unsafe impl pyo3::type_object::PyTypeInfo for MyClass {
unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
type AsRefTarget = pyo3::PyCell<Self>;
}
unsafe impl pyo3::type_object::PyTypeInfo for MyClass {
const NAME: &'static str = "MyClass";
const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None;
#[inline]
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3600.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Split some `PyTypeInfo` functionality into new traits `HasPyGilRef` and `PyTypeCheck`.
4 changes: 3 additions & 1 deletion pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,11 @@ fn impl_pytypeinfo(
};

quote! {
unsafe impl _pyo3::type_object::PyTypeInfo for #cls {
unsafe impl _pyo3::type_object::HasPyGilRef for #cls {
type AsRefTarget = _pyo3::PyCell<Self>;
}

unsafe impl _pyo3::type_object::PyTypeInfo for #cls {
const NAME: &'static str = #cls_name;
const MODULE: ::std::option::Option<&'static str> = #module;

Expand Down
120 changes: 52 additions & 68 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
PyTryFrom::try_from(obj).map_err(Into::into)
obj.downcast().map_err(Into::into)
}
}

Expand All @@ -313,7 +313,7 @@ where
T: PyClass + Clone,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<Self> = PyTryFrom::try_from(obj)?;
let cell: &PyCell<Self> = obj.downcast()?;
Ok(unsafe { cell.try_borrow_unguarded()?.clone() })
}
}
Expand All @@ -323,7 +323,7 @@ where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
let cell: &PyCell<T> = obj.downcast()?;
cell.try_borrow().map_err(Into::into)
}
}
Expand All @@ -333,7 +333,7 @@ where
T: PyClass<Frozen = False>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
let cell: &PyCell<T> = obj.downcast()?;
cell.try_borrow_mut().map_err(Into::into)
}
}
Expand Down Expand Up @@ -381,78 +381,61 @@ pub trait PyTryInto<T>: Sized {
fn try_into_exact(&self) -> Result<&T, PyDowncastError<'_>>;
}

// TryFrom implies TryInto
impl<U> PyTryInto<U> for PyAny
where
U: for<'v> PyTryFrom<'v>,
{
fn try_into(&self) -> Result<&U, PyDowncastError<'_>> {
<U as PyTryFrom<'_>>::try_from(self)
}
fn try_into_exact(&self) -> Result<&U, PyDowncastError<'_>> {
U::try_from_exact(self)
}
}
mod implementations {
use super::*;

impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
// TryFrom implies TryInto
impl<U> PyTryInto<U> for PyAny
where
U: for<'v> PyTryFrom<'v>,
{
fn try_into(&self) -> Result<&U, PyDowncastError<'_>> {
<U as PyTryFrom<'_>>::try_from(self)
}
fn try_into_exact(&self) -> Result<&U, PyDowncastError<'_>> {
U::try_from_exact(self)
}
}

fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo<AsRefTarget = Self> + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
}

#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast_exact()
}

impl<'v, T> PyTryFrom<'v> for PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))

impl<'v, T> PyTryFrom<'v> for PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
}

Expand Down Expand Up @@ -572,10 +555,11 @@ mod test_no_clone {}

#[cfg(test)]
mod tests {
use crate::types::{IntoPyDict, PyAny, PyDict, PyList};
use crate::{PyObject, Python, ToPyObject};
use crate::PyObject;

use super::PyTryFrom;
use super::super::PyTryFrom;
use crate::types::{IntoPyDict, PyAny, PyDict, PyList};
use crate::{Python, ToPyObject};

#[test]
fn test_try_from() {
Expand Down
20 changes: 10 additions & 10 deletions src/instance.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::conversion::PyTryFrom;
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::pyclass::boolean_struct::{False, True};
use crate::type_object::HasPyGilRef;
use crate::types::any::PyAnyMethods;
use crate::types::{PyDict, PyString, PyTuple};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer, PyRef, PyRefMut,
PyTypeInfo, Python, ToPyObject,
};
use crate::{gil, PyTypeCheck};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
use std::ops::Deref;
Expand Down Expand Up @@ -185,7 +185,7 @@ impl<'py, T> Py2<'py, T> {
#[doc(hidden)] // public and doc(hidden) to use in examples and tests for now
pub fn as_gil_ref(&'py self) -> &'py T::AsRefTarget
where
T: PyTypeInfo,
T: HasPyGilRef,
{
unsafe { self.py().from_borrowed_ptr(self.as_ptr()) }
}
Expand All @@ -194,7 +194,7 @@ impl<'py, T> Py2<'py, T> {
#[doc(hidden)] // public but hidden, to use for tests for now
pub fn into_gil_ref(self) -> &'py T::AsRefTarget
where
T: PyTypeInfo,
T: HasPyGilRef,
{
unsafe { self.py().from_owned_ptr(self.into_ptr()) }
}
Expand Down Expand Up @@ -437,7 +437,7 @@ where

impl<T> Py<T>
where
T: PyTypeInfo,
T: HasPyGilRef,
{
/// Borrows a GIL-bound reference to the contained `T`.
///
Expand Down Expand Up @@ -1314,11 +1314,11 @@ impl PyObject {
/// # }
/// ```
#[inline]
pub fn downcast<'p, T>(&'p self, py: Python<'p>) -> Result<&T, PyDowncastError<'_>>
pub fn downcast<'py, T>(&'py self, py: Python<'py>) -> Result<&'py T, PyDowncastError<'py>>
where
T: PyTryFrom<'p>,
T: PyTypeCheck<AsRefTarget = T>,
{
<T as PyTryFrom<'_>>::try_from(self.as_ref(py))
self.as_ref(py).downcast()
}

/// Casts the PyObject to a concrete Python object type without checking validity.
Expand All @@ -1329,9 +1329,9 @@ impl PyObject {
#[inline]
pub unsafe fn downcast_unchecked<'p, T>(&'p self, py: Python<'p>) -> &T
where
T: PyTryFrom<'p>,
T: HasPyGilRef<AsRefTarget = T>,
{
<T as PyTryFrom<'_>>::try_from_unchecked(self.as_ref(py))
self.as_ref(py).downcast_unchecked()
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,8 @@
//! [Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"
//! [`Ungil`]: crate::marker::Ungil
pub use crate::class::*;
pub use crate::conversion::{
AsPyPointer, FromPyObject, FromPyPointer, IntoPy, PyTryFrom, PyTryInto, ToPyObject,
};
pub use crate::conversion::{AsPyPointer, FromPyObject, FromPyPointer, IntoPy, ToPyObject};
pub use crate::conversion::{PyTryFrom, PyTryInto};
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult};
pub use crate::gil::GILPool;
#[cfg(not(PyPy))]
Expand All @@ -306,7 +305,7 @@ pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass::PyClass;
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::type_object::PyTypeInfo;
pub use crate::type_object::{PyTypeCheck, PyTypeInfo};
pub use crate::types::PyAny;
pub use crate::version::PythonVersionInfo;

Expand Down
13 changes: 6 additions & 7 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,12 @@
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil::{GILGuard, GILPool, SuspendGIL};
use crate::impl_::not_send::NotSend;
use crate::type_object::HasPyGilRef;
use crate::types::{
PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType,
};
use crate::version::PythonVersionInfo;
use crate::{ffi, FromPyPointer, IntoPy, Py, PyNativeType, PyObject, PyTryFrom, PyTypeInfo};
use crate::{ffi, FromPyPointer, IntoPy, Py, PyObject, PyTypeCheck, PyTypeInfo};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_int;
Expand Down Expand Up @@ -759,10 +760,9 @@ impl<'py> Python<'py> {
/// Registers the object in the release pool, and tries to downcast to specific type.
pub fn checked_cast_as<T>(self, obj: PyObject) -> Result<&'py T, PyDowncastError<'py>>
where
T: PyTryFrom<'py>,
T: PyTypeCheck<AsRefTarget = T>,
{
let any: &PyAny = unsafe { self.from_owned_ptr(obj.into_ptr()) };
<T as PyTryFrom>::try_from(any)
obj.into_ref(self).downcast()
}

/// Registers the object in the release pool, and does an unchecked downcast
Expand All @@ -773,10 +773,9 @@ impl<'py> Python<'py> {
/// Callers must ensure that ensure that the cast is valid.
pub unsafe fn cast_as<T>(self, obj: PyObject) -> &'py T
where
T: PyNativeType + PyTypeInfo,
T: HasPyGilRef<AsRefTarget = T>,
{
let any: &PyAny = self.from_owned_ptr(obj.into_ptr());
T::unchecked_downcast(any)
obj.into_ref(self).downcast_unchecked()
}

/// Registers the object pointer in the release pool,
Expand Down
3 changes: 2 additions & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
//! use pyo3::prelude::*;
//! ```

pub use crate::conversion::{FromPyObject, IntoPy, PyTryFrom, PyTryInto, ToPyObject};
pub use crate::conversion::{FromPyObject, IntoPy, ToPyObject};
pub use crate::conversion::{PyTryFrom, PyTryInto};
pub use crate::err::{PyErr, PyResult};
pub use crate::instance::{Py, PyObject};
pub use crate::marker::Python;
Expand Down
13 changes: 12 additions & 1 deletion src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ use crate::{
type_object::get_tp_free,
PyTypeInfo,
};
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python};
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, PyTypeCheck, Python};
use std::cell::UnsafeCell;
use std::fmt;
use std::mem::ManuallyDrop;
Expand Down Expand Up @@ -527,6 +527,17 @@ impl<T: PyClassImpl> PyCell<T> {
unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}

impl<T> PyTypeCheck for PyCell<T>
where
T: PyClass,
{
const NAME: &'static str = <T as PyTypeCheck>::NAME;

fn type_check(object: &PyAny) -> bool {
<T as PyTypeCheck>::type_check(object)
}
}

unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
Expand Down

0 comments on commit 7ecd127

Please sign in to comment.