Skip to content

Commit

Permalink
introduce PyType bound constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 29, 2023
1 parent a56895f commit 84633a0
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ generate-import-lib = ["pyo3-ffi/generate-import-lib"]
auto-initialize = []

# Allows use of the deprecated "GIL Refs" APIs.
gil-refs = []
gil-refs = ["pyo3-macros/gil-refs"]

# Optimizes PyObject to Vec conversion and so on.
nightly = []
Expand Down
1 change: 1 addition & 0 deletions pyo3-macros-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-trait

[features]
abi3 = []
gil-refs = []

[lints]
workspace = true
6 changes: 6 additions & 0 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,14 @@ impl FnType {
FnType::FnClass(span) | FnType::FnNewClass(span) => {
let py = syn::Ident::new("py", Span::call_site());
let slf: Ident = syn::Ident::new("_slf", Span::call_site());
let allow_deprecated = if cfg!(feature = "gil-refs") {
quote!()
} else {
quote!(#[allow(deprecated)])
};
quote_spanned! { *span =>
#[allow(clippy::useless_conversion)]
#allow_deprecated
::std::convert::Into::into(_pyo3::types::PyType::from_type_ptr(#py, #slf.cast())),
}
}
Expand Down
1 change: 1 addition & 0 deletions pyo3-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ proc-macro = true
multiple-pymethods = []

abi3 = ["pyo3-macros-backend/abi3"]
gil-refs = ["pyo3-macros-backend/gil-refs"]

[dependencies]
proc-macro2 = { version = "1", default-features = false }
Expand Down
6 changes: 3 additions & 3 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::instance::Bound;
use crate::panic::PanicException;
use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::{PyTraceback, PyType};
use crate::types::{typeobject::PyTypeMethods, PyTraceback, PyType};
use crate::{
exceptions::{self, PyBaseException},
ffi,
Expand Down Expand Up @@ -202,7 +202,7 @@ impl PyErr {
/// assert_eq!(err.to_string(), "TypeError: some type error");
///
/// // Case #2: Exception type
/// let err = PyErr::from_value(PyType::new::<PyTypeError>(py));
/// let err = PyErr::from_value(PyType::new_bound::<PyTypeError>(py).as_gil_ref());
/// assert_eq!(err.to_string(), "TypeError: ");
///
/// // Case #3: Invalid exception value
Expand Down Expand Up @@ -233,7 +233,7 @@ impl PyErr {
///
/// Python::with_gil(|py| {
/// let err: PyErr = PyTypeError::new_err(("some type error",));
/// assert!(err.get_type(py).is(PyType::new::<PyTypeError>(py)));
/// assert!(err.get_type(py).is(&PyType::new_bound::<PyTypeError>(py)));
/// });
/// ```
pub fn get_type<'py>(&'py self, py: Python<'py>) -> &'py PyType {
Expand Down
15 changes: 9 additions & 6 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult};
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::string::PyStringMethods;
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
use crate::types::{PyDict, PyString, PyTuple};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer, PyRef, PyRefMut,
Expand Down Expand Up @@ -312,6 +311,10 @@ impl<'a, 'py> Borrowed<'a, 'py, PyAny> {
pub(crate) unsafe fn from_ptr_unchecked(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
Self(NonNull::new_unchecked(ptr), PhantomData, py)
}

pub(crate) unsafe fn downcast_into_unchecked<T>(self) -> Borrowed<'a, 'py, T> {
Borrowed(self.0, PhantomData, self.py())
}
}

impl<'a, 'py, T> From<&'a Bound<'py, T>> for Borrowed<'a, 'py, T> {
Expand All @@ -325,10 +328,10 @@ impl<'py, T> Borrowed<'py, 'py, T>
where
T: HasPyGilRef,
{
// pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
// // Safety: self is a borrow over `'py`.
// unsafe { self.py().from_borrowed_ptr(self.0.as_ptr()) }
// }
pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
// Safety: self is a borrow over `'py`.
unsafe { self.py().from_borrowed_ptr(self.0.as_ptr()) }
}
}

impl<T> std::fmt::Debug for Borrowed<'_, '_, T> {
Expand Down
10 changes: 5 additions & 5 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ impl PyAny {

/// Returns the Python type object for this object's type.
pub fn get_type(&self) -> &PyType {
self.as_borrowed().get_type()
self.as_borrowed().get_type().into_gil_ref()
}

/// Returns the Python type pointer for this object.
Expand Down Expand Up @@ -1490,7 +1490,7 @@ pub trait PyAnyMethods<'py> {
fn iter(&self) -> PyResult<Bound<'py, PyIterator>>;

/// Returns the Python type object for this object's type.
fn get_type(&self) -> &'py PyType;
fn get_type(&self) -> Bound<'py, PyType>;

/// Returns the Python type pointer for this object.
fn get_type_ptr(&self) -> *mut ffi::PyTypeObject;
Expand Down Expand Up @@ -2021,8 +2021,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
PyIterator::from_object2(self)
}

fn get_type(&self) -> &'py PyType {
unsafe { PyType::from_type_ptr(self.py(), ffi::Py_TYPE(self.as_ptr())) }
fn get_type(&self) -> Bound<'py, PyType> {
unsafe { PyType::from_type_ptr_borrowed(self.py(), ffi::Py_TYPE(self.as_ptr())) }.to_owned()
}

#[inline]
Expand Down Expand Up @@ -2179,7 +2179,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {

#[cfg(not(PyPy))]
fn py_super(&self) -> PyResult<Bound<'py, PySuper>> {
PySuper::new_bound(&self.get_type().as_borrowed(), self)
PySuper::new_bound(&self.get_type(), self)
}
}

Expand Down
44 changes: 40 additions & 4 deletions src/types/typeobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,62 @@ pub struct PyType(PyAny);
pyobject_native_type_core!(PyType, pyobject_native_static_type_object!(ffi::PyType_Type), #checkfunction=ffi::PyType_Check);

impl PyType {
/// Creates a new type object.
/// Deprecated form of [`PyType::new_bound`].
#[inline]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`new` will be replaced by `new_bound` in a future PyO3 version"
)
)]
pub fn new<T: PyTypeInfo>(py: Python<'_>) -> &PyType {
T::type_object(py)
}

/// Creates a new type object.
#[inline]
pub fn new_bound<T: PyTypeInfo>(py: Python<'_>) -> Bound<'_, PyType> {
T::type_object(py).as_borrowed().to_owned()
}

/// Retrieves the underlying FFI pointer associated with this Python object.
#[inline]
pub fn as_type_ptr(&self) -> *mut ffi::PyTypeObject {
self.as_borrowed().as_type_ptr()
}

/// Deprecated form of [`PyType::from_type_ptr_borrowed`].
///
/// # Safety
///
/// See [`PyType::from_type_ptr_borrowed`].
#[inline]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`from_type_ptr` will be replaced by `from_type_ptr_borrowed` in a future PyO3 version"
)
)]
pub unsafe fn from_type_ptr(py: Python<'_>, p: *mut ffi::PyTypeObject) -> &PyType {
Self::from_type_ptr_borrowed(py, p).into_gil_ref()
}

/// Retrieves the `PyType` instance for the given FFI pointer.
///
/// # Safety
/// - The pointer must be non-null.
/// - The pointer must be valid for the entire of the lifetime for which the reference is used.
/// - The pointer must be valid for the entire of the lifetime 'a for which the reference is used,
/// as with `std::slice::from_raw_parts`.
#[inline]
pub unsafe fn from_type_ptr(py: Python<'_>, p: *mut ffi::PyTypeObject) -> &PyType {
py.from_borrowed_ptr(p as *mut ffi::PyObject)
pub unsafe fn from_type_ptr_borrowed<'a>(
py: Python<'_>,
p: *mut ffi::PyTypeObject,
) -> Borrowed<'a, '_, PyType> {
(p as *mut ffi::PyObject)
.assume_borrowed_unchecked(py)
.downcast_into_unchecked()
}

/// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`.
Expand Down
2 changes: 1 addition & 1 deletion tests/test_class_basics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ impl UnsendableChild {

fn test_unsendable<T: PyClass + 'static>() -> PyResult<()> {
let obj = Python::with_gil(|py| -> PyResult<_> {
let obj: Py<T> = PyType::new::<T>(py).call1((5,))?.extract()?;
let obj: Py<T> = PyType::new_bound::<T>(py).call1((5,))?.extract()?;

// Accessing the value inside this thread should not panic
let caught_panic =
Expand Down

0 comments on commit 84633a0

Please sign in to comment.