diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2009-06-18 23:51:22 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2009-06-18 23:51:22 +0400 |
commit | 94dbb3bbdd612247f757d1962d2604737eb72f64 (patch) | |
tree | e9fc78550ef41d997c034d3281a8edc957a06c3f /source/blender | |
parent | 4cd24cf05809557e0d620dccf1f19a570784f6fe (diff) |
2.5 Python
Merging changes made by Arystanbek in the soc-2009-kazanbas branch,
plus some things modified and added by me.
* Operator exec is called execute in python now, due to conflicts
with python exec keyword.
* Operator invoke/execute now get context argument.
* Fix crash executing operators due to bpy_import_main_set not being
set with Main pointer.
* The bpy.props module now has the FloatProperty/IntProperty/
StringProperty/BoolProperty functions to define RNA properties for
operators.
* Operators now have an __operator__ property to get the actual RNA
operator pointers, this is only temporary though.
* bpy.ops.add now allows the operator to be already registered, it
will simply overwrite the existing one.
* Both the ui and io directories are now scanned and run on startup.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/python/intern/bpy_interface.c | 100 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator_wrap.c | 74 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.c | 70 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.h | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_util.c | 4 |
5 files changed, 186 insertions, 64 deletions
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 0c063c0192b..559ed537757 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -59,6 +59,7 @@ static void bpy_init_modules( void ) PyModule_AddObject( mod, "data", BPY_rna_module() ); /* PyModule_AddObject( mod, "doc", BPY_rna_doc() ); */ PyModule_AddObject( mod, "types", BPY_rna_types() ); + PyModule_AddObject( mod, "props", BPY_rna_props() ); PyModule_AddObject( mod, "ops", BPY_operator_module() ); PyModule_AddObject( mod, "ui", BPY_ui_module() ); // XXX very experemental, consider this a test, especially PyCObject is not meant to be perminant @@ -103,6 +104,7 @@ static PyObject *CreateGlobalDictionary( bContext *C ) {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, {NULL, NULL, 0, NULL} }; @@ -369,70 +371,76 @@ void BPY_run_ui_scripts(bContext *C, int reload) DIR *dir; struct dirent *de; char *file_extension; + char *dirname; char path[FILE_MAX]; - char *dirname= BLI_gethome_folder("ui"); - int filelen; /* filename length */ + char *dirs[] = {"io", "ui", NULL}; + int a, filelen; /* filename length */ PyGILState_STATE gilstate; PyObject *mod; PyObject *sys_path_orig; PyObject *sys_path_new; - - if(!dirname) - return; - - dir = opendir(dirname); - if(!dir) - return; - gilstate = PyGILState_Ensure(); - /* backup sys.path */ - sys_path_orig= PySys_GetObject("path"); - Py_INCREF(sys_path_orig); /* dont free it */ - - sys_path_new= PyList_New(1); - PyList_SET_ITEM(sys_path_new, 0, PyUnicode_FromString(dirname)); - PySys_SetObject("path", sys_path_new); - Py_DECREF(sys_path_new); - // XXX - evil, need to access context BPy_SetContext(C); bpy_import_main_set(CTX_data_main(C)); - - while((de = readdir(dir)) != NULL) { - /* We could stat the file but easier just to let python - * import it and complain if theres a problem */ - - file_extension = strstr(de->d_name, ".py"); + + for(a=0; dirs[a]; a++) { + dirname= BLI_gethome_folder(dirs[a]); + + if(!dirname) + continue; + + dir = opendir(dirname); + + if(!dir) + continue; + + /* backup sys.path */ + sys_path_orig= PySys_GetObject("path"); + Py_INCREF(sys_path_orig); /* dont free it */ - if(file_extension && *(file_extension + 3) == '\0') { - filelen = strlen(de->d_name); - BLI_strncpy(path, de->d_name, filelen-2); /* cut off the .py on copy */ + sys_path_new= PyList_New(1); + PyList_SET_ITEM(sys_path_new, 0, PyUnicode_FromString(dirname)); + PySys_SetObject("path", sys_path_new); + Py_DECREF(sys_path_new); - mod= PyImport_ImportModuleLevel(path, NULL, NULL, NULL, 0); - if (mod) { - if (reload) { - PyObject *mod_orig= mod; - mod= PyImport_ReloadModule(mod); - Py_DECREF(mod_orig); - } - } + while((de = readdir(dir)) != NULL) { + /* We could stat the file but easier just to let python + * import it and complain if theres a problem */ + + file_extension = strstr(de->d_name, ".py"); - if(mod) { - Py_DECREF(mod); /* could be NULL from reloading */ - } else { - BPy_errors_to_report(NULL); // TODO - reports - fprintf(stderr, "unable to import \"%s\" %s/%s\n", path, dirname, de->d_name); + if(file_extension && *(file_extension + 3) == '\0') { + filelen = strlen(de->d_name); + BLI_strncpy(path, de->d_name, filelen-2); /* cut off the .py on copy */ + + mod= PyImport_ImportModuleLevel(path, NULL, NULL, NULL, 0); + if (mod) { + if (reload) { + PyObject *mod_orig= mod; + mod= PyImport_ReloadModule(mod); + Py_DECREF(mod_orig); + } + } + + if(mod) { + Py_DECREF(mod); /* could be NULL from reloading */ + } else { + BPy_errors_to_report(NULL); // TODO - reports + fprintf(stderr, "unable to import \"%s\" %s/%s\n", path, dirname, de->d_name); + } + } } - } - closedir(dir); - - PySys_SetObject("path", sys_path_orig); - Py_DECREF(sys_path_orig); + closedir(dir); + + PySys_SetObject("path", sys_path_orig); + Py_DECREF(sys_path_orig); + } bpy_import_main_set(NULL); diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 9b7893a949b..f4fdd0c6194 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -40,6 +40,8 @@ #include "bpy_compat.h" #include "bpy_util.h" +#include "../generic/bpy_internal_import.h" // our own imports + #define PYOP_ATTR_PROP "__props__" #define PYOP_ATTR_UINAME "__label__" #define PYOP_ATTR_IDNAME "__name__" /* use pythons class name */ @@ -177,9 +179,12 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve PyObject *ret= NULL, *py_class_instance, *item= NULL; int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED); PointerRNA ptr_context; - PyObject *py_context; + PointerRNA ptr_operator; + PyObject *py_operator; PyGILState_STATE gilstate = PyGILState_Ensure(); + + bpy_import_main_set(CTX_data_main(C)); BPY_update_modules(); // XXX - the RNA pointers can change so update before running, would like a nicer solutuon for this. @@ -213,20 +218,29 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve RNA_property_collection_end(&iter); } - + + /* set operator pointer RNA as instance "__operator__" attribute */ + RNA_pointer_create(NULL, &RNA_Operator, op, &ptr_operator); + py_operator= pyrna_struct_CreatePyObject(&ptr_operator); + PyObject_SetAttrString(py_class_instance, "__operator__", py_operator); + Py_DECREF(py_operator); + + RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); if (mode==PYOP_INVOKE) { item= PyObject_GetAttrString(py_class, "invoke"); - args = PyTuple_New(2); - PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event)); + args = PyTuple_New(3); + + // PyTuple_SET_ITEM "steals" object reference, it is + // an object passed shouldn't be DECREF'ed + PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context)); + PyTuple_SET_ITEM(args, 2, pyop_dict_from_event(event)); } else if (mode==PYOP_EXEC) { - item= PyObject_GetAttrString(py_class, "exec"); + item= PyObject_GetAttrString(py_class, "execute"); args = PyTuple_New(2); - RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context); - py_context = pyrna_struct_CreatePyObject(&ptr_context); - PyTuple_SET_ITEM(args, 1, py_context); + PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context)); } else if (mode==PYOP_POLL) { item= PyObject_GetAttrString(py_class, "poll"); @@ -258,7 +272,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve } else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) { /* the returned value could not be converted into a flag */ BPy_errors_to_report(op->reports); - + + ret_flag = OPERATOR_CANCELLED; } /* there is no need to copy the py keyword dict modified by * pyot->py_invoke(), back to the operator props since they are just @@ -271,7 +286,34 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve Py_DECREF(ret); } + /* print operator return value */ + if (mode != PYOP_POLL) { + char flag_str[100]; + char class_name[100]; + BPY_flag_def *flag_def = pyop_ret_flags; + + strcpy(flag_str, ""); + + while(flag_def->name) { + if (ret_flag & flag_def->flag) { + if(flag_str[1]) + sprintf(flag_str, "%s | %s", flag_str, flag_def->name); + else + strcpy(flag_str, flag_def->name); + } + flag_def++; + } + + /* get class name */ + item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME); + Py_DECREF(item); + strcpy(class_name, _PyUnicode_AsString(item)); + + fprintf(stderr, "%s's %s returned %s\n", class_name, mode == PYOP_EXEC ? "execute" : "invoke", flag_str); + } + PyGILState_Release(gilstate); + bpy_import_main_set(NULL); return ret_flag; } @@ -321,7 +363,7 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) /* api callbacks, detailed checks dont on adding */ if (PyObject_HasAttrString(py_class, "invoke")) ot->invoke= PYTHON_OT_invoke; - if (PyObject_HasAttrString(py_class, "exec")) + if (PyObject_HasAttrString(py_class, "execute")) ot->exec= PYTHON_OT_exec; if (PyObject_HasAttrString(py_class, "poll")) ot->poll= PYTHON_OT_poll; @@ -374,6 +416,7 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) { PyObject *base_class, *item; + wmOperatorType *ot; char *idname= NULL; @@ -384,8 +427,8 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL}, {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK}, - {"exec", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, - {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, + {"execute", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, + {"invoke", 'f', 3, BPY_CLASS_ATTR_OPTIONAL}, {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL}, {NULL, 0, 0, 0} }; @@ -404,9 +447,10 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class) Py_DECREF(item); idname = _PyUnicode_AsString(item); - if (WM_operatortype_find(idname)) { - PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name \"%s\"", idname); - return NULL; + /* remove if it already exists */ + if ((ot=WM_operatortype_find(idname))) { + Py_XDECREF((PyObject*)ot->pyop_data); + WM_operatortype_remove(idname); } /* If we have properties set, check its a list of dicts */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index a0fb4865548..57a4de21443 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1760,7 +1760,44 @@ PyObject *BPY_rna_types(void) return (PyObject *)self; } +static struct PyMethodDef props_methods[] = { + {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, + {NULL, NULL, 0, NULL} +}; +#if PY_VERSION_HEX >= 0x03000000 +static struct PyModuleDef props_module = { + PyModuleDef_HEAD_INIT, + "bpyprops", + "", + -1,/* multiple "initialization" just copies the module dict. */ + props_methods, + NULL, NULL, NULL, NULL +}; +#endif + +PyObject *BPY_rna_props( void ) +{ + PyObject *submodule, *mod; +#if PY_VERSION_HEX >= 0x03000000 + submodule= PyModule_Create(&props_module); +#else /* Py2.x */ + submodule= Py_InitModule3( "bpy.props", props_methods, "" ); +#endif + + mod = PyModule_New("props"); + PyModule_AddObject( submodule, "props", mod ); + + /* INCREF since its its assumed that all these functions return the + * module with a new ref like PyDict_New, since they are passed to + * PyModule_AddObject which steals a ref */ + Py_INCREF(submodule); + + return submodule; +} /* Orphan functions, not sure where they should go */ @@ -1780,7 +1817,7 @@ PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - if (self) { + if (self && PyCObject_Check(self)) { StructRNA *srna = PyCObject_AsVoidPtr(self); RNA_def_float(srna, id, def, min, max, name, description, soft_min, soft_max); Py_RETURN_NONE; @@ -1807,7 +1844,7 @@ PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - if (self) { + if (self && PyCObject_Check(self)) { StructRNA *srna = PyCObject_AsVoidPtr(self); RNA_def_int(srna, id, def, min, max, name, description, soft_min, soft_max); Py_RETURN_NONE; @@ -1834,7 +1871,7 @@ PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - if (self) { + if (self && PyCObject_Check(self)) { StructRNA *srna = PyCObject_AsVoidPtr(self); RNA_def_boolean(srna, id, def, name, description); Py_RETURN_NONE; @@ -1847,6 +1884,33 @@ PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) } } +PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) +{ + static char *kwlist[] = {"attr", "name", "description", "maxlen", "default", NULL}; + char *id, *name="", *description="", *def=""; + int maxlen=0; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssis:StringProperty", kwlist, &id, &name, &description, &maxlen, &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 && PyCObject_Check(self)) { + StructRNA *srna = PyCObject_AsVoidPtr(self); + RNA_def_string(srna, id, def, maxlen, name, description); + Py_RETURN_NONE; + } else { + PyObject *ret = PyTuple_New(2); + PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_StringProperty, NULL)); + PyTuple_SET_ITEM(ret, 1, kw); + Py_INCREF(kw); + return ret; + } +} + /*-------------------- Type Registration ------------------------*/ static int rna_function_arg_count(FunctionRNA *func) diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index a2a3015912b..d2f01b06336 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -63,6 +63,7 @@ typedef struct { PyObject *BPY_rna_module( void ); /*PyObject *BPY_rna_doc( void );*/ PyObject *BPY_rna_types( void ); +PyObject *BPY_rna_props( void ); PyObject *pyrna_struct_CreatePyObject( PointerRNA *ptr ); PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop ); @@ -76,6 +77,7 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop); 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); +PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw); /* function for registering types */ PyObject *pyrna_basetype_register(PyObject *self, PyObject *args); diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index d5b131583dc..d837892fb4d 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -81,6 +81,7 @@ int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag) char *cstring; PyObject *item; BPY_flag_def *fd; + *flag = 0; if (PySequence_Check(seq)) { i= PySequence_Length(seq); @@ -108,6 +109,9 @@ int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag) error_val= 1; } + if (*flag == 0) + error_val = 1; + if (error_val) { char *buf = bpy_flag_error_str(flagdef); PyErr_SetString(PyExc_AttributeError, buf); |