mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 13:41:03 +00:00
Keep Python Bytecode out of the Bundled Python Home (#16550)
* Keep Python Bytecode out of the Bundled Python Home The bundled stdlib ships .pyc files that are stale against their sources: they are compiled when the Python deps are installed, and the copy into the app does not keep the sources' mtimes. CPython then recompiles every module it imports and writes the result next to the source. A drag-installed macOS app rewrites files inside its own bundle on first launch, so its signature no longer verifies, and read-only installs (AppImage, Flatpak, Program Files) recompile the same modules on every launch. Point pycache_prefix at <data_dir>/python/pycache so the interpreter reads and writes bytecode there. The bundle is never written, the cache survives between launches, and the data directory is already a plugin read/write root. * Drop Cached Bytecode Through the Interpreter in the Module Release Test
This commit is contained in:
1 parent
a315a96c5c
commit
d3699dd9f6
2 files changed
+29
-2
No files matched your search
@@ -584,6 +584,19 @@ bool PythonInterpreter::initialize()
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Program name set successfully";
|
||||
|
||||
// Keep bytecode out of the Python home. The shipped .pyc files are stale against their
|
||||
// sources, so a writable install would rewrite them inside the app (breaking a signed
|
||||
// macOS bundle's seal) and a read-only one would recompile them on every launch.
|
||||
const std::string pycache_prefix = data_dir() + "/python/pycache";
|
||||
#ifdef _WIN32
|
||||
status = PyConfig_SetString(&config, &config.pycache_prefix, boost::nowide::widen(pycache_prefix).c_str());
|
||||
#else
|
||||
status = PyConfig_SetBytesString(&config, &config.pycache_prefix, pycache_prefix.c_str());
|
||||
#endif
|
||||
if (PyStatus_Exception(status))
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failed to set the Python bytecode cache to " << pycache_prefix << ": "
|
||||
<< (status.err_msg ? status.err_msg : "unknown error");
|
||||
|
||||
// Use pybind11's scoped_interpreter which properly initializes
|
||||
// pybind11 internals for multi-threaded use — raw Py_InitializeFromConfig
|
||||
// does not set up thread state tracking that pybind11 requires.
|
||||
|
||||
@@ -615,8 +615,22 @@ TEST_CASE("Python module release removes package submodules and owned sys.path",
|
||||
|
||||
// Ensure the next import executes the new submodule rather than reusing a stale package child.
|
||||
write_package("'new'");
|
||||
boost::system::error_code ec;
|
||||
fs::remove_all(package_root / "__pycache__", ec);
|
||||
{
|
||||
// The rewrite can keep the old mtime and size, so drop the bytecode wherever the
|
||||
// interpreter caches it.
|
||||
PythonGILState gil;
|
||||
REQUIRE(gil);
|
||||
PyObjectPtr util(PyImport_ImportModule("importlib.util"));
|
||||
REQUIRE(util);
|
||||
for (const char* file : {"__init__.py", "sub.py"}) {
|
||||
PyObjectPtr source(PyUnicode_DecodeFSDefault((package_root / file).string().c_str()));
|
||||
REQUIRE(source);
|
||||
PyObjectPtr cached(PyObject_CallMethod(util.get(), "cache_from_source", "O", source.get()));
|
||||
REQUIRE(cached);
|
||||
boost::system::error_code ec;
|
||||
fs::remove(fs::path(PyUnicode_AsUTF8(cached.get())), ec);
|
||||
}
|
||||
}
|
||||
|
||||
paths.clear();
|
||||
modules.clear();
|
||||
|
||||
Reference in new issue
Block a user