diff options
author | Steve Dower <steve.dower@python.org> | 2019-06-28 10:02:13 -0700 |
---|---|---|
committer | Ned Deily <nad@python.org> | 2019-07-01 22:24:26 -0400 |
commit | 3c34ea97a341e4dd80b542c99c593f014a8ae410 (patch) | |
tree | 0c65fde80bdd1fb2fe7f9613cada893d15bfd180 | |
parent | cc0bf97d61fbe844843f28abc510a11f3ef09942 (diff) | |
download | cpython-git-3c34ea97a341e4dd80b542c99c593f014a8ae410.tar.gz |
bpo-37369: Fixes path for sys.executable when running from the Microsoft Store (GH-14450)
-rw-r--r-- | Lib/site.py | 7 | ||||
-rw-r--r-- | Lib/test/test_venv.py | 8 | ||||
-rw-r--r-- | Lib/venv/__init__.py | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Windows/2019-06-28-08-09-08.bpo-37369.1iVpxq.rst | 1 | ||||
-rw-r--r-- | PC/getpathp.c | 12 | ||||
-rw-r--r-- | PC/python_uwp.cpp | 218 | ||||
-rw-r--r-- | Python/sysmodule.c | 14 |
7 files changed, 115 insertions, 147 deletions
diff --git a/Lib/site.py b/Lib/site.py index ad1146332b..878658827c 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -458,13 +458,6 @@ def venv(known_paths): env = os.environ if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env: executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__'] - elif sys.platform == 'win32' and '__PYVENV_LAUNCHER__' in env: - executable = sys.executable - import _winapi - sys._base_executable = _winapi.GetModuleFileName(0) - # bpo-35873: Clear the environment variable to avoid it being - # inherited by child processes. - del os.environ['__PYVENV_LAUNCHER__'] else: executable = sys.executable exe_dir, _ = os.path.split(os.path.abspath(executable)) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index a8dc59cb81..c3ccb92913 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -127,10 +127,6 @@ class BasicTest(BaseTest): """ Test that the prefix values are as expected. """ - #check our prefixes - self.assertEqual(sys.base_prefix, sys.prefix) - self.assertEqual(sys.base_exec_prefix, sys.exec_prefix) - # check a venv's prefixes rmtree(self.env_dir) self.run_with_capture(venv.create, self.env_dir) @@ -139,8 +135,8 @@ class BasicTest(BaseTest): for prefix, expected in ( ('prefix', self.env_dir), ('prefix', self.env_dir), - ('base_prefix', sys.prefix), - ('base_exec_prefix', sys.exec_prefix)): + ('base_prefix', sys.base_prefix), + ('base_exec_prefix', sys.base_exec_prefix)): cmd[2] = 'import sys; print(sys.%s)' % prefix out, err = check_output(cmd) self.assertEqual(out.strip(), expected.encode()) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 7836dfba04..95c05486af 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -243,7 +243,7 @@ class EnvBuilder: for suffix in suffixes: src = os.path.join(dirname, suffix) - if os.path.exists(src): + if os.path.lexists(src): copier(src, os.path.join(binpath, suffix)) if sysconfig.is_python_build(True): diff --git a/Misc/NEWS.d/next/Windows/2019-06-28-08-09-08.bpo-37369.1iVpxq.rst b/Misc/NEWS.d/next/Windows/2019-06-28-08-09-08.bpo-37369.1iVpxq.rst new file mode 100644 index 0000000000..5eaed61a92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-06-28-08-09-08.bpo-37369.1iVpxq.rst @@ -0,0 +1 @@ +Fixes path for :data:`sys.executable` when running from the Microsoft Store. diff --git a/PC/getpathp.c b/PC/getpathp.c index 1b553d53af..04764c9e5a 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -540,14 +540,20 @@ get_program_full_path(const _PyCoreConfig *core_config, wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); + if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + /* GetModuleFileName should never fail when passed NULL */ + return _Py_INIT_ERR("Cannot determine program path"); + } + /* The launcher may need to force the executable path to a * different environment, so override it here. */ pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); if (pyvenv_launcher && pyvenv_launcher[0]) { + _wputenv_s(L"__PYVENV_BASE_EXECUTABLE__", program_full_path); wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); - } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { - /* GetModuleFileName should never fail when passed NULL */ - return _Py_INIT_ERR("Cannot determine program path"); + /* bpo-35873: Clear the environment variable to avoid it being + * inherited by child processes. */ + _wputenv_s(L"__PYVENV_LAUNCHER__", L""); } config->program_full_path = PyMem_RawMalloc( diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index 5c8caa6666..166a977cfb 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> @@ -46,170 +49,129 @@ set_user_base() } } -static const wchar_t * -get_argv0(const wchar_t *argv0) +static winrt::hstring +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(); + return id ? id.FamilyName() : winrt::hstring(); } } catch (...) { } - if (!installPath.empty()) { - len = installPath.size() + wcslen(PROGNAME) + 2; - } else { - len = wcslen(argv0) + wcslen(PROGNAME) + 1; - } + return winrt::hstring(); +} - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } +static int +set_process_name() +{ + std::wstring executable, home; - 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 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); } + } - wchar_t *name = wcsrchr(buffer, L'\\'); - if (name) { - name[1] = L'\0'; + home.resize(MAX_PATH); + while (true) { + DWORD len = GetModuleFileNameW( + NULL, home.data(), (DWORD)home.size()); + if (len == 0) { + home.clear(); + break; + } else if (len == home.size() && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + home.resize(len * 2); } else { - buffer[0] = L'\0'; + home.resize(len); + size_t bslash = home.find_last_of(L"/\\"); + if (bslash != std::wstring::npos) { + home.erase(bslash); + } + break; } } - if (wcscat_s(buffer, len, PROGNAME)) { - Py_FatalError("failed to concatenate program name"); - return NULL; + if (executable.empty() && !home.empty()) { + executable = home + L"\\" + PROGNAME; } - 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; - } - 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; + if (!home.empty()) { + Py_SetPythonHome(home.c_str()); + } + if (!executable.empty()) { + const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (launcherPath) { + _wputenv_s(L"__PYVENV_BASE_EXECUTABLE__", executable.c_str()); + _Py_SetProgramFullPath(launcherPath); + /* bpo-35873: Clear the environment variable to avoid it being + * inherited by child processes. */ + _wputenv_s(L"__PYVENV_LAUNCHER__", L""); + } else { + _Py_SetProgramFullPath(executable.c_str()); } } - return r; + return 1; } int wmain(int argc, wchar_t **argv) { - const wchar_t **new_argv; - int new_argc; - const wchar_t *exeName; - - 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; + if (!set_process_name()) { + return 121; } + set_user_base(); - 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]; + const wchar_t *p = wcsrchr(argv[0], L'\\'); + if (!p) { + p = argv[0]; } + if (p) { + if (*p == L'\\') { + p++; + } - set_user_base(); - - 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"; - _wputenv_s(L"PIP_USER", L"true"); - } - else if (wcsnicmp(p, L"idle", 4) == 0) { - moduleName = L"idlelib"; - } - } + 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) { - 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; + if (moduleName) { + /* Not even pretending we're going to free this memory. + * The OS will clean it all up when our process exits + */ + wchar_t **new_argv = (wchar_t **)PyMem_RawMalloc((argc + 2) * sizeof(wchar_t *)); + new_argv[0] = argv[0]; + new_argv[1] = _PyMem_RawWcsdup(L"-m"); + new_argv[2] = _PyMem_RawWcsdup(moduleName); + for (int i = 1; i < argc; ++i) { + new_argv[i + 2] = argv[i]; } + argv = new_argv; + argc += 2; } } - /* 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); - - free((void *)exeName); - free((void *)new_argv); - - return result; + return Py_Main(argc, (wchar_t**)argv); } #ifdef PYTHONW diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d87b4e2c01..942a8b6bec 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2448,8 +2448,6 @@ err_occurred: return _Py_INIT_ERR("can't initialize sys module"); } -#undef SET_SYS_FROM_STRING - /* Updating the sys namespace, returning integer error codes */ #define SET_SYS_FROM_STRING_INT_RESULT(key, value) \ do { \ @@ -2483,6 +2481,17 @@ _PySys_EndInit(PyObject *sysdict, _PyMainInterpreterConfig *config) SET_SYS_FROM_STRING_BORROW("exec_prefix", config->exec_prefix); SET_SYS_FROM_STRING_BORROW("base_exec_prefix", config->base_exec_prefix); +#ifdef MS_WINDOWS + const wchar_t *baseExecutable = _wgetenv(L"__PYVENV_BASE_EXECUTABLE__"); + if (baseExecutable) { + SET_SYS_FROM_STRING("_base_executable", + PyUnicode_FromWideChar(baseExecutable, -1)); + _wputenv_s(L"__PYVENV_BASE_EXECUTABLE__", L""); + } else { + SET_SYS_FROM_STRING_BORROW("_base_executable", config->executable); + } +#endif + if (config->argv != NULL) { SET_SYS_FROM_STRING_BORROW("argv", config->argv); } @@ -2528,6 +2537,7 @@ err_occurred: return -1; } +#undef SET_SYS_FROM_STRING #undef SET_SYS_FROM_STRING_BORROW #undef SET_SYS_FROM_STRING_INT_RESULT |