feat: Add self-contained venv support for pip packages (closes #137)
- Set sys.executable in PyConfig for subprocess/pip calls
- Detect sibling venv/ directory and prepend site-packages to sys.path
- Add mcrf_venv.py reference implementation for bootstrapping pip
- Supports both Linux (lib/python3.14/site-packages) and Windows (Lib/site-packages)
Usage: ./mcrogueface -m pip install numpy
Or via Python: mcrf_venv.pip_install("numpy")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3f6ea4fe33
commit
bbc744f8dc
2 changed files with 344 additions and 11 deletions
|
|
@ -395,25 +395,49 @@ PyStatus init_python(const char *program_name)
|
|||
PyConfig_SetString(&config, &config.stdio_errors, L"surrogateescape");
|
||||
config.configure_c_stdio = 1;
|
||||
|
||||
PyConfig_SetBytesString(&config, &config.home,
|
||||
// Set sys.executable to the McRogueFace binary path
|
||||
auto exe_filename = executable_filename();
|
||||
PyConfig_SetString(&config, &config.executable, exe_filename.c_str());
|
||||
|
||||
PyConfig_SetBytesString(&config, &config.home,
|
||||
narrow_string(executable_path() + L"/lib/Python").c_str());
|
||||
|
||||
status = PyConfig_SetBytesString(&config, &config.program_name,
|
||||
program_name);
|
||||
|
||||
// Check for sibling venv/ directory (self-contained deployment)
|
||||
auto exe_dir = std::filesystem::path(executable_path());
|
||||
auto sibling_venv = exe_dir / "venv";
|
||||
if (std::filesystem::exists(sibling_venv)) {
|
||||
// Platform-specific site-packages path
|
||||
#ifdef _WIN32
|
||||
auto site_packages = sibling_venv / "Lib" / "site-packages";
|
||||
#else
|
||||
auto site_packages = sibling_venv / "lib" / "python3.14" / "site-packages";
|
||||
#endif
|
||||
if (std::filesystem::exists(site_packages)) {
|
||||
// Prepend so venv packages take priority over bundled
|
||||
PyWideStringList_Insert(&config.module_search_paths, 0,
|
||||
site_packages.wstring().c_str());
|
||||
config.module_search_paths_set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// under Windows, the search paths are correct; under Linux, they need manual insertion
|
||||
#if __PLATFORM_SET_PYTHON_SEARCH_PATHS == 1
|
||||
config.module_search_paths_set = 1;
|
||||
|
||||
// search paths for python libs/modules/scripts
|
||||
if (!config.module_search_paths_set) {
|
||||
config.module_search_paths_set = 1;
|
||||
}
|
||||
|
||||
// search paths for python libs/modules/scripts
|
||||
const wchar_t* str_arr[] = {
|
||||
L"/scripts",
|
||||
L"/lib/Python/lib.linux-x86_64-3.14",
|
||||
L"/lib/Python",
|
||||
L"/lib/Python/Lib",
|
||||
L"/venv/lib/python3.14/site-packages"
|
||||
L"/lib/Python",
|
||||
L"/lib/Python/Lib"
|
||||
// Note: venv site-packages handled above via sibling_venv detection
|
||||
};
|
||||
|
||||
|
||||
|
||||
for(auto s : str_arr) {
|
||||
status = PyWideStringList_Append(&config.module_search_paths, (executable_path() + s).c_str());
|
||||
|
|
@ -446,6 +470,10 @@ PyStatus McRFPy_API::init_python_with_config(const McRogueFaceConfig& config)
|
|||
PyConfig_SetString(&pyconfig, &pyconfig.stdio_errors, L"surrogateescape");
|
||||
pyconfig.configure_c_stdio = 1;
|
||||
|
||||
// Set sys.executable to the McRogueFace binary path
|
||||
auto exe_path = executable_filename();
|
||||
PyConfig_SetString(&pyconfig, &pyconfig.executable, exe_path.c_str());
|
||||
|
||||
// Set interactive mode (replaces deprecated Py_InspectFlag)
|
||||
if (config.interactive_mode) {
|
||||
pyconfig.inspect = 1;
|
||||
|
|
@ -497,7 +525,7 @@ PyStatus McRFPy_API::init_python_with_config(const McRogueFaceConfig& config)
|
|||
return status;
|
||||
}
|
||||
|
||||
// Check if we're in a virtual environment
|
||||
// Check if we're in a virtual environment (symlinked into a venv)
|
||||
auto exe_wpath = executable_filename();
|
||||
auto exe_path_fs = std::filesystem::path(exe_wpath);
|
||||
auto exe_dir = exe_path_fs.parent_path();
|
||||
|
|
@ -512,6 +540,23 @@ PyStatus McRFPy_API::init_python_with_config(const McRogueFaceConfig& config)
|
|||
pyconfig.module_search_paths_set = 1;
|
||||
}
|
||||
|
||||
// Check for sibling venv/ directory (self-contained deployment)
|
||||
auto sibling_venv = exe_dir / "venv";
|
||||
if (std::filesystem::exists(sibling_venv)) {
|
||||
// Platform-specific site-packages path
|
||||
#ifdef _WIN32
|
||||
auto site_packages = sibling_venv / "Lib" / "site-packages";
|
||||
#else
|
||||
auto site_packages = sibling_venv / "lib" / "python3.14" / "site-packages";
|
||||
#endif
|
||||
if (std::filesystem::exists(site_packages)) {
|
||||
// Prepend so venv packages take priority over bundled
|
||||
PyWideStringList_Insert(&pyconfig.module_search_paths, 0,
|
||||
site_packages.wstring().c_str());
|
||||
pyconfig.module_search_paths_set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set Python home to our bundled Python
|
||||
auto python_home = executable_path() + L"/lib/Python";
|
||||
PyConfig_SetString(&pyconfig, &pyconfig.home, python_home.c_str());
|
||||
|
|
@ -527,8 +572,8 @@ PyStatus McRFPy_API::init_python_with_config(const McRogueFaceConfig& config)
|
|||
L"/scripts",
|
||||
L"/lib/Python/lib.linux-x86_64-3.14",
|
||||
L"/lib/Python",
|
||||
L"/lib/Python/Lib",
|
||||
L"/venv/lib/python3.14/site-packages"
|
||||
L"/lib/Python/Lib"
|
||||
// Note: venv site-packages handled above via sibling_venv detection
|
||||
};
|
||||
|
||||
for(auto s : str_arr) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue