Vendor deps.
This commit is contained in:
1 parent
294527085e
commit
91e18fde3a
1714 files changed
+556667
-7
No files matched your search
+200
@@ -0,0 +1,200 @@
|
||||
// TODO https://github.com/PyO3/pyo3/issues/5487
|
||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
||||
|
||||
//! Contains initialization utilities for `#[pyclass]`.
|
||||
use crate::exceptions::PyTypeError;
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
use crate::impl_::pyclass::PyClassBaseType;
|
||||
use crate::internal::get_slot::TP_NEW;
|
||||
use crate::types::{PyTuple, PyType, PyTypeMethods};
|
||||
use crate::{
|
||||
ffi, IntoPyObject, IntoPyObjectExt, PyClass, PyClassInitializer, PyErr, PyResult, Python,
|
||||
};
|
||||
use crate::{ffi::PyTypeObject, type_object::PyTypeInfo};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Initializer for Python types.
|
||||
///
|
||||
/// This trait is used internally for distinguishing `#[pyclass]` and
|
||||
/// Python native types.
|
||||
pub(crate) trait PyObjectInit<T>: Sized {
|
||||
/// # Safety
|
||||
/// - `subtype` must be a valid pointer to a type object of T or a subclass.
|
||||
unsafe fn into_new_object(
|
||||
self,
|
||||
py: Python<'_>,
|
||||
subtype: *mut PyTypeObject,
|
||||
) -> PyResult<*mut ffi::PyObject>;
|
||||
}
|
||||
|
||||
/// Initializer for Python native types, like `PyDict`.
|
||||
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(pub PhantomData<T>);
|
||||
|
||||
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
||||
unsafe fn into_new_object(
|
||||
self,
|
||||
py: Python<'_>,
|
||||
subtype: *mut PyTypeObject,
|
||||
) -> PyResult<*mut ffi::PyObject> {
|
||||
unsafe fn inner(
|
||||
py: Python<'_>,
|
||||
type_ptr: *mut PyTypeObject,
|
||||
subtype: *mut PyTypeObject,
|
||||
) -> PyResult<*mut ffi::PyObject> {
|
||||
let tp_new = unsafe {
|
||||
type_ptr
|
||||
.cast::<ffi::PyObject>()
|
||||
.assume_borrowed_unchecked(py)
|
||||
.cast_unchecked::<PyType>()
|
||||
.get_slot(TP_NEW)
|
||||
.ok_or_else(|| PyTypeError::new_err("base type without tp_new"))?
|
||||
};
|
||||
|
||||
// TODO: make it possible to provide real arguments to the base tp_new
|
||||
let obj =
|
||||
unsafe { tp_new(subtype, PyTuple::empty(py).as_ptr(), core::ptr::null_mut()) };
|
||||
if obj.is_null() {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
unsafe { inner(py, T::type_object_raw(py), subtype) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TpNewValueTypeResolver<ClassT, ValueT>(TpNewTupleResolver<ClassT, ValueT>);
|
||||
pub struct TpNewTupleResolver<ClassT, ValueT>(
|
||||
ResolveToArbitraryObject,
|
||||
PhantomData<(ClassT, ValueT)>,
|
||||
);
|
||||
pub struct ResolveToArbitraryObject(());
|
||||
|
||||
/// First step of machinery for resolving the type of `#[new]` return values, which is used to
|
||||
/// implement specialization for the various cases.
|
||||
///
|
||||
/// Call `.resolve()` on the returned tag to get the final tag type.
|
||||
///
|
||||
/// This resolution step is necessary in order to encode the preference to go via `PyClassInitializer<T>`
|
||||
/// for new instances of `ClassT`. Without this step, the fallback to `IntoPyObject` conflicts for
|
||||
/// `ClassT` because that implementation ignores the `cls` parameter for `PyClassInit` (and would
|
||||
/// therefore be incorrect when instantiating subclasses).
|
||||
pub fn tp_new_resolver<ClassT, ValueT>(_: &ValueT) -> TpNewValueTypeResolver<ClassT, ValueT> {
|
||||
TpNewValueTypeResolver(TpNewTupleResolver(
|
||||
ResolveToArbitraryObject(()),
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
|
||||
impl<ClassT, ValueT> core::ops::Deref for TpNewValueTypeResolver<ClassT, ValueT> {
|
||||
type Target = TpNewTupleResolver<ClassT, ValueT>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<ClassT, ValueT> core::ops::Deref for TpNewTupleResolver<ClassT, ValueT> {
|
||||
type Target = ResolveToArbitraryObject;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Second stage of machinery for resolving the type of `#[new]` return values. This
|
||||
/// is the specialized implementation for the case where the return type should be
|
||||
/// used to build a new class object (`T`, `PyClassInitializer<T>`, `(S, B)` tuples).
|
||||
#[expect(private_bounds, reason = "internal trait implementation")]
|
||||
impl<ClassT, ValueT> TpNewValueTypeResolver<ClassT, ValueT>
|
||||
where
|
||||
ClassT: PyClass,
|
||||
ValueT: IntoPyClassInitializer<ClassT>,
|
||||
{
|
||||
pub fn resolve(&self, value: ValueT) -> PyClassInitializer<ClassT> {
|
||||
value.into_pyclass_initializer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> TpNewTupleResolver<S, (S, B)>
|
||||
where
|
||||
S: PyClass<BaseType = B>,
|
||||
B: PyClass + PyClassBaseType<Initializer = PyClassInitializer<B>>,
|
||||
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
|
||||
{
|
||||
#[deprecated(
|
||||
since = "0.29.0",
|
||||
note = "Tuple syntax for super class initialization is phased out. Use `PyClassInitializer` instead."
|
||||
)]
|
||||
pub fn resolve(&self, value: (S, B)) -> PyClassInitializer<S> {
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// All other conversions fall back to IntoPyObject via the deref implementation
|
||||
impl ResolveToArbitraryObject {
|
||||
pub fn resolve<ValueT>(&self, value: ValueT) -> ValueT {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[diagnostic::on_unimplemented(
|
||||
message = "`{Self}` cannot be used as the return value for `#[new]` methods",
|
||||
note = "all types which implement `IntoPyObject` are suitable return values for `#[new]` methods",
|
||||
note = "`PyClassInitializer<T: PyClass>` may also be used and is necessary for `#[pyclass(extends = '...')]` types",
|
||||
label = "must be a type which implements `IntoPyObject`, a `PyClassInitializer<{Self}>`, or a `Result<T>` wrapping such types"
|
||||
)]
|
||||
pub(crate) trait PyClassInit<'py, T> {
|
||||
/// # Safety
|
||||
/// - `cls` must be the type object for `T` (or a subclass)
|
||||
unsafe fn init(
|
||||
self,
|
||||
cls: crate::Borrowed<'_, 'py, crate::types::PyType>,
|
||||
) -> PyResult<crate::Bound<'py, crate::PyAny>>;
|
||||
}
|
||||
|
||||
impl<'py, ClassT, T> PyClassInit<'py, ClassT> for T
|
||||
where
|
||||
T: IntoPyObject<'py>,
|
||||
{
|
||||
unsafe fn init(
|
||||
self,
|
||||
cls: crate::Borrowed<'_, 'py, crate::types::PyType>,
|
||||
) -> PyResult<crate::Bound<'py, crate::PyAny>> {
|
||||
self.into_bound_py_any(cls.py())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py, T> PyClassInit<'py, T> for PyClassInitializer<T>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
unsafe fn init(
|
||||
self,
|
||||
cls: crate::Borrowed<'_, 'py, crate::types::PyType>,
|
||||
) -> PyResult<crate::Bound<'py, crate::PyAny>> {
|
||||
// SAFETY: caller has guaranteed that `cls` is correct object
|
||||
unsafe { self.create_class_object_of_type(cls.py(), cls.as_type_ptr()) }
|
||||
.map(crate::Bound::into_any)
|
||||
}
|
||||
}
|
||||
|
||||
/// Analogous to `Into<PyClassInitializer<T>>`, but just an internal equivalent
|
||||
/// to avoid allowing user code to define custom return types from `#[new]`.
|
||||
trait IntoPyClassInitializer<T: PyClass>: Sized {
|
||||
fn into_pyclass_initializer(self) -> PyClassInitializer<T>;
|
||||
}
|
||||
|
||||
impl<T: PyClass> IntoPyClassInitializer<T> for PyClassInitializer<T> {
|
||||
fn into_pyclass_initializer(self) -> PyClassInitializer<T> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPyClassInitializer<T> for T
|
||||
where
|
||||
T: PyClass,
|
||||
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
|
||||
{
|
||||
fn into_pyclass_initializer(self) -> PyClassInitializer<T> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user