2.5 Python
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 18 Jun 2009 19:51:22 +0000 (19:51 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 18 Jun 2009 19:51:22 +0000 (19:51 +0000)
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.

SConstruct
source/blender/python/intern/bpy_interface.c
source/blender/python/intern/bpy_operator_wrap.c
source/blender/python/intern/bpy_rna.c
source/blender/python/intern/bpy_rna.h
source/blender/python/intern/bpy_util.c

index dcea0f511ebb9f94cfb788391b38c7b25254cddf..808fa09bea03684a26c3d57b4840bb14e49fe375 100644 (file)
@@ -468,27 +468,17 @@ if  env['OURPLATFORM']!='darwin':
                        dotblenderinstall.append(env.Install(dir=td, source=srcfile))
                
                if env['WITH_BF_PYTHON']:
-                       #-- .blender/scripts    
-                       scriptpath='release/scripts'
-                       for dp, dn, df in os.walk(scriptpath):
-                               if 'CVS' in dn:
-                                       dn.remove('CVS')
-                               if '.svn' in dn:
-                                       dn.remove('.svn')
-                               dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
-                               source=[dp+os.sep+f for f in df]
-                               scriptinstall.append(env.Install(dir=dir,source=source))
-
-                       #-- .blender/ui 
-                       scriptpath='release/ui'
-                       for dp, dn, df in os.walk(scriptpath):
-                               if 'CVS' in dn:
-                                       dn.remove('CVS')
-                               if '.svn' in dn:
-                                       dn.remove('.svn')
-                               dir=env['BF_INSTALLDIR']+'/.blender/ui'+dp[len(scriptpath):]
-                               source=[dp+os.sep+f for f in df]
-                               scriptinstall.append(env.Install(dir=dir,source=source))
+                       #-- .blender/scripts, .blender/ui, .blender/io
+                       scriptpaths=['release/scripts', 'release/ui', 'release/io']
+                       for scriptpath in scriptpaths:
+                               for dp, dn, df in os.walk(scriptpath):
+                                       if 'CVS' in dn:
+                                               dn.remove('CVS')
+                                       if '.svn' in dn:
+                                               dn.remove('.svn')
+                                       dir=env['BF_INSTALLDIR']+'/.blender/'+os.path.basename(scriptpath)+dp[len(scriptpath):]
+                                       source=[dp+os.sep+f for f in df]
+                                       scriptinstall.append(env.Install(dir=dir,source=source))
 
 #-- icons
 if env['OURPLATFORM']=='linux2':
index 0c063c0192bbb20ad5c57d98ff50ae7f25463924..559ed5377571b9b7ebb8568bdb55d92584752d89 100644 (file)
@@ -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);
        
index 9b7893a949b82af3c949f80ffcb0d48b73c789c5..f4fdd0c61940053d509efede2ac18d08a8f3d329 100644 (file)
@@ -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 */
index a0fb486554895b5a61a76a3a85bb96a20cd5be43..57a4de21443d2cf91b2fd7fbb0fb99a0baa54863 100644 (file)
@@ -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)
index a2a3015912b09bb576a4a1bbcc215fc24dcefdf2..d2f01b06336f86431f7ad2d37c96735a92a6c83e 100644 (file)
@@ -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);
index d5b131583dce7f8fab4be0bb1061158633e88631..d837892fb4d06d27de157dd48ec3a20af1310f9e 100644 (file)
@@ -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);