Introduce PyClass trait and PyClassShell

This commit is contained in:
kngwyu 2019-12-07 17:56:49 +09:00
parent 352707c29f
commit 4b5fa7e977
16 changed files with 482 additions and 869 deletions

View file

@ -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>,
{

View file

@ -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>)

View file

@ -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
View 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::*;

View file

@ -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),
}
}
}

View file

@ -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,

View file

@ -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;

View file

@ -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) }
}
}

View file

@ -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
View 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()
}

View file

@ -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.

View file

@ -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()
}

View file

@ -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) }
}
}

View file

@ -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;

View file

@ -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())
}

View 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");
}