// See PythonHost.h: the interpreter setup 40250 got from a real python27 installation. #include "ScriptLib/StdAfx.h" #include "PythonHost.h" #include #include namespace { // The pending exception as text, so a failed bootstrap says what went wrong instead of leaving the // error for the first unrelated script to trip over. std::string take_error() { if (!PyErr_Occurred()) return "no exception"; PyObject *type = NULL, *value = NULL, *tb = NULL; PyErr_Fetch(&type, &value, &tb); PyErr_NormalizeException(&type, &value, &tb); std::string text; if (PyObject* str = value ? PyObject_Str(value) : NULL) { if (PyString_Check(str)) text = PyString_AS_STRING(str); Py_DECREF(str); } if (type && text.empty()) text = ((PyTypeObject*) type)->tp_name; Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(tb); return text.empty() ? "unknown error" : text; } bool fail(std::string* error, const std::string& text) { if (error) *error = text; return false; } } namespace PythonHost { void Configure(const char* program_name) { // site.py lives in a prefix this interpreter does not have, the environment must not be able to // redirect a game's script layer (PYTHONPATH/PYTHONHOME), the "could not find platform libraries" // probe has nothing to find, and the pack directories are read-only on the mobile targets. Py_NoSiteFlag = 1; Py_IgnoreEnvironmentFlag = 1; Py_FrozenFlag = 1; Py_DontWriteBytecodeFlag = 1; Py_SetProgramName((char*) program_name); } bool InstallStdLib(const char* stdlib_path, std::string* error) { if (!Py_IsInitialized()) return fail(error, "interpreter is not initialized"); if (!stdlib_path || !*stdlib_path) return fail(error, "no stdlib path (set MT_PYTHON_STDLIB or pass python27.zip)"); // zipimport and the source importer both go through the C library, so the path has to be a real // file: a pack entry or an Android asset must be staged out first (step 3c). if (FILE* fp = fopen(stdlib_path, "rb")) fclose(fp); else if (FILE* dir = fopen((std::string(stdlib_path) + "/os.py").c_str(), "rb")) fclose(dir); else return fail(error, std::string("cannot open ") + stdlib_path); PySys_SetPath((char*) stdlib_path); // Py_Initialize ran _PyCodecRegistry_Init against the then-empty path and 2.7 never retries it // ("no codec search functions registered" for the rest of the process), so the search function is // registered again now that encodings is importable. PyObject* encodings = PyImport_ImportModule("encodings"); if (!encodings) return fail(error, "import encodings: " + take_error()); PyObject* search = PyObject_GetAttrString(encodings, (char*) "search_function"); Py_DECREF(encodings); if (!search) return fail(error, "encodings.search_function: " + take_error()); PyObject* codecs = PyImport_ImportModule("codecs"); PyObject* registered = codecs ? PyObject_CallMethod(codecs, (char*) "register", (char*) "O", search) : NULL; Py_DECREF(search); Py_XDECREF(codecs); if (!registered) return fail(error, "codecs.register: " + take_error()); Py_DECREF(registered); return true; } namespace { std::string g_staged_path; } void SetDefaultStdLibPath(const char* stdlib_path) { g_staged_path = stdlib_path ? stdlib_path : ""; } std::string DefaultStdLibPath() { // The environment still wins, so a developer can point a build at another stdlib without rebuilding. if (const char* env = getenv("MT_PYTHON_STDLIB")) return env; // Whatever staged the zip for this platform (extension/src/python_stdlib.cpp inside Godot; nothing // in the native tests, which pass the build tree's copy on the command line). return g_staged_path; } }