From 05b4deda4f1eb3da4186116d15be79d2486ffb3b Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 1 Jun 2020 19:55:15 -0400 Subject: [PATCH] Bump to Python 3.8. This is required due to reports from multiple testers that the client exits on loading StartUp. The issue is that Python 3 loads modules during its initialization--before we have installed our PEP 451 import machinery for python.pak. The previous code worked for us because we have Python 3 installed on our systems, which is not a reasonable assumption to have for our players. Python 3.8 adds APIs that allow us to initialize just the "core" and use the API, then initialize the interpreter itself once we have added the import machinery. --- .github/workflows/linux-ci.yml | 2 +- .../Apps/plPythonPack/PythonInterface.cpp | 147 ++++------- .../Apps/plPythonPack/PythonInterface.h | 9 +- Sources/Plasma/Apps/plPythonPack/main.cpp | 25 +- .../FeatureLib/pfPython/cyPythonInterface.cpp | 244 ++++++++---------- .../FeatureLib/pfPython/cyPythonInterface.h | 1 - .../FeatureLib/pfPython/cyPythonModule451.cpp | 36 ++- Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp | 2 +- 8 files changed, 200 insertions(+), 266 deletions(-) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index ff879542f3..b508fe7d54 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8] + python-version: [3.8] steps: - uses: actions/checkout@v2 diff --git a/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp b/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp index a00802630d..bb726ebb2c 100644 --- a/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp +++ b/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp @@ -43,7 +43,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include +#include #include +#include + + #include "plFileSystem.h" #include @@ -52,73 +56,50 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com static PyObject* stdOut; // python object of the stdout file static PyObject* stdErr; // python object of the stderr file -void PythonInterface::initPython(const plFileName& rootDir, FILE* outstream, FILE* errstream) +static inline void IAddWideString(PyWideStringList& list, const plFileName& filename) { - // if haven't been initialized then do it - if (Py_IsInitialized() == 0) - { - // initialize the Python stuff - // let Python do some intialization... - Py_SetProgramName(L"plasma"); - Py_NoSiteFlag = 1; - Py_NoUserSiteDirectory = 1; - Py_IgnoreEnvironmentFlag = 1; - Py_Initialize(); - - // if we need the builtins then find the builtin module - PyObject* sysmod = PyImport_ImportModule("sys"); - // then add the builtin dictionary to our module's dictionary - if (sysmod != NULL) - { - // get the sys's dictionary to find the stdout and stderr - PyObject* sys_dict = PyModule_GetDict(sysmod); - if (sys_dict != nullptr && stdOut != nullptr) { - PyDict_SetItemString(sys_dict, "stdout", stdOut); - } - if (sys_dict != nullptr && stdErr != nullptr) { - PyDict_SetItemString(sys_dict, "stderr", stdErr); - } - // NOTE: we will reset the path to not include paths - // ...that Python may have found in the registry - PyObject* path_list = PyList_New(0); - ST::printf(outstream, "Setting up include dirs:\n"); - - ST::printf(outstream, "{}\n", rootDir); - PyObject* more_path = PyUnicode_FromString(rootDir.AsString().c_str()); - PyList_Append(path_list, more_path); - - // make sure that our plasma libraries are gotten before the system ones - plFileName temp = plFileName::Join(rootDir, "plasma"); - ST::printf(outstream, "{}\n", temp); - PyObject* more_path3 = PyUnicode_FromString(temp.AsString().c_str()); - PyList_Append(path_list, more_path3); - - temp = plFileName::Join(rootDir, "system"); - ST::printf(outstream, "{}\n\n", temp); - PyObject* more_path2 = PyUnicode_FromString(temp.AsString().c_str()); - PyList_Append(path_list, more_path2); - - // set the path to be this one - PyDict_SetItemString(sys_dict, "path", path_list); - - Py_DECREF(sysmod); - } - } + PyWideStringList_Append(&list, filename.AsString().to_wchar().data()); } -void PythonInterface::addPythonPath(const plFileName& path, FILE* outstream) +void PythonInterface::initPython(const plFileName& rootDir, const std::vector& extraDirs, + FILE* outstream, FILE* errstream) { - PyObject* sysmod = PyImport_ImportModule("sys"); - if (sysmod != NULL) - { - PyObject* sys_dict = PyModule_GetDict(sysmod); - PyObject* path_list = PyDict_GetItemString(sys_dict, "path"); + // if haven't been initialized then do it + if (Py_IsInitialized() == 0) { + PyPreConfig preConfig; + PyPreConfig_InitIsolatedConfig(&preConfig); + PyStatus status = Py_PreInitialize(&preConfig); + if (PyStatus_Exception(status)) { + ST::printf(stderr, "Python {} pre-init failed: {}", PY_VERSION, status.err_msg); + return; + } + + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + config.optimization_level = 2; + config.write_bytecode = 0; + config.user_site_directory = 0; + config.program_name = L"plasma"; + + // Explicit module search paths so no build-env specific stuff gets in. + IAddWideString(config.module_search_paths, rootDir); + IAddWideString(config.module_search_paths, plFileName::Join(rootDir, "plasma")); + IAddWideString(config.module_search_paths, plFileName::Join(rootDir, "system")); + for (const auto& dir : extraDirs) + IAddWideString(config.module_search_paths, plFileName::Join(rootDir, dir)); + config.module_search_paths_set = 1; - ST::printf(outstream, "Adding path {}\n", path); - PyObject* more_path = PyUnicode_FromString(path.AsString().c_str()); - PyList_Append(path_list, more_path); + // initialize the Python stuff + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + ST::printf(stderr, "Python {} init failed: {}", PY_VERSION, status.err_msg); + return; + } - Py_DECREF(sysmod); + if (stdOut) + PySys_SetObject("stdout", stdOut); + if (stdErr) + PySys_SetObject("stderr", stdErr); } } @@ -152,7 +133,7 @@ PyObject* PythonInterface::CompileString(const char *command, const plFileName& // // PURPOSE : marshals an object into a char string // -bool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, int32_t* size) +bool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Py_ssize_t* size) { PyObject *s; // the python string object where the marsalled object wil go // convert object to a marshalled string python object @@ -162,52 +143,22 @@ bool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, int32_t* size) { // yes, then get the size and the string address *size = PyBytes_Size(s); - *pickle = PyBytes_AsString(s); + *pickle = new char[*size]; + memcpy(*pickle, PyBytes_AS_STRING(s), *size); + Py_DECREF(s); return true; } else // otherwise, there was an error { + *pickle = nullptr; + *size = 0; + // Yikes! errors! PyErr_Print(); // FUTURE: we may have to get the string to display in max...later return false; } } -///////////////////////////////////////////////////////////////////////////// -// -// Function : CreateModule -// PARAMETERS : module - module name to create -// -// PURPOSE : create a new module with built-ins -// -PyObject* PythonInterface::CreateModule(const char* module) -{ - PyObject *m, *d; -// first we must get rid of any old modules of the same name, we'll replace it - PyObject *modules = PyImport_GetModuleDict(); - if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m)) - // clear it - _PyModule_Clear(m); - -// create the module - m = PyImport_AddModule(module); - if (m == NULL) - return nil; - d = PyModule_GetDict(m); -// add in the built-ins - // first make sure that we don't already have the builtins - if (PyDict_GetItemString(d, "__builtins__") == NULL) - { - // if we need the builtins then find the builtin module - PyObject *bimod = PyImport_ImportModule("builtins"); - // then add the builtin dicitionary to our module's dictionary - if (bimod == NULL || PyDict_SetItemString(d, "__builtins__", bimod) != 0) - return nil; - Py_DECREF(bimod); - } - return m; -} - ///////////////////////////////////////////////////////////////////////////// // // Function : RunPYC diff --git a/Sources/Plasma/Apps/plPythonPack/PythonInterface.h b/Sources/Plasma/Apps/plPythonPack/PythonInterface.h index 5b98867e26..5baa9aac0c 100644 --- a/Sources/Plasma/Apps/plPythonPack/PythonInterface.h +++ b/Sources/Plasma/Apps/plPythonPack/PythonInterface.h @@ -42,21 +42,18 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include "HeadSpin.h" -#include +#include class plFileName; namespace ST { class string; } namespace PythonInterface { - void initPython(const plFileName& rootDir, FILE* outstream=stdout, FILE* errstream=stderr); + void initPython(const plFileName& rootDir, const std::vector& paths, FILE* outstream=stdout, FILE* errstream=stderr); void finiPython(); - // So the Python packer can add extra paths - void addPythonPath(const plFileName& dir, FILE* outstream=stdout); PyObject* CompileString(const char *command, const plFileName& filename); - bool DumpObject(PyObject* pyobj, char** pickle, int32_t* size); - PyObject* CreateModule(const char* module); + bool DumpObject(PyObject* pyobj, char** pickle, Py_ssize_t* size); bool RunPYC(PyObject* code, PyObject* module); PyObject* GetModuleItem(const char* item, PyObject* module); } diff --git a/Sources/Plasma/Apps/plPythonPack/main.cpp b/Sources/Plasma/Apps/plPythonPack/main.cpp index 4aa402f571..44bb501225 100644 --- a/Sources/Plasma/Apps/plPythonPack/main.cpp +++ b/Sources/Plasma/Apps/plPythonPack/main.cpp @@ -123,14 +123,13 @@ void WritePythonFile(const plFileName &fileName, const plFileName &path, hsStrea } // import the module first, to make packages work correctly - PyImport_ImportModule(fileName.AsString().c_str()); + PyObject* fModule = PyImport_ImportModule(fileName.AsString().c_str()); + if (!fModule) + ST::printf(stderr, "......import failed "); + PyObject* pythonCode = PythonInterface::CompileString(code, fileName); if (pythonCode) { - // We need to find out if this is a PythonFile module. - // Create a module name (with the '.' as an 'x') - // and create a python file name that is without the ".py" - PyObject* fModule = PythonInterface::CreateModule(fileName.AsString().c_str()); // run the code if (PythonInterface::RunPYC(pythonCode, fModule) ) { @@ -138,6 +137,7 @@ void WritePythonFile(const plFileName &fileName, const plFileName &path, hsStrea PyObject* dict = PyModule_GetDict(fModule); PyObject* pfilename = PyUnicode_FromString(fileName.AsString().c_str()); PyDict_SetItemString(dict, "glue_name", pfilename); + Py_DECREF(pfilename); // next we need to: // - create instance of class @@ -177,13 +177,14 @@ void WritePythonFile(const plFileName &fileName, const plFileName &path, hsStrea else { ST::printf(stderr, "......blast! Error during run-code in {}!\n", fileName); + PyErr_Print(); } } // make sure that we have code to save if (pythonCode) { - int32_t size; + Py_ssize_t size; char* pycode; PythonInterface::DumpObject(pythonCode,&pycode,&size); @@ -191,6 +192,7 @@ void WritePythonFile(const plFileName &fileName, const plFileName &path, hsStrea s->WriteLE32(size); s->Write(size, pycode); + delete[] pycode; } else { @@ -202,6 +204,8 @@ void WritePythonFile(const plFileName &fileName, const plFileName &path, hsStrea } delete [] code; + Py_XDECREF(pythonCode); + Py_XDECREF(fModule); pyStream.Close(); glueStream.Close(); @@ -300,14 +304,7 @@ void PackDirectory(const plFileName& dir, const plFileName& rootPath, const plFi s.WriteLE32(0); } - PythonInterface::initPython(rootPath, out, stderr); - - for (i = 0; i < extraDirs.size(); i++) - PythonInterface::addPythonPath(plFileName::Join(rootPath, extraDirs[i]), out); - ST::printf(out, "\n"); - - // set to maximum optimization (includes removing __doc__ strings) - Py_OptimizeFlag = 2; + PythonInterface::initPython(rootPath, extraDirs, out, stderr); std::vector filePositions; filePositions.resize(fileNames.size()); diff --git a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp index 4764fc6393..c4a03f19c6 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp @@ -55,6 +55,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pyObjectRef.h" #pragma hdrstop +// CPython specific init stuff +#include +#include + #include "cyPythonInterface.h" #include "plPythonPack.h" @@ -893,12 +897,6 @@ PyObject* PythonInterface::initPlasmaModule() getOutputAndReset(); } - AddPep451Classes(); - if (PyErr_Occurred()) { - dbgLog->AddLine("Python error while adding classes to Plasma:\n"); - getOutputAndReset(); - } - return plasmaMod; } @@ -966,149 +964,131 @@ PyObject* PythonInterface::initPlasmaVaultConstantsModule() return plasmaVaultConstantsMod; } +template +static bool ICheckedInit(const _ConfigT& config, plStatusLog* dbgLog, const char* errmsg) +{ + PyStatus status = _FuncT(&config); + if (PyStatus_Exception(status)) { + dbgLog->AddLineF(plStatusLog::kRed, "Python {} {}!", PY_VERSION, errmsg); + if (status.func) + dbgLog->AddLineF(plStatusLog::kRed, "{}: {}", status.func, status.err_msg); + else + dbgLog->AddLine(plStatusLog::kRed, status.err_msg); + return false; + } + return true; +} + void PythonInterface::initPython() { // if haven't been initialized then do it - if ( FirstTimeInit && Py_IsInitialized() == 0 ) - { - FirstTimeInit = false; - // initialize the Python stuff - PyImport_AppendInittab(s_PlasmaModuleDef.m_name, initPlasmaModule); - PyImport_AppendInittab(s_PlasmaConstantsModuleDef.m_name, initPlasmaConstantsModule); - PyImport_AppendInittab(s_PlasmaNetConstantsModuleDef.m_name, initPlasmaNetConstantsModule); - PyImport_AppendInittab(s_PlasmaVaultConstantsModuleDef.m_name, initPlasmaVaultConstantsModule); - - // let Python do some initialization... - Py_SetProgramName(L"plasma"); - Py_NoSiteFlag = 1; - Py_NoUserSiteDirectory = 1; - Py_IgnoreEnvironmentFlag = 1; -#ifdef PLASMA_EXTERNAL_RELEASE - Py_IsolatedFlag = 1; + if (!FirstTimeInit || Py_IsInitialized()) + return; + + if (!dbgLog) { + dbgLog = plStatusLogMgr::GetInstance().CreateStatusLog(30, "Python.log", + plStatusLog::kFilledBackground | + plStatusLog::kAlignToTop | + plStatusLog::kTimestamp); + } + + FirstTimeInit = false; + + // Lazy-init most Plasma Python modules at import time. + PyImport_AppendInittab(s_PlasmaModuleDef.m_name, initPlasmaModule); + PyImport_AppendInittab(s_PlasmaConstantsModuleDef.m_name, initPlasmaConstantsModule); + PyImport_AppendInittab(s_PlasmaNetConstantsModuleDef.m_name, initPlasmaNetConstantsModule); + PyImport_AppendInittab(s_PlasmaVaultConstantsModuleDef.m_name, initPlasmaVaultConstantsModule); + + // In Python 2, we could rely on a single PEP 302 hook class installed into sys.path_hooks to + // handle importing modules from python.pak -- this could be initialized after Python. In Python 3, + // however, the initialization process imports the encodings module and dies if it is not available. + // This module is written in Python code and found in python.pak, but the python.pak import machinery + // is not available. If you have Python 3.(whatever) installed locally and are using a DLL, it + // works. If you violate either of those cases, plClient silently exits (if you're not watching + // stderr). To fix this, will use the provisional core/main init split introduced in Python 3.8 + // and described in PEPs 432 and 587 to init the "core" (much like _freeze_importlib) and install + // our PEP 451 import machinery. Then, we'll do the whole main init thingo. + PyPreConfig preConfig; + PyPreConfig_InitIsolatedConfig(&preConfig); + if (!ICheckedInit(preConfig, dbgLog, "Pre-init failed!")) + return; + + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + config.site_import = 0; + config.program_name = L"plasma"; + config._init_main = 0; + + // Allow importing from the local python directory if and only if this is an internal client. +#ifndef PLASMA_EXTERNAL_RELEASE + PyWideStringList_Append(&config.module_search_paths, L"./python"); + PyWideStringList_Append(&config.module_search_paths, L"./python/plasma"); + PyWideStringList_Append(&config.module_search_paths, L"./python/system"); + config.module_search_paths_set = 1; #endif - Py_Initialize(); + + if (!ICheckedInit(config, dbgLog, "Core init failed!")) + return; + + // We now have enough Python to insert our PEP 451 import machinery. + initPyPackHook(); + + // Now, init the interpreter. + config._init_main = 1; + if (!ICheckedInit(config, dbgLog, "Main init failed!")) + return; + + // Woo, we now have a functional Python 3 interpreter... + dbgLog->AddLineF("Python {} interpreter is now alive!", PY_VERSION); #if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE) - if (usePythonDebugger) - { - debugServer.SetCallbackClass(&debServerCallback); - debugServer.Init(); - PyEval_SetTrace((Py_tracefunc)PythonTraceCallback, NULL); - } + if (usePythonDebugger) + { + debugServer.SetCallbackClass(&debServerCallback); + debugServer.Init(); + PyEval_SetTrace((Py_tracefunc)PythonTraceCallback, NULL); + } #endif - if (!dbgLog) - { - dbgLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "Python.log", - plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp ); - } + // create the output redirector for the stdout and stderr file + stdOut = pyOutputRedirector::New(); + stdErr = pyErrorRedirector::New(); - // create the output redirector for the stdout and stderr file - stdOut = pyOutputRedirector::New(); - stdErr = pyErrorRedirector::New(); + if (stdOut) { + if (PySys_SetObject("stdout", stdOut) != 0) + dbgLog->AddLine(plStatusLog::kRed, "Could not redirect stdout, Python output may not appear in the log"); + } else { + dbgLog->AddLine(plStatusLog::kRed, "Could not create python redirector, Python output will not appear in the log"); + } - // if we need the builtins then find the builtin module - PyObject* sysmod = PyImport_ImportModule("sys"); - // then add the builtin dictionary to our module's dictionary - // get the sys's dictionary to find the stdout and stderr - PyObject* sys_dict = PyModule_GetDict(sysmod); - Py_INCREF(sys_dict); - if (stdOut != nil) - { - if (PyDict_SetItemString(sys_dict,"stdout", stdOut)) - dbgLog->AddLine("Could not redirect stdout, Python output may not appear in the log\n"); - } - else - dbgLog->AddLine("Could not create python redirector, Python output will not appear in the log\n"); - - if (stdErr != nil) - { - if (!PyDict_SetItemString(sys_dict,"stderr", stdErr)) - { - bool dontLog = false; + if (stdErr) { + if (PySys_SetObject("stderr", stdErr) == 0) { + bool dontLog = false; - // Find the excepthook - PyObject* stdErrExceptHook = PyObject_GetAttrString(stdErr, "excepthook"); - if (stdErrExceptHook) - { - if (!PyCallable_Check(stdErrExceptHook) || PyDict_SetItemString(sys_dict,"excepthook", stdErrExceptHook)) - { - dbgLog->AddLine("Could not redirect excepthook, Python error output will not get to the log server\n"); - dontLog = true; - } - Py_DECREF(stdErrExceptHook); - } - else - { - dbgLog->AddLine("Could not find stdErr excepthook, Python error output will not get to the log server\n"); + // Find the excepthook + pyObjectRef stdErrExceptHook = PyObject_GetAttrString(stdErr, "excepthook"); + if (stdErrExceptHook) { + if (!PyCallable_Check(stdErrExceptHook.Get()) || PySys_SetObject("excepthook", stdErrExceptHook.Get()) != 0) { + dbgLog->AddLine(plStatusLog::kRed, "Could not redirect excepthook, Python error output will not get to the log server"); dontLog = true; } - - if (dontLog) - { - if (pyErrorRedirector::Check(stdErr)) - { - pyErrorRedirector* redir = pyErrorRedirector::ConvertFrom(stdErr); - redir->SetLogging(false); - } - } + } else { + dbgLog->AddLine(plStatusLog::kRed, "Could not find stdErr excepthook, Python error output will not get to the log server"); + dontLog = true; } - else - { - dbgLog->AddLine("Could not redirect stderr, Python error output may not appear in the log or on the log server\n"); - } - } - else - { - dbgLog->AddLine("Could not create python redirector, Python error output will not appear in the log\n"); - } - // NOTE: we will reset the path to not include paths - // that Python may have found in the registry - PyObject* path_list = PyList_New(3); - if (PyList_SetItem(path_list, 0, PyUnicode_FromString(".\\python"))) - { - Py_DECREF(sys_dict); - Py_DECREF(path_list); - dbgLog->AddLine("Error while creating python path:\n"); - getOutputAndReset(); - return; - } - // make sure that our plasma libraries are gotten before the system ones - if (PyList_SetItem(path_list, 1, PyUnicode_FromString(".\\python\\plasma"))) - { - Py_DECREF(sys_dict); - Py_DECREF(path_list); - dbgLog->AddLine("Error while creating python path:\n"); - getOutputAndReset(); - return; - } - if (PyList_SetItem(path_list, 2, PyUnicode_FromString(".\\python\\system"))) - { - Py_DECREF(sys_dict); - Py_DECREF(path_list); - dbgLog->AddLine("Error while creating python path:\n"); - getOutputAndReset(); - return; - } - - // set the path to be this one - if (PyDict_SetItemString(sys_dict,"path",path_list)) - { - Py_DECREF(sys_dict); - Py_DECREF(path_list); - dbgLog->AddLine("Error while setting python path:\n"); - getOutputAndReset(); - return; + if (dontLog && pyErrorRedirector::Check(stdErr)) { + pyErrorRedirector* redir = pyErrorRedirector::ConvertFrom(stdErr); + redir->SetLogging(false); + } + } else { + dbgLog->AddLine(plStatusLog::kRed, "Could not redirect stderr, Python error output may not appear in the log or on the log server"); } - - Py_DECREF(path_list); - - // PEP451 - initPyPackHook(); - - Py_DECREF(sys_dict); + } else { + dbgLog->AddLine(plStatusLog::kRed, "Could not create python redirector, Python error output will not appear in the log"); } + initialized++; } diff --git a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h index 5af4848172..41c2066de1 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h +++ b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h @@ -107,7 +107,6 @@ class PythonInterface // Initialize the Plasma module static void AddPlasmaMethods(std::vector &methods); static void AddPlasmaClasses(); - static void AddPep451Classes(); // Initialize the PlasmaConstants module static void AddPlasmaConstantsClasses(); diff --git a/Sources/Plasma/FeatureLib/pfPython/cyPythonModule451.cpp b/Sources/Plasma/FeatureLib/pfPython/cyPythonModule451.cpp index 2014f503ea..cd13eea730 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyPythonModule451.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyPythonModule451.cpp @@ -50,6 +50,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "cyPythonInterface.h" #include "plPythonPack.h" +#include "plStatusLog/plStatusLog.h" + // ========================================================================== struct pyModulePackFinder @@ -302,23 +304,31 @@ PYTHON_CLASS_NEW_IMPL(ptModulePackLoader, pyModulePackLoader); // ========================================================================== -void PythonInterface::AddPep451Classes() -{ - PYTHON_CLASS_IMPORT_START(plasmaMod); - PYTHON_CLASS_IMPORT(plasmaMod, ptModulePackFinder); - PYTHON_CLASS_IMPORT(plasmaMod, ptModulePackSpec); - PYTHON_CLASS_IMPORT(plasmaMod, ptModulePackLoader); - PYTHON_CLASS_IMPORT_END(plasmaMod); -} - void PythonInterface::initPyPackHook() { - // At this point, the Plasma module has never been imported. Therefore, any attempt to use - // its classes will explode with null references. Import the module first. - pyObjectRef mod = PyImport_ImportModule("Plasma"); +#ifdef HS_DEBUGGING + dbgLog->AddLine(plStatusLog::kGreen, "Creating PEP 451 module..."); +#endif + + // note: steals ref + PyObject* mod = PyImport_AddModule("_PlasmaImport"); + if (!mod) { + dbgLog->AddLine(plStatusLog::kRed, "Failed to create _PlasmaImport module!"); + return; + } + + PYTHON_CLASS_IMPORT_START(mod); + PYTHON_CLASS_IMPORT(mod, ptModulePackFinder); + PYTHON_CLASS_IMPORT(mod, ptModulePackSpec); + PYTHON_CLASS_IMPORT(mod, ptModulePackLoader); + PYTHON_CLASS_IMPORT_END(mod); + +#ifdef HS_DEBUGGING + dbgLog->AddLine(plStatusLog::kGreen, "Installing PEP 451 machinery..."); +#endif + pyObjectRef metaFinder = pyModulePackFinder::New(); ((ptModulePackFinder*)metaFinder.Get())->fThis->fLoader = pyModulePackLoader::New(); - PyObject* finders = PySys_GetObject("meta_path"); hsAssert(finders, "Failed to access sys.meta_path"); hsAssert(PyList_Check(finders), "sys.meta_path finders is not a list"); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp b/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp index 114b3a8657..264dff95d9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp @@ -311,7 +311,7 @@ PYTHON_START_AS_NUMBER_TABLE(EnumValue) 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ - 0, /* nb_index */ + (unaryfunc)EnumValue_long, /* nb_index */ 0, /* nb_matrix_multiply */ 0, /* nb_inplace_matrix_multiply */ PYTHON_END_AS_NUMBER_TABLE;