Skip to content

Commit

Permalink
Avoids panic on class initialization when adding a class to the module
Browse files Browse the repository at this point in the history
  • Loading branch information
Tpt committed Feb 19, 2024
1 parent 22ad44f commit 72495f1
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 11 deletions.
9 changes: 9 additions & 0 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1287,8 +1287,17 @@ fn impl_pytypeinfo(

<#cls as _pyo3::impl_::pyclass::PyClassImpl>::lazy_type_object()
.get_or_init(py)
.as_gil_ref()
.as_type_ptr()
}

#[inline]
fn try_type_object_bound(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::Bound<'_, _pyo3::types::PyType>> {
#deprecations

<#cls as _pyo3::impl_::pyclass::PyClassImpl>::lazy_type_object()
.get_or_try_init(py)
}
}
}
}
Expand Down
17 changes: 9 additions & 8 deletions src/impl_/pyclass/lazy_type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
pyclass::{create_type_object, PyClassTypeObject},
sync::{GILOnceCell, GILProtected},
types::PyType,
PyClass, PyErr, PyMethodDefType, PyObject, PyResult, Python,
Bound, PyClass, PyErr, PyMethodDefType, PyObject, PyResult, Python,
};

use super::PyClassItemsIter;
Expand Down Expand Up @@ -46,15 +46,15 @@ impl<T> LazyTypeObject<T> {

impl<T: PyClass> LazyTypeObject<T> {
/// Gets the type object contained by this `LazyTypeObject`, initializing it if needed.
pub fn get_or_init<'py>(&'py self, py: Python<'py>) -> &'py PyType {
pub fn get_or_init<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
self.get_or_try_init(py).unwrap_or_else(|err| {
err.print(py);
panic!("failed to create type object for {}", T::NAME)
})
}

/// Fallible version of the above.
pub(crate) fn get_or_try_init<'py>(&'py self, py: Python<'py>) -> PyResult<&'py PyType> {
pub fn get_or_try_init<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyType>> {
self.0
.get_or_try_init(py, create_type_object::<T>, T::NAME, T::items_iter())
}
Expand All @@ -65,19 +65,20 @@ impl LazyTypeObjectInner {
// so that this code is only instantiated once, instead of for every T
// like the generic LazyTypeObject<T> methods above.
fn get_or_try_init<'py>(
&'py self,
&self,
py: Python<'py>,
init: fn(Python<'py>) -> PyResult<PyClassTypeObject>,
name: &str,
items_iter: PyClassItemsIter,
) -> PyResult<&'py PyType> {
) -> PyResult<Bound<'py, PyType>> {
(|| -> PyResult<_> {
let type_object = self
.value
.get_or_try_init(py, || init(py))?
.type_object
.as_ref(py);
self.ensure_init(type_object, name, items_iter)?;
.clone()
.into_bound(py);
self.ensure_init(&type_object, name, items_iter)?;
Ok(type_object)
})()
.map_err(|err| {
Expand All @@ -91,7 +92,7 @@ impl LazyTypeObjectInner {

fn ensure_init(
&self,
type_object: &PyType,
type_object: &Bound<'_, PyType>,
name: &str,
items_iter: PyClassItemsIter,
) -> PyResult<()> {
Expand Down
2 changes: 1 addition & 1 deletion src/impl_/pymodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub trait PyAddToModule {

impl<T: PyTypeInfo> PyAddToModule for T {
fn add_to_module(module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add(Self::NAME, Self::type_object_bound(module.py()))
module.add(Self::NAME, Self::try_type_object_bound(module.py())?)
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyType};
use crate::{ffi, Bound, PyNativeType, Python};
use crate::{ffi, Bound, PyNativeType, PyResult, Python};

/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap.
/// E.g., `PyCell` is a concrete representation of all `pyclass`es, and `ffi::PyObject`
Expand Down Expand Up @@ -96,6 +96,12 @@ pub unsafe trait PyTypeInfo: Sized + HasPyGilRef {
}
}

/// Returns the safe abstraction over the type object or an error if initialization fails
#[inline]
fn try_type_object_bound(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
Ok(Self::type_object_bound(py))
}

/// Checks if `object` is an instance of this type or a subclass of this type.
#[inline]
#[cfg_attr(
Expand Down
2 changes: 1 addition & 1 deletion src/types/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
T: PyClass,
{
let py = self.py();
self.add(T::NAME, T::lazy_type_object().get_or_try_init(py)?)
self.add(T::NAME, T::try_type_object_bound(py)?)
}

fn add_wrapped<T>(&self, wrapper: &impl Fn(Python<'py>) -> T) -> PyResult<()>
Expand Down

0 comments on commit 72495f1

Please sign in to comment.