summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@python.org>2019-06-28 10:02:13 -0700
committerNed Deily <nad@python.org>2019-07-01 22:24:26 -0400
commit3c34ea97a341e4dd80b542c99c593f014a8ae410 (patch)
tree0c65fde80bdd1fb2fe7f9613cada893d15bfd180
parentcc0bf97d61fbe844843f28abc510a11f3ef09942 (diff)
downloadcpython-git-3c34ea97a341e4dd80b542c99c593f014a8ae410.tar.gz
bpo-37369: Fixes path for sys.executable when running from the Microsoft Store (GH-14450)
-rw-r--r--Lib/site.py7
-rw-r--r--Lib/test/test_venv.py8
-rw-r--r--Lib/venv/__init__.py2
-rw-r--r--Misc/NEWS.d/next/Windows/2019-06-28-08-09-08.bpo-37369.1iVpxq.rst1
-rw-r--r--PC/getpathp.c12
-rw-r--r--PC/python_uwp.cpp218
-rw-r--r--Python/sysmodule.c14
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