Skip to content

Commit e286ec7

Browse files
committed
Improved first run and logging
1 parent 1f65d80 commit e286ec7

File tree

4 files changed

+108
-42
lines changed

4 files changed

+108
-42
lines changed

src/_native/misc.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ PyObject *get_current_package(PyObject *, PyObject *, PyObject *) {
127127
int err = GetCurrentPackageFamilyName(&cch, package_name);
128128
switch (err) {
129129
case ERROR_SUCCESS:
130-
return PyUnicode_FromWideChar(package_name, cch);
130+
return PyUnicode_FromWideChar(package_name, cch ? cch - 1 : 0);
131131
case APPMODEL_ERROR_NO_PACKAGE:
132132
return Py_GetConstant(Py_CONSTANT_NONE);
133133
default:

src/manage/firstrun.py

Lines changed: 94 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
import sys
3+
import time
4+
35

46
if __name__ == "__main__":
57
__package__ = "manage"
@@ -13,9 +15,11 @@
1315
setattr(_native, k, getattr(_native_test, k))
1416

1517

16-
from .logging import LOGGER
18+
from . import logging
1719
from .pathutils import Path
1820

21+
LOGGER = logging.LOGGER
22+
1923

2024
def _package_name():
2125
from _native import get_current_package
@@ -50,7 +54,7 @@ def check_app_alias(cmd):
5054
try:
5155
LOGGER.debug("Reading from %s", exe)
5256
package = read_alias_package(exe)
53-
LOGGER.debug("Package: %r", package)
57+
LOGGER.debug("Package: %s", package)
5458
if package != pkg:
5559
LOGGER.debug("Check failed: package did not match identity")
5660
return False
@@ -164,7 +168,10 @@ def do_global_dir_on_path(cmd):
164168
if p.casefold() == str(cmd.global_dir).casefold():
165169
LOGGER.debug("Path is already found.")
166170
return
167-
newpath = initial + (";" if initial else "") + str(Path(cmd.global_dir).absolute())
171+
newpath = initial.rstrip(";")
172+
if newpath:
173+
newpath += ";"
174+
newpath += str(Path(cmd.global_dir).absolute())
168175
LOGGER.debug("New path: %s", newpath)
169176
# Expand the value and ensure we are found
170177
for p in os.path.expandvars(newpath).split(";"):
@@ -195,10 +202,13 @@ def do_global_dir_on_path(cmd):
195202
LOGGER.warn("Failed to notify of PATH environment variable change.")
196203
LOGGER.info("You may need to sign out or restart to see the changes.")
197204
elif not added:
198-
LOGGER.warn("Failed to update PATH environment variable successfully.")
205+
LOGGER.error("Failed to update PATH environment variable successfully.")
199206
LOGGER.info("You may add it yourself by opening 'Edit environment "
200207
"variables' and adding this directory to 'PATH': !B!%s!W!",
201208
cmd.global_dir)
209+
else:
210+
LOGGER.info("PATH has been updated, and will take effect after "
211+
"opening a new terminal.")
202212

203213

204214
def check_any_install(cmd):
@@ -214,15 +224,15 @@ def do_install(cmd):
214224
from .commands import find_command
215225
try:
216226
inst_cmd = find_command(["install", "default", "--automatic"], cmd.root)
217-
except Exception as ex:
227+
except Exception:
218228
LOGGER.debug("Failed to find 'install' command.", exc_info=True)
219229
LOGGER.warn("We couldn't install right now.")
220230
LOGGER.info("Use !B!py install default!W! later to install.")
221231
sys.exit(1)
222232
else:
223233
try:
224234
inst_cmd.execute()
225-
except Exception as ex:
235+
except Exception:
226236
LOGGER.debug("Failed to run 'install' command.", exc_info=True)
227237
raise
228238

@@ -232,9 +242,14 @@ class _Welcome:
232242
def __call__(self):
233243
if not self._shown:
234244
self._shown = True
235-
LOGGER.info("!G!Welcome to the Python installation manager "
236-
"configuration helper.!W!")
237-
LOGGER.info("")
245+
LOGGER.print("!G!Welcome to the Python installation manager "
246+
"configuration helper.!W!")
247+
248+
249+
def line_break():
250+
LOGGER.print()
251+
LOGGER.print("!B!" + "*" * logging.CONSOLE_MAX_WIDTH + "!W!")
252+
LOGGER.print()
238253

239254

240255
def first_run(cmd):
@@ -245,20 +260,32 @@ def first_run(cmd):
245260
if cmd.explicit:
246261
welcome()
247262

263+
shown_any = False
264+
248265
if cmd.check_app_alias:
249266
r = check_app_alias(cmd)
250267
if not r:
251268
welcome()
252-
LOGGER.warn("Your app execution alias settings are configured to launch "
253-
"other commands besides 'py' and 'python'.")
254-
LOGGER.info("This can be fixed by opening the '!B!Manage app execution "
255-
"aliases!W!' settings page and enabling each item labelled "
256-
"'!B!Python (default)!W!' and '!B!Python install manager!W!'.")
269+
line_break()
270+
shown_any = True
271+
LOGGER.print("!Y!Your app execution alias settings are configured to launch "
272+
"other commands besides 'py' and 'python'.!W!",
273+
level=logging.WARN)
274+
LOGGER.print("\nThis can be fixed by opening the '!B!Manage app "
275+
"execution aliases!W!' settings page and enabling each "
276+
"item labelled '!B!Python (default)!W!' and '!B!Python "
277+
"install manager!W!'.\n", wrap=True)
257278
if (
258279
cmd.confirm and
259-
not cmd.ask_ny("Open Settings now? (Select !B!App execution aliases!W! after opening)")
280+
not cmd.ask_ny("Open Settings now? Select !B!App execution "
281+
"aliases!W! after opening and scroll to the "
282+
"'!B!Python!W!' entries.")
260283
):
261284
os.startfile("ms-settings:advanced-apps")
285+
LOGGER.print("\nThe Settings app should be open. Navigate to the "
286+
"!B!App execution aliases!W! page and scroll to the "
287+
"'!B!Python!W!' entries to enable the new commands.",
288+
wrap=True)
262289
elif cmd.explicit:
263290
if r == "skip":
264291
LOGGER.info("Skipped app execution aliases check")
@@ -268,28 +295,43 @@ def first_run(cmd):
268295
if cmd.check_long_paths:
269296
if not check_long_paths(cmd):
270297
welcome()
271-
LOGGER.warn("Windows is not configured to allow paths longer than "
272-
"260 characters.")
273-
LOGGER.info("Python and some other apps can bypass this setting, but it "
274-
"requires changing a system-wide setting and a reboot. "
275-
"Some packages may fail to install without long path "
276-
"support enabled.")
298+
line_break()
299+
shown_any = True
300+
LOGGER.print("!Y!Windows is not configured to allow paths longer than "
301+
"260 characters.!W!", level=logging.WARN)
302+
LOGGER.print("\nPython and some other apps can exceed this limit, "
303+
"but it requires changing a system-wide setting and a "
304+
"reboot. Some packages may fail to install without long "
305+
"path support enabled.\n", wrap=True)
277306
if (
278307
cmd.confirm and
279308
not cmd.ask_ny("Update setting now? You may be prompted for "
280-
"administrator credentials.")
309+
"administrator credentials, and must reboot for "
310+
"the change to take effect.")
281311
):
282312
os.startfile(sys.executable, "runas", "**configure-long-paths", show_cmd=0)
313+
for _ in range(5):
314+
time.sleep(0.25)
315+
if check_long_paths(cmd):
316+
LOGGER.info("The setting has been successfully updated.")
317+
break
318+
else:
319+
LOGGER.warn("The setting may not have been updated. Please "
320+
"visit the additional help link at the end for "
321+
"more assistance.")
283322
elif cmd.explicit:
284323
LOGGER.info("Checked system long paths setting")
285324

286325
if cmd.check_py_on_path:
287326
r = check_py_on_path(cmd)
288327
if not r:
289328
welcome()
290-
LOGGER.warn("The legacy 'py' command is still installed.")
291-
LOGGER.info("This may interfere with launching the new 'py' command, "
292-
"and may be resolved by uninstalling '!B!Python launcher!W!'.")
329+
line_break()
330+
shown_any = True
331+
LOGGER.print("!Y!The legacy 'py' command is still installed.!W!", level=logging.WARN)
332+
LOGGER.print("\nThis may interfere with launching the new 'py' "
333+
"command, and may be resolved by uninstalling "
334+
"'!B!Python launcher!W!'.\n", wrap=True)
293335
if (
294336
cmd.confirm and
295337
not cmd.ask_ny("Open Installed apps now?")
@@ -305,14 +347,18 @@ def first_run(cmd):
305347
r = check_global_dir(cmd)
306348
if not r:
307349
welcome()
308-
LOGGER.warn("The directory for versioned Python commands is not configured.")
309-
LOGGER.info("This will prevent commands like !B!python3.14.exe!W! "
310-
"working, but will not affect the !B!python!W! or "
311-
"!B!py!W! commands (for example, !B!py -V:3.14!W!).")
312-
LOGGER.info("We can add the directory to PATH now, but you will need "
313-
"to restart your terminal to see the change, and must "
314-
"manually edit environment variables to later remove the "
315-
"entry.")
350+
line_break()
351+
shown_any = True
352+
LOGGER.print("!Y!The directory for versioned Python commands is not "
353+
"configured.!W!", level=logging.WARN)
354+
LOGGER.print("\nThis will prevent commands like !B!python3.14.exe!W! "
355+
"working, but will not affect the !B!python!W! or "
356+
"!B!py!W! commands (for example, !B!py -V:3.14!W!).",
357+
wrap=True)
358+
LOGGER.print("\nWe can add the directory to PATH now, but you will "
359+
"need to restart your terminal to see the change, and "
360+
"must manually edit environment variables to later "
361+
"remove the entry.\n", wrap=True)
316362
if (
317363
cmd.confirm and
318364
not cmd.ask_ny("Add commands directory to your PATH now?")
@@ -328,17 +374,26 @@ def first_run(cmd):
328374
if cmd.check_any_install:
329375
if not check_any_install(cmd):
330376
welcome()
331-
LOGGER.warn("You do not have any Python runtimes installed.")
332-
LOGGER.info("Install the current latest version of CPython? If not, "
333-
"you can use !B!py install default!W! later to install, or "
334-
"one will be installed automatically when needed.")
377+
line_break()
378+
shown_any = True
379+
LOGGER.print("!Y!You do not have any Python runtimes installed.!W!",
380+
level=logging.WARN)
381+
LOGGER.print("\nInstall the current latest version of CPython? If "
382+
"not, you can use !B!py install default!W! later to "
383+
"install, or one will be installed automatically when "
384+
"needed.\n", wrap=True)
385+
LOGGER.info("")
335386
if cmd.ask_yn("Install CPython now?"):
336387
do_install(cmd)
337388
elif cmd.explicit:
338389
LOGGER.info("Checked for any Python installs")
339390

340-
if cmd.explicit:
341-
LOGGER.info("!G!All checks passed.!W!")
391+
if shown_any or cmd.explicit:
392+
line_break()
393+
LOGGER.print("!G!Configuration checks completed.!W!", level=logging.WARN)
394+
LOGGER.print("To run these checks again, launch !B!Python install "
395+
"manager!W! from your Start menu, or !B!py install "
396+
"--configure!W! from the terminal.", wrap=True)
342397

343398

344399
if __name__ == "__main__":

src/manage/install_command.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,8 @@ def execute(cmd):
761761
LOGGER.info("Skipping shortcut refresh due to --dry-run")
762762
else:
763763
update_all_shortcuts(cmd)
764-
print_cli_shortcuts(cmd)
764+
if not cmd.automatic:
765+
print_cli_shortcuts(cmd)
765766

766767
finally:
767768
if cmd.automatic:

src/manage/logging.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def would_print(self, *args, always=False, level=INFO, **kwargs):
162162
return False
163163
return True
164164

165-
def print(self, msg=None, *args, always=False, level=INFO, colours=True, **kwargs):
165+
def print(self, msg=None, *args, always=False, level=INFO, colours=True, wrap=False, **kwargs):
166166
if self._list is not None:
167167
if args:
168168
self._list.append(((msg or "") % args, ()))
@@ -186,6 +186,16 @@ def print(self, msg=None, *args, always=False, level=INFO, colours=True, **kwarg
186186
msg = str(args[0])
187187
else:
188188
msg = ""
189+
if wrap:
190+
while len(msg) > CONSOLE_MAX_WIDTH:
191+
part = msg[:CONSOLE_MAX_WIDTH]
192+
n = 0
193+
while len(part) > n:
194+
n = len(part)
195+
part = msg[:CONSOLE_MAX_WIDTH + 5 * part.count("\033")]
196+
pre, sep, rest = part.rpartition(" ")
197+
print(pre, **kwargs, file=self.print_console)
198+
msg = rest + msg[len(part):]
189199
print(msg, **kwargs, file=self.print_console)
190200

191201
def print_raw(self, *msg, **kwargs):

0 commit comments

Comments
 (0)