Skip to content

Commit 4c1976e

Browse files
authored
Pass through list of installs to warn about to shortcut implementations. (#99)
Fixes #85
1 parent 1ddf8e9 commit 4c1976e

File tree

9 files changed

+119
-38
lines changed

9 files changed

+119
-38
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ jobs:
149149

150150
- name: 'Launch default runtime'
151151
run: pymanager exec -m site
152+
env:
153+
PYMANAGER_DEBUG: true
154+
155+
- name: 'Uninstall runtime'
156+
run: pymanager uninstall -y default
157+
env:
158+
PYMANAGER_DEBUG: true
152159

153160
- name: 'Emulate first launch'
154161
run: |

ci/release.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,14 @@ stages:
256256
- powershell: |
257257
pymanager exec -m site
258258
displayName: 'Launch default runtime'
259+
env:
260+
PYMANAGER_DEBUG: true
261+
262+
- powershell: |
263+
pymanager uninstall -y default
264+
displayName: 'Uninstall runtime'
265+
env:
266+
PYMANAGER_DEBUG: true
259267
260268
- powershell: |
261269
$i = (mkdir -force test_installs)

src/manage/arputils.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from .fsutils import rglob
66
from .logging import LOGGER
77
from .pathutils import Path
8+
from .tagutils import install_matches_any
9+
810

911
def _root():
1012
return winreg.CreateKey(
@@ -59,7 +61,7 @@ def _set_value(key, name, value):
5961
winreg.SetValueEx(key, name, None, winreg.REG_SZ, str(value))
6062

6163

62-
def _make(key, item, shortcut):
64+
def _make(key, item, shortcut, *, allow_warn=True):
6365
prefix = Path(item["prefix"])
6466

6567
for value, from_dict, value_name, relative_to in [
@@ -122,21 +124,22 @@ def _iter_keys(key):
122124
return
123125

124126

125-
def create_one(install, shortcut):
127+
def create_one(install, shortcut, warn_for=[]):
128+
allow_warn = install_matches_any(install, warn_for)
126129
with _root() as root:
127130
install_id = f"pymanager-{install['id']}"
128131
LOGGER.debug("Creating ARP entry for %s", install_id)
129132
try:
130133
with winreg.CreateKey(root, install_id) as key:
131-
_make(key, install, shortcut)
134+
_make(key, install, shortcut, allow_warn=allow_warn)
132135
except OSError:
133136
LOGGER.debug("Failed to create entry for %s", install_id)
134137
LOGGER.debug("TRACEBACK:", exc_info=True)
135138
_delete_key(root, install_id)
136139
raise
137140

138141

139-
def cleanup(preserve_installs):
142+
def cleanup(preserve_installs, warn_for=[]):
140143
keep = {f"pymanager-{i['id']}".casefold() for i in preserve_installs}
141144
to_delete = []
142145
with _root() as root:

src/manage/install_command.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,41 +242,44 @@ def _write_alias(cmd, install, alias, target):
242242
launcher = _if_exists(launcher, "-64")
243243
LOGGER.debug("Create %s linking to %s using %s", alias["name"], target, launcher)
244244
if not launcher or not launcher.is_file():
245-
LOGGER.warn("Skipping %s alias because the launcher template was not found.", alias["name"])
245+
if install_matches_any(install, getattr(cmd, "tags", None)):
246+
LOGGER.warn("Skipping %s alias because the launcher template was not found.", alias["name"])
247+
else:
248+
LOGGER.debug("Skipping %s alias because the launcher template was not found.", alias["name"])
246249
return
247250
p.write_bytes(launcher.read_bytes())
248251
p.with_name(p.name + ".__target__").write_text(str(target), encoding="utf-8")
249252

250253

251254
def _create_shortcut_pep514(cmd, install, shortcut):
252255
from .pep514utils import update_registry
253-
update_registry(cmd.pep514_root, install, shortcut)
256+
update_registry(cmd.pep514_root, install, shortcut, cmd.tags)
254257

255258

256259
def _cleanup_shortcut_pep514(cmd, install_shortcut_pairs):
257260
from .pep514utils import cleanup_registry
258-
cleanup_registry(cmd.pep514_root, {s["Key"] for i, s in install_shortcut_pairs})
261+
cleanup_registry(cmd.pep514_root, {s["Key"] for i, s in install_shortcut_pairs}, cmd.tags)
259262

260263

261264
def _create_start_shortcut(cmd, install, shortcut):
262265
from .startutils import create_one
263-
create_one(cmd.start_folder, install, shortcut)
266+
create_one(cmd.start_folder, install, shortcut, cmd.tags)
264267

265268

266269
def _cleanup_start_shortcut(cmd, install_shortcut_pairs):
267270
from .startutils import cleanup
268-
cleanup(cmd.start_folder, [s for i, s in install_shortcut_pairs])
271+
cleanup(cmd.start_folder, [s for i, s in install_shortcut_pairs], cmd.tags)
269272

270273

271274
def _create_arp_entry(cmd, install, shortcut):
272275
# ARP = Add/Remove Programs
273276
from .arputils import create_one
274-
create_one(install, shortcut)
277+
create_one(install, shortcut, cmd.tags)
275278

276279

277280
def _cleanup_arp_entries(cmd, install_shortcut_pairs):
278281
from .arputils import cleanup
279-
cleanup([i for i, s in install_shortcut_pairs])
282+
cleanup([i for i, s in install_shortcut_pairs], cmd.tags)
280283

281284

282285
SHORTCUT_HANDLERS = {
@@ -359,7 +362,7 @@ def print_cli_shortcuts(cmd):
359362
names = get_install_alias_names(aliases, windowed=True)
360363
LOGGER.debug("%s will be launched by %s", i["display-name"], ", ".join(names))
361364

362-
if tags and not install_matches_any(i, cmd.tags):
365+
if not install_matches_any(i, tags):
363366
continue
364367

365368
names = get_install_alias_names(aliases, windowed=False)

src/manage/pep514utils.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .logging import LOGGER
66
from .pathutils import Path
7+
from .tagutils import install_matches_any
78
from .verutils import Version
89

910

@@ -136,7 +137,7 @@ def _update_reg_values(key, data, install, exclude=set()):
136137
winreg.SetValueEx(key, k, None, v_kind, v)
137138

138139

139-
def _is_tag_managed(company_key, tag_name, *, creating=False):
140+
def _is_tag_managed(company_key, tag_name, *, creating=False, allow_warn=True):
140141
try:
141142
tag = winreg.OpenKey(company_key, tag_name)
142143
except FileNotFoundError:
@@ -188,12 +189,15 @@ def _is_tag_managed(company_key, tag_name, *, creating=False):
188189
new_name = f"{orig_name}.{i}"
189190
# Raises standard PermissionError (5) if new_name exists
190191
reg_rename_key(tag.handle, orig_name, new_name)
191-
LOGGER.warn("An existing registry key for %s was renamed to %s "
192-
"because it appeared to be invalid. If this is "
193-
"correct, the registry key can be safely deleted. "
194-
"To avoid this in future, ensure that the "
195-
"InstallPath key refers to a valid path.",
196-
tag_name, new_name)
192+
if allow_warn:
193+
LOGGER.warn("An existing registry key for %s was renamed to %s "
194+
"because it appeared to be invalid. If this is "
195+
"correct, the registry key can be safely deleted. "
196+
"To avoid this in future, ensure that the "
197+
"InstallPath key refers to a valid path.",
198+
tag_name, new_name)
199+
else:
200+
LOGGER.debug("Renamed %s to %s", tag_name, new_name)
197201
break
198202
except FileNotFoundError:
199203
LOGGER.debug("Original key disappeared, so we will claim it")
@@ -207,8 +211,12 @@ def _is_tag_managed(company_key, tag_name, *, creating=False):
207211
orig_name, new_name, exc_info=True)
208212
raise
209213
else:
210-
LOGGER.warn("Attempted to clean up invalid registry key %s but "
211-
"failed after too many attempts.", tag_name)
214+
if allow_warn:
215+
LOGGER.warn("Attempted to clean up invalid registry key %s but "
216+
"failed after too many attempts.", tag_name)
217+
else:
218+
LOGGER.debug("Attempted to clean up invalid registry key %s but "
219+
"failed after too many attempts.", tag_name)
212220
return False
213221
return True
214222

@@ -226,31 +234,40 @@ def _split_root(root_name):
226234
return hive, name
227235

228236

229-
def update_registry(root_name, install, data):
237+
def update_registry(root_name, install, data, warn_for=[]):
230238
hive, name = _split_root(root_name)
231239
with winreg.CreateKey(hive, name) as root:
232-
if _is_tag_managed(root, data["Key"], creating=True):
240+
allow_warn = install_matches_any(install, warn_for)
241+
if _is_tag_managed(root, data["Key"], creating=True, allow_warn=allow_warn):
233242
with winreg.CreateKey(root, data["Key"]) as tag:
234243
LOGGER.debug("Creating/updating %s\\%s", root_name, data["Key"])
235244
winreg.SetValueEx(tag, "ManagedByPyManager", None, winreg.REG_DWORD, 1)
236245
_update_reg_values(tag, data, install, {"kind", "Key", "ManagedByPyManager"})
237-
else:
246+
elif allow_warn:
238247
LOGGER.warn("An existing runtime is registered at %s in the registry, "
239248
"and so the new one has not been registered.", data["Key"])
240249
LOGGER.info("This may prevent some other applications from detecting "
241250
"the new installation, although 'py -V:...' will work. "
242251
"To register the new installation, remove the existing "
243252
"runtime and then run 'py install --refresh'")
253+
else:
254+
LOGGER.debug("An existing runtime is registered at %s and so the new "
255+
"install has not been registered.", data["Key"])
244256

245257

246-
def cleanup_registry(root_name, keep):
258+
def cleanup_registry(root_name, keep, warn_for=[]):
247259
hive, name = _split_root(root_name)
248260
with _reg_open(hive, name, writable=True) as root:
249261
for company_name in _iter_keys(root):
250262
any_left = False
251263
with winreg.OpenKey(root, company_name, access=winreg.KEY_ALL_ACCESS) as company:
252264
for tag_name in _iter_keys(company):
253-
if f"{company_name}\\{tag_name}" in keep or not _is_tag_managed(company, tag_name):
265+
# Calculate whether to show warnings or not
266+
install = {"company": company_name, "tag": tag_name}
267+
allow_warn = install_matches_any(install, warn_for)
268+
269+
if (f"{company_name}\\{tag_name}" in keep
270+
or not _is_tag_managed(company, tag_name, allow_warn=allow_warn)):
254271
any_left = True
255272
else:
256273
_reg_rmtree(company, tag_name)

src/manage/startutils.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .fsutils import rmtree, unlink
44
from .logging import LOGGER
55
from .pathutils import Path
6+
from .tagutils import install_matches_any
67

78

89
def _unprefix(p, prefix):
@@ -25,7 +26,7 @@ def _unprefix(p, prefix):
2526
return p
2627

2728

28-
def _make(root, prefix, item):
29+
def _make(root, prefix, item, allow_warn=True):
2930
n = item["Name"]
3031
try:
3132
_make_directory(root, n, prefix, item["Items"])
@@ -39,7 +40,10 @@ def _make(root, prefix, item):
3940
try:
4041
lnk.relative_to(root)
4142
except ValueError:
42-
LOGGER.warn("Package attempted to create shortcut outside of its directory")
43+
if allow_warn:
44+
LOGGER.warn("Package attempted to create shortcut outside of its directory")
45+
else:
46+
LOGGER.debug("Package attempted to create shortcut outside of its directory")
4347
LOGGER.debug("Path: %s", lnk)
4448
LOGGER.debug("Directory: %s", root)
4549
return None
@@ -136,12 +140,12 @@ def _get_to_keep(keep, root, item):
136140
pass
137141

138142

139-
def create_one(root, install, shortcut):
143+
def create_one(root, install, shortcut, warn_for=[]):
140144
root = Path(_native.shortcut_get_start_programs()) / root
141-
_make(root, install["prefix"], shortcut)
145+
_make(root, install["prefix"], shortcut, allow_warn=install_matches_any(install, warn_for))
142146

143147

144-
def cleanup(root, preserve):
148+
def cleanup(root, preserve, warn_for=[]):
145149
root = Path(_native.shortcut_get_start_programs()) / root
146150

147151
if not root.is_dir():

src/manage/uninstall_command.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ def execute(cmd):
2626
cmd.virtual_env = None
2727
installed = list(cmd.get_installs())
2828

29+
cmd.tags = []
30+
2931
if cmd.purge:
3032
if cmd.ask_yn("Uninstall all runtimes?"):
3133
for i in installed:
@@ -61,16 +63,18 @@ def execute(cmd):
6163
to_uninstall = []
6264
if not cmd.by_id:
6365
for tag in cmd.args:
64-
if tag.casefold() == "default".casefold():
65-
tag = cmd.default_tag
6666
try:
67-
t_or_r = tag_or_range(tag)
67+
if tag.casefold() == "default".casefold():
68+
cmd.tags.append(tag_or_range(cmd.default_tag))
69+
else:
70+
cmd.tags.append(tag_or_range(tag))
6871
except ValueError as ex:
6972
LOGGER.warn("%s", ex)
70-
continue
73+
74+
for tag in cmd.tags:
7175
candidates = get_matching_install_tags(
7276
installed,
73-
t_or_r,
77+
tag,
7478
default_platform=cmd.default_platform,
7579
)
7680
if not candidates:

tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ def quiet_log():
6060

6161

6262
class LogCaptureHandler(list):
63-
def skip_until(self, pattern, args=()):
63+
def skip_until(self, pattern, args=None):
6464
return ('until', pattern, args)
6565

66-
def not_logged(self, pattern, args=()):
66+
def not_logged(self, pattern, args=None):
6767
return ('not', pattern, args)
6868

6969
def __call__(self, *cmp):

tests/test_pep514utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import winreg
33

44
from manage import pep514utils
5+
from manage import tagutils
56

67
def test_is_tag_managed(registry, tmp_path):
78
registry.setup(Company={
@@ -23,3 +24,37 @@ def test_is_tag_managed(registry, tmp_path):
2324
assert pep514utils._is_tag_managed(registry.key, r"Company\3.0", creating=True)
2425
with winreg.OpenKey(registry.key, r"Company\3.0.2"):
2526
pass
27+
28+
29+
def test_is_tag_managed_warning_suppressed(registry, tmp_path, assert_log):
30+
registry.setup(Company={
31+
"3.0.0": {"": "Just in the way here"},
32+
"3.0.1": {"": "Also in the way here"},
33+
})
34+
pep514utils.update_registry(
35+
rf"HKEY_CURRENT_USER\{registry.root}",
36+
dict(company="Company", tag="3.0.0"),
37+
dict(kind="pep514", Key="Company\\3.0.0", InstallPath=dict(_="dir")),
38+
warn_for=[tagutils.tag_or_range(r"Company\3.0.1")],
39+
)
40+
assert_log(
41+
"Registry key %s appears invalid.+",
42+
assert_log.not_logged("An existing runtime is registered at %s"),
43+
)
44+
45+
46+
def test_is_tag_managed_warning(registry, tmp_path, assert_log):
47+
registry.setup(Company={
48+
"3.0.0": {"": "Just in the way here"},
49+
"3.0.1": {"": "Also in the way here"},
50+
})
51+
pep514utils.update_registry(
52+
rf"HKEY_CURRENT_USER\{registry.root}",
53+
dict(company="Company", tag="3.0.0"),
54+
dict(kind="pep514", Key="Company\\3.0.0", InstallPath=dict(_="dir")),
55+
warn_for=[tagutils.tag_or_range(r"Company\3.0.0")],
56+
)
57+
assert_log(
58+
"Registry key %s appears invalid.+",
59+
assert_log.skip_until("An existing registry key for %s"),
60+
)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy