renamed bpy.sys to bpy.utils, since it used to be a attempt to replace pythons sys...
[blender.git] / source / blender / python / intern / bpy_interface.c
index 69b733d3fda9d392721ab0f79353925e17017389..b465a494a4d88013d9b7e641420e1998bc8c5186 100644 (file)
@@ -1,13 +1,37 @@
-
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * Contributor(s): Michel Selten, Willian P. Germano, Stephen Swaney,
+ * Chris Keith, Chris Want, Ken Hughes, Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 
-#ifndef WIN32
-#include <dirent.h>
-#else
-#include "BLI_winstuff.h"
+
+/* grr, python redefines */
+#ifdef _POSIX_C_SOURCE
+#undef _POSIX_C_SOURCE
 #endif
 
 #include <Python.h>
 #include "bpy_ui.h"
 #include "bpy_util.h"
 
+#ifndef WIN32
+#include <dirent.h>
+#else
+#include "BLI_winstuff.h"
+#endif
+
 #include "DNA_anim_types.h"
 #include "DNA_space_types.h"
 #include "DNA_text_types.h"
 #include "MEM_guardedalloc.h"
 
 #include "BLI_util.h"
+#include "BLI_storage.h"
 #include "BLI_fileops.h"
 #include "BLI_string.h"
 
 #include "BKE_context.h"
 #include "BKE_fcurve.h"
 #include "BKE_text.h"
+#include "BKE_context.h"
 
 #include "BPY_extern.h"
 
@@ -116,6 +148,17 @@ void bpy_context_clear(bContext *C, PyGILState_STATE *gilstate)
        }
 }
 
+static void bpy_import_test(char *modname)
+{
+       PyObject *mod= PyImport_ImportModuleLevel(modname, NULL, NULL, NULL, 0);
+       if(mod) {
+               Py_DECREF(mod);
+       }
+       else {
+               PyErr_Print();
+               PyErr_Clear();
+       }       
+}
 
 void BPY_free_compiled_text( struct Text *text )
 {
@@ -145,7 +188,22 @@ static void bpy_init_modules( void )
        PyDict_SetItemString(PySys_GetObject("modules"), "bpy", mod);
        Py_DECREF(mod);
 
-
+       /* add our own modules dir */
+       {
+               char *modpath= BLI_gethome_folder("scripts/modules", BLI_GETHOME_ALL);
+               
+               if(modpath) {
+                       PyObject *sys_path= PySys_GetObject("path"); /* borrow */
+                       PyObject *py_modpath= PyUnicode_FromString(modpath);
+                       PyList_Insert(sys_path, 0, py_modpath); /* add first */
+                       Py_DECREF(py_modpath);
+               }
+               
+               bpy_import_test("bpy_ops"); /* adds its self to bpy.ops */
+               bpy_import_test("bpy_utils"); /* adds its self to bpy.sys */
+               bpy_import_test("bpy_ext"); /* extensions to our existing types */
+       }
+       
        /* stand alone utility modules not related to blender directly */
        Geometry_Init();
        Mathutils_Init();
@@ -185,6 +243,9 @@ static PyObject *CreateGlobalDictionary( bContext *C )
                        {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""},
                        {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""},
                        {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""},
+                       {"EnumProperty", (PyCFunction)BPy_EnumProperty, METH_VARARGS|METH_KEYWORDS, ""},
+                       {"PointerProperty", (PyCFunction)BPy_PointerProperty, METH_VARARGS|METH_KEYWORDS, ""},
+                       {"CollectionProperty", (PyCFunction)BPy_CollectionProperty, METH_VARARGS|METH_KEYWORDS, ""},
                        {NULL, NULL, 0, NULL}
                };
                
@@ -201,26 +262,10 @@ static PyObject *CreateGlobalDictionary( bContext *C )
        return dict;
 }
 
-/* Use this so we can include our own python bundle */
-#if 0
-wchar_t* Py_GetPath(void)
-{
-       int i;
-       static wchar_t py_path[FILE_MAXDIR] = L"";
-       char *dirname= BLI_gethome_folder("python");
-       if(dirname) {
-               i= mbstowcs(py_path, dirname, FILE_MAXDIR);
-               printf("py path %s, %d\n", dirname, i);
-       }
-       return py_path;
-}
-#endif
-
-
 /* must be called before Py_Initialize */
 void BPY_start_python_path(void)
 {
-       char *py_path_bundle= BLI_gethome_folder("python");
+       char *py_path_bundle= BLI_gethome_folder("python", BLI_GETHOME_ALL);
 
        if(py_path_bundle==NULL)
                return;
@@ -228,8 +273,17 @@ void BPY_start_python_path(void)
        /* set the environment path */
        printf("found bundled python: %s\n", py_path_bundle);
 
+#if 0
        BLI_setenv("PYTHONHOME", py_path_bundle);
        BLI_setenv("PYTHONPATH", py_path_bundle);
+#endif
+
+       {
+               static wchar_t py_path_bundle_wchar[FILE_MAXDIR];
+
+               mbstowcs(py_path_bundle_wchar, py_path_bundle, FILE_MAXDIR);
+               Py_SetPythonHome(py_path_bundle_wchar);
+       }
 }
 
 
