diff options
Diffstat (limited to 'source/blender/python/intern/bpy_interface.c')
-rw-r--r-- | source/blender/python/intern/bpy_interface.c | 447 |
1 files changed, 348 insertions, 99 deletions
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 8b5ad36f349..3e28bc7968f 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -1,26 +1,54 @@ - +/** + * $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 "compile.h" /* for the PyCodeObject */ #include "eval.h" /* for PyEval_EvalCode */ -#include "bpy_compat.h" - #include "bpy_rna.h" #include "bpy_operator.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" @@ -28,6 +56,8 @@ #include "MEM_guardedalloc.h" #include "BLI_util.h" +#include "BLI_storage.h" +#include "BLI_fileops.h" #include "BLI_string.h" #include "BKE_context.h" @@ -37,13 +67,87 @@ #include "BPY_extern.h" #include "../generic/bpy_internal_import.h" // our own imports -/* external util modukes */ +/* external util modules */ #include "../generic/Mathutils.h" #include "../generic/Geometry.h" #include "../generic/BGL.h" +/* for internal use, when starting and ending python scripts */ + +/* incase a python script triggers another python call, stop bpy_context_clear from invalidating */ +static int py_call_level= 0; + + +// only for tests +#define TIME_PY_RUN + +#ifdef TIME_PY_RUN +#include "PIL_time.h" +static int bpy_timer_count = 0; +static double bpy_timer; /* time since python starts */ +static double bpy_timer_run; /* time for each python script run */ +static double bpy_timer_run_tot; /* accumulate python runs */ +#endif + +void bpy_context_set(bContext *C, PyGILState_STATE *gilstate) +{ + py_call_level++; + + if(gilstate) + *gilstate = PyGILState_Ensure(); + + if(py_call_level==1) { + + BPY_update_modules(); /* can give really bad results if this isnt here */ + + if(C) { // XXX - should always be true. + BPy_SetContext(C); + bpy_import_main_set(CTX_data_main(C)); + } + else { + fprintf(stderr, "ERROR: Python context called with a NULL Context. this should not happen!\n"); + } + +#ifdef TIME_PY_RUN + if(bpy_timer_count==0) { + /* record time from the beginning */ + bpy_timer= PIL_check_seconds_timer(); + bpy_timer_run = bpy_timer_run_tot = 0.0; + } + bpy_timer_run= PIL_check_seconds_timer(); + + + bpy_timer_count++; +#endif + } +} + +void bpy_context_clear(bContext *C, PyGILState_STATE *gilstate) +{ + py_call_level--; + + if(gilstate) + PyGILState_Release(*gilstate); + + if(py_call_level < 0) { + fprintf(stderr, "ERROR: Python context internal state bug. this should not happen!\n"); + } + else if(py_call_level==0) { + // XXX - Calling classes currently wont store the context :\, cant set NULL because of this. but this is very flakey still. + //BPy_SetContext(NULL); + //bpy_import_main_set(NULL); + +#ifdef TIME_PY_RUN + bpy_timer_run_tot += PIL_check_seconds_timer() - bpy_timer_run; + bpy_timer_count++; +#endif + + } +} + + void BPY_free_compiled_text( struct Text *text ) { if( text->compiled ) { @@ -65,7 +169,7 @@ static void bpy_init_modules( void ) /* 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, "__ops__", BPY_operator_module() ); /* ops is now a python module that does the conversion from SOME_OT_foo -> some.foo */ PyModule_AddObject( mod, "ui", BPY_ui_module() ); // XXX very experimental, consider this a test, especially PyCObject is not meant to be permanent /* add the module so we can import it */ @@ -74,23 +178,22 @@ static void bpy_init_modules( void ) /* stand alone utility modules not related to blender directly */ - Geometry_Init("Geometry"); - Mathutils_Init("Mathutils"); - BGL_Init("BGL"); -} - -#if (PY_VERSION_HEX < 0x02050000) -PyObject *PyImport_ImportModuleLevel(char *name, void *a, void *b, void *c, int d) -{ - return PyImport_ImportModule(name); + Geometry_Init(); + Mathutils_Init(); + BGL_Init(); } -#endif void BPY_update_modules( void ) { +#if 0 // slow, this runs all the time poll, draw etc 100's of time a sec. PyObject *mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject( mod, "data", BPY_rna_module() ); - PyModule_AddObject( mod, "types", BPY_rna_types() ); + PyModule_AddObject( mod, "types", BPY_rna_types() ); // atm this does not need updating +#endif + + /* refreshes the main struct */ + BPY_update_rna_module(); + } /***************************************************************************** @@ -105,9 +208,6 @@ static PyObject *CreateGlobalDictionary( bContext *C ) PyDict_SetItemString( dict, "__name__", item ); Py_DECREF(item); - // XXX - evil, need to access context - BPy_SetContext(C); - // XXX - put somewhere more logical { PyMethodDef *ml; @@ -132,13 +232,51 @@ static PyObject *CreateGlobalDictionary( bContext *C ) return dict; } +/* must be called before Py_Initialize */ +void BPY_start_python_path(void) +{ + char *py_path_bundle= BLI_gethome_folder("python", BLI_GETHOME_ALL); + + if(py_path_bundle==NULL) + return; + + /* 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); + } +} + + void BPY_start_python( int argc, char **argv ) { PyThreadState *py_tstate = NULL; + BPY_start_python_path(); /* allow to use our own included python */ + Py_Initialize( ); - //PySys_SetArgv( argc_copy, argv_copy ); + // PySys_SetArgv( argc, argv); // broken in py3, not a huge deal + /* sigh, why do python guys not have a char** version anymore? :( */ + { + int i; + PyObject *py_argv= PyList_New(argc); + + for (i=0; i<argc; i++) + PyList_SET_ITEM(py_argv, i, PyUnicode_FromString(argv[i])); + + PySys_SetObject("argv", py_argv); + Py_DECREF(py_argv); + } /* Initialize thread support (also acquires lock) */ PyEval_InitThreads(); @@ -154,40 +292,64 @@ 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(); + py_tstate = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(py_tstate); } void BPY_end_python( void ) { + // fprintf(stderr, "Ending Python!\n"); + PyGILState_Ensure(); /* finalizing, no need to grab the state */ // free other python data. - //BPY_rna_free_types(); + pyrna_free_types(); + + /* clear all python data from structs */ Py_Finalize( ); - return; +#ifdef TIME_PY_RUN + // measure time since py started + bpy_timer = PIL_check_seconds_timer() - bpy_timer; + + printf("*bpy stats* - "); + printf("tot exec: %d, ", bpy_timer_count); + printf("tot run: %.4fsec, ", bpy_timer_run_tot); + if(bpy_timer_count>0) + printf("average run: %.6fsec, ", (bpy_timer_run_tot/bpy_timer_count)); + + if(bpy_timer>0.0) + printf("tot usage %.4f%%", (bpy_timer_run_tot/bpy_timer)*100.0); + + printf("\n"); + + // fprintf(stderr, "Ending Python Done!\n"); + +#endif + } /* Can run a file or text block */ int BPY_run_python_script( bContext *C, const char *fn, struct Text *text, struct ReportList *reports) { - PyObject *py_dict, *py_result; + PyObject *py_dict, *py_result= NULL; PyGILState_STATE gilstate; if (fn==NULL && text==NULL) { return 0; } - //BPY_start_python(); - - gilstate = PyGILState_Ensure(); - - BPY_update_modules(); /* can give really bad results if this isnt here */ - bpy_import_main_set(CTX_data_main(C)); + bpy_context_set(C, &gilstate); py_dict = CreateGlobalDictionary(C); @@ -202,19 +364,31 @@ int BPY_run_python_script( bContext *C, const char *fn, struct Text *text, struc MEM_freeN( buf ); if( PyErr_Occurred( ) ) { - BPy_errors_to_report(reports); BPY_free_compiled_text( text ); - PyGILState_Release(gilstate); - return 0; } } - py_result = PyEval_EvalCode( text->compiled, py_dict, py_dict ); + if(text->compiled) + py_result = PyEval_EvalCode( text->compiled, py_dict, py_dict ); } else { - char pystring[512]; - /* TODO - look into a better way to run a file */ - sprintf(pystring, "exec(open(r'%s').read())", fn); - py_result = PyRun_String( pystring, Py_file_input, py_dict, py_dict ); +#if 0 + char *pystring; + pystring= malloc(strlen(fn) + 32); + pystring[0]= '\0'; + sprintf(pystring, "exec(open(r'%s').read())", fn); + py_result = PyRun_String( pystring, Py_file_input, py_dict, py_dict ); + free(pystring); +#else + FILE *fp= fopen(fn, "r"); + if(fp) { + py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict); + fclose(fp); + } + else { + PyErr_Format(PyExc_SystemError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno)); + py_result= NULL; + } +#endif } if (!py_result) { @@ -224,10 +398,9 @@ int BPY_run_python_script( bContext *C, const char *fn, struct Text *text, struc } Py_DECREF(py_dict); - PyGILState_Release(gilstate); - bpy_import_main_set(NULL); - //BPY_end_python(); + bpy_context_clear(C, &gilstate); + return py_result ? 1:0; } @@ -283,9 +456,9 @@ static int bpy_run_script_init(bContext *C, SpaceScript * sc) return 1; } -int BPY_run_script_space_draw(struct bContext *C, SpaceScript * sc) +int BPY_run_script_space_draw(const struct bContext *C, SpaceScript * sc) { - if (bpy_run_script_init(C, sc)) { + if (bpy_run_script_init( (bContext *)C, sc)) { PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *result = PyObject_CallObject( sc->script->py_draw, NULL ); @@ -373,6 +546,26 @@ int BPY_run_python_script_space(const char *modulename, const char *func) #include "PIL_time.h" #endif +/* for use by BPY_run_ui_scripts only */ +static int bpy_import_module(char *modname, int reload) +{ + PyObject *mod= PyImport_ImportModuleLevel(modname, 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 */ + return 0; + } else { + return -1; + } +} + /* XXX this is temporary, need a proper script registration system for 2.5 */ void BPY_run_ui_scripts(bContext *C, int reload) { @@ -384,81 +577,84 @@ void BPY_run_ui_scripts(bContext *C, int reload) char *file_extension; char *dirname; char path[FILE_MAX]; - char *dirs[] = {"io", "ui", NULL}; - int a, filelen; /* filename length */ + char *dirs[] = {"ui", "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 *mod; - PyObject *sys_path_orig; - PyObject *sys_path_new; + PyObject *sys_path; - gilstate = PyGILState_Ensure(); - - // XXX - evil, need to access context - BPy_SetContext(C); - bpy_import_main_set(CTX_data_main(C)); + bpy_context_set(C, &gilstate); - for(a=0; dirs[a]; a++) { - dirname= BLI_gethome_folder(dirs[a]); + sys_path= PySys_GetObject("path"); /* borrow */ + PyList_Insert(sys_path, 0, Py_None); /* place holder, resizes the list */ - if(!dirname) - continue; + /* 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]); - dir = opendir(dirname); + if(!dirname) + continue; - if(!dir) - continue; + dir = opendir(dirname); - /* 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); - - 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(!dir) + continue; - 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 */ + /* set the first dir in the sys.path for fast importing of modules */ + PyList_SetItem(sys_path, 0, PyUnicode_FromString(dirname)); /* steals the ref */ - 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 */ + 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); } } - - 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); +#ifndef __linux__ + 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); +#endif + /* support packages */ + BLI_join_dirfile(path, path, "__init__.py"); + + 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); + } } - } - - closedir(dir); - PySys_SetObject("path", sys_path_orig); - Py_DECREF(sys_path_orig); + closedir(dir); + } } - bpy_import_main_set(NULL); + PyList_SetSlice(sys_path, 0, 1, NULL); /* remove the first item */ + + bpy_context_clear(C, &gilstate); - PyGILState_Release(gilstate); #ifdef TIME_REGISTRATION printf("script time %f\n", (PIL_check_seconds_timer()-time)); #endif + + /* reset the timer so as not to take loading into the stats */ + bpy_timer_count = 0; } /* ****************************************** */ @@ -656,3 +852,56 @@ float BPY_pydriver_eval (ChannelDriver *driver) return result; } + +int BPY_button_eval(bContext *C, char *expr, double *value) +{ + PyGILState_STATE gilstate; + PyObject *dict, *retval; + int error_ret = 0; + + if (!value || !expr || expr[0]=='\0') return -1; + + bpy_context_set(C, &gilstate); + + dict= CreateGlobalDictionary(C); + retval = PyRun_String(expr, Py_eval_input, dict, dict); + + if (retval == NULL) { + error_ret= -1; + } + else { + double val; + + if(PyTuple_Check(retval)) { + /* Users my have typed in 10km, 2m + * add up all values */ + int i; + val= 0.0; + + for(i=0; i<PyTuple_GET_SIZE(retval); i++) { + val+= PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i)); + } + } + else { + val = PyFloat_AsDouble(retval); + } + Py_DECREF(retval); + + if(val==-1 && PyErr_Occurred()) { + error_ret= -1; + } + else { + *value= val; + } + } + + if(error_ret) { + BPy_errors_to_report(CTX_wm_reports(C)); + } + + Py_DECREF(dict); + bpy_context_clear(C, &gilstate); + + return error_ret; +} + |