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;