Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/intern/bpy_driver.c
| Show All 32 Lines | |||||
| #include <Python.h> | #include <Python.h> | ||||
| #include "DNA_anim_types.h" | #include "DNA_anim_types.h" | ||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_math_base.h" | #include "BLI_math_base.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_dynstr.h" | |||||
| #include "BKE_fcurve.h" | #include "BKE_fcurve.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "bpy_driver.h" | #include "bpy_driver.h" | ||||
| #include "../generic/py_capi_utils.h" | |||||
| extern void BPY_update_rna_module(void); | extern void BPY_update_rna_module(void); | ||||
| /* for pydrivers (drivers using one-line Python expressions to express relationships between targets) */ | /* for pydrivers (drivers using one-line Python expressions to express relationships between targets) */ | ||||
| PyObject *bpy_pydriver_Dict = NULL; | PyObject *bpy_pydriver_Dict = NULL; | ||||
| /* For faster execution we keep a special dictionary for pydrivers, with | /* For faster execution we keep a special dictionary for pydrivers, with | ||||
| * the needed modules and aliases. | * the needed modules and aliases. | ||||
| ▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | |||||
| * | * | ||||
| * (new)note: checking if python is running is not threadsafe [#28114] | * (new)note: checking if python is running is not threadsafe [#28114] | ||||
| * now release the GIL on python operator execution instead, using | * now release the GIL on python operator execution instead, using | ||||
| * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. | * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. | ||||
| */ | */ | ||||
| float BPY_driver_exec(ChannelDriver *driver, const float evaltime) | float BPY_driver_exec(ChannelDriver *driver, const float evaltime) | ||||
| { | { | ||||
| PyObject *driver_vars = NULL; | PyObject *driver_vars = NULL; | ||||
| PyObject *retval = NULL; | |||||
| PyObject *expr_vars; /* speed up by pre-hashing string & avoids re-converting unicode strings for every execution */ | |||||
| PyObject *expr_code; | |||||
| PyGILState_STATE gilstate; | PyGILState_STATE gilstate; | ||||
| bool use_gil; | bool use_gil; | ||||
| DriverVar *dvar; | DriverVar *dvar; | ||||
| double result = 0.0; /* default return */ | double result = 0.0; /* default return */ | ||||
| const char *expr; | const char *expr; | ||||
| short targets_ok = 1; | short targets_ok = 1; | ||||
| int i; | int i; | ||||
| /* get the py expression to be evaluated */ | /* get the py expression to be evaluated */ | ||||
| expr = driver->expression; | expr = driver->expression; | ||||
| if (expr[0] == '\0') | if (expr[0] == '\0') | ||||
| return 0.0f; | return 0.0f; | ||||
| bool use_sandbox = (G.f & G_SCRIPT_AUTOEXEC) == 0; | |||||
| #if 0 | |||||
| if (!(G.f & G_SCRIPT_AUTOEXEC)) { | if (!(G.f & G_SCRIPT_AUTOEXEC)) { | ||||
| if (!(G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET)) { | if (!(G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET)) { | ||||
| G.f |= G_SCRIPT_AUTOEXEC_FAIL; | G.f |= G_SCRIPT_AUTOEXEC_FAIL; | ||||
| BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr); | BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr); | ||||
| printf("skipping driver '%s', automatic scripts are disabled\n", expr); | printf("skipping driver '%s', automatic scripts are disabled\n", expr); | ||||
| } | } | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| #endif | |||||
| use_gil = true; /* !PyC_IsInterpreterActive(); */ | use_gil = true; /* !PyC_IsInterpreterActive(); */ | ||||
| if (use_gil) | if (use_gil) | ||||
| gilstate = PyGILState_Ensure(); | gilstate = PyGILState_Ensure(); | ||||
| /* needed since drivers are updated directly after undo where 'main' is | /* needed since drivers are updated directly after undo where 'main' is | ||||
| * re-allocated [#28807] */ | * re-allocated [#28807] */ | ||||
| BPY_update_rna_module(); | BPY_update_rna_module(); | ||||
| /* init global dictionary for py-driver evaluation settings */ | /* init global dictionary for py-driver evaluation settings */ | ||||
| if (!bpy_pydriver_Dict) { | if (!bpy_pydriver_Dict) { | ||||
| if (bpy_pydriver_create_dict() != 0) { | if (bpy_pydriver_create_dict() != 0) { | ||||
| fprintf(stderr, "PyDriver error: couldn't create Python dictionary\n"); | fprintf(stderr, "PyDriver error: couldn't create Python dictionary\n"); | ||||
| if (use_gil) | if (use_gil) | ||||
| PyGILState_Release(gilstate); | PyGILState_Release(gilstate); | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| /* update global namespace */ | /* update global namespace */ | ||||
| bpy_pydriver_update_dict(evaltime); | bpy_pydriver_update_dict(evaltime); | ||||
| if (driver->expr_comp == NULL) { | |||||
| if (driver->expr_comp == NULL) | |||||
| driver->flag |= DRIVER_FLAG_RECOMPILE; | driver->flag |= DRIVER_FLAG_RECOMPILE; | ||||
| } | |||||
| else { | |||||
| /* if we change security settings, recompile. */ | |||||
| if ((driver->flag & DRIVER_FLAG_RECOMPILE) == 0) { | |||||
| PyObject *expr_code_or_func = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 0); | |||||
| if ((Py_TYPE(expr_code_or_func) == &PyCode_Type) != (use_sandbox == false)) { | |||||
| driver->flag |= DRIVER_FLAG_RECOMPILE; | |||||
| printf("PyDriver info: Security changed, recompiling!\n"); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* These blocks execute the code and assign 'retval' */ | |||||
| PyObject *retval = NULL; | |||||
| if (use_sandbox == false) { | |||||
| /* Python (Full) ------------------------------------------- */ | |||||
| /* speed up by pre-hashing string & avoids re-converting unicode strings for every execution */ | |||||
| PyObject *expr_vars; | |||||
| PyObject *expr_code; | |||||
| /* --- Compile --- */ | |||||
| /* compile the expression first if it hasn't been compiled or needs to be rebuilt */ | |||||
| if (driver->flag & DRIVER_FLAG_RECOMPILE) { | if (driver->flag & DRIVER_FLAG_RECOMPILE) { | ||||
| Py_XDECREF(driver->expr_comp); | Py_XDECREF(driver->expr_comp); | ||||
| driver->expr_comp = PyTuple_New(2); | driver->expr_comp = PyTuple_New(2); | ||||
| expr_code = Py_CompileString(expr, "<bpy driver>", Py_eval_input); | expr_code = Py_CompileString(expr, "<bpy driver>", Py_eval_input); | ||||
| PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 0, expr_code); | |||||
| driver->flag &= ~DRIVER_FLAG_RECOMPILE; | driver->flag &= ~DRIVER_FLAG_RECOMPILE; | ||||
| driver->flag |= DRIVER_FLAG_RENAMEVAR; /* maybe this can be removed but for now best keep until were sure */ | driver->flag |= DRIVER_FLAG_RENAMEVAR; /* maybe this can be removed but for now best keep until were sure */ | ||||
| } | } | ||||
| else { | else { | ||||
| expr_code = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 0); | expr_code = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 0); | ||||
| } | } | ||||
| if (driver->flag & DRIVER_FLAG_RENAMEVAR) { | if (driver->flag & DRIVER_FLAG_RENAMEVAR) { | ||||
| /* may not be set */ | /* may not be set */ | ||||
| expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1); | expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1); | ||||
| Py_XDECREF(expr_vars); | Py_XDECREF(expr_vars); | ||||
| expr_vars = PyTuple_New(BLI_listbase_count(&driver->variables)); | expr_vars = PyTuple_New(BLI_listbase_count(&driver->variables)); | ||||
| PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 1, expr_vars); | PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 1, expr_vars); | ||||
| for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { | for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { | ||||
| PyTuple_SET_ITEM(expr_vars, i++, PyUnicode_FromString(dvar->name)); | PyTuple_SET_ITEM(expr_vars, i++, PyUnicode_FromString(dvar->name)); | ||||
| } | } | ||||
| driver->flag &= ~DRIVER_FLAG_RENAMEVAR; | driver->flag &= ~DRIVER_FLAG_RENAMEVAR; | ||||
| } | } | ||||
| else { | else { | ||||
| expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1); | expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1); | ||||
| } | } | ||||
| /* add target values to a dict that will be used as '__locals__' dict */ | /* add target values to a dict that will be used as '__locals__' dict */ | ||||
| driver_vars = PyDict_New(); | driver_vars = PyDict_New(); | ||||
| for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { | for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { | ||||
| PyObject *driver_arg = NULL; | PyObject *driver_arg = NULL; | ||||
| float tval = 0.0f; | float tval = 0.0f; | ||||
| /* try to get variable value */ | /* try to get variable value */ | ||||
| tval = driver_get_variable_value(driver, dvar); | tval = driver_get_variable_value(driver, dvar); | ||||
| driver_arg = PyFloat_FromDouble((double)tval); | driver_arg = PyFloat_FromDouble((double)tval); | ||||
| /* try to add to dictionary */ | /* try to add to dictionary */ | ||||
| /* if (PyDict_SetItemString(driver_vars, dvar->name, driver_arg)) { */ | /* if (PyDict_SetItemString(driver_vars, dvar->name, driver_arg)) { */ | ||||
| if (PyDict_SetItem(driver_vars, PyTuple_GET_ITEM(expr_vars, i++), driver_arg) < 0) { | if (PyDict_SetItem(driver_vars, PyTuple_GET_ITEM(expr_vars, i++), driver_arg) < 0) { | ||||
| /* this target failed - bad name */ | /* this target failed - bad name */ | ||||
| if (targets_ok) { | if (targets_ok) { | ||||
| /* first one - print some extra info for easier identification */ | /* first one - print some extra info for easier identification */ | ||||
| fprintf(stderr, "\nBPY_driver_eval() - Error while evaluating PyDriver:\n"); | fprintf(stderr, "\nBPY_driver_eval() - Error while evaluating PyDriver:\n"); | ||||
| targets_ok = 0; | targets_ok = 0; | ||||
| } | } | ||||
| fprintf(stderr, "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n", dvar->name); | fprintf(stderr, "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n", dvar->name); | ||||
| // BPy_errors_to_report(NULL); // TODO - reports | // BPy_errors_to_report(NULL); // TODO - reports | ||||
| PyErr_Print(); | PyErr_Print(); | ||||
| PyErr_Clear(); | PyErr_Clear(); | ||||
| } | } | ||||
| } | } | ||||
| /* --- Execute --- */ | |||||
| #if 0 /* slow, with this can avoid all Py_CompileString above. */ | #if 0 /* slow, with this can avoid all Py_CompileString above. */ | ||||
| /* execute expression to get a value */ | /* execute expression to get a value */ | ||||
| retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars); | retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars); | ||||
| #else | #else | ||||
| /* evaluate the compiled expression */ | /* evaluate the compiled expression */ | ||||
| if (expr_code) | if (expr_code) | ||||
| retval = PyEval_EvalCode((void *)expr_code, bpy_pydriver_Dict, driver_vars); | retval = PyEval_EvalCode((void *)expr_code, bpy_pydriver_Dict, driver_vars); | ||||
| #endif | #endif | ||||
| /* decref the driver vars first... */ | /* decref the driver vars first... */ | ||||
| Py_DECREF(driver_vars); | Py_DECREF(driver_vars); | ||||
| } | |||||
| else { | |||||
| /* Python (Sandbox) ---------------------------------------- */ | |||||
| PyObject *expr_func; /* assign either one of these based on 'use_sandbox' */ | |||||
| /* var rename only triggers recompile for restricted expression | |||||
| * (needed because we use a function call) */ | |||||
| if (driver->flag & DRIVER_FLAG_RENAMEVAR) { | |||||
| driver->flag |= DRIVER_FLAG_RECOMPILE; | |||||
| driver->flag &= ~DRIVER_FLAG_RENAMEVAR; | |||||
| } | |||||
| /* --- Compile --- */ | |||||
| if (driver->flag & DRIVER_FLAG_RECOMPILE) { | |||||
| /* only need single item for sandbox, use tuple only to keep to convention */ | |||||
| driver->expr_comp = PyTuple_New(1); | |||||
| char *expr_as_fn; | |||||
| { | |||||
| DynStr *dstr = BLI_dynstr_new(); | |||||
| /* imports */ | |||||
| BLI_dynstr_append(dstr, "import numba\n"); | |||||
| BLI_dynstr_append(dstr, "from math import *\n"); | |||||
| /* declare function */ | |||||
| BLI_dynstr_append(dstr, "@numba.jit(nopython=True)\n"); | |||||
| BLI_dynstr_append(dstr, "def fn("); | |||||
| for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) { | |||||
| BLI_dynstr_append(dstr, dvar->name); | |||||
| BLI_dynstr_append(dstr, ", "); | |||||
| } | |||||
| BLI_dynstr_append(dstr, "):\n"); | |||||
| /* function body */ | |||||
| BLI_dynstr_append(dstr, " return "); | |||||
| BLI_dynstr_append(dstr, expr); | |||||
| expr_as_fn = PyMem_MALLOC(BLI_dynstr_get_len(dstr) + 1); | |||||
| BLI_dynstr_get_cstring_ex(dstr, expr_as_fn); | |||||
| BLI_dynstr_free(dstr); | |||||
| } | |||||
| PyObject *expr_as_fn_code = Py_CompileString( | |||||
| expr_as_fn, | |||||
| "<bpy driver jit>", Py_file_input); | |||||
| PyMem_FREE(expr_as_fn); | |||||
| if (expr_as_fn_code == NULL) { | |||||
| fprintf(stderr, "PyDriver error: jit error creating function:\n%s\n", expr_as_fn); | |||||
| PyErr_Print(); | |||||
| PyErr_Clear(); | |||||
| Py_DECREF(driver->expr_comp); | |||||
| driver->expr_comp = NULL; | |||||
| if (use_gil) | |||||
| PyGILState_Release(gilstate); | |||||
| return 0.0f; | |||||
| } | |||||
| PyObject *py_namespace = PyC_DefaultNameSpace("<dummy jit>"); | |||||
| PyObject *py_result = PyEval_EvalCode((void *)expr_as_fn_code, py_namespace, py_namespace); | |||||
| if (py_result == NULL) { | |||||
| fprintf(stderr, "PyDriver error: jit error evaluating function:\n%s\n", expr_as_fn); | |||||
| PyErr_Print(); | |||||
| PyErr_Clear(); | |||||
| Py_DECREF(driver->expr_comp); | |||||
| driver->expr_comp = NULL; | |||||
| Py_DECREF(py_namespace); | |||||
| if (use_gil) | |||||
| PyGILState_Release(gilstate); | |||||
| return 0.0f; | |||||
| } | |||||
| else { | |||||
| Py_DECREF(py_result); | |||||
| } | |||||
| expr_func = PyDict_GetItemString(py_namespace, "fn"); | |||||
| Py_DECREF(py_namespace); | |||||
| driver->flag &= ~DRIVER_FLAG_RECOMPILE; | |||||
| } | |||||
| else { | |||||
| expr_func = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 0); | |||||
| } | |||||
| /* --- Execute --- */ | |||||
| { | |||||
| PyObject *args = PyTuple_New(BLI_listbase_count(&driver->variables)); | |||||
| for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next, i++) { | |||||
| float tval = driver_get_variable_value(driver, dvar); | |||||
| PyObject *driver_arg = PyFloat_FromDouble((double)tval); | |||||
| PyTuple_SET_ITEM(args, i, driver_arg); | |||||
| } | |||||
| if (expr_func) | |||||
| retval = PyObject_Call(expr_func, args, NULL); | |||||
| Py_DECREF(args); | |||||
| } | |||||
| } | |||||
| /* process the result */ | /* process the result */ | ||||
| if (retval == NULL) { | if (retval == NULL) { | ||||
| pydriver_error(driver); | pydriver_error(driver); | ||||
| } | } | ||||
| else if ((result = PyFloat_AsDouble(retval)) == -1.0 && PyErr_Occurred()) { | else if ((result = PyFloat_AsDouble(retval)) == -1.0 && PyErr_Occurred()) { | ||||
| pydriver_error(driver); | pydriver_error(driver); | ||||
| Py_DECREF(retval); | Py_DECREF(retval); | ||||
| Show All 19 Lines | |||||