From 7341ceb674b2fc5c01d4328f398516ef8f358ae5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 17 Aug 2020 17:46:06 +1000 Subject: Cleanup: move Python script execution into BPY_extern_run.h This commit renames 'execute' to 'run' because: - This follows Python's "PyRun" which these functions wrap. - Execution functions can use either exec/eval modes, making naming awkward (for future API refactoring). --- source/blender/python/BPY_extern.h | 34 -- source/blender/python/BPY_extern_run.h | 68 ++++ source/blender/python/intern/CMakeLists.txt | 1 + source/blender/python/intern/bpy_interface.c | 332 +------------------ source/blender/python/intern/bpy_interface_run.c | 391 +++++++++++++++++++++++ 5 files changed, 462 insertions(+), 364 deletions(-) create mode 100644 source/blender/python/BPY_extern_run.h create mode 100644 source/blender/python/intern/bpy_interface_run.c (limited to 'source/blender/python') diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 3fc4df270d5..e1e0d01055a 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -67,40 +67,6 @@ void BPY_thread_restore(BPy_ThreadStatePtr tstate); } \ (void)0 -bool BPY_execute_filepath(struct bContext *C, const char *filepath, struct ReportList *reports); -bool BPY_execute_text(struct bContext *C, - struct Text *text, - struct ReportList *reports, - const bool do_jump); - -bool BPY_execute_string_as_number(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - double *r_value); -bool BPY_execute_string_as_intptr(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - intptr_t *r_value); -bool BPY_execute_string_as_string_and_size(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value, - size_t *r_value_size); -bool BPY_execute_string_as_string(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value); - -bool BPY_execute_string_ex(struct bContext *C, - const char *imports[], - const char *expr, - bool use_eval); -bool BPY_execute_string(struct bContext *C, const char *imports[], const char *expr); - void BPY_text_free_code(struct Text *text); void BPY_modules_update( struct bContext *C); // XXX - annoying, need this for pointers that get out of date diff --git a/source/blender/python/BPY_extern_run.h b/source/blender/python/BPY_extern_run.h new file mode 100644 index 00000000000..67ac56f2798 --- /dev/null +++ b/source/blender/python/BPY_extern_run.h @@ -0,0 +1,68 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup python + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_sys_types.h" + +struct ReportList; +struct Text; +struct bContext; + +/* bpy_interface_run.c */ +bool BPY_run_filepath(struct bContext *C, const char *filepath, struct ReportList *reports); +bool BPY_run_text(struct bContext *C, + struct Text *text, + struct ReportList *reports, + const bool do_jump); + +bool BPY_run_string_as_number(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value); +bool BPY_run_string_as_intptr(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value); +bool BPY_run_string_as_string_and_size(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value, + size_t *r_value_size); +bool BPY_run_string_as_string(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value); + +bool BPY_run_string_ex(struct bContext *C, const char *imports[], const char *expr, bool use_eval); + +bool BPY_run_string(struct bContext *C, const char *imports[], const char *expr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 769618005af..44949c478cc 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC bpy_gizmo_wrap.c bpy_interface.c bpy_interface_atexit.c + bpy_interface_run.c bpy_intern_string.c bpy_library_load.c bpy_library_write.c diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 81e1905bed7..3941d25e9f7 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -64,6 +64,7 @@ #include "BPY_extern.h" #include "BPY_extern_python.h" +#include "BPY_extern_run.h" #include "../generic/py_capi_utils.h" @@ -428,18 +429,6 @@ void BPY_python_use_system_env(void) py_use_system_env = true; } -static void python_script_error_jump_text(struct Text *text) -{ - int lineno; - int offset; - python_script_error_jump(text->id.name + 2, &lineno, &offset); - if (lineno != -1) { - /* select the line with the error */ - txt_move_to(text, lineno - 1, INT_MAX, false); - txt_move_to(text, lineno - 1, offset, true); - } -} - void BPY_python_backtrace(FILE *fp) { fputs("\n# Python backtrace\n", fp); @@ -467,152 +456,6 @@ typedef struct { } PyModuleObject; #endif -/* returns a dummy filename for a textblock so we can tell what file a text block comes from */ -static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text) -{ - BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2); -} - -static bool python_script_exec( - bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump) -{ - Main *bmain_old = CTX_data_main(C); - PyObject *main_mod = NULL; - PyObject *py_dict = NULL, *py_result = NULL; - PyGILState_STATE gilstate; - - BLI_assert(fn || text); - - if (fn == NULL && text == NULL) { - return 0; - } - - bpy_context_set(C, &gilstate); - - PyC_MainModule_Backup(&main_mod); - - if (text) { - char fn_dummy[FILE_MAXDIR]; - bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text); - - if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */ - char *buf; - PyObject *fn_dummy_py; - - fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); - - buf = txt_to_buf(text, NULL); - text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); - MEM_freeN(buf); - - Py_DECREF(fn_dummy_py); - - if (PyErr_Occurred()) { - if (do_jump) { - python_script_error_jump_text(text); - } - BPY_text_free_code(text); - } - } - - if (text->compiled) { - py_dict = PyC_DefaultNameSpace(fn_dummy); - py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict); - } - } - else { - FILE *fp = BLI_fopen(fn, "r"); - - if (fp) { - py_dict = PyC_DefaultNameSpace(fn); - -#ifdef _WIN32 - /* Previously we used PyRun_File to run directly the code on a FILE - * object, but as written in the Python/C API Ref Manual, chapter 2, - * 'FILE structs for different C libraries can be different and - * incompatible'. - * So now we load the script file data to a buffer. - * - * Note on use of 'globals()', it's important not copy the dictionary because - * tools may inspect 'sys.modules["__main__"]' for variables defined in the code - * where using a copy of 'globals()' causes code execution - * to leave the main namespace untouched. see: T51444 - * - * This leaves us with the problem of variables being included, - * currently this is worked around using 'dict.__del__' it's ugly but works. - */ - { - const char *pystring = - "with open(__file__, 'rb') as f:" - "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())"; - - fclose(fp); - - py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict); - } -#else - py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict); - fclose(fp); -#endif - } - else { - PyErr_Format( - PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno)); - py_result = NULL; - } - } - - if (!py_result) { - if (text) { - if (do_jump) { - /* ensure text is valid before use, the script may have freed its self */ - Main *bmain_new = CTX_data_main(C); - if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) { - python_script_error_jump_text(text); - } - } - } - BPy_errors_to_report(reports); - } - else { - Py_DECREF(py_result); - } - - if (py_dict) { -#ifdef PYMODULE_CLEAR_WORKAROUND - PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(), - bpy_intern_str___main__); - PyObject *dict_back = mmod->md_dict; - /* freeing the module will clear the namespace, - * gives problems running classes defined in this namespace being used later. */ - mmod->md_dict = NULL; - Py_DECREF(dict_back); -#endif - -#undef PYMODULE_CLEAR_WORKAROUND - } - - PyC_MainModule_Restore(main_mod); - - bpy_context_clear(C, &gilstate); - - return (py_result != NULL); -} - -/* Can run a file or text block */ -bool BPY_execute_filepath(bContext *C, const char *filepath, struct ReportList *reports) -{ - return python_script_exec(C, filepath, NULL, reports, false); -} - -bool BPY_execute_text(bContext *C, - struct Text *text, - struct ReportList *reports, - const bool do_jump) -{ - return python_script_exec(C, NULL, text, reports, do_jump); -} - void BPY_DECREF(void *pyob_ptr) { PyGILState_STATE gilstate = PyGILState_Ensure(); @@ -631,177 +474,6 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) PyGILState_Release(gilstate); } -/** - * \return success - */ -bool BPY_execute_string_as_number(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - double *r_value) -{ - PyGILState_STATE gilstate; - bool ok = true; - - if (!r_value || !expr) { - return -1; - } - - if (expr[0] == '\0') { - *r_value = 0.0; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsNumber(imports, expr, "", r_value); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -/** - * \return success - */ -bool BPY_execute_string_as_string_and_size(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value, - size_t *r_value_size) -{ - BLI_assert(r_value && expr); - PyGILState_STATE gilstate; - bool ok = true; - - if (expr[0] == '\0') { - *r_value = NULL; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsStringAndSize(imports, expr, "", r_value, r_value_size); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string_as_string(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value) -{ - size_t value_dummy_size; - return BPY_execute_string_as_string_and_size( - C, imports, expr, report_prefix, r_value, &value_dummy_size); -} - -/** - * Support both int and pointers. - * - * \return success - */ -bool BPY_execute_string_as_intptr(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - intptr_t *r_value) -{ - BLI_assert(r_value && expr); - PyGILState_STATE gilstate; - bool ok = true; - - if (expr[0] == '\0') { - *r_value = 0; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsIntPtr(imports, expr, "", r_value); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, bool use_eval) -{ - BLI_assert(expr); - PyGILState_STATE gilstate; - PyObject *main_mod = NULL; - PyObject *py_dict, *retval; - bool ok = true; - - if (expr[0] == '\0') { - return ok; - } - - bpy_context_set(C, &gilstate); - - PyC_MainModule_Backup(&main_mod); - - py_dict = PyC_DefaultNameSpace(""); - - if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) { - Py_DECREF(py_dict); - retval = NULL; - } - else { - retval = PyRun_String(expr, use_eval ? Py_eval_input : Py_file_input, py_dict, py_dict); - } - - if (retval == NULL) { - ok = false; - BPy_errors_to_report(CTX_wm_reports(C)); - } - else { - Py_DECREF(retval); - } - - PyC_MainModule_Restore(main_mod); - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string(bContext *C, const char *imports[], const char *expr) -{ - return BPY_execute_string_ex(C, imports, expr, true); -} - void BPY_modules_load_user(bContext *C) { PyGILState_STATE gilstate; @@ -834,7 +506,7 @@ void BPY_modules_load_user(bContext *C) } } else { - BPY_execute_text(C, text, NULL, false); + BPY_run_text(C, text, NULL, false); /* Check if the script loaded a new file. */ if (bmain != CTX_data_main(C)) { diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c new file mode 100644 index 00000000000..f936be2edbf --- /dev/null +++ b/source/blender/python/intern/bpy_interface_run.c @@ -0,0 +1,391 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup pythonintern + */ + +#include + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_text.h" + +#include "DNA_text_types.h" + +#include "BPY_extern.h" +#include "BPY_extern_run.h" + +#include "bpy_capi_utils.h" +#include "bpy_traceback.h" + +#include "../generic/py_capi_utils.h" + +/* -------------------------------------------------------------------- */ +/** \name Private Utilities + * \{ */ + +static void python_script_error_jump_text(Text *text) +{ + int lineno; + int offset; + python_script_error_jump(text->id.name + 2, &lineno, &offset); + if (lineno != -1) { + /* select the line with the error */ + txt_move_to(text, lineno - 1, INT_MAX, false); + txt_move_to(text, lineno - 1, offset, true); + } +} + +/* returns a dummy filename for a textblock so we can tell what file a text block comes from */ +static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text) +{ + BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2); +} + +static bool python_script_exec( + bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump) +{ + Main *bmain_old = CTX_data_main(C); + PyObject *main_mod = NULL; + PyObject *py_dict = NULL, *py_result = NULL; + PyGILState_STATE gilstate; + + BLI_assert(fn || text); + + if (fn == NULL && text == NULL) { + return 0; + } + + bpy_context_set(C, &gilstate); + + PyC_MainModule_Backup(&main_mod); + + if (text) { + char fn_dummy[FILE_MAXDIR]; + bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text); + + if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */ + char *buf; + PyObject *fn_dummy_py; + + fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); + + buf = txt_to_buf(text, NULL); + text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); + MEM_freeN(buf); + + Py_DECREF(fn_dummy_py); + + if (PyErr_Occurred()) { + if (do_jump) { + python_script_error_jump_text(text); + } + BPY_text_free_code(text); + } + } + + if (text->compiled) { + py_dict = PyC_DefaultNameSpace(fn_dummy); + py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict); + } + } + else { + FILE *fp = BLI_fopen(fn, "r"); + + if (fp) { + py_dict = PyC_DefaultNameSpace(fn); + +#ifdef _WIN32 + /* Previously we used PyRun_File to run directly the code on a FILE + * object, but as written in the Python/C API Ref Manual, chapter 2, + * 'FILE structs for different C libraries can be different and + * incompatible'. + * So now we load the script file data to a buffer. + * + * Note on use of 'globals()', it's important not copy the dictionary because + * tools may inspect 'sys.modules["__main__"]' for variables defined in the code + * where using a copy of 'globals()' causes code execution + * to leave the main namespace untouched. see: T51444 + * + * This leaves us with the problem of variables being included, + * currently this is worked around using 'dict.__del__' it's ugly but works. + */ + { + const char *pystring = + "with open(__file__, 'rb') as f:" + "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())"; + + fclose(fp); + + py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict); + } +#else + py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict); + fclose(fp); +#endif + } + else { + PyErr_Format( + PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno)); + py_result = NULL; + } + } + + if (!py_result) { + if (text) { + if (do_jump) { + /* ensure text is valid before use, the script may have freed its self */ + Main *bmain_new = CTX_data_main(C); + if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) { + python_script_error_jump_text(text); + } + } + } + BPy_errors_to_report(reports); + } + else { + Py_DECREF(py_result); + } + + if (py_dict) { +#ifdef PYMODULE_CLEAR_WORKAROUND + PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(), + bpy_intern_str___main__); + PyObject *dict_back = mmod->md_dict; + /* freeing the module will clear the namespace, + * gives problems running classes defined in this namespace being used later. */ + mmod->md_dict = NULL; + Py_DECREF(dict_back); +#endif + +#undef PYMODULE_CLEAR_WORKAROUND + } + + PyC_MainModule_Restore(main_mod); + + bpy_context_clear(C, &gilstate); + + return (py_result != NULL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Run Text / Filename / String + * \{ */ + +/* Can run a file or text block */ +bool BPY_run_filepath(bContext *C, const char *filepath, struct ReportList *reports) +{ + return python_script_exec(C, filepath, NULL, reports, false); +} + +bool BPY_run_text(bContext *C, struct Text *text, struct ReportList *reports, const bool do_jump) +{ + return python_script_exec(C, NULL, text, reports, do_jump); +} + +bool BPY_run_string_ex(bContext *C, const char *imports[], const char *expr, bool use_eval) +{ + BLI_assert(expr); + PyGILState_STATE gilstate; + PyObject *main_mod = NULL; + PyObject *py_dict, *retval; + bool ok = true; + + if (expr[0] == '\0') { + return ok; + } + + bpy_context_set(C, &gilstate); + + PyC_MainModule_Backup(&main_mod); + + py_dict = PyC_DefaultNameSpace(""); + + if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) { + Py_DECREF(py_dict); + retval = NULL; + } + else { + retval = PyRun_String(expr, use_eval ? Py_eval_input : Py_file_input, py_dict, py_dict); + } + + if (retval == NULL) { + ok = false; + BPy_errors_to_report(CTX_wm_reports(C)); + } + else { + Py_DECREF(retval); + } + + PyC_MainModule_Restore(main_mod); + + bpy_context_clear(C, &gilstate); + + return ok; +} + +bool BPY_run_string(bContext *C, const char *imports[], const char *expr) +{ + return BPY_run_string_ex(C, imports, expr, true); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Run Python & Evaluate Utilities + * + * Return values as plain C types, useful to run Python scripts + * in code that doesn't deal with Python data-types. + * \{ */ + +/** + * \return success + */ +bool BPY_run_string_as_number(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value) +{ + PyGILState_STATE gilstate; + bool ok = true; + + if (!r_value || !expr) { + return -1; + } + + if (expr[0] == '\0') { + *r_value = 0.0; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsNumber(imports, expr, "", r_value); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +/** + * \return success + */ +bool BPY_run_string_as_string_and_size(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value, + size_t *r_value_size) +{ + BLI_assert(r_value && expr); + PyGILState_STATE gilstate; + bool ok = true; + + if (expr[0] == '\0') { + *r_value = NULL; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsStringAndSize(imports, expr, "", r_value, r_value_size); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +bool BPY_run_string_as_string(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value) +{ + size_t value_dummy_size; + return BPY_run_string_as_string_and_size( + C, imports, expr, report_prefix, r_value, &value_dummy_size); +} + +/** + * Support both int and pointers. + * + * \return success + */ +bool BPY_run_string_as_intptr(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value) +{ + BLI_assert(r_value && expr); + PyGILState_STATE gilstate; + bool ok = true; + + if (expr[0] == '\0') { + *r_value = 0; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsIntPtr(imports, expr, "", r_value); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +/** \} */ -- cgit v1.2.3