use crate::{
PyPayload,
builtins::{PyBaseObject, PyType, PyTypeRef, descriptor::PyWrapper},
function::PyMethodDef,
object::Py,
types::{PyTypeFlags, PyTypeSlots, SLOT_DEFS, hash_not_implemented},
vm::Context,
};
use rustpython_common::static_cell;
pub fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
for def in SLOT_DEFS.iter() {
if def.name == "__new__" {
continue;
}
if def.name == "__hash__"
&& class
.slots
.hash
.load()
.is_some_and(|h| h as usize == hash_not_implemented as *const () as usize)
{
class.set_attr(ctx.names.__hash__, ctx.none.clone().into());
continue;
}
if def.name == "__getattr__" {
continue;
}
let Some(slot_func) = def.accessor.get_slot_func_with_op(&class.slots, def.op) else {
continue;
};
let attr_name = ctx.intern_str(def.name);
if class.attributes.read().contains_key(attr_name) {
continue;
}
let wrapper = PyWrapper {
typ: class,
name: attr_name,
wrapped: slot_func,
doc: Some(def.doc),
};
class.set_attr(attr_name, wrapper.into_ref(ctx).into());
}
}
pub trait StaticType {
fn static_cell() -> &'static static_cell::StaticCell<PyTypeRef>;
#[inline]
fn static_metaclass() -> &'static Py<PyType> {
PyType::static_type()
}
#[inline]
fn static_baseclass() -> &'static Py<PyType> {
PyBaseObject::static_type()
}
#[inline]
fn static_type() -> &'static Py<PyType> {
#[cold]
fn fail() -> ! {
panic!(
"static type has not been initialized. e.g. the native types defined in different module may be used before importing library."
);
}
Self::static_cell().get().unwrap_or_else(|| fail())
}
fn init_manually(typ: PyTypeRef) -> &'static Py<PyType> {
let cell = Self::static_cell();
cell.set(typ)
.unwrap_or_else(|_| panic!("double initialization from init_manually"));
cell.get().unwrap()
}
fn init_builtin_type() -> &'static Py<PyType>
where
Self: PyClassImpl,
{
let typ = Self::create_static_type();
let cell = Self::static_cell();
cell.set(typ)
.unwrap_or_else(|_| panic!("double initialization of {}", Self::NAME));
cell.get().unwrap()
}
fn create_static_type() -> PyTypeRef
where
Self: PyClassImpl,
{
PyType::new_static(
Self::static_baseclass().to_owned(),
Default::default(),
Self::make_slots(),
Self::static_metaclass().to_owned(),
)
.unwrap()
}
}
pub trait PyClassDef {
const NAME: &'static str;
const MODULE_NAME: Option<&'static str>;
const TP_NAME: &'static str;
const DOC: Option<&'static str> = None;
const BASICSIZE: usize;
const ITEMSIZE: usize = 0;
const UNHASHABLE: bool = false;
type Base: PyClassDef;
}
pub trait PyClassImpl: PyClassDef {
const TP_FLAGS: PyTypeFlags = PyTypeFlags::DEFAULT;
fn extend_class(ctx: &'static Context, class: &'static Py<PyType>)
where
Self: Sized,
{
#[cfg(debug_assertions)]
{
assert!(class.slots.flags.is_created_with_flags());
}
let _ = ctx.intern_str(Self::NAME);
if Self::TP_FLAGS.has_feature(PyTypeFlags::HAS_DICT) {
let __dict__ = identifier!(ctx, __dict__);
class.set_attr(
__dict__,
ctx.new_static_getset(
"__dict__",
class,
crate::builtins::object::object_get_dict,
crate::builtins::object::object_set_dict,
)
.into(),
);
}
Self::impl_extend_class(ctx, class);
if let Some(doc) = Self::DOC {
let doc_attr_name = identifier!(ctx, __doc__);
if class.attributes.read().get(doc_attr_name).is_none() {
class.set_attr(doc_attr_name, ctx.new_str(doc).into());
}
}
if let Some(module_name) = Self::MODULE_NAME {
let module_key = identifier!(ctx, __module__);
let has_getset = class
.attributes
.read()
.get(module_key)
.is_some_and(|v| v.downcastable::<crate::builtins::PyGetSet>());
if !has_getset {
class.set_attr(module_key, ctx.new_str(module_name).into());
}
}
if let Some(slot_new) = class.slots.new.load() {
let object_new = ctx.types.object_type.slots.new.load();
let is_object_itself = core::ptr::eq(class, ctx.types.object_type);
let is_inherited_from_object = !is_object_itself
&& object_new.is_some_and(|obj_new| slot_new as usize == obj_new as usize);
if !is_inherited_from_object {
let bound_new =
ctx.slot_new_wrapper
.build_bound_method(ctx, class.to_owned().into(), class);
class.set_attr(identifier!(ctx, __new__), bound_new.into());
}
}
add_operators(class, ctx);
for base in class.bases.read().iter() {
class.inherit_slots(base);
}
class.extend_methods(class.slots.methods, ctx);
}
fn make_static_type() -> PyTypeRef
where
Self: StaticType + Sized,
{
(*Self::static_cell().get_or_init(|| {
let typ = Self::create_static_type();
Self::extend_class(Context::genesis(), unsafe {
let r: &Py<PyType> = &typ;
let r: &'static Py<PyType> = core::mem::transmute(r);
r
});
typ
}))
.to_owned()
}
fn impl_extend_class(ctx: &'static Context, class: &'static Py<PyType>);
const METHOD_DEFS: &'static [PyMethodDef];
fn extend_slots(slots: &mut PyTypeSlots);
fn make_slots() -> PyTypeSlots {
let mut slots = PyTypeSlots {
flags: Self::TP_FLAGS,
name: Self::TP_NAME,
basicsize: Self::BASICSIZE,
itemsize: Self::ITEMSIZE,
doc: Self::DOC,
methods: Self::METHOD_DEFS,
..Default::default()
};
if Self::UNHASHABLE {
slots.hash.store(Some(hash_not_implemented));
}
Self::extend_slots(&mut slots);
slots
}
}
pub trait PySubclass: crate::PyPayload {
type Base: crate::PyPayload;
fn as_base(&self) -> &Self::Base;
}