diff --git a/src/slic3r/plugin/PythonInterpreter.cpp b/src/slic3r/plugin/PythonInterpreter.cpp index 4a08431117..89dc91723c 100644 --- a/src/slic3r/plugin/PythonInterpreter.cpp +++ b/src/slic3r/plugin/PythonInterpreter.cpp @@ -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. diff --git a/tests/slic3rutils/test_plugin_lifecycle.cpp b/tests/slic3rutils/test_plugin_lifecycle.cpp index 58a4f1a079..498935c2f6 100644 --- a/tests/slic3rutils/test_plugin_lifecycle.cpp +++ b/tests/slic3rutils/test_plugin_lifecycle.cpp @@ -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();