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_run.c')
-rw-r--r--source/blender/python/intern/bpy_interface_run.c391
1 files changed, 391 insertions, 0 deletions
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 <stdio.h>
+
+#include <Python.h>
+
+#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("<blender string>");
+
+ 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, "<expr as number>", 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, "<expr as str>", 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, "<expr as intptr>", 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;
+}
+
+/** \} */