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).
This commit is contained in:
Campbell Barton
2020-08-17 17:46:06 +10:00
parent 397cec6a4d
commit 7341ceb674
15 changed files with 503 additions and 399 deletions

View File

@@ -69,7 +69,9 @@
#include "RNA_access.h"
#include "BPY_extern.h"
#ifdef WITH_PYTHON
# include "BPY_extern_run.h"
#endif
#include "ED_numinput.h"
#include "ED_screen.h"
@@ -2812,7 +2814,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
}
/**
* Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number
* Report a generic error prefix when evaluating a string with #BPY_run_string_as_number
* as the Python error on it's own doesn't provide enough context.
*/
#define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details")
@@ -2837,7 +2839,7 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
{
bool ok;
#ifdef WITH_PYTHON
ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
#else
UNUSED_VARS(C);
*r_value = atof(str);

View File

@@ -47,7 +47,10 @@
#include "RNA_access.h"
#include "BPY_extern.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_run.h"
#endif
#include "WM_api.h"
#include "WM_types.h"
@@ -407,7 +410,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
"'%s').label",
idname);
char *expr_result = NULL;
if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
STRNCPY(drawstr, expr_result);
MEM_freeN(expr_result);
}

View File

@@ -63,7 +63,7 @@
#include "BLT_translation.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_run.h"
#endif
#include "ED_screen.h"
@@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
shortcut = BLI_strdup(has_valid_context_error);
}
else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -658,7 +658,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
else if (BPY_execute_string_as_string_and_size(
else if (BPY_run_string_as_string_and_size(
C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
/* pass. */
}
@@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
{
uiTooltipField *field = text_field_add(data,

View File

@@ -42,7 +42,7 @@
#include "script_intern.h" // own include
#ifdef WITH_PYTHON
# include "BPY_extern.h" /* BPY_script_exec */
# include "BPY_extern_run.h"
#endif
static int run_pyfile_exec(bContext *C, wmOperator *op)
@@ -50,7 +50,7 @@ static int run_pyfile_exec(bContext *C, wmOperator *op)
char path[512];
RNA_string_get(op->ptr, "filepath", path);
#ifdef WITH_PYTHON
if (BPY_execute_filepath(C, path, op->reports)) {
if (BPY_run_filepath(C, path, op->reports)) {
ARegion *region = CTX_wm_region(C);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
@@ -120,8 +120,7 @@ static int script_reload_exec(bContext *C, wmOperator *op)
/* TODO, this crashes on netrender and keying sets, need to look into why
* disable for now unless running in debug mode */
WM_cursor_wait(1);
BPY_execute_string(
C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)");
BPY_run_string(C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)");
WM_cursor_wait(0);
WM_event_add_notifier(C, NC_WINDOW, NULL);
return OPERATOR_FINISHED;

View File

@@ -55,6 +55,7 @@
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_run.h"
#endif
#include "text_format.h"
@@ -756,7 +757,7 @@ static int text_run_script(bContext *C, ReportList *reports)
void *curl_prev = text->curl;
int curc_prev = text->curc;
if (BPY_execute_text(C, text, reports, !is_live)) {
if (BPY_run_text(C, text, reports, !is_live)) {
if (is_live) {
/* for nice live updates */
WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);

View File

@@ -38,7 +38,7 @@
#include "WM_types.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_run.h"
#endif
#include "ED_numinput.h"
@@ -294,10 +294,10 @@ bool user_string_to_number(bContext *C,
bUnit_ReplaceString(
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
return BPY_run_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
}
int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value);
int success = BPY_run_string_as_number(C, NULL, str, error_prefix, r_value);
*r_value *= bUnit_PreferredInputUnitScalar(unit, type);
*r_value /= unit_scale;
return success;

View File

@@ -42,7 +42,7 @@ extern "C" {
#include "BKE_report.h"
#include "BKE_text.h"
#include "BPY_extern.h"
#include "BPY_extern_run.h"
#include "bpy_capi_utils.h"
@@ -68,12 +68,12 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
char *fn = const_cast<char *>(filename.c_str());
#if 0
bool ok = BPY_execute_filepath(_context, fn, reports);
bool ok = BPY_run_filepath(_context, fn, reports);
#else
bool ok;
Text *text = BKE_text_load(&_freestyle_bmain, fn, G_MAIN->name);
if (text) {
ok = BPY_execute_text(_context, text, reports, false);
ok = BPY_run_text(_context, text, reports, false);
BKE_id_delete(&_freestyle_bmain, text);
}
else {
@@ -102,7 +102,7 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
if (!BPY_execute_string(_context, NULL, str.c_str())) {
if (!BPY_run_string(_context, NULL, str.c_str())) {
BPy_errors_to_report(reports);
cerr << "\nError executing Python script from PythonInterpreter::interpretString" << endl;
cerr << "Name: " << name << endl;
@@ -122,7 +122,7 @@ class PythonInterpreter : public Interpreter {
BKE_reports_clear(reports);
if (!BPY_execute_text(_context, text, reports, false)) {
if (!BPY_run_text(_context, text, reports, false)) {
cerr << "\nError executing Python script from PythonInterpreter::interpretText" << endl;
cerr << "Name: " << name << endl;
cerr << "Errors: " << endl;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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, "<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_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, "<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_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, "<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;
}
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("<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_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)) {

View File

@@ -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;
}
/** \} */

View File

@@ -66,6 +66,7 @@
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_run.h"
#endif
/* ****************************************************** */
@@ -270,7 +271,7 @@ void WM_keyconfig_reload(bContext *C)
{
if (CTX_py_init_get(C) && !G.background) {
#ifdef WITH_PYTHON
BPY_execute_string(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()");
BPY_run_string(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()");
#endif
}
}

View File

@@ -121,8 +121,8 @@
#include "RE_engine.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_python.h"
# include "BPY_extern_run.h"
#endif
#include "DEG_depsgraph.h"
@@ -580,14 +580,14 @@ static void wm_file_read_post(bContext *C,
if (use_userdef || reset_app_template) {
/* Only run when we have a template path found. */
if (BKE_appdir_app_template_any()) {
BPY_execute_string(
BPY_run_string(
C, (const char *[]){"bl_app_template_utils", NULL}, "bl_app_template_utils.reset()");
reset_all = true;
}
}
if (reset_all) {
/* sync addons, these may have changed from the defaults */
BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
BPY_run_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
}
if (use_data) {
BPY_python_reset(C);
@@ -924,7 +924,7 @@ void wm_homefile_read(bContext *C,
*
* Note that this fits into 'wm_file_read_pre' function but gets messy
* since we need to know if 'reset_app_template' is true. */
BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()");
BPY_run_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()");
}
#endif /* WITH_PYTHON */
}

View File

@@ -61,8 +61,8 @@
# endif
# ifdef WITH_PYTHON
# include "BPY_extern.h"
# include "BPY_extern_python.h"
# include "BPY_extern_run.h"
# endif
# include "RE_engine.h"
@@ -1780,7 +1780,7 @@ static int arg_handle_python_file_run(int argc, const char **argv, void *data)
BLI_path_abs_from_cwd(filename, sizeof(filename));
bool ok;
BPY_CTX_SETUP(ok = BPY_execute_filepath(C, filename, NULL));
BPY_CTX_SETUP(ok = BPY_run_filepath(C, filename, NULL));
if (!ok && app_state.exit_code_on_error.python) {
printf("\nError: script failed, file: '%s', exiting.\n", argv[1]);
BPY_python_end();
@@ -1815,7 +1815,7 @@ static int arg_handle_python_text_run(int argc, const char **argv, void *data)
bool ok;
if (text) {
BPY_CTX_SETUP(ok = BPY_execute_text(C, text, NULL, false));
BPY_CTX_SETUP(ok = BPY_run_text(C, text, NULL, false));
}
else {
printf("\nError: text block not found %s.\n", argv[1]);
@@ -1852,7 +1852,7 @@ static int arg_handle_python_expr_run(int argc, const char **argv, void *data)
/* workaround for scripts not getting a bpy.context.scene, causes internal errors elsewhere */
if (argc > 1) {
bool ok;
BPY_CTX_SETUP(ok = BPY_execute_string_ex(C, NULL, argv[1], false));
BPY_CTX_SETUP(ok = BPY_run_string_ex(C, NULL, argv[1], false));
if (!ok && app_state.exit_code_on_error.python) {
printf("\nError: script failed, expr: '%s', exiting.\n", argv[1]);
BPY_python_end();
@@ -1879,7 +1879,7 @@ static int arg_handle_python_console_run(int UNUSED(argc), const char **argv, vo
# ifdef WITH_PYTHON
bContext *C = data;
BPY_CTX_SETUP(BPY_execute_string(C, (const char *[]){"code", NULL}, "code.interact()"));
BPY_CTX_SETUP(BPY_run_string(C, (const char *[]){"code", NULL}, "code.interact()"));
return 0;
# else
@@ -1952,7 +1952,7 @@ static int arg_handle_addons_set(int argc, const char **argv, void *data)
BLI_snprintf(str, slen, script_str, argv[1]);
BLI_assert(strlen(str) + 1 == slen);
BPY_CTX_SETUP(BPY_execute_string_ex(C, NULL, str, false));
BPY_CTX_SETUP(BPY_run_string_ex(C, NULL, str, false));
free(str);
# else
UNUSED_VARS(argv, data);