@@ -268,6 +322,11 @@ void BPY_start_python( int argc, char **argv )
                PyObject *d = PyEval_GetBuiltins(  );
                PyDict_SetItemString(d, "reload",               item=PyCFunction_New(bpy_reload_meth, NULL));   Py_DECREF(item);
                PyDict_SetItemString(d, "__import__",   item=PyCFunction_New(bpy_import_meth, NULL));   Py_DECREF(item);
+               
+               /* a bit nasty but this prevents help() and input() from locking blender
+                * Ideally we could have some way for the console to replace sys.stdin but
+                * python would lock blender while waiting for a return value, not easy :| */
+               PySys_SetObject("stdin", Py_None);
        }
        
        pyrna_alloc_types();
@@ -548,8 +607,9 @@ void BPY_run_ui_scripts(bContext *C, int reload)
        char *file_extension;
        char *dirname;
        char path[FILE_MAX];
-       char *dirs[] = {"ui", "io", NULL};
-       int a, err;
+       char *dirs[] = {"scripts/ui", "scripts/io", NULL};
+       int path_flags[] = {BLI_GETHOME_LOCAL|BLI_GETHOME_SYSTEM, BLI_GETHOME_USER}; /* SYSTEM / NON-SYSTEM */
+       int a, err, flag_iter;
        
        PyGILState_STATE gilstate;
        PyObject *sys_path;
@@ -559,56 +619,60 @@ void BPY_run_ui_scripts(bContext *C, int reload)
        sys_path= PySys_GetObject("path"); /* borrow */
        PyList_Insert(sys_path, 0, Py_None); /* place holder, resizes the list */
 
