diff options
author | Steve Dower <steve.dower@python.org> | 2019-06-29 21:28:59 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-29 21:28:59 (GMT) |
commit | 323e743d4879f1cd861d0b252775797fb7938755 (patch) | |
tree | cfaccee98a1695fabc022c04450b972e4f79071d /PC/python_uwp.cpp | |
parent | 0cba121029bae0a891b02c493cb77633620701be (diff) | |
download | cpython-323e743d4879f1cd861d0b252775797fb7938755.zip cpython-323e743d4879f1cd861d0b252775797fb7938755.tar.gz cpython-323e743d4879f1cd861d0b252775797fb7938755.tar.bz2 |
bpo-37369: Fix initialization of sys members when launched via an app container (GH-14467)
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
Diffstat (limited to 'PC/python_uwp.cpp')
-rw-r--r-- | PC/python_uwp.cpp | 287 |
1 files changed, 159 insertions, 128 deletions
diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index dd1edde..2352f45 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -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,220 @@ 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; + try { + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + if (package) { + const auto id = package.Id(); + if (id) { + return std::wstring(id.FamilyName()); + } } - return buffer; } + catch (...) { + } + + return std::wstring(); +} +static std::wstring +get_package_home() +{ try { const auto package = winrt::Windows::ApplicationModel::Package::Current(); if (package) { - const auto install = package.InstalledLocation(); - if (install) { - installPath = install.Path(); + const auto path = package.InstalledLocation(); + if (path) { + return std::wstring(path.Path()); } } } catch (...) { } - if (!installPath.empty()) { - len = installPath.size() + wcslen(PROGNAME) + 2; - } else { - len = wcslen(argv0) + wcslen(PROGNAME) + 1; - } + return std::wstring(); +} - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } +static PyStatus +set_process_name(PyConfig *config) +{ + PyStatus status = PyStatus_Ok(); + std::wstring executable; - 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; - } + const auto home = get_package_home(); + const auto family = get_package_family(); - wchar_t *name = wcsrchr(buffer, L'\\'); - if (name) { - name[1] = L'\0'; - } else { - buffer[0] = L'\0'; + 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); } } - if (wcscat_s(buffer, len, PROGNAME)) { - Py_FatalError("failed to concatenate program name"); - return NULL; + /* 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; + } + } } - return buffer; -} - -static wchar_t * -get_process_name() -{ - 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; + if (!home.empty()) { + status = PyConfig_SetString(config, &config->home, home.c_str()); + if (PyStatus_Exception(status)) { + return status; } - 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; + } + + 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 r; + 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_without_config; } - exeName = get_process_name(); + status = PyConfig_InitPythonConfig(&config); + if (PyStatus_Exception(status)) { + goto fail_without_config; + } - new_argv[0] = get_argv0(exeName ? exeName : argv[0]); - for (int i = 1; i < argc; ++i) { - new_argv[i] = argv[i]; + status = PyConfig_SetArgv(&config, argc, argv); + if (PyStatus_Exception(status)) { + goto fail; } - set_user_base(); + status = set_process_name(&config); + 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"; - } - } + const wchar_t *p = _wgetenv(L"PYTHONUSERBASE"); + if (!p || !*p) { + _wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str()); + } - 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; + 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; + } + 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]); - - int result = Py_Main(new_argc, (wchar_t **)new_argv); + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + goto fail; + } + PyConfig_Clear(&config); - free((void *)exeName); - free((void *)new_argv); + return Py_RunMain(); - return result; +fail: + PyConfig_Clear(&config); +fail_without_config: + if (PyStatus_IsExit(status)) { + return status.exitcode; + } + assert(PyStatus_Exception(status)); + Py_ExitStatusException(status); + /* Unreachable code */ + return 0; } #ifdef PYTHONW |