add py_tphookattributes

This commit is contained in:
blueloveTH 2025-07-02 01:01:52 +08:00
parent 2f296994ae
commit b6cefdeedc
4 changed files with 42 additions and 7 deletions

View File

@ -16,6 +16,10 @@ typedef struct py_TypeInfo {
bool is_python; // is it a python class? (not derived from c object)
bool is_sealed; // can it be subclassed?
bool (*getattribute)(py_Ref self, py_Name name);
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val);
bool (*delattribute)(py_Ref self, py_Name name);
py_TValue annotations;
py_Dtor dtor; // destructor for this type, NULL if no dtor
void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module

View File

@ -30,7 +30,10 @@ typedef void (*py_Dtor)(void*);
#ifdef PK_IS_PUBLIC_INCLUDE
typedef struct py_TValue {
int64_t _[2];
py_Type type;
bool is_ptr;
int extra;
int64_t _i64;
} py_TValue;
#endif
@ -355,6 +358,12 @@ PK_API const char* py_tpname(py_Type type);
/// Call a type to create a new instance.
PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
/// Set attribute hooks for the given type.
PK_API void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name),
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
bool (*delattribute)(py_Ref self, py_Name name));
/// Check if the object is an instance of the given type exactly.
/// Raise `TypeError` if the check fails.
PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
@ -760,11 +769,11 @@ enum py_PredefinedType {
tp_bool,
tp_str,
tp_str_iterator,
tp_list, // c11_vector
tp_tuple, // N slots
tp_list, // c11_vector
tp_tuple, // N slots
tp_list_iterator, // 1 slot
tp_tuple_iterator, // 1 slot
tp_slice, // 3 slots (start, stop, step)
tp_slice, // 3 slots (start, stop, step)
tp_range,
tp_range_iterator,
tp_module,

View File

@ -81,6 +81,10 @@ static void py_TypeInfo__common_init(py_Name name,
self->is_python = is_python;
self->is_sealed = is_sealed;
self->getattribute = NULL;
self->setattribute = NULL;
self->delattribute = NULL;
self->annotations = *py_NIL();
self->dtor = dtor;
self->on_end_subclass = NULL;
@ -150,4 +154,15 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
py_Type type = pk_newtype(name, base, module, dtor, false, false);
if(module) py_setdict(module, py_name(name), py_tpobject(type));
return type;
}
}
void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name),
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
bool (*delattribute)(py_Ref self, py_Name name)) {
assert(type);
py_TypeInfo* ti = pk_typeinfo(type);
ti->getattribute = getattribute;
ti->setattribute = setattribute;
ti->delattribute = delattribute;
}

View File

@ -122,6 +122,8 @@ int py_next(py_Ref val) {
bool py_getattr(py_Ref self, py_Name name) {
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->getattribute) return ti->getattribute(self, name);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) {
// handle descriptor
@ -212,8 +214,10 @@ bool py_getattr(py_Ref self, py_Name name) {
}
bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
py_Type type = self->type;
py_Ref cls_var = py_tpfindname(type, name);
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->setattribute) return ti->setattribute(self, name, val);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) {
// handle descriptor
if(py_istype(cls_var, tp_property)) {
@ -239,6 +243,9 @@ bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
}
bool py_delattr(py_Ref self, py_Name name) {
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->delattribute) return ti->delattribute(self, name);
if(self->is_ptr && self->_obj->slots == -1) {
if(py_deldict(self, name)) return true;
return AttributeError(self, name);