Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/intern/bpy_driver.c
| Show All 27 Lines | |||||
| * to deal with the namespace used for driver execution. | * to deal with the namespace used for driver execution. | ||||
| */ | */ | ||||
| /* ****************************************** */ | /* ****************************************** */ | ||||
| /* Drivers - PyExpression Evaluation */ | /* Drivers - PyExpression Evaluation */ | ||||
| #include <Python.h> | #include <Python.h> | ||||
| #include "CLG_log.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 "BKE_fcurve.h" | #include "BKE_fcurve.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| Show All 10 Lines | |||||
| #define USE_RNA_AS_PYOBJECT | #define USE_RNA_AS_PYOBJECT | ||||
| #define USE_BYTECODE_WHITELIST | #define USE_BYTECODE_WHITELIST | ||||
| #ifdef USE_BYTECODE_WHITELIST | #ifdef USE_BYTECODE_WHITELIST | ||||
| # include <opcode.h> | # include <opcode.h> | ||||
| #endif | #endif | ||||
| static CLG_LogRef LOG = {"bpy.driver"}; | |||||
| /* 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; | ||||
| #ifdef USE_BYTECODE_WHITELIST | #ifdef USE_BYTECODE_WHITELIST | ||||
| static PyObject *bpy_pydriver_Dict__whitelist = NULL; | static PyObject *bpy_pydriver_Dict__whitelist = NULL; | ||||
| #endif | #endif | ||||
| /* For faster execution we keep a special dictionary for pydrivers, with | /* For faster execution we keep a special dictionary for pydrivers, with | ||||
| ▲ Show 20 Lines • Show All 167 Lines • ▼ Show 20 Lines | #endif | ||||
| return; | return; | ||||
| } | } | ||||
| /* error return function for BPY_eval_pydriver */ | /* error return function for BPY_eval_pydriver */ | ||||
| static void pydriver_error(ChannelDriver *driver) | static void pydriver_error(ChannelDriver *driver) | ||||
| { | { | ||||
| driver->flag |= DRIVER_FLAG_INVALID; /* py expression failed */ | driver->flag |= DRIVER_FLAG_INVALID; /* py expression failed */ | ||||
| fprintf(stderr, "\nError in Driver: The following Python expression failed:\n\t'%s'\n\n", driver->expression); | CLOG_ERROR(&LOG, "The following Python expression failed:\n\t'%s'", driver->expression); | ||||
| // BPy_errors_to_report(NULL); // TODO - reports | // BPy_errors_to_report(NULL); // TODO - reports | ||||
| PyErr_Print(); | PyErr_Print(); | ||||
| PyErr_Clear(); | PyErr_Clear(); | ||||
| } | } | ||||
| #ifdef USE_BYTECODE_WHITELIST | #ifdef USE_BYTECODE_WHITELIST | ||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | for (int i = 0; i < PyTuple_GET_SIZE(py_code->co_names); i++) { | ||||
| for (int j = 0; dict_arr[j]; j++) { | for (int j = 0; dict_arr[j]; j++) { | ||||
| if (PyDict_Contains(dict_arr[j], name)) { | if (PyDict_Contains(dict_arr[j], name)) { | ||||
| contains_name = true; | contains_name = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (contains_name == false) { | if (contains_name == false) { | ||||
| fprintf(stderr, "\tBPY_driver_eval() - restructed access disallows name '%s', " | CLOG_ERROR(&LOG, "restructed access disallows name '%s', " | ||||
| "enable auto-execution to support\n", _PyUnicode_AsString(name)); | "enable auto-execution to support", _PyUnicode_AsString(name)); | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* Check opcodes. */ | /* Check opcodes. */ | ||||
| { | { | ||||
| const _Py_CODEUNIT *codestr; | const _Py_CODEUNIT *codestr; | ||||
| Py_ssize_t code_len; | Py_ssize_t code_len; | ||||
| PyBytes_AsStringAndSize(py_code->co_code, (char **)&codestr, &code_len); | PyBytes_AsStringAndSize(py_code->co_code, (char **)&codestr, &code_len); | ||||
| code_len /= sizeof(*codestr); | code_len /= sizeof(*codestr); | ||||
| for (Py_ssize_t i = 0; i < code_len; i++) { | for (Py_ssize_t i = 0; i < code_len; i++) { | ||||
| const int opcode = _Py_OPCODE(codestr[i]); | const int opcode = _Py_OPCODE(codestr[i]); | ||||
| if (secure_opcodes[opcode] == 0) { | if (secure_opcodes[opcode] == 0) { | ||||
| fprintf(stderr, "\tBPY_driver_eval() - restructed access disallows opcode '%d', " | CLOG_ERROR(&LOG, "restructed access disallows opcode '%d', " | ||||
| "enable auto-execution to support\n", opcode); | "enable auto-execution to support", opcode); | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| #undef CODESIZE | #undef CODESIZE | ||||
| } | } | ||||
| return true; | return true; | ||||
| Show All 39 Lines | if (expr[0] == '\0') | ||||
| return 0.0f; | return 0.0f; | ||||
| #ifndef USE_BYTECODE_WHITELIST | #ifndef USE_BYTECODE_WHITELIST | ||||
| 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); | CLOG_WARN(&LOG, "skipping driver '%s', automatic scripts are disabled", expr); | ||||
| } | } | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| #else | #else | ||||
| bool is_recompile = false; | bool is_recompile = false; | ||||
| #endif | #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"); | CLOG_ERROR(&LOG, "couldn't create Python dictionary"); | ||||
| 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_namespace_update_frame(evaltime); | bpy_pydriver_namespace_update_frame(evaltime); | ||||
| ▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | #endif | ||||
| /* 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) != -1) { | if (PyDict_SetItem(driver_vars, PyTuple_GET_ITEM(expr_vars, i++), driver_arg) != -1) { | ||||
| Py_DECREF(driver_arg); | Py_DECREF(driver_arg); | ||||
| } | } | ||||
| else { | else { | ||||
| /* 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"); | CLOG_ERROR(&LOG, "Error while evaluating PyDriver:"); | ||||
| targets_ok = 0; | targets_ok = 0; | ||||
| } | } | ||||
| fprintf(stderr, "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n", dvar->name); | CLOG_ERROR(&LOG, "couldn't add variable '%s' to namespace", dvar->name); | ||||
| // BPy_errors_to_report(NULL); // TODO - reports | // BPy_errors_to_report(NULL); // TODO - reports | ||||
| // TODO: access pyerr data and send through CLOG? | |||||
| PyErr_Print(); | PyErr_Print(); | ||||
| PyErr_Clear(); | PyErr_Clear(); | ||||
| } | } | ||||
| } | } | ||||
| #ifdef USE_BYTECODE_WHITELIST | #ifdef USE_BYTECODE_WHITELIST | ||||
| if (is_recompile && expr_code) { | if (is_recompile && expr_code) { | ||||
| if (!(G.f & G_SCRIPT_AUTOEXEC)) { | if (!(G.f & G_SCRIPT_AUTOEXEC)) { | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | #endif | ||||
| if (use_gil) | if (use_gil) | ||||
| PyGILState_Release(gilstate); | PyGILState_Release(gilstate); | ||||
| if (isfinite(result)) { | if (isfinite(result)) { | ||||
| return (float)result; | return (float)result; | ||||
| } | } | ||||
| else { | else { | ||||
| fprintf(stderr, "\tBPY_driver_eval() - driver '%s' evaluates to '%f'\n", driver->expression, result); | CLOG_ERROR(&LOG, "driver '%s' evaluates to '%f'", driver->expression, result); | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| } | } | ||||