Skip to content

Commit 241aac8

Browse files
committed
Improve first run -y behaviour and add tests.
1 parent 81341f6 commit 241aac8

File tree

3 files changed

+78
-25
lines changed

3 files changed

+78
-25
lines changed

src/manage/firstrun.py

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import sys
33
import time
4+
import winreg
45

56
from . import logging
67
from .pathutils import Path
@@ -51,14 +52,20 @@ def check_app_alias(cmd):
5152
LOGGER.debug("Check passed: aliases are correct")
5253
return True
5354

55+
_LONG_PATH_KEY = r"System\CurrentControlSet\Control\FileSystem"
56+
_LONG_PATH_VALUENAME = "LongPathsEnabled"
5457

55-
def check_long_paths(cmd):
58+
def check_long_paths(
59+
cmd,
60+
*,
61+
hive=winreg.HKEY_LOCAL_MACHINE,
62+
keyname=_LONG_PATH_KEY,
63+
valuename=_LONG_PATH_VALUENAME,
64+
):
5665
LOGGER.debug("Checking long paths setting")
57-
import winreg
5866
try:
59-
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE,
60-
r"System\CurrentControlSet\Control\FileSystem") as key:
61-
if winreg.QueryValueEx(key, "LongPathsEnabled") == (1, winreg.REG_DWORD):
67+
with winreg.OpenKeyEx(hive, keyname) as key:
68+
if winreg.QueryValueEx(key, valuename) == (1, winreg.REG_DWORD):
6269
LOGGER.debug("Check passed: registry key is OK")
6370
return True
6471
except FileNotFoundError:
@@ -67,6 +74,41 @@ def check_long_paths(cmd):
6774
return False
6875

6976

77+
def do_configure_long_paths(
78+
cmd,
79+
*,
80+
hive=winreg.HKEY_LOCAL_MACHINE,
81+
keyname=_LONG_PATH_KEY,
82+
valuename=_LONG_PATH_VALUENAME,
83+
):
84+
LOGGER.debug("Updating long paths setting")
85+
try:
86+
with winreg.CreateKeyEx(hive, keyname) as key:
87+
winreg.SetValueEx(key, valuename, None, winreg.REG_DWORD, 1)
88+
LOGGER.info("The setting has been successfully updated, and will "
89+
"take effect after the next reboot.")
90+
return
91+
except OSError:
92+
pass
93+
if not cmd.confirm:
94+
# Without confirmation, we assume we can't elevate, so attempt
95+
# as the current user and if it fails just print a message.
96+
LOGGER.warn("The setting has not been updated. Please rerun '!B!py "
97+
"install --configure!W! with administrative privileges.")
98+
return
99+
os.startfile(sys.executable, "runas", "**configure-long-paths", show_cmd=0)
100+
for _ in range(5):
101+
time.sleep(0.25)
102+
if check_long_paths(cmd):
103+
LOGGER.info("The setting has been successfully updated, and will "
104+
"take effect after the next reboot.")
105+
break
106+
else:
107+
LOGGER.warn("The setting may not have been updated. Please "
108+
"visit the additional help link at the end for "
109+
"more assistance.")
110+
111+
70112
def check_py_on_path(cmd):
71113
LOGGER.debug("Checking for legacy py.exe on PATH")
72114
from _native import read_alias_package
@@ -120,7 +162,6 @@ def check_global_dir(cmd):
120162

121163

122164
def _check_global_dir_registry(cmd):
123-
import winreg
124165
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, "Environment") as key:
125166
path, kind = winreg.QueryValueEx(key, "Path")
126167
LOGGER.debug("Current registry path: %s", path)
@@ -139,7 +180,6 @@ def _check_global_dir_registry(cmd):
139180

140181

141182
def do_global_dir_on_path(cmd):
142-
import winreg
143183
added = notified = False
144184
try:
145185
LOGGER.debug("Adding %s to PATH", cmd.global_dir)
@@ -290,17 +330,8 @@ def first_run(cmd):
290330
"may need an administrator to approve, and will require a "
291331
"reboot. Some packages may fail to install without long "
292332
"path support enabled.\n", wrap=True)
293-
if cmd.confirm and not cmd.ask_ny("Update setting now?"):
294-
os.startfile(sys.executable, "runas", "**configure-long-paths", show_cmd=0)
295-
for _ in range(5):
296-
time.sleep(0.25)
297-
if check_long_paths(cmd):
298-
LOGGER.info("The setting has been successfully updated.")
299-
break
300-
else:
301-
LOGGER.warn("The setting may not have been updated. Please "
302-
"visit the additional help link at the end for "
303-
"more assistance.")
333+
if not cmd.confirm or not cmd.ask_ny("Update setting now?"):
334+
do_configure_long_paths(cmd)
304335
elif cmd.explicit:
305336
LOGGER.info("Checked system long paths setting")
306337

