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:
Diffstat (limited to 'source/blender/python/intern/bpy_interface.c')
-rw-r--r--source/blender/python/intern/bpy_interface.c447
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;
+}
+