mirror of
https://github.com/python/cpython.git
synced 2025-07-09 20:35:26 +00:00
bpo-37369: Fix initialization of sys members when launched via an app container (GH-14428)
sys._base_executable is now always defined on all platforms, and can be overridden through configuration. Also adds test.support.PythonSymlink to encapsulate platform-specific logic for symlinking sys.executable
This commit is contained in:
parent
80097e089b
commit
9048c49322
17 changed files with 410 additions and 277 deletions
|
@ -6,6 +6,9 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <winrt\Windows.ApplicationModel.h>
|
||||
#include <winrt\Windows.Storage.h>
|
||||
|
@ -24,192 +27,219 @@ const wchar_t *PROGNAME = L"python.exe";
|
|||
#endif
|
||||
#endif
|
||||
|
||||
static void
|
||||
set_user_base()
|
||||
static std::wstring
|
||||
get_user_base()
|
||||
{
|
||||
wchar_t envBuffer[2048];
|
||||
try {
|
||||
const auto appData = winrt::Windows::Storage::ApplicationData::Current();
|
||||
if (appData) {
|
||||
const auto localCache = appData.LocalCacheFolder();
|
||||
if (localCache) {
|
||||
auto path = localCache.Path();
|
||||
if (!path.empty() &&
|
||||
!wcscpy_s(envBuffer, path.c_str()) &&
|
||||
!wcscat_s(envBuffer, L"\\local-packages")
|
||||
) {
|
||||
_wputenv_s(L"PYTHONUSERBASE", envBuffer);
|
||||
if (!path.empty()) {
|
||||
return std::wstring(path) + L"\\local-packages";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
static const wchar_t *
|
||||
get_argv0(const wchar_t *argv0)
|
||||
static std::wstring
|
||||
get_package_family()
|
||||
{
|
||||
winrt::hstring installPath;
|
||||
const wchar_t *launcherPath;
|
||||
wchar_t *buffer;
|
||||
size_t len;
|
||||
|
||||
launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
|
||||
if (launcherPath && launcherPath[0]) {
|
||||
len = wcslen(launcherPath) + 1;
|
||||
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
||||
if (!buffer) {
|
||||
Py_FatalError("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
if (wcscpy_s(buffer, len, launcherPath)) {
|
||||
Py_FatalError("failed to copy to buffer");
|
||||
return NULL;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
||||
if (package) {
|
||||
const auto install = package.InstalledLocation();
|
||||
if (install) {
|
||||
installPath = install.Path();
|
||||
const auto id = package.Id();
|
||||
if (id) {
|
||||
return std::wstring(id.FamilyName());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
||||
if (!installPath.empty()) {
|
||||
len = installPath.size() + wcslen(PROGNAME) + 2;
|
||||
} else {
|
||||
len = wcslen(argv0) + wcslen(PROGNAME) + 1;
|
||||
}
|
||||
|
||||
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
||||
if (!buffer) {
|
||||
Py_FatalError("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!installPath.empty()) {
|
||||
if (wcscpy_s(buffer, len, installPath.c_str())) {
|
||||
Py_FatalError("failed to copy to buffer");
|
||||
return NULL;
|
||||
}
|
||||
if (wcscat_s(buffer, len, L"\\")) {
|
||||
Py_FatalError("failed to concatenate backslash");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (wcscpy_s(buffer, len, argv0)) {
|
||||
Py_FatalError("failed to copy argv[0]");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t *name = wcsrchr(buffer, L'\\');
|
||||
if (name) {
|
||||
name[1] = L'\0';
|
||||
} else {
|
||||
buffer[0] = L'\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (wcscat_s(buffer, len, PROGNAME)) {
|
||||
Py_FatalError("failed to concatenate program name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
static wchar_t *
|
||||
get_process_name()
|
||||
static std::wstring
|
||||
get_package_home()
|
||||
{
|
||||
DWORD bufferLen = MAX_PATH;
|
||||
DWORD len = bufferLen;
|
||||
wchar_t *r = NULL;
|
||||
|
||||
while (!r) {
|
||||
r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
|
||||
if (!r) {
|
||||
Py_FatalError("out of memory");
|
||||
return NULL;
|
||||
try {
|
||||
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
||||
if (package) {
|
||||
const auto path = package.InstalledLocation();
|
||||
if (path) {
|
||||
return std::wstring(path.Path());
|
||||
}
|
||||
}
|
||||
len = GetModuleFileNameW(NULL, r, bufferLen);
|
||||
if (len == 0) {
|
||||
free((void *)r);
|
||||
return NULL;
|
||||
} else if (len == bufferLen &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
free(r);
|
||||
r = NULL;
|
||||
bufferLen *= 2;
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
static PyStatus
|
||||
set_process_name(PyConfig *config)
|
||||
{
|
||||
PyStatus status = PyStatus_Ok();
|
||||
std::wstring executable;
|
||||
|
||||
const auto home = get_package_home();
|
||||
const auto family = get_package_family();
|
||||
|
||||
if (!family.empty()) {
|
||||
PWSTR localAppData;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
|
||||
NULL, &localAppData))) {
|
||||
executable = std::wstring(localAppData)
|
||||
+ L"\\Microsoft\\WindowsApps\\"
|
||||
+ family
|
||||
+ L"\\"
|
||||
+ PROGNAME;
|
||||
|
||||
CoTaskMemFree(localAppData);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
/* Only use module filename if we don't have a home */
|
||||
if (home.empty() && executable.empty()) {
|
||||
executable.resize(MAX_PATH);
|
||||
while (true) {
|
||||
DWORD len = GetModuleFileNameW(
|
||||
NULL, executable.data(), (DWORD)executable.size());
|
||||
if (len == 0) {
|
||||
executable.clear();
|
||||
break;
|
||||
} else if (len == executable.size() &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
executable.resize(len * 2);
|
||||
} else {
|
||||
executable.resize(len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!home.empty()) {
|
||||
status = PyConfig_SetString(config, &config->home, home.c_str());
|
||||
if (PyStatus_Exception(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
|
||||
if (launcherPath) {
|
||||
if (!executable.empty()) {
|
||||
status = PyConfig_SetString(config, &config->base_executable,
|
||||
executable.c_str());
|
||||
if (PyStatus_Exception(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
status = PyConfig_SetString(
|
||||
config, &config->executable, launcherPath);
|
||||
|
||||
/* bpo-35873: Clear the environment variable to avoid it being
|
||||
* inherited by child processes. */
|
||||
_wputenv_s(L"__PYVENV_LAUNCHER__", L"");
|
||||
} else if (!executable.empty()) {
|
||||
status = PyConfig_SetString(
|
||||
config, &config->executable, executable.c_str());
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
wmain(int argc, wchar_t **argv)
|
||||
{
|
||||
const wchar_t **new_argv;
|
||||
int new_argc;
|
||||
const wchar_t *exeName;
|
||||
PyStatus status;
|
||||
|
||||
new_argc = argc;
|
||||
new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2));
|
||||
if (new_argv == NULL) {
|
||||
Py_FatalError("out of memory");
|
||||
return -1;
|
||||
PyPreConfig preconfig;
|
||||
PyConfig config;
|
||||
|
||||
PyPreConfig_InitPythonConfig(&preconfig);
|
||||
status = Py_PreInitializeFromArgs(&preconfig, argc, argv);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
exeName = get_process_name();
|
||||
|
||||
new_argv[0] = get_argv0(exeName ? exeName : argv[0]);
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
new_argv[i] = argv[i];
|
||||
status = PyConfig_InitPythonConfig(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
set_user_base();
|
||||
status = PyConfig_SetArgv(&config, argc, argv);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (exeName) {
|
||||
const wchar_t *p = wcsrchr(exeName, L'\\');
|
||||
if (p) {
|
||||
const wchar_t *moduleName = NULL;
|
||||
if (*p++ == L'\\') {
|
||||
if (wcsnicmp(p, L"pip", 3) == 0) {
|
||||
moduleName = L"pip";
|
||||
/* No longer required when pip 19.1 is added */
|
||||
_wputenv_s(L"PIP_USER", L"true");
|
||||
} else if (wcsnicmp(p, L"idle", 4) == 0) {
|
||||
moduleName = L"idlelib";
|
||||
}
|
||||
status = set_process_name(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const wchar_t *p = _wgetenv(L"PYTHONUSERBASE");
|
||||
if (!p || !*p) {
|
||||
_wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str());
|
||||
}
|
||||
|
||||
p = wcsrchr(argv[0], L'\\');
|
||||
if (!p) {
|
||||
p = argv[0];
|
||||
}
|
||||
if (p) {
|
||||
if (*p == L'\\') {
|
||||
p++;
|
||||
}
|
||||
|
||||
const wchar_t *moduleName = NULL;
|
||||
if (wcsnicmp(p, L"pip", 3) == 0) {
|
||||
moduleName = L"pip";
|
||||
/* No longer required when pip 19.1 is added */
|
||||
_wputenv_s(L"PIP_USER", L"true");
|
||||
} else if (wcsnicmp(p, L"idle", 4) == 0) {
|
||||
moduleName = L"idlelib";
|
||||
}
|
||||
|
||||
if (moduleName) {
|
||||
status = PyConfig_SetString(&config, &config.run_module, moduleName);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (moduleName) {
|
||||
new_argc += 2;
|
||||
for (int i = argc; i >= 1; --i) {
|
||||
new_argv[i + 2] = new_argv[i];
|
||||
}
|
||||
new_argv[1] = L"-m";
|
||||
new_argv[2] = moduleName;
|
||||
status = PyConfig_SetString(&config, &config.run_filename, NULL);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
status = PyConfig_SetString(&config, &config.run_command, NULL);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Override program_full_path from here so that
|
||||
sys.executable is set correctly. */
|
||||
_Py_SetProgramFullPath(new_argv[0]);
|
||||
status = Py_InitializeFromConfig(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
goto fail;
|
||||
}
|
||||
PyConfig_Clear(&config);
|
||||
|
||||
int result = Py_Main(new_argc, (wchar_t **)new_argv);
|
||||
return Py_RunMain();
|
||||
|
||||
free((void *)exeName);
|
||||
free((void *)new_argv);
|
||||
|
||||
return result;
|
||||
fail:
|
||||
PyConfig_Clear(&config);
|
||||
if (PyStatus_IsExit(status)) {
|
||||
return status.exitcode;
|
||||
}
|
||||
assert(PyStatus_Exception(status));
|
||||
Py_ExitStatusException(status);
|
||||
/* Unreachable code */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PYTHONW
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue