Introduce PyClass trait and PyClassShell
This commit is contained in:
parent
352707c29f
commit
4b5fa7e977
|
@ -4,11 +4,8 @@
|
|||
|
||||
use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
|
||||
use crate::err::PyResult;
|
||||
use crate::instance::PyRefMut;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
use crate::{ffi, IntoPy, PyClass, PyClassShell, PyObject};
|
||||
use crate::{IntoPyPointer, Python};
|
||||
use std::ptr;
|
||||
|
||||
/// Python Iterator Interface.
|
||||
|
@ -16,15 +13,15 @@ use std::ptr;
|
|||
/// more information
|
||||
/// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter`
|
||||
#[allow(unused_variables)]
|
||||
pub trait PyIterProtocol<'p>: PyTypeInfo + Sized {
|
||||
fn __iter__(slf: PyRefMut<'p, Self>) -> Self::Result
|
||||
pub trait PyIterProtocol<'p>: PyClass {
|
||||
fn __iter__(slf: &mut PyClassShell<Self>) -> Self::Result
|
||||
where
|
||||
Self: PyIterIterProtocol<'p>,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn __next__(slf: PyRefMut<'p, Self>) -> Self::Result
|
||||
fn __next__(slf: &mut PyClassShell<Self>) -> Self::Result
|
||||
where
|
||||
Self: PyIterNextProtocol<'p>,
|
||||
{
|
||||
|
|
|
@ -35,11 +35,11 @@ macro_rules! py_unary_pyref_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::instance::PyRefMut;
|
||||
use $crate::{FromPyPointer, PyClassShell};
|
||||
let py = $crate::Python::assume_gil_acquired();
|
||||
let _pool = $crate::GILPool::new(py);
|
||||
let slf = py.mut_from_borrowed_ptr::<T>(slf);
|
||||
let res = $class::$f(PyRefMut::from_mut(slf)).into();
|
||||
let slf: &mut PyClassShell<T> = FromPyPointer::from_borrowed_ptr_or_panic(py, slf);
|
||||
let res = $class::$f(slf).into();
|
||||
$crate::callback::cb_convert($conv, py, res)
|
||||
}
|
||||
Some(wrap::<$class>)
|
||||
|
|
|
@ -393,22 +393,14 @@ where
|
|||
#[inline]
|
||||
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v T {
|
||||
let value = value.into();
|
||||
let ptr = if T::OFFSET == 0 {
|
||||
value as *const _ as *const u8 as *const T
|
||||
} else {
|
||||
(value.as_ptr() as *const u8).offset(T::OFFSET) as *const T
|
||||
};
|
||||
let ptr = value as *const _ as *const u8 as *const T;
|
||||
&*ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut T {
|
||||
let value = value.into();
|
||||
let ptr = if T::OFFSET == 0 {
|
||||
value as *const _ as *mut u8 as *mut T
|
||||
} else {
|
||||
(value.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T
|
||||
};
|
||||
let ptr = value as *const _ as *mut u8 as *mut T;
|
||||
&mut *ptr
|
||||
}
|
||||
}
|
||||
|
|
2
src/ffi/mod.rs
Normal file → Executable file
2
src/ffi/mod.rs
Normal file → Executable file
|
@ -1,6 +1,6 @@
|
|||
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||
#![cfg_attr(Py_LIMITED_API, allow(unused_imports))]
|
||||
#![cfg_attr(feature="cargo-clippy", allow(clippy::inline_always))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
|
||||
|
||||
pub use self::bltinmodule::*;
|
||||
pub use self::boolobject::*;
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
//! Free allocation list
|
||||
|
||||
use crate::ffi;
|
||||
use crate::type_object::{pytype_drop, PyObjectAlloc, PyTypeInfo};
|
||||
use crate::pyclass::{tp_free_fallback, PyClassAlloc};
|
||||
use crate::type_object::{PyConcreteObject, PyTypeInfo};
|
||||
use crate::Python;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
@ -11,7 +12,7 @@ use std::os::raw::c_void;
|
|||
/// Implementing this trait for custom class adds free allocation list to class.
|
||||
/// The performance improvement applies to types that are often created and deleted in a row,
|
||||
/// so that they can benefit from a freelist.
|
||||
pub trait PyObjectWithFreeList: PyTypeInfo {
|
||||
pub trait PyObjectWithFreeList {
|
||||
fn get_free_list() -> &'static mut FreeList<*mut ffi::PyObject>;
|
||||
}
|
||||
|
||||
|
@ -67,22 +68,23 @@ impl<T> FreeList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PyObjectAlloc for T
|
||||
impl<T> PyClassAlloc for T
|
||||
where
|
||||
T: PyObjectWithFreeList,
|
||||
T: PyTypeInfo + PyObjectWithFreeList,
|
||||
{
|
||||
unsafe fn alloc(_py: Python) -> *mut ffi::PyObject {
|
||||
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
|
||||
if let Some(obj) = <Self as PyObjectWithFreeList>::get_free_list().pop() {
|
||||
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object());
|
||||
obj
|
||||
obj as _
|
||||
} else {
|
||||
ffi::PyType_GenericAlloc(<Self as PyTypeInfo>::type_object(), 0)
|
||||
ffi::PyType_GenericAlloc(<Self as PyTypeInfo>::type_object(), 0) as _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
|
||||
pytype_drop::<Self>(py, obj);
|
||||
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
|
||||
(*self_).py_drop(py);
|
||||
|
||||
let obj = self_ as _;
|
||||
if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 {
|
||||
return;
|
||||
}
|
||||
|
@ -90,20 +92,7 @@ where
|
|||
if let Some(obj) = <Self as PyObjectWithFreeList>::get_free_list().insert(obj) {
|
||||
match Self::type_object().tp_free {
|
||||
Some(free) => free(obj as *mut c_void),
|
||||
None => {
|
||||
let ty = ffi::Py_TYPE(obj);
|
||||
if ffi::PyType_IS_GC(ty) != 0 {
|
||||
ffi::PyObject_GC_Del(obj as *mut c_void);
|
||||
} else {
|
||||
ffi::PyObject_Free(obj as *mut c_void);
|
||||
}
|
||||
|
||||
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
|
||||
// so we need to call DECREF here:
|
||||
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
|
||||
ffi::Py_DECREF(ty as *mut ffi::PyObject);
|
||||
}
|
||||
}
|
||||
None => tp_free_fallback(obj),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
314
src/instance.rs
314
src/instance.rs
|
@ -2,17 +2,15 @@
|
|||
use crate::err::{PyErr, PyResult};
|
||||
use crate::gil;
|
||||
use crate::instance;
|
||||
use crate::internal_tricks::Unsendable;
|
||||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::type_object::PyTypeCreate;
|
||||
use crate::type_object::{PyTypeInfo, PyTypeObject};
|
||||
use crate::pyclass::{PyClass, PyClassShell};
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::PyAny;
|
||||
use crate::{ffi, IntoPy};
|
||||
use crate::{AsPyPointer, FromPyObject, FromPyPointer, IntoPyPointer, Python, ToPyObject};
|
||||
use crate::{AsPyPointer, FromPyObject, IntoPyPointer, Python, ToPyObject};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// Types that are built into the python interpreter.
|
||||
|
@ -25,243 +23,6 @@ pub unsafe trait PyNativeType: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
/// A special reference of type `T`. `PyRef<T>` refers a instance of T, which exists in the Python
|
||||
/// heap as a part of a Python object.
|
||||
///
|
||||
/// We can't implement `AsPyPointer` or `ToPyObject` for `pyclass`es, because they're not Python
|
||||
/// objects until copied to the Python heap. So, instead of treating `&pyclass`es as Python objects,
|
||||
/// we need to use special reference types `PyRef` and `PyRefMut`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::IntoPyDict;
|
||||
/// #[pyclass]
|
||||
/// struct Point {
|
||||
/// x: i32,
|
||||
/// y: i32,
|
||||
/// }
|
||||
/// #[pymethods]
|
||||
/// impl Point {
|
||||
/// fn length(&self) -> i32 {
|
||||
/// self.x * self.y
|
||||
/// }
|
||||
/// }
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let obj = PyRef::new(gil.python(), Point { x: 3, y: 4 }).unwrap();
|
||||
/// let d = [("p", obj)].into_py_dict(py);
|
||||
/// py.run("assert p.length() == 12", None, Some(d)).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct PyRef<'a, T: PyTypeInfo>(&'a T, Unsendable);
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
fn ref_to_ptr<T>(t: &T) -> *mut ffi::PyObject
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
unsafe { (t as *const _ as *mut u8).offset(-T::OFFSET) as *mut _ }
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> PyRef<'a, T> {
|
||||
pub(crate) fn from_ref(r: &'a T) -> Self {
|
||||
PyRef(r, Unsendable::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, T> PyRef<'p, T>
|
||||
where
|
||||
T: PyTypeInfo + PyTypeObject + PyTypeCreate,
|
||||
{
|
||||
pub fn new(py: Python<'p>, value: T) -> PyResult<PyRef<T>> {
|
||||
let obj = T::create(py)?;
|
||||
obj.init(value);
|
||||
unsafe { Self::from_owned_ptr_or_err(py, obj.into_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> AsPyPointer for PyRef<'a, T> {
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
ref_to_ptr(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> ToPyObject for PyRef<'a, T> {
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> IntoPy<PyObject> for PyRef<'a, T> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> Deref for PyRef<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'p, T> FromPyPointer<'p> for PyRef<'p, T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
FromPyPointer::from_owned_ptr_or_opt(py, ptr).map(Self::from_ref)
|
||||
}
|
||||
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
FromPyPointer::from_borrowed_ptr_or_opt(py, ptr).map(Self::from_ref)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutable version of [`PyRef`](struct.PyRef.html).
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::IntoPyDict;
|
||||
/// #[pyclass]
|
||||
/// struct Point {
|
||||
/// x: i32,
|
||||
/// y: i32,
|
||||
/// }
|
||||
/// #[pymethods]
|
||||
/// impl Point {
|
||||
/// fn length(&self) -> i32 {
|
||||
/// self.x * self.y
|
||||
/// }
|
||||
/// }
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let mut obj = PyRefMut::new(gil.python(), Point { x: 3, y: 4 }).unwrap();
|
||||
/// let d = vec![("p", obj.to_object(py))].into_py_dict(py);
|
||||
/// obj.x = 5; obj.y = 20;
|
||||
/// py.run("assert p.length() == 100", None, Some(d)).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct PyRefMut<'a, T: PyTypeInfo>(&'a mut T, Unsendable);
|
||||
|
||||
impl<'a, T: PyTypeInfo> PyRefMut<'a, T> {
|
||||
pub(crate) fn from_mut(t: &'a mut T) -> Self {
|
||||
PyRefMut(t, Unsendable::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, T> PyRefMut<'p, T>
|
||||
where
|
||||
T: PyTypeInfo + PyTypeObject + PyTypeCreate,
|
||||
{
|
||||
pub fn new(py: Python<'p>, value: T) -> PyResult<PyRefMut<T>> {
|
||||
let obj = T::create(py)?;
|
||||
obj.init(value);
|
||||
unsafe { Self::from_owned_ptr_or_err(py, obj.into_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> AsPyPointer for PyRefMut<'a, T> {
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
ref_to_ptr(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> ToPyObject for PyRefMut<'a, T> {
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> IntoPy<PyObject> for PyRefMut<'a, T> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> Deref for PyRefMut<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> DerefMut for PyRefMut<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'p, T> FromPyPointer<'p> for PyRefMut<'p, T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
FromPyPointer::from_owned_ptr_or_opt(py, ptr).map(Self::from_mut)
|
||||
}
|
||||
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
FromPyPointer::from_borrowed_ptr_or_opt(py, ptr).map(Self::from_mut)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implements object reference extraction from python managed pointer.
|
||||
pub trait AsPyRef<T: PyTypeInfo>: Sized {
|
||||
/// Return reference to object.
|
||||
fn as_ref(&self, py: Python) -> PyRef<T>;
|
||||
|
||||
/// Return mutable reference to object.
|
||||
fn as_mut(&mut self, py: Python) -> PyRefMut<T>;
|
||||
|
||||
/// Acquire python gil and call closure with object reference.
|
||||
fn with<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Python, PyRef<T>) -> R,
|
||||
{
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
f(py, self.as_ref(py))
|
||||
}
|
||||
|
||||
/// Acquire python gil and call closure with mutable object reference.
|
||||
fn with_mut<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Python, PyRefMut<T>) -> R,
|
||||
{
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
f(py, self.as_mut(py))
|
||||
}
|
||||
|
||||
fn into_py<F, R>(self, f: F) -> R
|
||||
where
|
||||
Self: IntoPyPointer,
|
||||
F: FnOnce(Python, PyRef<T>) -> R,
|
||||
{
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let result = f(py, self.as_ref(py));
|
||||
py.xdecref(self);
|
||||
result
|
||||
}
|
||||
|
||||
fn into_mut_py<F, R>(mut self, f: F) -> R
|
||||
where
|
||||
Self: IntoPyPointer,
|
||||
F: FnOnce(Python, PyRefMut<T>) -> R,
|
||||
{
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let result = f(py, self.as_mut(py));
|
||||
py.xdecref(self);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper around unsafe `*mut ffi::PyObject` pointer with specified type information.
|
||||
///
|
||||
/// `Py<T>` is thread-safe, because any python related operations require a Python<'p> token.
|
||||
|
@ -277,12 +38,10 @@ impl<T> Py<T> {
|
|||
/// Create new instance of T and move it under python management
|
||||
pub fn new(py: Python, value: T) -> PyResult<Py<T>>
|
||||
where
|
||||
T: PyTypeCreate,
|
||||
T: PyClass,
|
||||
{
|
||||
let ob = T::create(py)?;
|
||||
ob.init(value);
|
||||
|
||||
let ob = unsafe { Py::from_owned_ptr(ob.into_ptr()) };
|
||||
let obj = unsafe { PyClassShell::<T>::new(py, value) };
|
||||
let ob = unsafe { Py::from_owned_ptr(obj as _) };
|
||||
Ok(ob)
|
||||
}
|
||||
|
||||
|
@ -357,48 +116,15 @@ impl<T> Py<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specialization workaround
|
||||
trait AsPyRefDispatch<T: PyTypeInfo>: AsPyPointer {
|
||||
fn as_ref_dispatch(&self, _py: Python) -> &T;
|
||||
fn as_mut_dispatch(&mut self, _py: Python) -> &mut T;
|
||||
pub trait AsPyRef<T: PyTypeInfo>: Sized {
|
||||
/// Return reference to object.
|
||||
fn as_ref(&self, py: Python) -> &T;
|
||||
}
|
||||
|
||||
impl<T: PyTypeInfo> AsPyRefDispatch<T> for Py<T> {
|
||||
default fn as_ref_dispatch(&self, _py: Python) -> &T {
|
||||
unsafe {
|
||||
let ptr = (self.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
ptr.as_ref().expect("Py has a null pointer")
|
||||
}
|
||||
}
|
||||
default fn as_mut_dispatch(&mut self, _py: Python) -> &mut T {
|
||||
unsafe {
|
||||
let ptr = (self.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
ptr.as_mut().expect("Py has a null pointer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyTypeInfo + PyNativeType> AsPyRefDispatch<T> for Py<T> {
|
||||
fn as_ref_dispatch(&self, _py: Python) -> &T {
|
||||
impl<T: PyTypeInfo> AsPyRef<T> for Py<T> {
|
||||
fn as_ref(&self, _py: Python) -> &T {
|
||||
unsafe { &*(self as *const instance::Py<T> as *const T) }
|
||||
}
|
||||
fn as_mut_dispatch(&mut self, _py: Python) -> &mut T {
|
||||
unsafe { &mut *(self as *mut _ as *mut T) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsPyRef<T> for Py<T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
#[inline]
|
||||
fn as_ref(&self, py: Python) -> PyRef<T> {
|
||||
PyRef::from_ref(self.as_ref_dispatch(py))
|
||||
}
|
||||
#[inline]
|
||||
fn as_mut(&mut self, py: Python) -> PyRefMut<T> {
|
||||
PyRefMut::from_mut(self.as_mut_dispatch(py))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToPyObject for Py<T> {
|
||||
|
@ -459,24 +185,6 @@ impl<T> std::convert::From<Py<T>> for PyObject {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::convert::From<PyRef<'a, T>> for Py<T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
fn from(ob: PyRef<'a, T>) -> Self {
|
||||
unsafe { Py::from_borrowed_ptr(ob.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
fn from(ob: PyRefMut<'a, T>) -> Self {
|
||||
unsafe { Py::from_borrowed_ptr(ob.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::convert::From<&'a T> for PyObject
|
||||
where
|
||||
T: AsPyPointer,
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -124,11 +124,12 @@ pub use crate::conversion::{
|
|||
};
|
||||
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
|
||||
pub use crate::gil::{init_once, GILGuard, GILPool};
|
||||
pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType, PyRef, PyRefMut};
|
||||
pub use crate::instance::{ManagedPyRef, Py, PyNativeType};
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::objectprotocol::ObjectProtocol;
|
||||
pub use crate::pyclass::{PyClass, PyClassAlloc, PyClassShell};
|
||||
pub use crate::python::{prepare_freethreaded_python, Python};
|
||||
pub use crate::type_object::{PyObjectAlloc, PyRawObject, PyTypeInfo};
|
||||
pub use crate::type_object::{PyConcreteObject, PyTypeInfo};
|
||||
|
||||
// Re-exported for wrap_function
|
||||
#[doc(hidden)]
|
||||
|
@ -146,9 +147,6 @@ pub use libc;
|
|||
#[doc(hidden)]
|
||||
pub use unindent;
|
||||
|
||||
/// Raw ffi declarations for the c interface of python
|
||||
pub mod ffi;
|
||||
|
||||
pub mod buffer;
|
||||
#[doc(hidden)]
|
||||
pub mod callback;
|
||||
|
@ -158,6 +156,8 @@ mod conversion;
|
|||
pub mod derive_utils;
|
||||
mod err;
|
||||
pub mod exceptions;
|
||||
/// Raw ffi declarations for the c interface of python
|
||||
pub mod ffi;
|
||||
pub mod freelist;
|
||||
mod gil;
|
||||
mod instance;
|
||||
|
@ -166,6 +166,7 @@ pub mod marshal;
|
|||
mod object;
|
||||
mod objectprotocol;
|
||||
pub mod prelude;
|
||||
pub mod pyclass;
|
||||
mod python;
|
||||
pub mod type_object;
|
||||
pub mod types;
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
use crate::err::{PyDowncastError, PyErr, PyResult};
|
||||
use crate::ffi;
|
||||
use crate::gil;
|
||||
use crate::instance::{AsPyRef, PyNativeType, PyRef, PyRefMut};
|
||||
use crate::instance::{AsPyRef, PyNativeType};
|
||||
use crate::types::{PyAny, PyDict, PyTuple};
|
||||
use crate::AsPyPointer;
|
||||
use crate::Py;
|
||||
use crate::Python;
|
||||
use crate::{AsPyPointer, Py, Python};
|
||||
use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject};
|
||||
use std::convert::AsRef;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// A python object
|
||||
|
@ -253,13 +252,8 @@ impl PyObject {
|
|||
}
|
||||
|
||||
impl AsPyRef<PyAny> for PyObject {
|
||||
#[inline]
|
||||
fn as_ref(&self, _py: Python) -> PyRef<PyAny> {
|
||||
unsafe { PyRef::from_ref(&*(self as *const _ as *const PyAny)) }
|
||||
}
|
||||
#[inline]
|
||||
fn as_mut(&mut self, _py: Python) -> PyRefMut<PyAny> {
|
||||
unsafe { PyRefMut::from_mut(&mut *(self as *mut _ as *mut PyAny)) }
|
||||
fn as_ref(&self, _py: Python) -> &PyAny {
|
||||
unsafe { &*(self as *const _ as *const PyAny) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,12 @@
|
|||
|
||||
pub use crate::err::{PyErr, PyResult};
|
||||
pub use crate::gil::GILGuard;
|
||||
pub use crate::instance::{AsPyRef, Py, PyRef, PyRefMut};
|
||||
pub use crate::instance::Py;
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::objectprotocol::ObjectProtocol;
|
||||
pub use crate::python::Python;
|
||||
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
|
||||
// This is only part of the prelude because we need it for the pymodule function
|
||||
pub use crate::types::PyModule;
|
||||
// This is required for the constructor
|
||||
pub use crate::PyRawObject;
|
||||
pub use pyo3cls::pymodule;
|
||||
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
|
||||
|
|
366
src/pyclass.rs
Normal file
366
src/pyclass.rs
Normal file
|
@ -0,0 +1,366 @@
|
|||
//! An experiment module which has all codes related only to #[pyclass]
|
||||
use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
|
||||
use crate::conversion::FromPyPointer;
|
||||
use crate::type_object::{PyConcreteObject, PyTypeObject};
|
||||
use crate::{class, ffi, gil, PyErr, PyResult, PyTypeInfo, Python};
|
||||
use std::ffi::CString;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
/// A trait that enables custome alloc/dealloc implementations for pyclasses.
|
||||
pub trait PyClassAlloc: PyTypeInfo + Sized {
|
||||
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
|
||||
let tp_ptr = Self::type_object();
|
||||
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
|
||||
alloc(tp_ptr, 0) as _
|
||||
}
|
||||
|
||||
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
|
||||
(*self_).py_drop(py);
|
||||
let obj = self_ as _;
|
||||
if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
match Self::type_object().tp_free {
|
||||
Some(free) => free(obj as *mut c_void),
|
||||
None => tp_free_fallback(obj),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) {
|
||||
let ty = ffi::Py_TYPE(obj);
|
||||
if ffi::PyType_IS_GC(ty) != 0 {
|
||||
ffi::PyObject_GC_Del(obj as *mut c_void);
|
||||
} else {
|
||||
ffi::PyObject_Free(obj as *mut c_void);
|
||||
}
|
||||
|
||||
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
|
||||
// so we need to call DECREF here:
|
||||
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
|
||||
ffi::Py_DECREF(ty as *mut ffi::PyObject);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PyClass: PyTypeInfo + Sized + PyClassAlloc + PyMethodsProtocol {}
|
||||
|
||||
unsafe impl<T> PyTypeObject for T
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
fn init_type() -> NonNull<ffi::PyTypeObject> {
|
||||
let type_object = unsafe { <Self as PyTypeInfo>::type_object() };
|
||||
|
||||
if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
|
||||
// automatically initialize the class on-demand
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
initialize_type::<Self>(py, <Self as PyTypeInfo>::MODULE).unwrap_or_else(|e| {
|
||||
e.print(py);
|
||||
panic!("An error occurred while initializing class {}", Self::NAME)
|
||||
});
|
||||
}
|
||||
|
||||
unsafe { NonNull::new_unchecked(type_object) }
|
||||
}
|
||||
}
|
||||
|
||||
/// So this is a shell for our *sweet* pyclasses to survive in *harsh* Python world.
|
||||
#[repr(C)]
|
||||
pub struct PyClassShell<T: PyClass> {
|
||||
ob_base: <T::BaseType as PyTypeInfo>::ConcreteLayout,
|
||||
pyclass: ManuallyDrop<T>,
|
||||
}
|
||||
|
||||
impl<T: PyClass> PyClassShell<T> {
|
||||
pub unsafe fn new(py: Python, value: T) -> *mut Self {
|
||||
T::init_type();
|
||||
let base = T::alloc(py);
|
||||
let self_ = base as *mut Self;
|
||||
(*self_).pyclass = ManuallyDrop::new(value);
|
||||
self_
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyClass> PyConcreteObject for PyClassShell<T> {
|
||||
unsafe fn py_drop(&mut self, py: Python) {
|
||||
ManuallyDrop::drop(&mut self.pyclass);
|
||||
self.ob_base.py_drop(py);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyClass> std::ops::Deref for PyClassShell<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
self.pyclass.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyClass> std::ops::DerefMut for PyClassShell<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
self.pyclass.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'p, T> FromPyPointer<'p> for &'p PyClassShell<T>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| &**(gil::register_owned(py, p) as *const _ as *const Self))
|
||||
}
|
||||
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| &**(gil::register_borrowed(py, p) as *const _ as *const Self))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'p, T> FromPyPointer<'p> for &'p mut PyClassShell<T>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| &mut **(gil::register_owned(py, p) as *const _ as *mut Self))
|
||||
}
|
||||
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| &mut **(gil::register_borrowed(py, p) as *const _ as *mut Self))
|
||||
}
|
||||
}
|
||||
|
||||
/// type object supports python GC
|
||||
const PY_TYPE_FLAG_GC: usize = 1;
|
||||
|
||||
/// Type object supports python weak references
|
||||
const PY_TYPE_FLAG_WEAKREF: usize = 1 << 1;
|
||||
|
||||
/// Type object can be used as the base type of another type
|
||||
const PY_TYPE_FLAG_BASETYPE: usize = 1 << 2;
|
||||
|
||||
/// The instances of this type have a dictionary containing instance variables
|
||||
const PY_TYPE_FLAG_DICT: usize = 1 << 3;
|
||||
|
||||
/// Register new type in python object system.
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
let type_object: &mut ffi::PyTypeObject = unsafe { T::type_object() };
|
||||
let base_type_object: &mut ffi::PyTypeObject =
|
||||
unsafe { <T::BaseType as PyTypeInfo>::type_object() };
|
||||
|
||||
// PyPy will segfault if passed only a nul terminator as `tp_doc`.
|
||||
// ptr::null() is OK though.
|
||||
if T::DESCRIPTION == "\0" {
|
||||
type_object.tp_doc = ptr::null();
|
||||
} else {
|
||||
type_object.tp_doc = T::DESCRIPTION.as_ptr() as *const _;
|
||||
};
|
||||
|
||||
type_object.tp_base = base_type_object;
|
||||
|
||||
let name = match module_name {
|
||||
Some(module_name) => format!("{}.{}", module_name, T::NAME),
|
||||
None => T::NAME.to_string(),
|
||||
};
|
||||
let name = CString::new(name).expect("Module name/type name must not contain NUL byte");
|
||||
type_object.tp_name = name.into_raw();
|
||||
|
||||
// dealloc
|
||||
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where
|
||||
T: PyClassAlloc,
|
||||
{
|
||||
let py = Python::assume_gil_acquired();
|
||||
let _pool = gil::GILPool::new_no_pointers(py);
|
||||
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::ConcreteLayout) as _)
|
||||
}
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = std::mem::size_of::<T::ConcreteLayout>() as isize;
|
||||
|
||||
// weakref support (check py3cls::py_class::impl_class)
|
||||
if T::FLAGS & PY_TYPE_FLAG_WEAKREF != 0 {
|
||||
// STUB
|
||||
type_object.tp_weaklistoffset = 0isize;
|
||||
}
|
||||
|
||||
// __dict__ support
|
||||
let has_dict = T::FLAGS & PY_TYPE_FLAG_DICT != 0;
|
||||
if has_dict {
|
||||
// STUB
|
||||
type_object.tp_dictoffset = 0isize;
|
||||
}
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// descriptor protocol
|
||||
<T as class::descr::PyDescrProtocolImpl>::tp_as_descr(type_object);
|
||||
|
||||
// iterator methods
|
||||
<T as class::iter::PyIterProtocolImpl>::tp_as_iter(type_object);
|
||||
|
||||
// basic methods
|
||||
<T as class::basic::PyObjectProtocolImpl>::tp_as_object(type_object);
|
||||
|
||||
fn to_ptr<T>(value: Option<T>) -> *mut T {
|
||||
value
|
||||
.map(|v| Box::into_raw(Box::new(v)))
|
||||
.unwrap_or_else(ptr::null_mut)
|
||||
}
|
||||
|
||||
// number methods
|
||||
type_object.tp_as_number = to_ptr(<T as class::number::PyNumberProtocolImpl>::tp_as_number());
|
||||
// mapping methods
|
||||
type_object.tp_as_mapping =
|
||||
to_ptr(<T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping());
|
||||
// sequence methods
|
||||
type_object.tp_as_sequence =
|
||||
to_ptr(<T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence());
|
||||
// async methods
|
||||
type_object.tp_as_async = to_ptr(<T as class::pyasync::PyAsyncProtocolImpl>::tp_as_async());
|
||||
// buffer protocol
|
||||
type_object.tp_as_buffer = to_ptr(<T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer());
|
||||
|
||||
// normal methods
|
||||
let (new, call, mut methods) = py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = Box::into_raw(methods.into_boxed_slice()) as *mut _;
|
||||
}
|
||||
|
||||
// __new__ method
|
||||
type_object.tp_new = new;
|
||||
// __call__ method
|
||||
type_object.tp_call = call;
|
||||
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
|
||||
if has_dict {
|
||||
props.push(ffi::PyGetSetDef_DICT);
|
||||
}
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
type_object.tp_getset = Box::into_raw(props.into_boxed_slice()) as *mut _;
|
||||
}
|
||||
|
||||
// set type flags
|
||||
py_class_flags::<T>(type_object);
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(type_object as *mut ffi::PyTypeObject)
|
||||
} else {
|
||||
PyErr::fetch(py).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
|
||||
if type_object.tp_traverse != None
|
||||
|| type_object.tp_clear != None
|
||||
|| T::FLAGS & PY_TYPE_FLAG_GC != 0
|
||||
{
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
|
||||
} else {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT;
|
||||
}
|
||||
if T::FLAGS & PY_TYPE_FLAG_BASETYPE != 0 {
|
||||
type_object.tp_flags |= ffi::Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_method_defs<T: PyMethodsProtocol>() -> (
|
||||
Option<ffi::newfunc>,
|
||||
Option<ffi::PyCFunctionWithKeywords>,
|
||||
Vec<ffi::PyMethodDef>,
|
||||
) {
|
||||
let mut defs = Vec::new();
|
||||
let mut call = None;
|
||||
let mut new = None;
|
||||
|
||||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::New(ref def) => {
|
||||
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
|
||||
new = Some(meth)
|
||||
}
|
||||
}
|
||||
PyMethodDefType::Call(ref def) => {
|
||||
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
|
||||
call = Some(meth)
|
||||
} else {
|
||||
panic!("Method type is not supoorted by tp_call slot")
|
||||
}
|
||||
}
|
||||
PyMethodDefType::Method(ref def)
|
||||
| PyMethodDefType::Class(ref def)
|
||||
| PyMethodDefType::Static(ref def) => {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
for def in <T as class::basic::PyObjectProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::context::PyContextProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::mapping::PyMappingProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::number::PyNumberProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::descr::PyDescrProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
|
||||
py_class_async_methods::<T>(&mut defs);
|
||||
|
||||
(new, call, defs)
|
||||
}
|
||||
|
||||
fn py_class_async_methods<T>(defs: &mut Vec<ffi::PyMethodDef>) {
|
||||
for def in <T as class::pyasync::PyAsyncProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_properties<T: PyMethodsProtocol>() -> Vec<ffi::PyGetSetDef> {
|
||||
let mut defs = std::collections::HashMap::new();
|
||||
|
||||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::Getter(ref getter) => {
|
||||
let name = getter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
getter.copy_to(def);
|
||||
}
|
||||
PyMethodDefType::Setter(ref setter) => {
|
||||
let name = setter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
setter.copy_to(def);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
defs.values().cloned().collect()
|
||||
}
|
|
@ -272,23 +272,14 @@ impl<'p> Python<'p> {
|
|||
}
|
||||
|
||||
impl<'p> Python<'p> {
|
||||
// TODO(kngwyu): Now offset dies, so what should this functions do
|
||||
pub(crate) unsafe fn unchecked_downcast<T: PyTypeInfo>(self, ob: &PyAny) -> &'p T {
|
||||
if T::OFFSET == 0 {
|
||||
&*(ob as *const _ as *const T)
|
||||
} else {
|
||||
let ptr = (ob.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
&*ptr
|
||||
}
|
||||
&*(ob as *const _ as *const T)
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ref_to_mut)] // FIXME
|
||||
pub(crate) unsafe fn unchecked_mut_downcast<T: PyTypeInfo>(self, ob: &PyAny) -> &'p mut T {
|
||||
if T::OFFSET == 0 {
|
||||
&mut *(ob as *const _ as *mut T)
|
||||
} else {
|
||||
let ptr = (ob.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
&mut *ptr
|
||||
}
|
||||
&mut *(ob as *const _ as *mut T)
|
||||
}
|
||||
|
||||
/// Register object in release pool, and try to downcast to specific type.
|
||||
|
|
|
@ -2,20 +2,25 @@
|
|||
|
||||
//! Python type object information
|
||||
|
||||
use crate::class::methods::PyMethodDefType;
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::instance::{Py, PyNativeType};
|
||||
use crate::ffi;
|
||||
use crate::instance::Py;
|
||||
use crate::types::PyAny;
|
||||
use crate::types::PyType;
|
||||
use crate::AsPyPointer;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{class, ffi, gil};
|
||||
use class::methods::PyMethodsProtocol;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub trait PyConcreteObject: Sized {
|
||||
unsafe fn py_drop(&mut self, _py: Python) {}
|
||||
}
|
||||
|
||||
impl<T: PyConcreteObject> AsPyPointer for T {
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
(self as *const _) as _
|
||||
}
|
||||
}
|
||||
|
||||
impl PyConcreteObject for ffi::PyObject {}
|
||||
|
||||
/// Python type information.
|
||||
pub trait PyTypeInfo {
|
||||
|
@ -31,9 +36,6 @@ pub trait PyTypeInfo {
|
|||
/// Class doc string
|
||||
const DESCRIPTION: &'static str = "\0";
|
||||
|
||||
/// Size of the rust PyObject structure (PyObject + rust structure)
|
||||
const SIZE: usize;
|
||||
|
||||
/// `Type` instance offset inside PyObject structure
|
||||
const OFFSET: isize;
|
||||
|
||||
|
@ -43,6 +45,9 @@ pub trait PyTypeInfo {
|
|||
/// Base class
|
||||
type BaseType: PyTypeInfo;
|
||||
|
||||
/// Layout
|
||||
type ConcreteLayout: PyConcreteObject;
|
||||
|
||||
/// PyTypeObject instance for this type, which might still need to
|
||||
/// be initialized
|
||||
unsafe fn type_object() -> &'static mut ffi::PyTypeObject;
|
||||
|
@ -58,180 +63,6 @@ pub trait PyTypeInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// type object supports python GC
|
||||
pub const PY_TYPE_FLAG_GC: usize = 1;
|
||||
|
||||
/// Type object supports python weak references
|
||||
pub const PY_TYPE_FLAG_WEAKREF: usize = 1 << 1;
|
||||
|
||||
/// Type object can be used as the base type of another type
|
||||
pub const PY_TYPE_FLAG_BASETYPE: usize = 1 << 2;
|
||||
|
||||
/// The instances of this type have a dictionary containing instance variables
|
||||
pub const PY_TYPE_FLAG_DICT: usize = 1 << 3;
|
||||
|
||||
/// Special object that is used for python object creation.
|
||||
/// `pyo3` library automatically creates this object for class `__new__` method.
|
||||
/// Behavior is undefined if constructor of custom class does not initialze
|
||||
/// instance of `PyRawObject` with rust value with `init` method.
|
||||
/// Calling of `__new__` method of base class is developer's responsibility.
|
||||
///
|
||||
/// Example of custom class implementation with `__new__` method:
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// #[pyclass]
|
||||
/// struct MyClass { }
|
||||
///
|
||||
/// #[pymethods]
|
||||
/// impl MyClass {
|
||||
/// #[new]
|
||||
/// fn new(obj: &PyRawObject) {
|
||||
/// obj.init(MyClass { })
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
pub struct PyRawObject {
|
||||
ptr: *mut ffi::PyObject,
|
||||
/// Type object of class which __new__ method get called
|
||||
tp_ptr: *mut ffi::PyTypeObject,
|
||||
/// Type object of top most class in inheritance chain,
|
||||
/// it might be python class.
|
||||
curr_ptr: *mut ffi::PyTypeObject,
|
||||
// initialized: usize,
|
||||
}
|
||||
|
||||
impl PyRawObject {
|
||||
#[must_use]
|
||||
pub unsafe fn new(
|
||||
py: Python,
|
||||
tp_ptr: *mut ffi::PyTypeObject,
|
||||
curr_ptr: *mut ffi::PyTypeObject,
|
||||
) -> PyResult<PyRawObject> {
|
||||
let alloc = (*curr_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
|
||||
let ptr = alloc(curr_ptr, 0);
|
||||
|
||||
if !ptr.is_null() {
|
||||
Ok(PyRawObject {
|
||||
ptr,
|
||||
tp_ptr,
|
||||
curr_ptr,
|
||||
// initialized: 0,
|
||||
})
|
||||
} else {
|
||||
PyErr::fetch(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub unsafe fn new_with_ptr(
|
||||
py: Python,
|
||||
ptr: *mut ffi::PyObject,
|
||||
tp_ptr: *mut ffi::PyTypeObject,
|
||||
curr_ptr: *mut ffi::PyTypeObject,
|
||||
) -> PyResult<PyRawObject> {
|
||||
if !ptr.is_null() {
|
||||
Ok(PyRawObject {
|
||||
ptr,
|
||||
tp_ptr,
|
||||
curr_ptr,
|
||||
// initialized: 0,
|
||||
})
|
||||
} else {
|
||||
PyErr::fetch(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init<T: PyTypeInfo>(&self, value: T) {
|
||||
unsafe {
|
||||
// The `as *mut u8` part is required because the offset is in bytes
|
||||
let ptr = (self.ptr as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
std::ptr::write(ptr, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Type object
|
||||
pub fn type_object(&self) -> &PyType {
|
||||
unsafe { PyType::from_type_ptr(self.py(), self.curr_ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyTypeInfo> AsRef<T> for PyRawObject {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &T {
|
||||
// TODO: check is object initialized
|
||||
unsafe {
|
||||
let ptr = (self.ptr as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
ptr.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyPointer for PyRawObject {
|
||||
fn into_ptr(self) -> *mut ffi::PyObject {
|
||||
// TODO: panic if not all types initialized
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl PyNativeType for PyRawObject {}
|
||||
|
||||
pub(crate) unsafe fn pytype_drop<T: PyTypeInfo>(py: Python, obj: *mut ffi::PyObject) {
|
||||
if T::OFFSET != 0 {
|
||||
let ptr = (obj as *mut u8).offset(T::OFFSET) as *mut T;
|
||||
std::ptr::drop_in_place(ptr);
|
||||
pytype_drop::<T::BaseType>(py, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// A Python object allocator that is usable as a base type for `#[pyclass]`
|
||||
///
|
||||
/// All native types and all `#[pyclass]` types use the default functions, while
|
||||
/// [PyObjectWithFreeList](crate::freelist::PyObjectWithFreeList) gets a special version.
|
||||
pub trait PyObjectAlloc: PyTypeInfo + Sized {
|
||||
unsafe fn alloc(_py: Python) -> *mut ffi::PyObject {
|
||||
let tp_ptr = Self::type_object();
|
||||
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
|
||||
alloc(tp_ptr, 0)
|
||||
}
|
||||
|
||||
/// Calls the rust destructor for the object and frees the memory
|
||||
/// (usually by calling ptr->ob_type->tp_free).
|
||||
/// This function is used as tp_dealloc implementation.
|
||||
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
|
||||
Self::drop(py, obj);
|
||||
|
||||
if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
match Self::type_object().tp_free {
|
||||
Some(free) => free(obj as *mut c_void),
|
||||
None => {
|
||||
let ty = ffi::Py_TYPE(obj);
|
||||
if ffi::PyType_IS_GC(ty) != 0 {
|
||||
ffi::PyObject_GC_Del(obj as *mut c_void);
|
||||
} else {
|
||||
ffi::PyObject_Free(obj as *mut c_void);
|
||||
}
|
||||
|
||||
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
|
||||
// so we need to call DECREF here:
|
||||
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
|
||||
ffi::Py_DECREF(ty as *mut ffi::PyObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unconditional_recursion)]
|
||||
/// Calls the rust destructor for the object.
|
||||
unsafe fn drop(py: Python, obj: *mut ffi::PyObject) {
|
||||
pytype_drop::<Self>(py, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// Python object types that have a corresponding type object.
|
||||
///
|
||||
/// This trait is marked unsafe because not fulfilling the contract for [PyTypeObject::init_type]
|
||||
|
@ -246,272 +77,3 @@ pub unsafe trait PyTypeObject {
|
|||
unsafe { Py::from_borrowed_ptr(Self::init_type().as_ptr() as *mut ffi::PyObject) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> PyTypeObject for T
|
||||
where
|
||||
T: PyTypeInfo + PyMethodsProtocol + PyObjectAlloc,
|
||||
{
|
||||
fn init_type() -> NonNull<ffi::PyTypeObject> {
|
||||
let type_object = unsafe { <Self as PyTypeInfo>::type_object() };
|
||||
|
||||
if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
|
||||
// automatically initialize the class on-demand
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
initialize_type::<Self>(py, <Self as PyTypeInfo>::MODULE).unwrap_or_else(|e| {
|
||||
e.print(py);
|
||||
panic!("An error occurred while initializing class {}", Self::NAME)
|
||||
});
|
||||
}
|
||||
|
||||
unsafe { NonNull::new_unchecked(type_object) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Python object types that can be instanciated with [Self::create()]
|
||||
///
|
||||
/// We can't just make this a part of [PyTypeObject] because exceptions have
|
||||
/// no PyTypeInfo
|
||||
pub trait PyTypeCreate: PyObjectAlloc + PyTypeObject + Sized {
|
||||
/// Create PyRawObject which can be initialized with rust value
|
||||
#[must_use]
|
||||
fn create(py: Python) -> PyResult<PyRawObject> {
|
||||
Self::init_type();
|
||||
|
||||
unsafe {
|
||||
let ptr = Self::alloc(py);
|
||||
PyRawObject::new_with_ptr(
|
||||
py,
|
||||
ptr,
|
||||
<Self as PyTypeInfo>::type_object(),
|
||||
<Self as PyTypeInfo>::type_object(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyTypeCreate for T where T: PyObjectAlloc + PyTypeObject + Sized {}
|
||||
|
||||
/// Register new type in python object system.
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
|
||||
where
|
||||
T: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol,
|
||||
{
|
||||
let type_object: &mut ffi::PyTypeObject = unsafe { T::type_object() };
|
||||
let base_type_object: &mut ffi::PyTypeObject =
|
||||
unsafe { <T::BaseType as PyTypeInfo>::type_object() };
|
||||
|
||||
// PyPy will segfault if passed only a nul terminator as `tp_doc`.
|
||||
// ptr::null() is OK though.
|
||||
if T::DESCRIPTION == "\0" {
|
||||
type_object.tp_doc = ptr::null();
|
||||
} else {
|
||||
type_object.tp_doc = T::DESCRIPTION.as_ptr() as *const _;
|
||||
};
|
||||
|
||||
type_object.tp_base = base_type_object;
|
||||
|
||||
let name = match module_name {
|
||||
Some(module_name) => format!("{}.{}", module_name, T::NAME),
|
||||
None => T::NAME.to_string(),
|
||||
};
|
||||
let name = CString::new(name).expect("Module name/type name must not contain NUL byte");
|
||||
type_object.tp_name = name.into_raw();
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as PyTypeInfo>::SIZE as ffi::Py_ssize_t;
|
||||
|
||||
let mut offset = T::SIZE;
|
||||
// weakref support (check py3cls::py_class::impl_class)
|
||||
if T::FLAGS & PY_TYPE_FLAG_WEAKREF != 0 {
|
||||
offset -= std::mem::size_of::<*const ffi::PyObject>();
|
||||
type_object.tp_weaklistoffset = offset as isize;
|
||||
}
|
||||
|
||||
// __dict__ support
|
||||
let has_dict = T::FLAGS & PY_TYPE_FLAG_DICT != 0;
|
||||
if has_dict {
|
||||
offset -= std::mem::size_of::<*const ffi::PyObject>();
|
||||
type_object.tp_dictoffset = offset as isize;
|
||||
}
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// descriptor protocol
|
||||
<T as class::descr::PyDescrProtocolImpl>::tp_as_descr(type_object);
|
||||
|
||||
// iterator methods
|
||||
<T as class::iter::PyIterProtocolImpl>::tp_as_iter(type_object);
|
||||
|
||||
// basic methods
|
||||
<T as class::basic::PyObjectProtocolImpl>::tp_as_object(type_object);
|
||||
|
||||
fn to_ptr<T>(value: Option<T>) -> *mut T {
|
||||
value
|
||||
.map(|v| Box::into_raw(Box::new(v)))
|
||||
.unwrap_or_else(ptr::null_mut)
|
||||
}
|
||||
|
||||
// number methods
|
||||
type_object.tp_as_number = to_ptr(<T as class::number::PyNumberProtocolImpl>::tp_as_number());
|
||||
// mapping methods
|
||||
type_object.tp_as_mapping =
|
||||
to_ptr(<T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping());
|
||||
// sequence methods
|
||||
type_object.tp_as_sequence =
|
||||
to_ptr(<T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence());
|
||||
// async methods
|
||||
type_object.tp_as_async = to_ptr(<T as class::pyasync::PyAsyncProtocolImpl>::tp_as_async());
|
||||
// buffer protocol
|
||||
type_object.tp_as_buffer = to_ptr(<T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer());
|
||||
|
||||
// normal methods
|
||||
let (new, call, mut methods) = py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = Box::into_raw(methods.into_boxed_slice()) as *mut _;
|
||||
}
|
||||
|
||||
// __new__ method
|
||||
type_object.tp_new = new;
|
||||
// __call__ method
|
||||
type_object.tp_call = call;
|
||||
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
|
||||
if has_dict {
|
||||
props.push(ffi::PyGetSetDef_DICT);
|
||||
}
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
type_object.tp_getset = Box::into_raw(props.into_boxed_slice()) as *mut _;
|
||||
}
|
||||
|
||||
// set type flags
|
||||
py_class_flags::<T>(type_object);
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(type_object as *mut ffi::PyTypeObject)
|
||||
} else {
|
||||
PyErr::fetch(py).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where
|
||||
T: PyObjectAlloc,
|
||||
{
|
||||
let py = Python::assume_gil_acquired();
|
||||
let _pool = gil::GILPool::new_no_pointers(py);
|
||||
<T as PyObjectAlloc>::dealloc(py, obj)
|
||||
}
|
||||
fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
|
||||
if type_object.tp_traverse != None
|
||||
|| type_object.tp_clear != None
|
||||
|| T::FLAGS & PY_TYPE_FLAG_GC != 0
|
||||
{
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
|
||||
} else {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT;
|
||||
}
|
||||
if T::FLAGS & PY_TYPE_FLAG_BASETYPE != 0 {
|
||||
type_object.tp_flags |= ffi::Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_method_defs<T: PyMethodsProtocol>() -> (
|
||||
Option<ffi::newfunc>,
|
||||
Option<ffi::PyCFunctionWithKeywords>,
|
||||
Vec<ffi::PyMethodDef>,
|
||||
) {
|
||||
let mut defs = Vec::new();
|
||||
let mut call = None;
|
||||
let mut new = None;
|
||||
|
||||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::New(ref def) => {
|
||||
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
|
||||
new = Some(meth)
|
||||
}
|
||||
}
|
||||
PyMethodDefType::Call(ref def) => {
|
||||
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
|
||||
call = Some(meth)
|
||||
} else {
|
||||
panic!("Method type is not supoorted by tp_call slot")
|
||||
}
|
||||
}
|
||||
PyMethodDefType::Method(ref def)
|
||||
| PyMethodDefType::Class(ref def)
|
||||
| PyMethodDefType::Static(ref def) => {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
for def in <T as class::basic::PyObjectProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::context::PyContextProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::mapping::PyMappingProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::number::PyNumberProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
for def in <T as class::descr::PyDescrProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
|
||||
py_class_async_methods::<T>(&mut defs);
|
||||
|
||||
(new, call, defs)
|
||||
}
|
||||
|
||||
fn py_class_async_methods<T>(defs: &mut Vec<ffi::PyMethodDef>) {
|
||||
for def in <T as class::pyasync::PyAsyncProtocolImpl>::methods() {
|
||||
defs.push(def.as_method_def());
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_properties<T: PyMethodsProtocol>() -> Vec<ffi::PyGetSetDef> {
|
||||
let mut defs = HashMap::new();
|
||||
|
||||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::Getter(ref getter) => {
|
||||
let name = getter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
getter.copy_to(def);
|
||||
}
|
||||
PyMethodDefType::Setter(ref setter) => {
|
||||
let name = setter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
setter.copy_to(def);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
defs.values().cloned().collect()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::conversion::AsPyPointer;
|
||||
use crate::conversion::PyTryFrom;
|
||||
use crate::err::PyDowncastError;
|
||||
use crate::internal_tricks::Unsendable;
|
||||
use crate::{ffi, PyObject, PyRef, PyRefMut, PyTryFrom, PyTypeInfo};
|
||||
use crate::{ffi, PyObject};
|
||||
|
||||
/// Represents a python's [Any](https://docs.python.org/3/library/typing.html#typing.Any) type.
|
||||
/// We can convert all python objects as `PyAny`.
|
||||
|
@ -41,21 +42,3 @@ impl PyAny {
|
|||
T::try_from_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<PyRef<'a, T>> for &'a PyAny
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
fn from(pref: PyRef<'a, T>) -> &'a PyAny {
|
||||
unsafe { &*(pref.as_ptr() as *const PyAny) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<PyRefMut<'a, T>> for &'a PyAny
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
{
|
||||
fn from(pref: PyRefMut<'a, T>) -> &'a PyAny {
|
||||
unsafe { &*(pref.as_ptr() as *const PyAny) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ macro_rules! pyobject_native_type_named (
|
|||
macro_rules! pyobject_native_type (
|
||||
($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
pyobject_native_type_named!($name $(,$type_param)*);
|
||||
pyobject_native_type_convert!($name, $typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
pyobject_native_type_convert!($name, ffi::PyObject, $typeobject, $module, $checkfunction $(,$type_param)*);
|
||||
|
||||
impl<'a, $($type_param,)*> ::std::convert::From<&'a $name> for &'a $crate::types::PyAny {
|
||||
fn from(ob: &'a $name) -> Self {
|
||||
|
@ -91,20 +91,23 @@ macro_rules! pyobject_native_type (
|
|||
}
|
||||
};
|
||||
($name: ty, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
pyobject_native_type!{$name, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*}
|
||||
pyobject_native_type! {
|
||||
$name, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_convert(
|
||||
($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
($name: ty, $layout: ty, $typeobject: expr,
|
||||
$module: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
impl<$($type_param,)*> $crate::type_object::PyTypeInfo for $name {
|
||||
type Type = ();
|
||||
type BaseType = $crate::types::PyAny;
|
||||
type ConcreteLayout = $layout;
|
||||
|
||||
const NAME: &'static str = stringify!($name);
|
||||
const MODULE: Option<&'static str> = $module;
|
||||
const SIZE: usize = ::std::mem::size_of::<$crate::ffi::PyObject>();
|
||||
const OFFSET: isize = 0;
|
||||
|
||||
#[inline]
|
||||
|
@ -120,12 +123,12 @@ macro_rules! pyobject_native_type_convert(
|
|||
}
|
||||
}
|
||||
|
||||
impl<$($type_param,)*> $crate::type_object::PyObjectAlloc for $name {}
|
||||
|
||||
unsafe impl<$($type_param,)*> $crate::type_object::PyTypeObject for $name {
|
||||
fn init_type() -> std::ptr::NonNull<$crate::ffi::PyTypeObject> {
|
||||
unsafe {
|
||||
std::ptr::NonNull::new_unchecked(<Self as $crate::type_object::PyTypeInfo>::type_object() as *mut _)
|
||||
std::ptr::NonNull::new_unchecked(
|
||||
<Self as $crate::type_object::PyTypeInfo>::type_object() as *mut _
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,8 +163,15 @@ macro_rules! pyobject_native_type_convert(
|
|||
}
|
||||
};
|
||||
($name: ty, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
pyobject_native_type_convert!{$name, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*}
|
||||
pyobject_native_type_convert! {
|
||||
$name, ffi::PyObject, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*
|
||||
}
|
||||
};
|
||||
// ($name: ty, $layout: ty, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
|
||||
// pyobject_native_type_convert! {
|
||||
// $name, $layout, $typeobject, Some("builtins"), $checkfunction $(,$type_param)*
|
||||
// }
|
||||
// };
|
||||
);
|
||||
|
||||
mod any;
|
||||
|
|
|
@ -9,15 +9,11 @@ use crate::instance::PyNativeType;
|
|||
use crate::internal_tricks::Unsendable;
|
||||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::type_object::PyTypeCreate;
|
||||
use crate::pyclass::PyClass;
|
||||
use crate::type_object::PyTypeObject;
|
||||
use crate::types::PyTuple;
|
||||
use crate::types::{PyAny, PyDict, PyList};
|
||||
use crate::AsPyPointer;
|
||||
use crate::IntoPy;
|
||||
use crate::Py;
|
||||
use crate::Python;
|
||||
use crate::ToPyObject;
|
||||
use crate::{AsPyPointer, IntoPy, Py, Python, ToPyObject};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
use std::str;
|
||||
|
@ -173,7 +169,7 @@ impl PyModule {
|
|||
/// and adds the type to this module.
|
||||
pub fn add_class<T>(&self) -> PyResult<()>
|
||||
where
|
||||
T: PyTypeCreate,
|
||||
T: PyClass,
|
||||
{
|
||||
self.add(T::NAME, <T as PyTypeObject>::type_object())
|
||||
}
|
||||
|
|
26
tests/test_pyclass_shell.rs
Normal file
26
tests/test_pyclass_shell.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::pyclass::PyClassShell;
|
||||
use pyo3::types::PyAny;
|
||||
use pyo3::FromPyPointer;
|
||||
|
||||
#[pyclass]
|
||||
struct Class {
|
||||
member: i32,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl Class {
|
||||
fn hello(&self) -> i32 {
|
||||
self.member
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shell() {
|
||||
let class = Class { member: 128 };
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
// let obj: &PyAny = unsafe { FromPyPointer::from_owned_ptr(py, PyClassShell::new(py, class)) };
|
||||
// py_run!(py, obj, "assert obj.hello() == 128");
|
||||
}
|
Loading…
Reference in a new issue