@@ -314,10 +345,7 @@ def first_run(cmd):
314345
LOGGER.print("\nThis may interfere with launching the new 'py' "
315346
"command, and may be resolved by uninstalling "
316347
"'!B!Python launcher!W!'.\n", wrap=True)
317-
if (
318-
cmd.confirm and
319-
not cmd.ask_ny("Open Installed apps now?")
320-
):
348+
if cmd.confirm and not cmd.ask_ny("Open Installed apps now?"):
321349
os.startfile("ms-settings:appsfeatures")
322350
elif cmd.explicit:
323351
if r == "skip":
@@ -342,7 +370,7 @@ def first_run(cmd):
342370
"must manually edit environment variables to later "
343371
"remove the entry.\n", wrap=True)
344372
if (
345-
cmd.confirm and
373+
not cmd.confirm or
346374
not cmd.ask_ny("Add commands directory to your PATH now?")
347375
):
348376
do_global_dir_on_path(cmd)
@@ -352,7 +380,7 @@ def first_run(cmd):
352380
else:
353381
LOGGER.info("Checked PATH for versioned commands directory")
354382

355-
# This check must be last, because 'do_install' will exit the program.
383+
# This check must be last, because a failed install may exit the program.
356384
if cmd.check_any_install:
357385
if not check_any_install(cmd):
358386
welcome()
@@ -365,7 +393,7 @@ def first_run(cmd):
365393
"install, or one will be installed automatically when "
366394
"needed.\n", wrap=True)
367395
LOGGER.info("")
368-
if cmd.ask_yn("Install CPython now?"):
396+
if not cmd.confirm or cmd.ask_yn("Install CPython now?"):
369397
do_install(cmd)
370398
elif cmd.explicit:
371399
LOGGER.info("Checked for any Python installs")

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ def localserver():
148148
class FakeConfig:
149149
def __init__(self, global_dir, installs=[]):
150150
self.global_dir = global_dir
151+
self.confirm = False
151152
self.installs = list(installs)
152153
self.shebang_can_run_anything = True
153154
self.shebang_can_run_anything_silently = False

tests/test_firstrun.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,27 @@ def test_do_global_dir_path_fail_broadcast(protect_reg, fake_config, assert_log,
237237
monkeypatch.setattr(winreg, "SetValueEx", lambda *a: None)
238238
firstrun.do_global_dir_on_path(fake_config)
239239
assert_log(assert_log.skip_until("Failed to notify of PATH environment.+"))
240+
241+
242+
def test_check_long_paths(registry, fake_config):
243+
assert not firstrun.check_long_paths(fake_config, hive=registry.hive, keyname=registry.root)
244+
registry.setup(LongPathsEnabled=1)
245+
assert firstrun.check_long_paths(fake_config, hive=registry.hive, keyname=registry.root)
246+
247+
248+
def test_do_configure_long_paths(registry, fake_config, monkeypatch):
249+
monkeypatch.setattr(os, "startfile", _raise_oserror)
250+
firstrun.do_configure_long_paths(fake_config, hive=registry.hive, keyname=registry.root)
251+
assert winreg.QueryValueEx(registry.key, "LongPathsEnabled") == (1, winreg.REG_DWORD)
252+
253+
254+
def test_do_configure_long_paths_elevated(protect_reg, fake_config, monkeypatch):
255+
startfile_calls = []
256+
def startfile(*a, **kw):
257+
startfile_calls.append((a, kw))
258+
monkeypatch.setattr(os, "startfile", startfile)
259+
# Pretend we can interact, so that os.startfile gets called
260+
fake_config.confirm = True
261+
firstrun.do_configure_long_paths(fake_config)
262+
assert startfile_calls
263+
assert startfile_calls[0][0][1:] == ("runas", "**configure-long-paths")

0 commit comments

Comments
 (0)