use crate::conversion::IntoPyPointer;
use crate::once_cell::GILOnceCell;
use crate::pyclass::{initialize_type_object, py_class_attributes, PyClass};
use crate::pyclass_init::PyObjectInit;
use crate::types::{PyAny, PyType};
use crate::{ffi, AsPyPointer, PyErr, PyNativeType, PyObject, PyResult, Python};
use parking_lot::{const_mutex, Mutex};
use std::thread::{self, ThreadId};
pub unsafe trait PyLayout<T: PyTypeInfo> {
const IS_NATIVE_TYPE: bool = true;
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
None
}
unsafe fn py_init(&mut self, _value: T) {}
unsafe fn py_drop(&mut self, _py: Python) {}
}
pub trait PySizedLayout<T: PyTypeInfo>: PyLayout<T> + Sized {}
pub unsafe trait PyBorrowFlagLayout<T: PyTypeInfo>: PyLayout<T> + Sized {}
#[doc(hidden)]
pub mod type_flags {
pub const GC: usize = 1;
pub const WEAKREF: usize = 1 << 1;
pub const BASETYPE: usize = 1 << 2;
pub const DICT: usize = 1 << 3;
pub const EXTENDED: usize = 1 << 4;
}
pub unsafe trait PyTypeInfo: Sized {
type Type;
const NAME: &'static str;
const MODULE: Option<&'static str>;
const DESCRIPTION: &'static str = "\0";
const FLAGS: usize = 0;
type BaseType: PyTypeInfo + PyTypeObject;
type Layout: PyLayout<Self>;
type BaseLayout: PySizedLayout<Self::BaseType>;
type Initializer: PyObjectInit<Self>;
type AsRefTarget: crate::PyNativeType;
fn type_object_raw(py: Python) -> *mut ffi::PyTypeObject;
fn is_instance(object: &PyAny) -> bool {
unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 }
}
fn is_exact_instance(object: &PyAny) -> bool {
unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) }
}
}
pub unsafe trait PyTypeObject {
fn type_object(py: Python) -> &PyType;
}
unsafe impl<T> PyTypeObject for T
where
T: PyTypeInfo,
{
fn type_object(py: Python) -> &PyType {
unsafe { py.from_borrowed_ptr(Self::type_object_raw(py) as _) }
}
}
#[doc(hidden)]
pub struct LazyStaticType {
value: GILOnceCell<*mut ffi::PyTypeObject>,
initializing_threads: Mutex<Vec<ThreadId>>,
tp_dict_filled: GILOnceCell<PyResult<()>>,
}
impl LazyStaticType {
pub const fn new() -> Self {
LazyStaticType {
value: GILOnceCell::new(),
initializing_threads: const_mutex(Vec::new()),
tp_dict_filled: GILOnceCell::new(),
}
}
pub fn get_or_init<T: PyClass>(&self, py: Python) -> *mut ffi::PyTypeObject {
let type_object = *self.value.get_or_init(py, || {
let mut type_object = Box::new(ffi::PyTypeObject_INIT);
initialize_type_object::<T>(py, T::MODULE, type_object.as_mut()).unwrap_or_else(|e| {
e.print(py);
panic!("An error occurred while initializing class {}", T::NAME)
});
Box::into_raw(type_object)
});
if self.tp_dict_filled.get(py).is_some() {
return type_object;
}
{
let thread_id = thread::current().id();
let mut threads = self.initializing_threads.lock();
if threads.contains(&thread_id) {
return type_object;
}
threads.push(thread_id);
}
let mut items = vec![];
for attr in py_class_attributes::<T>() {
items.push((attr.name, (attr.meth)(py)));
}
let result = self.tp_dict_filled.get_or_init(py, move || {
let tp_dict = unsafe { (*type_object).tp_dict };
let result = initialize_tp_dict(py, tp_dict, items);
unsafe { ffi::PyType_Modified(type_object) };
*self.initializing_threads.lock() = Vec::new();
result
});
if let Err(err) = result {
err.clone_ref(py).print(py);
panic!("An error occured while initializing `{}.__dict__`", T::NAME);
}
type_object
}
}
fn initialize_tp_dict(
py: Python,
tp_dict: *mut ffi::PyObject,
items: Vec<(&'static std::ffi::CStr, PyObject)>,
) -> PyResult<()> {
for (key, val) in items {
let ret = unsafe { ffi::PyDict_SetItemString(tp_dict, key.as_ptr(), val.into_ptr()) };
if ret < 0 {
return Err(PyErr::fetch(py));
}
}
Ok(())
}
unsafe impl Sync for LazyStaticType {}