diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 923e9f91c9e..9219f7edcb0 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -18,6 +18,65 @@ # + +class RNA_IDProp_Meta(type): + # metaclass for all structures which can have rna prop's defined. + # important this class is defined first. + + # setattr, so we can do this... + # bpy.types.Scene.myprop = bpy.props.BoolProperty() + def __setattr__(cls, attr, value): + if type(value) == tuple and len(value) == 2: + print(cls, attr, value) + if attr in cls.bl_rna.properties: + _bpy.props.RemoveProperty(cls, attr=attr) + func, kw = value + kw["attr"] = attr + func(cls, **kw) + else: + # XXX, pure evil, need to find a better way + _setattr = RNA_IDProp_Meta.__setattr__ + del RNA_IDProp_Meta.__setattr__ + try: + setattr(cls, attr, value) + except Exception as exc: + RNA_IDProp_Meta.__setattr__ = _setattr + raise exc + + RNA_IDProp_Meta.__setattr__ = _setattr + + def __getattr__(cls, attr): + if attr in cls.bl_rna.properties: + return cls.bl_rna.properties[attr] + elif attr: + # XXX, pure evil, need to find a better way + _getattr = RNA_IDProp_Meta.__getattr__ + del RNA_IDProp_Meta.__getattr__ + try: + ret = getattr(cls, attr) + except Exception as exc: + RNA_IDProp_Meta.__getattr__ = _getattr + raise exc + + RNA_IDProp_Meta.__getattr__ = _getattr + return ret + + def __delattr__(cls, attr): + if attr in cls.bl_rna.properties: + _bpy.props.RemoveProperty(cls, attr=attr) + elif attr: + # XXX, pure evil, need to find a better way + _delattr = RNA_IDProp_Meta.__delattr__ + del RNA_IDProp_Meta.__delattr__ + try: + delattr(cls, attr, value) + except Exception as exc: + RNA_IDProp_Meta.__delattr__ = _delattr + raise exc + + RNA_IDProp_Meta.__delattr__ = _delattr + + from _bpy import types as bpy_types import _bpy from mathutils import Vector @@ -42,6 +101,10 @@ class Context(StructRNA): return new_context +class ID(StructRNA, metaclass=RNA_IDProp_Meta): + __slots__ = () + + class Library(bpy_types.ID): __slots__ = () @@ -60,7 +123,7 @@ class Library(bpy_types.ID): return tuple(id_block for attr in attr_links for id_block in getattr(bpy.data, attr) if id_block.library == self) -class Texture(bpy_types.ID): +class Texture(bpy_types.ID, metaclass=RNA_IDProp_Meta): __slots__ = () @property @@ -257,15 +320,15 @@ class _GenericBone: return bones -class PoseBone(StructRNA, _GenericBone): +class PoseBone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta): __slots__ = () -class Bone(StructRNA, _GenericBone): +class Bone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta): __slots__ = () -class EditBone(StructRNA, _GenericBone): +class EditBone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta): __slots__ = () def align_orientation(self, other): @@ -617,12 +680,13 @@ class RNAMeta(type): return result -class RNAMetaRegister(RNAMeta): +class RNAMetaRegister(RNAMeta, RNA_IDProp_Meta): @classmethod def _register_immediate(cls): return True + class OrderedMeta(RNAMeta): def __init__(cls, name, bases, attributes): diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 908cd25b024..46e02117e77 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -50,6 +50,7 @@ #define USE_MATHUTILS #define USE_STRING_COERCE +#define USE_PY_METACLASS #ifdef USE_MATHUTILS #include "../generic/mathutils.h" /* so we can have mathutils callbacks */ @@ -61,6 +62,7 @@ static PyObject *pyrna_prop_array_subscript_slice(BPy_PropertyArrayRNA *self, Po static Py_ssize_t pyrna_prop_array_length(BPy_PropertyArrayRNA *self); static Py_ssize_t pyrna_prop_collection_length(BPy_PropertyRNA *self); static short pyrna_rotation_euler_order_get(PointerRNA *ptr, PropertyRNA **prop_eul_order, short order_fallback); +static int deferred_register_prop(StructRNA *srna, PyObject *item, PyObject *key); /* bpyrna vector/euler/quat callbacks */ static int mathutils_rna_array_cb_index= -1; /* index for our callbacks */ @@ -2583,6 +2585,24 @@ static int pyrna_struct_pydict_contains(PyObject *self, PyObject *pyname) #endif //--------------- setattr------------------------------------------- + +#ifndef USE_PY_METACLASS +static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *pyname, PyObject *value) +{ + /* check if the value is a property */ + if(PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value)==2) { /* weak */ + StructRNA *srna= srna_from_self(cls, "struct_meta_idprop.setattr()"); + if(srna==NULL) { + return -1; + } + return deferred_register_prop(srna, pyname, value); + } + else { + return PyType_Type.tp_setattro(cls, pyname, value); + } +} +#endif + static int pyrna_struct_setattro( BPy_StructRNA *self, PyObject *pyname, PyObject *value ) { char *name = _PyUnicode_AsString(pyname); @@ -3742,6 +3762,43 @@ static PyObject * pyrna_func_call(PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } +#ifndef USE_PY_METACLASS +/* subclasses of pyrna_struct_Type which support idprop definitions use this as a metaclass */ +/* note: tp_base member is set to &PyType_Type on init */ +PyTypeObject pyrna_struct_meta_idprop_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "bpy_struct_meta_idprop", /* tp_name */ + sizeof(PyTypeObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + NULL, /* tp_dealloc */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ + NULL, /* tp_repr */ + + /* Method suites for standard classes */ + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* pyrna_struct_meta_idprop_getattro*/ /* getattrofunc tp_getattro; */ + NULL /*( setattrofunc ) pyrna_struct_meta_idprop_setattro*/, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* long tp_flags; */ +}; +#endif + + /*-----------------------BPy_StructRNA method def------------------------------*/ PyTypeObject pyrna_struct_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -4199,9 +4256,14 @@ static PyObject* pyrna_srna_PyBase(StructRNA *srna) //, PyObject *bpy_types_dict /* check if we have a native python subclass, use it when it exists * return a borrowed reference */ +static PyObject *bpy_types_dict= NULL; +#ifdef USE_PY_METACLASS +#define BPY_SRNA_IDPROP_META "RNA_IDProp_Meta" +static PyObject *bpy_types_rna_meta_base= NULL; +#endif + static PyObject* pyrna_srna_ExternalType(StructRNA *srna) { - PyObject *bpy_types_dict= NULL; const char *idname= RNA_struct_identifier(srna); PyObject *newclass; @@ -4214,8 +4276,10 @@ static PyObject* pyrna_srna_ExternalType(StructRNA *srna) fprintf(stderr, "pyrna_srna_ExternalType: failed to find 'bpy_types' module\n"); return NULL; } - +#ifdef USE_PY_METACLASS bpy_types_dict = PyModule_GetDict(bpy_types); // borrow + bpy_types_rna_meta_base = PyDict_GetItemString(bpy_types_dict, BPY_SRNA_IDPROP_META); +#endif Py_DECREF(bpy_types); // fairly safe to assume the dict is kept } @@ -4276,16 +4340,30 @@ static PyObject* pyrna_srna_Subtype(StructRNA *srna) /* Assume RNA_struct_py_type_get(srna) was already checked */ PyObject *py_base= pyrna_srna_PyBase(srna); - + PyObject *metaclass; const char *idname= RNA_struct_identifier(srna); - + /* remove __doc__ for now */ // const char *descr= RNA_struct_ui_description(srna); // if(!descr) descr= "(no docs)"; // "__doc__",descr - + +#ifdef USE_PY_METACLASS + if(RNA_struct_idprops_check(srna) && !PyObject_IsSubclass(py_base, bpy_types_rna_meta_base)) { + metaclass= bpy_types_rna_meta_base; + } +#else + if(RNA_struct_idprops_check(srna) && !PyObject_IsSubclass(py_base, &pyrna_struct_meta_idprop_Type)) { + metaclass= (PyObject *)&pyrna_struct_meta_idprop_Type; + } +#endif + else { + metaclass= (PyObject *)&PyType_Type; + } + /* always use O not N when calling, N causes refcount errors */ - newclass = PyObject_CallFunction((PyObject*)&PyType_Type, "s(O){sss()}", idname, py_base, "__module__","bpy.types", "__slots__"); + newclass = PyObject_CallFunction(metaclass, "s(O){sss()}", idname, py_base, "__module__","bpy.types", "__slots__"); + /* newclass will now have 2 ref's, ???, probably 1 is internal since decrefing here segfaults */ /* PyC_ObSpit("new class ref", newclass); */ @@ -4299,6 +4377,7 @@ static PyObject* pyrna_srna_Subtype(StructRNA *srna) } else { /* this should not happen */ + printf("Error registering '%s'\n", idname); PyErr_Print(); PyErr_Clear(); } @@ -4403,7 +4482,14 @@ void BPY_rna_init(void) mathutils_rna_array_cb_index= Mathutils_RegisterCallback(&mathutils_rna_array_cb); mathutils_rna_matrix_cb_index= Mathutils_RegisterCallback(&mathutils_rna_matrix_cb); #endif - + +#ifndef USE_PY_METACLASS + /* metaclass */ + pyrna_struct_meta_idprop_Type.tp_base= &PyType_Type; + if( PyType_Ready( &pyrna_struct_meta_idprop_Type ) < 0 ) + return; +#endif + if( PyType_Ready( &pyrna_struct_Type ) < 0 ) return;