Skip to content

Commit a88652e

Browse files
zoobaned-deily
authored andcommitted
bpo-37369: Fix path handling when python.exe is used as a symlink (GH-14461)
1 parent 57eba3a commit a88652e

1 file changed

Lines changed: 62 additions & 44 deletions

File tree

PC/python_uwp.cpp

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,51 +27,70 @@ const wchar_t *PROGNAME = L"python.exe";
2727
#endif
2828
#endif
2929

30-
static void
31-
set_user_base()
30+
static std::wstring
31+
get_user_base()
3232
{
33-
wchar_t envBuffer[2048];
3433
try {
3534
const auto appData = winrt::Windows::Storage::ApplicationData::Current();
3635
if (appData) {
3736
const auto localCache = appData.LocalCacheFolder();
3837
if (localCache) {
3938
auto path = localCache.Path();
40-
if (!path.empty() &&
41-
!wcscpy_s(envBuffer, path.c_str()) &&
42-
!wcscat_s(envBuffer, L"\\local-packages")
43-
) {
44-
_wputenv_s(L"PYTHONUSERBASE", envBuffer);
39+
if (!path.empty()) {
40+
return std::wstring(path) + L"\\local-packages";
4541
}
4642
}
4743
}
4844
} catch (...) {
4945
}
46+
return std::wstring();
5047
}
5148

52-
static winrt::hstring
49+
static std::wstring
5350
get_package_family()
5451
{
5552
try {
5653
const auto package = winrt::Windows::ApplicationModel::Package::Current();
5754
if (package) {
5855
const auto id = package.Id();
59-
return id ? id.FamilyName() : winrt::hstring();
56+
if (id) {
57+
return std::wstring(id.FamilyName());
58+
}
6059
}
6160
}
6261
catch (...) {
6362
}
6463

65-
return winrt::hstring();
64+
return std::wstring();
65+
}
66+
67+
static std::wstring
68+
get_package_home()
69+
{
70+
try {
71+
const auto package = winrt::Windows::ApplicationModel::Package::Current();
72+
if (package) {
73+
const auto path = package.InstalledLocation();
74+
if (path) {
75+
return std::wstring(path.Path());
76+
}
77+
}
78+
}
79+
catch (...) {
80+
}
81+
82+
return std::wstring();
6683
}
6784

6885
static int
6986
set_process_name()
7087
{
71-
std::wstring executable, home;
72-
88+
const auto home = get_package_home();
7389
const auto family = get_package_family();
7490

91+
std::wstring executable;
92+
93+
/* If inside a package, use user's symlink name for executable */
7594
if (!family.empty()) {
7695
PWSTR localAppData;
7796
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
@@ -86,44 +105,40 @@ set_process_name()
86105
}
87106
}
88107

89-
home.resize(MAX_PATH);
90-
while (true) {
91-
DWORD len = GetModuleFileNameW(
92-
NULL, home.data(), (DWORD)home.size());
93-
if (len == 0) {
94-
home.clear();
95-
break;
96-
} else if (len == home.size() &&
97-
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
98-
home.resize(len * 2);
99-
} else {
100-
home.resize(len);
101-
size_t bslash = home.find_last_of(L"/\\");
102-
if (bslash != std::wstring::npos) {
103-
home.erase(bslash);
108+
/* Only use module filename if we don't have a home */
109+
if (home.empty() && executable.empty()) {
110+
executable.resize(MAX_PATH);
111+
while (true) {
112+
DWORD len = GetModuleFileNameW(
113+
NULL, executable.data(), (DWORD)executable.size());
114+
if (len == 0) {
115+
executable.clear();
116+
break;
117+
} else if (len == executable.size() &&
118+
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
119+
executable.resize(len * 2);
120+
} else {
121+
executable.resize(len);
122+
break;
104123
}
105-
break;
106124
}
107125
}
108126

109-
if (executable.empty() && !home.empty()) {
110-
executable = home + L"\\" + PROGNAME;
111-
}
112-
113127
if (!home.empty()) {
114128
Py_SetPythonHome(home.c_str());
115129
}
116-
if (!executable.empty()) {
117-
const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
118-
if (launcherPath) {
130+
131+
const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
132+
if (launcherPath) {
133+
if (!executable.empty()) {
119134
_wputenv_s(L"__PYVENV_BASE_EXECUTABLE__", executable.c_str());
120-
_Py_SetProgramFullPath(launcherPath);
121-
/* bpo-35873: Clear the environment variable to avoid it being
122-
* inherited by child processes. */
123-
_wputenv_s(L"__PYVENV_LAUNCHER__", L"");
124-
} else {
125-
_Py_SetProgramFullPath(executable.c_str());
126135
}
136+
_Py_SetProgramFullPath(launcherPath);
137+
/* bpo-35873: Clear the environment variable to avoid it being
138+
* inherited by child processes. */
139+
_wputenv_s(L"__PYVENV_LAUNCHER__", L"");
140+
} else if (!executable.empty()) {
141+
_Py_SetProgramFullPath(executable.c_str());
127142
}
128143

129144
return 1;
@@ -135,9 +150,12 @@ wmain(int argc, wchar_t **argv)
135150
if (!set_process_name()) {
136151
return 121;
137152
}
138-
set_user_base();
153+
const wchar_t *p = _wgetenv(L"PYTHONUSERBASE");
154+
if (!p || !*p) {
155+
_wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str());
156+
}
139157

140-
const wchar_t *p = wcsrchr(argv[0], L'\\');
158+
p = wcsrchr(argv[0], L'\\');
141159
if (!p) {
142160
p = argv[0];
143161
}

0 commit comments

Comments
 (0)