From dc8dd24351462e73c5d0260564aad9cd56fd6c33 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 18 Apr 2019 14:45:23 +0200 Subject: PyAPI: remove support for importing text blocks as modules Allowing direct import of text blocks isn't especially useful, instead add `text.as_module()` script authors can do this explicitly if it's needed. Now the text "Register" option executes instead of loading as a module. This removes the need to keep track of the current Main, and C code to override Python's import & reload. --- source/blender/python/generic/CMakeLists.txt | 2 - .../blender/python/generic/bpy_internal_import.c | 375 --------------------- .../blender/python/generic/bpy_internal_import.h | 50 --- source/blender/python/intern/bpy_interface.c | 32 +- source/blender/python/intern/bpy_operator.c | 4 - source/blender/python/intern/stubs.c | 1 - 6 files changed, 9 insertions(+), 455 deletions(-) delete mode 100644 source/blender/python/generic/bpy_internal_import.c delete mode 100644 source/blender/python/generic/bpy_internal_import.h (limited to 'source/blender/python') diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 3b4182bc795..fa73f161a5d 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -33,7 +33,6 @@ set(INC_SYS set(SRC bgl.c blf_py_api.c - bpy_internal_import.c bpy_threads.c idprop_py_api.c imbuf_py_api.c @@ -41,7 +40,6 @@ set(SRC bgl.h blf_py_api.h - bpy_internal_import.h idprop_py_api.h imbuf_py_api.h py_capi_utils.h diff --git a/source/blender/python/generic/bpy_internal_import.c b/source/blender/python/generic/bpy_internal_import.c deleted file mode 100644 index 2df828d89e0..00000000000 --- a/source/blender/python/generic/bpy_internal_import.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * 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 pygen - * - * This file defines replacements for pythons '__import__' and 'imp.reload' - * functions which can import from blender textblocks. - * - * \note - * This should eventually be replaced by import hooks (pep 302). - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_text_types.h" - -#include "BLI_listbase.h" -#include "BLI_path_util.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "BKE_main.h" -/* UNUSED */ -#include "BKE_text.h" /* txt_to_buf */ - -#include "py_capi_utils.h" - -#include "bpy_internal_import.h" /* own include */ - -static Main *bpy_import_main = NULL; -static ListBase bpy_import_main_list; - -static PyMethodDef bpy_import_meth; -static PyMethodDef bpy_reload_meth; -static PyObject *imp_reload_orig = NULL; - -/* 'builtins' is most likely PyEval_GetBuiltins() */ - -/** - * \note to the discerning developer, yes - this is nasty - * monkey-patching our own import into Python's builtin 'imp' module. - * - * However Python's alternative is to use import hooks, - * which are implemented in a way that we can't use our own importer as a - * fall-back (instead we must try and fail - raise an exception every time). - * Since importing from blenders text-blocks is not the common case - * I prefer to use Pythons import by default and fall-back to - * Blenders - which we can only do by intercepting import calls I'm afraid. - * - Campbell - */ -void bpy_import_init(PyObject *builtins) -{ - PyObject *item; - PyObject *mod; - - PyDict_SetItemString(builtins, "__import__", item = PyCFunction_New(&bpy_import_meth, NULL)); - Py_DECREF(item); - - /* move reload here - * XXX, use import hooks */ - mod = PyImport_ImportModuleLevel("importlib", NULL, NULL, NULL, 0); - if (mod) { - PyObject *mod_dict = PyModule_GetDict(mod); - - /* blender owns the function */ - imp_reload_orig = PyDict_GetItemString(mod_dict, "reload"); - Py_INCREF(imp_reload_orig); - - PyDict_SetItemString(mod_dict, "reload", item = PyCFunction_New(&bpy_reload_meth, NULL)); - Py_DECREF(item); - Py_DECREF(mod); - } - else { - BLI_assert(!"unable to load 'importlib' module."); - } -} - -static void free_compiled_text(Text *text) -{ - if (text->compiled) { - Py_DECREF((PyObject *)text->compiled); - } - text->compiled = NULL; -} - -struct Main *bpy_import_main_get(void) -{ - return bpy_import_main; -} - -void bpy_import_main_set(struct Main *maggie) -{ - bpy_import_main = maggie; -} - -/* returns a dummy filename for a textblock so we can tell what file a text block comes from */ -void bpy_text_filename_get(char *fn, size_t fn_len, Text *text) -{ - BLI_snprintf( - fn, fn_len, "%s%c%s", ID_BLEND_PATH(bpy_import_main, &text->id), SEP, text->id.name + 2); -} - -bool bpy_text_compile(Text *text) -{ - char fn_dummy[FILE_MAX]; - PyObject *fn_dummy_py; - char *buf; - - bpy_text_filename_get(fn_dummy, sizeof(fn_dummy), text); - - /* if previously compiled, free the object */ - free_compiled_text(text); - - fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); - - buf = txt_to_buf(text); - text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); - MEM_freeN(buf); - - Py_DECREF(fn_dummy_py); - - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - PySys_SetObject("last_traceback", NULL); - free_compiled_text(text); - return false; - } - else { - return true; - } -} - -PyObject *bpy_text_import(Text *text) -{ - char modulename[MAX_ID_NAME + 2]; - int len; - - if (!text->compiled) { - if (bpy_text_compile(text) == false) { - return NULL; - } - } - - len = strlen(text->id.name + 2); - BLI_strncpy(modulename, text->id.name + 2, len); - modulename[len - 3] = '\0'; /* remove .py */ - return PyImport_ExecCodeModule(modulename, text->compiled); -} - -PyObject *bpy_text_import_name(const char *name, int *found) -{ - Text *text; - char txtname[MAX_ID_NAME - 2]; - int namelen = strlen(name); - //XXX Main *maggie = bpy_import_main ? bpy_import_main : G_MAIN; - Main *maggie = bpy_import_main; - - *found = 0; - - if (!maggie) { - printf("ERROR: bpy_import_main_set() was not called before running python. this is a bug.\n"); - return NULL; - } - - /* we know this cant be importable, the name is too long for blender! */ - if (namelen >= (MAX_ID_NAME - 2) - 3) { - return NULL; - } - - memcpy(txtname, name, namelen); - memcpy(&txtname[namelen], ".py", 4); - - text = BLI_findstring(&maggie->texts, txtname, offsetof(ID, name) + 2); - - if (text) { - *found = 1; - return bpy_text_import(text); - } - - /* If we still haven't found the module try additional modules form bpy_import_main_list */ - maggie = bpy_import_main_list.first; - while (maggie && !text) { - text = BLI_findstring(&maggie->texts, txtname, offsetof(ID, name) + 2); - maggie = maggie->next; - } - - if (!text) { - return NULL; - } - else { - *found = 1; - } - - return bpy_text_import(text); -} - -/* - * find in-memory module and recompile - */ - -PyObject *bpy_text_reimport(PyObject *module, int *found) -{ - Text *text; - const char *name; - const char *filepath; - //XXX Main *maggie = bpy_import_main ? bpy_import_main : G_MAIN; - Main *maggie = bpy_import_main; - - if (!maggie) { - printf("ERROR: bpy_import_main_set() was not called before running python. this is a bug.\n"); - return NULL; - } - - *found = 0; - - /* get name, filename from the module itself */ - if ((name = PyModule_GetName(module)) == NULL) { - return NULL; - } - - { - PyObject *module_file = PyModule_GetFilenameObject(module); - if (module_file == NULL) { - return NULL; - } - filepath = _PyUnicode_AsString(module_file); - Py_DECREF(module_file); - if (filepath == NULL) { - return NULL; - } - } - - /* look up the text object */ - text = BLI_findstring(&maggie->texts, BLI_path_basename(filepath), offsetof(ID, name) + 2); - - /* uh-oh.... didn't find it */ - if (!text) { - return NULL; - } - else { - *found = 1; - } - - if (bpy_text_compile(text) == false) { - return NULL; - } - - /* make into a module */ - return PyImport_ExecCodeModule(name, text->compiled); -} - -static PyObject *blender_import(PyObject *UNUSED(self), PyObject *args, PyObject *kw) -{ - PyObject *exception, *err, *tb; - const char *name; - int found = 0; - PyObject *globals = NULL, *locals = NULL, *fromlist = NULL; - int level = 0; /* relative imports */ - PyObject *newmodule; - - static const char *_keywords[] = {"name", "globals", "locals", "fromlist", "level", NULL}; - static _PyArg_Parser _parser = {"s|OOOi:bpy_import_meth", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kw, &_parser, &name, &globals, &locals, &fromlist, &level)) { - return NULL; - } - - /* import existing builtin modules or modules that have been imported already */ - newmodule = PyImport_ImportModuleLevel(name, globals, locals, fromlist, level); - - if (newmodule) { - return newmodule; - } - - PyErr_Fetch(&exception, - &err, - &tb); /* get the python error in case we cant import as blender text either */ - - /* importing from existing modules failed, see if we have this module as blender text */ - newmodule = bpy_text_import_name(name, &found); - - if (newmodule) { /* found module as blender text, ignore above exception */ - PyErr_Clear(); - Py_XDECREF(exception); - Py_XDECREF(err); - Py_XDECREF(tb); - /* printf("imported from text buffer...\n"); */ - } - else if (found == - 1) { /* blender text module failed to execute but was found, use its error message */ - Py_XDECREF(exception); - Py_XDECREF(err); - Py_XDECREF(tb); - return NULL; - } - else { - /* no blender text was found that could import the module - * reuse the original error from PyImport_ImportModuleEx */ - PyErr_Restore(exception, err, tb); - } - return newmodule; -} - -/* - * our reload() module, to handle reloading in-memory scripts - */ - -static PyObject *blender_reload(PyObject *UNUSED(self), PyObject *module) -{ - PyObject *exception, *err, *tb; - PyObject *newmodule = NULL; - int found = 0; - - /* try reimporting from file */ - - /* in Py3.3 this just calls imp.reload() which we overwrite, causing recursive calls */ - //newmodule = PyImport_ReloadModule(module); - - newmodule = PyObject_CallFunctionObjArgs(imp_reload_orig, module, NULL); - - if (newmodule) { - return newmodule; - } - - /* no file, try importing from memory */ - PyErr_Fetch(&exception, &err, &tb); /*restore for probable later use */ - - newmodule = bpy_text_reimport(module, &found); - if (newmodule) { /* found module as blender text, ignore above exception */ - PyErr_Clear(); - Py_XDECREF(exception); - Py_XDECREF(err); - Py_XDECREF(tb); - /* printf("imported from text buffer...\n"); */ - } - else if (found == - 1) { /* blender text module failed to execute but was found, use its error message */ - Py_XDECREF(exception); - Py_XDECREF(err); - Py_XDECREF(tb); - return NULL; - } - else { - /* no blender text was found that could import the module - * reuse the original error from PyImport_ImportModuleEx */ - PyErr_Restore(exception, err, tb); - } - - return newmodule; -} - -static PyMethodDef bpy_import_meth = {"bpy_import_meth", - (PyCFunction)blender_import, - METH_VARARGS | METH_KEYWORDS, - "blenders import"}; -static PyMethodDef bpy_reload_meth = { - "bpy_reload_meth", (PyCFunction)blender_reload, METH_O, "blenders reload"}; diff --git a/source/blender/python/generic/bpy_internal_import.h b/source/blender/python/generic/bpy_internal_import.h deleted file mode 100644 index 2ab535d1018..00000000000 --- a/source/blender/python/generic/bpy_internal_import.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 pygen - */ - -/* Note, the BGE needs to use this too, keep it minimal */ - -#ifndef __BPY_INTERNAL_IMPORT_H__ -#define __BPY_INTERNAL_IMPORT_H__ - -/* python redefines :/ */ -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif - -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif - -struct Text; - -void bpy_import_init(PyObject *builtins); - -bool bpy_text_compile(struct Text *text); -PyObject *bpy_text_import(struct Text *text); -PyObject *bpy_text_import_name(const char *name, int *found); -PyObject *bpy_text_reimport(PyObject *module, int *found); -/* void bpy_text_clear_modules(int clear_all);*/ /* Clear user modules */ - -void bpy_text_filename_get(char *fn, size_t fn_len, struct Text *text); - -struct Main *bpy_import_main_get(void); -void bpy_import_main_set(struct Main *maggie); - -#endif /* __BPY_INTERNAL_IMPORT_H__ */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 774ce2b9422..90dfdcd33b7 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -63,7 +63,6 @@ #include "BPY_extern.h" -#include "../generic/bpy_internal_import.h" /* our own imports */ #include "../generic/py_capi_utils.h" /* inittab initialization functions */ @@ -105,7 +104,6 @@ void BPY_context_update(bContext *C) } BPy_SetContext(C); - bpy_import_main_set(CTX_data_main(C)); BPY_modules_update(C); /* can give really bad results if this isn't here */ } @@ -150,7 +148,6 @@ void bpy_context_clear(bContext *UNUSED(C), PyGILState_STATE *gilstate) * cant set NULL because of this. but this is very flakey still. */ #if 0 BPy_SetContext(NULL); - bpy_import_main_set(NULL); #endif #ifdef TIME_PY_RUN @@ -329,8 +326,6 @@ void BPY_python_start(int argc, const char **argv) /* bpy.* and lets us import it */ BPy_init_modules(); - bpy_import_init(PyEval_GetBuiltins()); - pyrna_alloc_types(); #ifndef WITH_PYTHON_MODULE @@ -427,6 +422,12 @@ 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) { @@ -447,7 +448,7 @@ static bool python_script_exec( if (text) { char fn_dummy[FILE_MAXDIR]; - bpy_text_filename_get(fn_dummy, sizeof(fn_dummy), text); + 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; @@ -696,8 +697,6 @@ bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, PyObject *main_mod = NULL; PyObject *py_dict, *retval; bool ok = true; - Main * - bmain_back; /* XXX, quick fix for release (Copy Settings crash), needs further investigation */ if (expr[0] == '\0') { return ok; @@ -709,9 +708,6 @@ bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, py_dict = PyC_DefaultNameSpace(""); - bmain_back = bpy_import_main_get(); - bpy_import_main_set(CTX_data_main(C)); - if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) { Py_DECREF(py_dict); retval = NULL; @@ -720,8 +716,6 @@ bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, retval = PyRun_String(expr, use_eval ? Py_eval_input : Py_file_input, py_dict, py_dict); } - bpy_import_main_set(bmain_back); - if (retval == NULL) { ok = false; BPy_errors_to_report(CTX_wm_reports(C)); @@ -774,17 +768,9 @@ void BPY_modules_load_user(bContext *C) } } else { - PyObject *module = bpy_text_import(text); - - if (module == NULL) { - PyErr_Print(); - PyErr_Clear(); - } - else { - Py_DECREF(module); - } + BPY_execute_text(C, text, NULL, false); - /* check if the script loaded a new file */ + /* Check if the script loaded a new file. */ if (bmain != CTX_data_main(C)) { break; } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 2c31fcda12d..0f66a9cbeb9 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -37,7 +37,6 @@ #include "bpy_operator_wrap.h" #include "bpy_rna.h" /* for setting arg props only - pyrna_py_to_prop() */ #include "bpy_capi_utils.h" -#include "../generic/bpy_internal_import.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" @@ -320,9 +319,6 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) * function corrects bpy.data (internal Main pointer) */ BPY_modules_update(C); - /* needed for when WM_OT_read_factory_settings us called from within a script */ - bpy_import_main_set(CTX_data_main(C)); - /* return operator_ret as a bpy enum */ return pyrna_enum_bitfield_to_py(rna_enum_operator_return_items, operator_ret); } diff --git a/source/blender/python/intern/stubs.c b/source/blender/python/intern/stubs.c index bf068f99722..448751b1155 100644 --- a/source/blender/python/intern/stubs.c +++ b/source/blender/python/intern/stubs.c @@ -33,7 +33,6 @@ /* python, will come back */ //void BPY_script_exec(void) {} //void BPY_python_start(void) {} -//void BPY_text_free_code(void) {} void BPY_pyconstraint_exec(struct bPythonConstraint *con, struct bConstraintOb *cob, struct ListBase *targets) -- cgit v1.2.3