Skip to content

Commit ffcd936

Browse files
authored
Update first launch test command (#109)
Fixes #105
1 parent 926f13b commit ffcd936

File tree

6 files changed

+81
-28
lines changed

6 files changed

+81
-28
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ jobs:
165165
download_dir="$i\_cache";
166166
global_dir="$i\_bin";
167167
} | Out-File $env:PYTHON_MANAGER_CONFIG -Encoding utf8
168-
pymanager exec
168+
pymanager install --configure -y
169169
if ($?) { pymanager list }
170170
env:
171171
PYTHON_MANAGER_INCLUDE_UNMANAGED: false

ci/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ stages:
279279
download_dir="$i\_cache";
280280
global_dir="$i\_bin";
281281
} | Out-File $env:PYTHON_MANAGER_CONFIG -Encoding utf8
282-
pymanager exec
282+
pymanager install --configure -y
283283
if ($?) { pymanager list }
284284
displayName: 'Emulate first launch'
285285
env:

src/manage/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,7 @@ def execute(self):
814814
self.show_welcome()
815815
if self.configure:
816816
cmd = FirstRun(["**first_run", "--explicit"], self.root)
817+
cmd.confirm = self.confirm
817818
cmd.execute()
818819
else:
819820
from .install_command import execute

src/manage/firstrun.py

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

121164

122165
def _check_global_dir_registry(cmd):
123-
import winreg
124166
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, "Environment") as key:
125167
path, kind = winreg.QueryValueEx(key, "Path")
126168
LOGGER.debug("Current registry path: %s", path)
@@ -139,7 +181,6 @@ def _check_global_dir_registry(cmd):
139181

140182

141183
def do_global_dir_on_path(cmd):
142-
import winreg
143184
added = notified = False
144185
try:
145186
LOGGER.debug("Adding %s to PATH", cmd.global_dir)
@@ -290,17 +331,8 @@ def first_run(cmd):
290331
"may need an administrator to approve, and will require a "
291332
"reboot. Some packages may fail to install without long "
292333
"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.")
334+
if not cmd.confirm or not cmd.ask_ny("Update setting now?"):
335+
do_configure_long_paths(cmd)
304336
elif cmd.explicit:
305337
LOGGER.info("Checked system long paths setting")
306338

@@ -314,10 +346,7 @@ def first_run(cmd):
314346
LOGGER.print("\nThis may interfere with launching the new 'py' "
315347
"command, and may be resolved by uninstalling "
316348
"'!B!Python launcher!W!'.\n", wrap=True)
317-
if (
318-
cmd.confirm and
319-
not cmd.ask_ny("Open Installed apps now?")
320-
):
349+
if cmd.confirm and not cmd.ask_ny("Open Installed apps now?"):
321350
os.startfile("ms-settings:appsfeatures")
322351
elif cmd.explicit:
323352
if r == "skip":
@@ -342,7 +371,7 @@ def first_run(cmd):
342371
"must manually edit environment variables to later "
343372
"remove the entry.\n", wrap=True)
344373
if (
345-
cmd.confirm and
374+
not cmd.confirm or
346375
not cmd.ask_ny("Add commands directory to your PATH now?")
347376
):
348377
do_global_dir_on_path(cmd)
@@ -352,7 +381,7 @@ def first_run(cmd):
352381
else:
353382
LOGGER.info("Checked PATH for versioned commands directory")
354383

355-
# This check must be last, because 'do_install' will exit the program.
384+
# This check must be last, because a failed install may exit the program.
356385
if cmd.check_any_install:
357386
if not check_any_install(cmd):
358387
welcome()
@@ -361,11 +390,11 @@ def first_run(cmd):
361390
LOGGER.print("!Y!You do not have any Python runtimes installed.!W!",
362391
level=logging.WARN)
363392
LOGGER.print("\nInstall the current latest version of CPython? If "
364-
"not, you can use !B!py install default!W! later to "
393+
"not, you can use '!B!py install default!W!' later to "
365394
"install, or one will be installed automatically when "
366395
"needed.\n", wrap=True)
367396
LOGGER.info("")
368-
if cmd.ask_yn("Install CPython now?"):
397+
if not cmd.confirm or cmd.ask_yn("Install CPython now?"):
369398
do_install(cmd)
370399
elif cmd.explicit:
371400
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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,25 @@ 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+
firstrun.do_configure_long_paths(fake_config, hive=registry.hive, keyname=registry.root, startfile=_raise_oserror)
250+
assert winreg.QueryValueEx(registry.key, "LongPathsEnabled") == (1, winreg.REG_DWORD)
251+
252+
253+
def test_do_configure_long_paths_elevated(protect_reg, fake_config, monkeypatch):
254+
startfile_calls = []
255+
def startfile(*a, **kw):
256+
startfile_calls.append((a, kw))
257+
# Pretend we can interact, so that os.startfile gets called
258+
fake_config.confirm = True
259+
firstrun.do_configure_long_paths(fake_config, startfile=startfile)
260+
assert startfile_calls
261+
assert startfile_calls[0][0][1:] == ("runas", "**configure-long-paths")

0 commit comments

Comments
 (0)