Skip to content

Commit c827c1f

Browse files
committed
Improved aliases check
1 parent 2f38121 commit c827c1f

File tree

4 files changed

+72
-18
lines changed

4 files changed

+72
-18
lines changed

_msbuild.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class ResourceFile(CSourceFile):
8484
CFunction('date_as_str'),
8585
CFunction('datetime_as_str'),
8686
CFunction('reg_rename_key'),
87+
CFunction('get_current_package'),
8788
CFunction('read_alias_package'),
8889
source='src/_native',
8990
RootNamespace='_native',

_msbuild_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
CFunction('date_as_str'),
5151
CFunction('datetime_as_str'),
5252
CFunction('reg_rename_key'),
53+
CFunction('get_current_package'),
5354
CFunction('read_alias_package'),
5455
source='src/_native',
5556
),

src/_native/misc.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <Python.h>
22
#include <windows.h>
3+
#include <appmodel.h>
34

45
#include "helpers.h"
56

@@ -120,6 +121,22 @@ PyObject *reg_rename_key(PyObject *, PyObject *args, PyObject *kwargs) {
120121
}
121122

122123

124+
PyObject *get_current_package(PyObject *, PyObject *, PyObject *) {
125+
wchar_t package_name[256];
126+
UINT32 cch = sizeof(package_name) / sizeof(package_name[0]);
127+
int err = GetCurrentPackageFamilyName(&cch, package_name);
128+
switch (err) {
129+
case ERROR_SUCCESS:
130+
return PyUnicode_FromWideChar(package_name, cch);
131+
case APPMODEL_ERROR_NO_PACKAGE:
132+
return Py_GetConstant(Py_CONSTANT_NONE);
133+
default:
134+
PyErr_SetFromWindowsErr(err);
135+
return NULL;
136+
}
137+
}
138+
139+
123140
PyObject *read_alias_package(PyObject *, PyObject *args, PyObject *kwargs) {
124141
static const char * keywords[] = {"path", NULL};
125142
wchar_t *path = NULL;
@@ -136,22 +153,32 @@ PyObject *read_alias_package(PyObject *, PyObject *args, PyObject *kwargs) {
136153
return NULL;
137154
}
138155

139-
wchar_t buffer[32768];
156+
struct {
157+
DWORD tag;
158+
DWORD _reserved1;
159+
DWORD _reserved2;
160+
wchar_t package_name[256];
161+
wchar_t nul;
162+
} buffer;
140163
DWORD nread;
141164

142165
if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0,
143-
buffer, sizeof(buffer), &nread, NULL)) {
166+
&buffer, sizeof(buffer), &nread, NULL)
167+
// we expect our buffer to be too small, but we only want the package
168+
&& GetLastError() != ERROR_MORE_DATA) {
144169
PyErr_SetFromWindowsErr(0);
145170
CloseHandle(h);
146171
return NULL;
147172
}
173+
148174
CloseHandle(h);
149175

150-
if (*(DWORD*)buffer != IO_REPARSE_TAG_APPEXECLINK) {
176+
if (buffer.tag != IO_REPARSE_TAG_APPEXECLINK) {
151177
return Py_GetConstant(Py_CONSTANT_NONE);
152178
}
153179

154-
return PyUnicode_FromWideChar(&buffer[4], nread / sizeof(wchar_t) - 5);
180+
buffer.nul = 0;
181+
return PyUnicode_FromWideChar(buffer.package_name, -1);
155182
}
156183

157184
}

src/manage/firstrun.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,36 @@
1919

2020
def check_app_alias(cmd):
2121
LOGGER.debug("Checking app execution aliases")
22-
from _native import read_alias_package
22+
from _native import get_current_package, read_alias_package
23+
# Expected identities:
24+
# Side-loaded MSIX
25+
# * "PythonSoftwareFoundation.PythonManager_3847v3x7pw1km",
26+
# Store package
27+
# * "PythonSoftwareFoundation.PythonManager_qbz5n2kfra8p0",
28+
# Development build
29+
# * "PythonSoftwareFoundation.PythonManager_m8z88z54g2w36",
30+
# MSI/dev install
31+
# * None
32+
try:
33+
pkg = get_current_package()
34+
except OSError:
35+
LOGGER.debug("Failed to get current package name.", exc_info=True)
36+
pkg = None
37+
if not pkg:
38+
pkg = ""
39+
#LOGGER.debug("Check skipped: MSI install can't do this check")
40+
#return True
41+
LOGGER.debug("Checking for %s", pkg)
2342
root = Path(os.environ["LocalAppData"]) / "Microsoft/WindowsApps"
2443
for name in ["py.exe", "pyw.exe", "python.exe", "pythonw.exe", "python3.exe", "pymanager.exe"]:
2544
exe = root / name
2645
try:
2746
LOGGER.debug("Reading from %s", exe)
28-
package = (read_alias_package(exe) or "").split("\0")
29-
LOGGER.debug("Data: %r", package)
30-
if package[1] not in (
31-
# Side-loaded MSIX
32-
"PythonSoftwareFoundation.PythonManager_3847v3x7pw1km",
33-
# Store packaged
34-
"PythonSoftwareFoundation.PythonManager_qbz5n2kfra8p0",
35-
# Development build
36-
"PythonSoftwareFoundation.PythonManager_m8z88z54g2w36",
37-
):
47+
package = read_alias_package(exe)
48+
LOGGER.debug("Package: %r", package)
49+
if package not in pkg:
3850
LOGGER.debug("Check failed: package did not match identity")
51+
return False
3952
except FileNotFoundError:
4053
LOGGER.debug("Check failed: did not find %s", exe)
4154
return False
@@ -59,7 +72,10 @@ def check_long_paths(cmd):
5972

6073
def check_py_on_path(cmd):
6174
LOGGER.debug("Checking for legacy py.exe on PATH")
62-
from _native import read_alias_package
75+
from _native import get_current_package, read_alias_package
76+
if not get_current_package():
77+
LOGGER.debug("Check skipped: MSI install can't do this check")
78+
return True
6379
for p in os.environ["PATH"].split(";"):
6480
if not p:
6581
continue
@@ -230,7 +246,7 @@ def first_run(cmd):
230246
if __name__ == "__main__":
231247
class TestCommand:
232248
enabled = True
233-
global_dir = r".\test-bin"
249+
global_dir = Path(os.path.expandvars(r"%LocalAppData%\Python\bin"))
234250
explicit = False
235251
confirm = True
236252
check_app_alias = True
@@ -241,7 +257,16 @@ class TestCommand:
241257
check_default_tag = True
242258

243259
def get_installs(self, *args, **kwargs):
244-
return []
260+
import json
261+
root = Path(os.path.expandvars(r"%LocalAppData%\Python"))
262+
result = []
263+
for d in root.iterdir():
264+
inst = d / "__install__.json"
265+
try:
266+
result.append(json.loads(inst.read_text()))
267+
except FileNotFoundError:
268+
pass
269+
return result
245270

246271
def _ask(self, fmt, *args, yn_text="Y/n", expect_char="y"):
247272
if not self.confirm:

0 commit comments

Comments
 (0)