-       for(a=0; dirs[a]; a++) {
-               dirname= BLI_gethome_folder(dirs[a]);
+       /* Scan system scripts first, then local/user */
+       for(flag_iter=0; flag_iter < sizeof(path_flags)/sizeof(int); flag_iter++) {
+               
+               for(a=0; dirs[a]; a++) {
+                       dirname= BLI_gethome_folder(dirs[a], path_flags[flag_iter]);
 
-               if(!dirname)
-                       continue;
+                       if(!dirname)
+                               continue;
 
-               dir = opendir(dirname);
+                       dir = opendir(dirname);
 
-               if(!dir)
-                       continue;
-               
-               /* set the first dir in the sys.path for fast importing of modules */
-               PyList_SetItem(sys_path, 0, PyUnicode_FromString(dirname)); /* steals the ref */
+                       if(!dir)
+                               continue;
                        
-               while((de = readdir(dir)) != NULL) {
-                       /* We could stat the file but easier just to let python
-                        * import it and complain if theres a problem */
-                       err = 0;
-
-                       if (de->d_name[0] == '.') {
-                               /* do nothing, probably .svn */
-                       }
-                       else if ((file_extension = strstr(de->d_name, ".py"))) {
-                               /* normal py files? */
-                               if(file_extension && file_extension[3] == '\0') {
-                                       de->d_name[(file_extension - de->d_name)] = '\0';
-                                       err= bpy_import_module(de->d_name, reload);
+                       /* set the first dir in the sys.path for fast importing of modules */
+                       PyList_SetItem(sys_path, 0, PyUnicode_FromString(dirname)); /* steals the ref */
+                               
+                       while((de = readdir(dir)) != NULL) {
+                               /* We could stat the file but easier just to let python
+                                * import it and complain if theres a problem */
+                               err = 0;
+
+                               if (de->d_name[0] == '.') {
+                                       /* do nothing, probably .svn */
+                               }
+                               else if ((file_extension = strstr(de->d_name, ".py"))) {
+                                       /* normal py files? */
+                                       if(file_extension && file_extension[3] == '\0') {
+                                               de->d_name[(file_extension - de->d_name)] = '\0';
+                                               err= bpy_import_module(de->d_name, reload);
+                                       }
                                }
-                       }
 #ifndef __linux__
-                       else if( BLI_join_dirfile(path, dirname, de->d_name), S_ISDIR(BLI_exists(path))) {
+                               else if( BLI_join_dirfile(path, dirname, de->d_name), S_ISDIR(BLI_exist(path))) {
 #else
-                       else if(de->d_type==DT_DIR) {
-                               BLI_join_dirfile(path, dirname, de->d_name);
+                               else if(de->d_type==DT_DIR) {
+                                       BLI_join_dirfile(path, dirname, de->d_name);
 #endif
-                               /* support packages */
-                               BLI_join_dirfile(path, path, "__init__.py");
+                                       /* support packages */
+                                       BLI_join_dirfile(path, path, "__init__.py");
 
-                               if(BLI_exists(path)) {
-                                       err= bpy_import_module(de->d_name, reload);
+                                       if(BLI_exists(path)) {
+                                               err= bpy_import_module(de->d_name, reload);
+                                       }
                                }
-                       }
 
-                       if(err==-1) {
-                               BPy_errors_to_report(NULL);
-                               fprintf(stderr, "unable to import %s/%s\n", dirname, de->d_name);
+                               if(err==-1) {
+                                       BPy_errors_to_report(NULL);
+                                       fprintf(stderr, "unable to import %s/%s\n", dirname, de->d_name);
+                               }
                        }
-               }
 
-               closedir(dir);
+                       closedir(dir);
+               }
        }
        
        PyList_SetSlice(sys_path, 0, 1, NULL); /* remove the first item */
@@ -822,7 +886,7 @@ float BPY_pydriver_eval (ChannelDriver *driver)
 int BPY_button_eval(bContext *C, char *expr, double *value)
 {
        PyGILState_STATE gilstate;
-       PyObject *dict, *retval;
+       PyObject *dict, *mod, *retval;
        int error_ret = 0;
        
        if (!value || !expr || expr[0]=='\0') return -1;
@@ -830,6 +894,20 @@ int BPY_button_eval(bContext *C, char *expr, double *value)
        bpy_context_set(C, &gilstate);
        
        dict= CreateGlobalDictionary(C);
+       
+       /* import some modules: builtins,math*/
+       PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
+
+       mod = PyImport_ImportModule("math");
+       if (mod) {
+               PyDict_Merge(dict, PyModule_GetDict(mod), 0); /* 0 - dont overwrite existing values */
+               
+               /* Only keep for backwards compat! - just import all math into root, they are standard */
+               PyDict_SetItemString(dict, "math", mod);
+               PyDict_SetItemString(dict, "m", mod);
+               Py_DECREF(mod);
+       } 
+       
        retval = PyRun_String(expr, Py_eval_input, dict, dict);
        
        if (retval == NULL) {
@@ -871,3 +949,65 @@ int BPY_button_eval(bContext *C, char *expr, double *value)
        return error_ret;
 }
 
+
+
+int bpy_context_get(bContext *C, const char *member, bContextDataResult *result)
+{
+       PyObject *pyctx= (PyObject *)CTX_py_dict_get(C);
+       PyObject *item= PyDict_GetItemString(pyctx, member);
+       PointerRNA *ptr= NULL;
+       int done= 0;
+
+       if(item==NULL) {
+               /* pass */
+       }
+       else if(item==Py_None) {
+               /* pass */
+       }
+       else if(BPy_StructRNA_Check(item)) {
+               ptr= &(((BPy_StructRNA *)item)->ptr);
+
+               //result->ptr= ((BPy_StructRNA *)item)->ptr;
+               CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data);
+               done= 1;
+       }
+       else if (PySequence_Check(item)) {
+               PyObject *seq_fast= PySequence_Fast(item, "bpy_context_get sequence conversion");
+               if (seq_fast==NULL) {
+                       PyErr_Print();
+                       PyErr_Clear();
+               }
+               else {
+                       int len= PySequence_Fast_GET_SIZE(seq_fast);
+                       int i;
+                       for(i = 0; i < len; i++) {
+                               PyObject *list_item= PySequence_Fast_GET_ITEM(seq_fast, i);
+
+                               if(BPy_StructRNA_Check(list_item)) {
+                                       /*
+                                       CollectionPointerLink *link= MEM_callocN(sizeof(CollectionPointerLink), "bpy_context_get");
+                                       link->ptr= ((BPy_StructRNA *)item)->ptr;
+                                       BLI_addtail(&result->list, link);
+                                       */
+                                       ptr= &(((BPy_StructRNA *)list_item)->ptr);
+                                       CTX_data_list_add(result, ptr->id.data, ptr->type, ptr->data);
+                               }
+                               else {
+                                       printf("List item not a valid type\n");
+                               }
+
+                       }
+                       Py_DECREF(seq_fast);
+
+                       done= 1;
+               }
+       }
+
+       if(done==0) {
+               if (item)       printf("Context '%s' not found\n", member);
+               else            printf("Context '%s' not a valid type\n", member);
+       }
+
+       return done;
+}
+