Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-03-16 18:54:43 +0300
committerCampbell Barton <ideasman42@gmail.com>2009-03-16 18:54:43 +0300
commit16fe92f86828d6ccec90ad4d8a1995afaf616964 (patch)
tree698094db706cd352a484ada3280f18771f9ef0fe /source/blender/python/intern
parent133e8827b7e9b76e61e405c6eeac88126a106b09 (diff)
2.5 PyAPI
Support for subclassing blenders operator, to be registered as a new operator. Still need to... * add constants like Operator.FINISHED * wrap context (with rna?) * poll() cant work right now because there is no way to access the operatorType that holds the python class. * Only float, int and bool properties can be added so far. working example operator. http://wiki.blender.org/index.php/BlenderDev/Blender2.5/WinterCamp/TechnicalDesign#Operator_Example_Code
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r--source/blender/python/intern/bpy_interface.c16
-rw-r--r--source/blender/python/intern/bpy_operator.c2
-rw-r--r--source/blender/python/intern/bpy_opwrapper.c394
-rw-r--r--source/blender/python/intern/bpy_rna.c132
-rw-r--r--source/blender/python/intern/bpy_rna.h13
5 files changed, 398 insertions, 159 deletions
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index fc43d7e5537..54f7bb55cc0 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -57,6 +57,22 @@ static PyObject *CreateGlobalDictionary( bContext *C )
PyDict_SetItemString( dict, "__bpy_context__", item );
Py_DECREF(item);
+
+ // XXX - put somewhere more logical
+ {
+ PyMethodDef *ml;
+ static PyMethodDef bpy_prop_meths[] = {
+ {"FloatProperty", BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""},
+ {"IntProperty", BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""},
+ {"BoolProperty", BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""},
+ {NULL, NULL, 0, NULL}
+ };
+
+ for(ml = &bpy_prop_meths; ml->ml_name; ml++) {
+ PyDict_SetItemString( dict, ml->ml_name, PyCFunction_New(ml, NULL));
+ }
+ }
+
return dict;
}
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 074a6ca499b..d2dbd6c91bb 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -111,7 +111,7 @@ static PyObject *pyop_base_dir(PyObject *self);
static struct PyMethodDef pyop_base_methods[] = {
{"__dir__", (PyCFunction)pyop_base_dir, METH_NOARGS, ""},
{"add", (PyCFunction)PYOP_wrap_add, METH_VARARGS, ""},
- {"remove", (PyCFunction)PYOP_wrap_remove, METH_VARARGS, ""},
+ {"remove", (PyCFunction)PYOP_wrap_remove, METH_O, ""},
{NULL, NULL, 0, NULL}
};
diff --git a/source/blender/python/intern/bpy_opwrapper.c b/source/blender/python/intern/bpy_opwrapper.c
index ca6104d087f..727dee01531 100644
--- a/source/blender/python/intern/bpy_opwrapper.c
+++ b/source/blender/python/intern/bpy_opwrapper.c
@@ -45,39 +45,9 @@ typedef struct PyOperatorType {
char idname[OP_MAX_TYPENAME];
char name[OP_MAX_TYPENAME];
char description[OP_MAX_TYPENAME]; // XXX should be longer?
- PyObject *py_invoke;
- PyObject *py_exec;
+ PyObject *py_class;
} PyOperatorType;
-static PyObject *pyop_kwargs_from_operator(wmOperator *op)
-{
- PyObject *dict = PyDict_New();
- PyObject *item;
- PropertyRNA *prop, *iterprop;
- CollectionPropertyIterator iter;
- const char *arg_name;
-
- iterprop= RNA_struct_iterator_property(op->ptr);
- RNA_property_collection_begin(op->ptr, iterprop, &iter);
-
- for(; iter.valid; RNA_property_collection_next(&iter)) {
- prop= iter.ptr.data;
-
- arg_name= RNA_property_identifier(&iter.ptr, prop);
-
- if (strcmp(arg_name, "rna_type")==0) continue;
-
- item = pyrna_prop_to_py(op->ptr, prop);
- PyDict_SetItemString(dict, arg_name, item);
- Py_DECREF(item);
- }
-
- RNA_property_collection_end(&iter);
-
- return dict;
-}
-
-
static PyObject *pyop_dict_from_event(wmEvent *event)
{
PyObject *dict= PyDict_New();
@@ -191,33 +161,6 @@ static struct BPY_flag_def pyop_ret_flags[] = {
{NULL, 0}
};
-/* exec only - no user input */
-static int PYTHON_OT_exec(bContext *C, wmOperator *op)
-{
- PyOperatorType *pyot = op->type->pyop_data;
- PyObject *args= PyTuple_New(0);
- PyObject *kw= pyop_kwargs_from_operator(op);
- PyObject *ret;
- int ret_flag;
-
- ret = PyObject_Call(pyot->py_exec, args, kw);
-
- if (ret == NULL) {
- pyop_error_report(op->reports);
- }
- else {
- if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
- /* the returned value could not be converted into a flag */
- pyop_error_report(op->reports);
- }
- }
-
- Py_DECREF(args);
- Py_DECREF(kw);
-
- return ret_flag;
-}
-
/* This invoke function can take events and
*
* It is up to the pyot->py_invoke() python func to run pyot->py_exec()
@@ -233,26 +176,93 @@ static int PYTHON_OT_exec(bContext *C, wmOperator *op)
* op_exec(**prop_defs)
*
* when there is no invoke function, C calls exec and sets the props.
+ * python class instance is stored in op->customdata so exec() can access
*/
-static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
- PyOperatorType *pyot = op->type->pyop_data;
- PyObject *args= PyTuple_New(2);
- PyObject *ret;
- int ret_flag;
- PyTuple_SET_ITEM(args, 0, pyop_dict_from_event(event));
- PyTuple_SET_ITEM(args, 1, pyop_kwargs_from_operator(op));
- ret = PyObject_Call(pyot->py_invoke, args, NULL);
+#define PYOP_EXEC 1
+#define PYOP_INVOKE 2
+#define PYOP_POLL 3
+
+static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *event)
+{
+ PyOperatorType *pyot = op->type->pyop_data;
+ PyObject *args;
+ PyObject *ret= NULL, *py_class_instance, *item;
+ int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
+
+ args = PyTuple_New(1);
+ PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(pyot->py_class, "__rna__")); // need to use an rna instance as the first arg
+ py_class_instance = PyObject_Call(pyot->py_class, args, NULL);
+ Py_DECREF(args);
+
+ if (py_class_instance) { /* Initializing the class worked, now run its invoke function */
+
+
+ /* Assign instance attributes from operator properties */
+ {
+ PropertyRNA *prop, *iterprop;
+ CollectionPropertyIterator iter;
+ const char *arg_name;
+
+ iterprop= RNA_struct_iterator_property(op->ptr);
+ RNA_property_collection_begin(op->ptr, iterprop, &iter);
+
+ for(; iter.valid; RNA_property_collection_next(&iter)) {
+ prop= iter.ptr.data;
+ arg_name= RNA_property_identifier(&iter.ptr, prop);
+
+ if (strcmp(arg_name, "rna_type")==0) continue;
+
+ item = pyrna_prop_to_py(op->ptr, prop);
+ PyObject_SetAttrString(py_class_instance, arg_name, item);
+ Py_DECREF(item);
+ }
+ RNA_property_collection_end(&iter);
+ }
+
+
+ if (mode==PYOP_INVOKE) {
+ item= PyObject_GetAttrString(pyot->py_class, "invoke");
+ args = PyTuple_New(2);
+ PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event));
+ }
+ else if (mode==PYOP_EXEC) {
+ item= PyObject_GetAttrString(pyot->py_class, "exec");
+ args = PyTuple_New(1);
+ }
+ else if (mode==PYOP_POLL) {
+ item= PyObject_GetAttrString(pyot->py_class, "poll");
+ args = PyTuple_New(2);
+ //XXX Todo - wrap context in a useful way, None for now.
+ PyTuple_SET_ITEM(args, 1, Py_None);
+ }
+ PyTuple_SET_ITEM(args, 0, py_class_instance);
+
+ ret = PyObject_Call(item, args, NULL);
+
+ Py_DECREF(args);
+ Py_DECREF(item);
+ }
+
if (ret == NULL) {
pyop_error_report(op->reports);
}
else {
- if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
+ if (mode==PYOP_POLL) {
+ if (PyBool_Check(ret) == 0) {
+ PyErr_SetString(PyExc_ValueError, "Python poll function return value ");
+ pyop_error_report(op->reports);
+ }
+ else {
+ ret_flag= ret==Py_True ? 1:0;
+ }
+
+ } else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
/* the returned value could not be converted into a flag */
pyop_error_report(op->reports);
+
}
/* there is no need to copy the py keyword dict modified by
* pyot->py_invoke(), back to the operator props since they are just
@@ -261,131 +271,234 @@ static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
* If we ever want to do this and use the props again,
* it can be done with - PYOP_props_from_dict(op->ptr, kw)
*/
+
+ Py_DECREF(ret);
}
-
+ return ret_flag;
+}
+
+static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ return PYTHON_OT_generic(PYOP_INVOKE, C, op, event);
+}
- Py_DECREF(args); /* also decref's kw */
+static int PYTHON_OT_exec(bContext *C, wmOperator *op)
+{
+ return PYTHON_OT_generic(PYOP_EXEC, C, op, NULL);
+}
- return ret_flag;
+static int PYTHON_OT_poll(bContext *C)
+{
+ // XXX TODO - no way to get the operator type (and therefor class) from the poll function.
+ //return PYTHON_OT_generic(PYOP_POLL, C, NULL, NULL);
+ return 1;
}
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
{
PyOperatorType *pyot = (PyOperatorType *)userdata;
+ PyObject *py_class = pyot->py_class;
/* identifiers */
ot->name= pyot->name;
ot->idname= pyot->idname;
ot->description= pyot->description;
- /* api callbacks */
- if (pyot->py_invoke != Py_None)
+ /* api callbacks, detailed checks dont on adding */
+ if (PyObject_HasAttrString(py_class, "invoke"))
ot->invoke= PYTHON_OT_invoke;
-
- ot->exec= PYTHON_OT_exec;
-
- ot->poll= ED_operator_screenactive; /* how should this work?? */
- /* ot->flag= OPTYPE_REGISTER; */
+ if (PyObject_HasAttrString(py_class, "exec"))
+ ot->exec= PYTHON_OT_exec;
+ if (PyObject_HasAttrString(py_class, "poll"))
+ ot->poll= PYTHON_OT_poll;
ot->pyop_data= userdata;
- /* inspect function keyword args to get properties */
- {
- PropertyRNA *prop;
-
- PyObject *var_names= PyObject_GetAttrString(PyFunction_GET_CODE(pyot->py_exec), "co_varnames");
- PyObject *var_vals = PyFunction_GET_DEFAULTS(pyot->py_exec);
- PyObject *py_val, *py_name;
+ // TODO - set properties
+ PyObject *props, *item;
+
+
+ if ((props=PyObject_GetAttrString(py_class, "properties"))) {
+ PyObject *dummy_args = PyTuple_New(0);
+
int i;
- char *name;
-
- if (PyTuple_Size(var_names) != PyTuple_Size(var_vals)) {
- printf("All args must be keywords");
- }
-
- for(i=0; i<PyTuple_Size(var_names); i++) {
- py_name = PyTuple_GetItem(var_names, i);
- name = _PyUnicode_AsString(py_name);
- py_val = PyTuple_GetItem(var_vals, i);
-
- if (PyBool_Check(py_val)) {
- prop = RNA_def_property(ot->srna, name, PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_default(prop, PyObject_IsTrue(py_val));
- }
- else if (PyLong_Check(py_val)) {
- prop = RNA_def_property(ot->srna, name, PROP_INT, PROP_NONE);
- RNA_def_property_int_default(prop, (int)PyLong_AsSsize_t(py_val));
- }
- else if (PyFloat_Check(py_val)) {
- prop = RNA_def_property(ot->srna, name, PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_default(prop, (float)PyFloat_AsDouble(py_val));
- }
- else if (PyUnicode_Check(py_val)) {
- /* WARNING - holding a reference to the string from py_val is
- * not ideal since we rely on python keeping it,
- * however we're also keeping a reference to this function
- * so it should be OK!. just be careful with changes */
- prop = RNA_def_property(ot->srna, name, PROP_STRING, PROP_NONE);
- RNA_def_property_string_default(prop, _PyUnicode_AsString(py_val));
- }
- else {
- printf("error, python function arg \"%s\" was not a bool, int, float or string type\n", name);
+
+ for(i=0; i<PyList_Size(props); i++) {
+ item = PyList_GET_ITEM(props, i);
+
+ PyObject *py_func_ptr, *py_kw, *py_srna_cobject, *py_ret;
+
+ if (PyArg_ParseTuple(item, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
+
+ PyObject *(*pyfunc)(PyObject *, PyObject *, PyObject *);
+ pyfunc = PyCObject_AsVoidPtr(py_func_ptr);
+ py_srna_cobject = PyCObject_FromVoidPtr(ot->srna, NULL);
+
+ py_ret = pyfunc(py_srna_cobject, dummy_args, py_kw);
+ if (py_ret) {
+ Py_DECREF(py_ret);
+ } else {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ Py_DECREF(py_srna_cobject);
+
+ } else {
+ /* cant return NULL from here */ // XXX a bit ugly
+ PyErr_Print();
+ PyErr_Clear();
}
+
+ // expect a tuple with a CObject and a dict
}
+ Py_DECREF(dummy_args);
+ } else {
+ PyErr_Clear();
}
-
}
+
/* pyOperators - Operators defined IN Python */
PyObject *PYOP_wrap_add(PyObject *self, PyObject *args)
{
+ // XXX ugly - store the Operator type elsewhere!, probably leaks memory
+ PyObject *optype = PyObject_GetAttrString(PyObject_GetAttrString(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), "types"), "Operator");
+ PyObject *value, *item;
+
PyOperatorType *pyot;
-
char *idname= NULL;
char *name= NULL;
char *description= NULL;
- PyObject *invoke= NULL;
- PyObject *exec= NULL;
-
- if (!PyArg_ParseTuple(args, "sssOO", &idname, &name, &description, &invoke, &exec)) {
- PyErr_SetString( PyExc_AttributeError, "expected 2 strings and 2 function objects");
+
+ static char *pyop_func_names[] = {"exec", "invoke", "poll", NULL};
+ static int *pyop_func_nargs[] = {1, 2, 2, 0};
+
+
+ if (!PyArg_ParseTuple(args, "O", &value) || !PyObject_IsSubclass(value, optype)) {
+ PyErr_SetString( PyExc_AttributeError, "expected Operator subclass of bpy.types.Operator");
return NULL;
}
-
+
+ /* class name is used for operator ID - this can be changed later if we want */
+ item = PyObject_GetAttrString(value, "__name__");
+ idname = _PyUnicode_AsString(item);
+ Py_DECREF(item);
+
if (WM_operatortype_find(idname)) {
- PyErr_Format( PyExc_AttributeError, "First argument \"%s\" operator alredy exists with this name", idname);
+ PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name", idname);
return NULL;
}
-
- if (((PyFunction_Check(invoke) || invoke==Py_None) && PyFunction_Check(exec)) == 0) {
- PyErr_SetString( PyExc_AttributeError, "the 2nd arg must be a function or None, the secons must be a function");
- return NULL;
+
+ /* Operator user readible name */
+ item = PyObject_GetAttrString(value, "name");
+ if (item) {
+ name = _PyUnicode_AsString(item);
+ Py_DECREF(item);
}
-
+ if (name == NULL) {
+ name = idname;
+ PyErr_Clear();
+ }
+
+ /* use py docstring for description, should always be None or a string */
+ item = PyObject_GetAttrString(value, "__doc__");
+ if (PyUnicode_Check(item)) {
+ description = _PyUnicode_AsString(item);
+ }
+ else {
+ description = "";
+ }
+ Py_DECREF(item);
+
+ /* Check known functions and argument lengths */
+ int i;
+ int argcount;
+ for (i=0; pyop_func_names[i]; i++) {
+ if (item=PyObject_GetAttrString(value, pyop_func_names[i])) {
+ /* check its callable */
+ if (!PyFunction_Check(item)) {
+ PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() is not a function", idname, pyop_func_names[i]);
+ Py_DECREF(item);
+ return NULL;
+ }
+ /* check the number of args is correct */
+ // MyClass.exec.func_code.co_argcount
+
+ PyObject *pyargcount = PyObject_GetAttrString(PyFunction_GetCode(item), "co_argcount");
+ argcount = PyLong_AsSsize_t(pyargcount);
+ Py_DECREF(pyargcount);
+
+ if (argcount != pyop_func_nargs[i]) {
+ PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() takes %d args, should be %d", idname, pyop_func_names[i], argcount, pyop_func_nargs[i]);
+ Py_DECREF(item);
+ return NULL;
+ }
+
+ } else {
+ PyErr_Clear();
+ }
+ Py_XDECREF(item);
+ }
+
+ /* If we have properties set, check its a list of dicts */
+ item = PyObject_GetAttrString(value, "properties");
+ if (item) {
+ if (!PyList_Check(item)) {
+ PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must be a list", idname);
+ Py_DECREF(item);
+ return NULL;
+ }
+
+ int i;
+ for(i=0; i<PyList_Size(item); i++) {
+ PyObject *py_args = PyList_GET_ITEM(item, i);
+ PyObject *py_func_ptr, *py_kw; /* place holders */
+
+ if (!PyArg_ParseTuple(py_args, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
+ PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must contain values from FloatProperty", idname);
+ Py_DECREF(item);
+ return NULL;
+ }
+ }
+
+ Py_DECREF(item);
+ }
+ else {
+ PyErr_Clear();
+ }
+
pyot= MEM_callocN(sizeof(PyOperatorType), "PyOperatorType");
strncpy(pyot->idname, idname, sizeof(pyot->idname));
strncpy(pyot->name, name, sizeof(pyot->name));
strncpy(pyot->description, description, sizeof(pyot->description));
- pyot->py_invoke= invoke;
- pyot->py_exec= exec;
- Py_INCREF(invoke);
- Py_INCREF(exec);
+ pyot->py_class= value;
+ Py_INCREF(value);
WM_operatortype_append_ptr(PYTHON_OT_wrapper, pyot);
Py_RETURN_NONE;
}
-PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
+PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
{
char *idname= NULL;
wmOperatorType *ot;
PyOperatorType *pyot;
- if (!PyArg_ParseTuple(args, "s", &idname))
+ if (PyUnicode_Check(value))
+ idname = _PyUnicode_AsString(value);
+ else if (PyCFunction_Check(value)) {
+ PyObject *cfunc_self = PyCFunction_GetSelf(value);
+ if (cfunc_self)
+ idname = _PyUnicode_AsString(cfunc_self);
+ }
+
+ if (idname==NULL) {
+ PyErr_SetString( PyExc_ValueError, "Expected the operator name as a string or the operator function");
return NULL;
+ }
if (!(ot= WM_operatortype_find(idname))) {
PyErr_Format( PyExc_AttributeError, "Operator \"%s\" does not exists, cant remove", idname);
@@ -397,8 +510,7 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
return NULL;
}
- Py_XDECREF(pyot->py_invoke);
- Py_XDECREF(pyot->py_exec);
+ Py_XDECREF(pyot->py_class);
MEM_freeN(pyot);
WM_operatortype_remove(idname);
@@ -406,3 +518,5 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
+
+
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index c9cd4414127..193f2a8d3d2 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -26,6 +26,9 @@
#include "bpy_compat.h"
//#include "blendef.h"
#include "BLI_dynstr.h"
+#include "float.h" /* FLT_MIN/MAX */
+
+#include "RNA_define.h" /* for defining our own rna */
#include "MEM_guardedalloc.h"
#include "BKE_global.h" /* evil G.* */
@@ -65,12 +68,16 @@ static PyObject *pyrna_prop_richcmp(BPy_PropertyRNA * a, BPy_PropertyRNA * b, in
/*----------------------repr--------------------------------------------*/
static PyObject *pyrna_struct_repr( BPy_StructRNA * self )
{
- return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\"]", RNA_struct_identifier(&self->ptr));
+ char str[512];
+ RNA_string_get(&self->ptr, "identifier", str);
+ return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\" -> \"%s\"]", RNA_struct_identifier(&self->ptr), str);
}
static PyObject *pyrna_prop_repr( BPy_PropertyRNA * self )
{
- return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), RNA_property_identifier(&self->ptr, self->prop) );
+ char str[512];
+ RNA_string_get(&self->ptr, "identifier", str);
+ return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), str, RNA_property_identifier(&self->ptr, self->prop) );
}
static long pyrna_struct_hash( BPy_StructRNA * self )
@@ -88,7 +95,7 @@ static void pyrna_struct_dealloc( BPy_StructRNA * self )
self->ptr.data= NULL;
}
- ((PyObject *)self)->ob_type->tp_free(self);
+ Py_TYPE(self)->tp_free(self);
return;
}
@@ -516,7 +523,7 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
char *keyname = NULL;
if (!RNA_property_editable(&self->ptr, self->prop)) {
- PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
+ PyErr_Format( PyExc_AttributeError, "PropertyRNA - attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
return -1;
}
@@ -525,26 +532,26 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
} else if (PyLong_Check(key)) {
keynum = PyLong_AsSsize_t(key);
} else {
- PyErr_SetString(PyExc_AttributeError, "invalid key, key must be a string or an int");
+ PyErr_SetString(PyExc_AttributeError, "PropertyRNA - invalid key, key must be a string or an int");
return -1;
}
if (RNA_property_type(&self->ptr, self->prop) == PROP_COLLECTION) {
- PyErr_SetString(PyExc_AttributeError, "assignment is not supported for collections (yet)");
+ PyErr_SetString(PyExc_AttributeError, "PropertyRNA - assignment is not supported for collections (yet)");
ret = -1;
} else if (keyname) {
- PyErr_SetString(PyExc_AttributeError, "string keys are only supported for collections");
+ PyErr_SetString(PyExc_AttributeError, "PropertyRNA - string keys are only supported for collections");
ret = -1;
} else {
int len = RNA_property_array_length(&self->ptr, self->prop);
if (len==0) { /* not an array*/
- PyErr_Format(PyExc_AttributeError, "not an array or collection %d", keynum);
+ PyErr_Format(PyExc_AttributeError, "PropertyRNA - not an array or collection %d", keynum);
ret = -1;
}
if (keynum >= len){
- PyErr_SetString(PyExc_AttributeError, "index out of range");
+ PyErr_SetString(PyExc_AttributeError, "PropertyRNA - index out of range");
ret = -1;
} else {
ret = pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value);
@@ -621,9 +628,11 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
/* Include this incase this instance is a subtype of a python class
* In these instances we may want to return a function or variable provided by the subtype
* */
- ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
- if (ret) return ret;
- else PyErr_Clear();
+ if (BPy_StructRNA_CheckExact(self) == 0) {
+ ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
+ if (ret) return ret;
+ else PyErr_Clear();
+ }
/* done with subtypes */
prop = RNA_struct_find_property(&self->ptr, name);
@@ -632,7 +641,7 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
ret = pyrna_prop_to_py(&self->ptr, prop);
}
else {
- PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
+ PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
ret = NULL;
}
@@ -646,12 +655,17 @@ static int pyrna_struct_setattro( BPy_StructRNA * self, PyObject *pyname, PyObje
PropertyRNA *prop = RNA_struct_find_property(&self->ptr, name);
if (prop==NULL) {
- PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
- return -1;
+ if (!BPy_StructRNA_CheckExact(self) && PyObject_GenericSetAttr((PyObject *)self, pyname, value) >= 0) {
+ return 0;
+ }
+ else {
+ PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
+ return -1;
+ }
}
if (!RNA_property_editable(&self->ptr, prop)) {
- PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
+ PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
return -1;
}
@@ -1200,3 +1214,89 @@ PyObject *BPY_rna_doc( void )
return mod;
}
+
+
+/* Orphan functions, not sure where they should go */
+
+/* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong
+ * This isnt incorrect since its a python object - but be careful */
+PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
+ char *id, *name="", *description="";
+ float min=FLT_MIN, max=FLT_MAX, soft_min=FLT_MIN, soft_max=FLT_MAX, def=0.0f;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssfffff:FloatProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
+ return NULL;
+
+ if (PyTuple_Size(args) > 0) {
+ PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
+ return NULL;
+ }
+
+ if (self) {
+ StructRNA *srna = PyCObject_AsVoidPtr(self);
+ RNA_def_float(srna, id, def, min, max, name, description, soft_min, soft_max);
+ Py_RETURN_NONE;
+ } else {
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_FloatProperty, NULL));
+ PyTuple_SET_ITEM(ret, 1, kw);
+ Py_INCREF(kw);
+ return ret;
+ }
+}
+
+PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
+ char *id, *name="", *description="";
+ int min=INT_MIN, max=INT_MAX, soft_min=INT_MIN, soft_max=INT_MAX, def=0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssiiiii:IntProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
+ return NULL;
+
+ if (PyTuple_Size(args) > 0) {
+ PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
+ return NULL;
+ }
+
+ if (self) {
+ StructRNA *srna = PyCObject_AsVoidPtr(self);
+ RNA_def_int(srna, id, def, min, max, name, description, soft_min, soft_max);
+ Py_RETURN_NONE;
+ } else {
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
+ PyTuple_SET_ITEM(ret, 1, kw);
+ Py_INCREF(kw);
+ return ret;
+ }
+}
+
+PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = {"attribute", "name", "description", "default", NULL};
+ char *id, *name="", *description="";
+ int def=0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssi:IntProperty", kwlist, &id, &name, &description, &def))
+ return NULL;
+
+ if (PyTuple_Size(args) > 0) {
+ PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
+ return NULL;
+ }
+
+ if (self) {
+ StructRNA *srna = PyCObject_AsVoidPtr(self);
+ RNA_def_boolean(srna, id, def, name, description);
+ Py_RETURN_NONE;
+ } else {
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
+ PyTuple_SET_ITEM(ret, 1, kw);
+ Py_INCREF(kw);
+ return ret;
+ }
+}
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 1d49d4d5485..904529b58de 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -33,8 +33,10 @@
extern PyTypeObject pyrna_struct_Type;
extern PyTypeObject pyrna_prop_Type;
-#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
-#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
+#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
+#define BPy_StructRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_struct_Type)
+#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
+#define BPy_PropertyRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_prop_Type)
//XXX add propper accessor function, we know this is just after next/prev pointers
@@ -69,4 +71,11 @@ PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop );
/* operators also need this to set args */
int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, PyObject *value);
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop);
+
+/* functions for setting up new props - experemental */
+PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw);
+
+
#endif