3333WELCOME = f"""!B!Python install manager was successfully updated to { __version__ } .!W!
3434"""
3535
36-
37- # The help text of subcommands is generated below - look for 'subcommands_list'
38-
39- GLOBAL_OPTIONS_HELP_TEXT = fr"""!G!Global options:!W!
36+ # The 'py help' or 'pymanager help' output is constructed by these default docs,
37+ # with individual subcommand docs added in usage_text_lines().
38+ #
39+ # Descriptive text (tuple element 1) will be aligned and rewrapped across all
40+ # commands.
41+ #
42+ # Where a command summary (tuple element 0) ends with a newline, it allows the
43+ # wrapping algorithm to start the description on the following line if the
44+ # command is too long.
45+ PY_USAGE_DOCS = [
46+ (f"{ EXE_NAME } !B!<regular Python options>!W!\n " ,
47+ "Launch the default runtime with specified options. " +
48+ "This is the equivalent of the !G!python!W! command." ),
49+ (f"{ EXE_NAME } -V:!B!<TAG>!W!" ,
50+ "Launch runtime identified by !B!<TAG>!W!, which should include the " +
51+ "company name if not !B!PythonCore!W!. Regular Python options may " +
52+ "follow this option." ),
53+ (f"{ EXE_NAME } -3!B!<VERSION>!W!" ,
54+ r"Equivalent to -V:PythonCore\3!B!<VERSION>!W!. The version must begin " +
55+ "with the digit 3, platform overrides are permitted, and regular Python " +
56+ "options may follow. " +
57+ "!G!py -3!W! is the equivalent of the !G!python3!W! command." ),
58+ ]
59+
60+
61+ PYMANAGER_USAGE_DOCS = [
62+ (f"{ EXE_NAME } exec !B!<regular Python options>!W!\n " ,
63+ "Launch the default runtime with specified options, installing it if needed. " +
64+ "This is the equivalent of the !G!python!W! command, but with auto-install." ),
65+ (f"{ EXE_NAME } exec -V:!B!<TAG>!W!" ,
66+ "Launch runtime identified by !B!<TAG>!W!, which should include the " +
67+ "company name if not !B!PythonCore!W!. Regular Python options may " +
68+ "follow this option. The runtime will be installed if needed." ),
69+ (f"{ EXE_NAME } exec -3!B!<VERSION>!W!\n " ,
70+ r"Equivalent to -V:PythonCore\3!B!<VERSION>!W!. The version must begin " +
71+ "with a '3', platform overrides are permitted, and regular Python " +
72+ "options may follow. The runtime will be installed if needed." ),
73+ ]
74+
75+
76+ GLOBAL_OPTIONS_HELP_TEXT = fr"""!G!Global options: !B!(options must come after a command)!W!
4077 -v, --verbose Increased output (!B!log_level={ logging .INFO } !W!)
4178 -vv Further increased output (!B!log_level={ logging .DEBUG } !W!)
4279 -q, --quiet Less output (!B!log_level={ logging .WARN } !W!)
4380 -qq Even less output (!B!log_level={ logging .ERROR } !W!)
4481 -y, --yes Always confirm prompts (!B!confirm=false!W!)
82+ -h, -?, --help Show help for a specific command
4583 --config=!B!<PATH>!W! Override configuration with JSON file
4684"""
4785
@@ -476,73 +514,62 @@ def execute(self):
476514
477515 @classmethod
478516 def usage_text_lines (cls ):
479- usage_docs = [
480- (f" { EXE_NAME } -V:!B!<TAG>!W!" ,
481- "Launch runtime identified by !B!<TAG>!W!, which should include the " +
482- "company name if not !B!PythonCore!W!. Regular Python options may " +
483- "follow this option." ),
484- (f" { EXE_NAME } -!B!<VERSION>!W!" ,
485- r"Equivalent to -V:PythonCore\!B!<VERSION>!W!. The version must " +
486- "begin with the digit 3, platform overrides are permitted, " +
487- "and regular Python options may follow." +
488- (" !G!py -3!W! is the equivalent of the !G!python3!W! command." if EXE_NAME == "py" else "" )),
489- (f" { EXE_NAME } !B!<COMMAND>!W!" ,
490- "Run a specific command (see list below)." ),
491- ]
492-
493- usage_ljust = max (len (logging .strip_colour (i [0 ])) for i in usage_docs )
517+ if EXE_NAME .casefold () in ("py" .casefold (), "pyw" .casefold ()):
518+ usage_docs = PY_USAGE_DOCS
519+ else :
520+ usage_docs = PYMANAGER_USAGE_DOCS
521+
522+ usage_docs .extend (
523+ [
524+ (
525+ f"{ EXE_NAME } " + getattr (COMMANDS [cmd ], "USAGE_LINE" , cmd ),
526+ getattr (COMMANDS [cmd ], "HELP_LINE" , "" )
527+ )
528+ for cmd in sorted (COMMANDS )
529+ if cmd [:1 ].isalpha ()
530+ ]
531+ )
532+
533+ usage_docs = [(" " + x .lstrip (), y ) for x , y in usage_docs ]
534+
535+ usage_ljust = max (len (logging .strip_colour (i [0 ])) for i in usage_docs if not i [0 ].endswith ("\n " ))
494536 if usage_ljust % 4 :
495537 usage_ljust += 4 - (usage_ljust % 4 )
496538 usage_ljust = max (usage_ljust , 16 ) + 1
497539 sp = " " * usage_ljust
498540
499541 yield "!G!Usage:!W!"
500- if EXE_NAME .casefold () in ("py" .casefold (), "pyw" .casefold ()):
501- yield f" { EXE_NAME } !B!<regular Python options>!W!"
502- yield sp + "Launch the default runtime with specified options."
503- yield sp + "This is the equivalent of the !G!python!W! command."
504542 for k , d in usage_docs :
505- r = k .ljust (usage_ljust + len (k ) - len (logging .strip_colour (k )))
543+ if k .endswith ("\n " ) and len (logging .strip_colour (k )) >= usage_ljust :
544+ yield k .rstrip ()
545+ r = sp
546+ else :
547+ k = k .rstrip ()
548+ r = k .ljust (usage_ljust + len (k ) - len (logging .strip_colour (k )))
506549 for b in d .split (" " ):
507- if len (r ) >= 80 :
550+ if len (r ) >= logging . CONSOLE_MAX_WIDTH :
508551 yield r .rstrip ()
509552 r = sp
510553 r += b + " "
511554 if r .rstrip ():
512555 yield r
513556
514557 yield ""
515- yield "Find additional information at !B!https://docs.python.org/using/windows.html!W!."
558+ # TODO: Remove the 3.14 for stable release
559+ yield "Find additional information at !B!https://docs.python.org/3.14/using/windows!W!."
516560 yield ""
517561
518562 @classmethod
519563 def usage_text (cls ):
520564 return "\n " .join (cls .usage_text_lines ())
521565
522- @classmethod
523- def subcommands_list (cls ):
524- usage_ljust = len (EXE_NAME ) + 1 + max (len (cmd ) for cmd in sorted (COMMANDS ) if cmd [:1 ].isalpha ())
525- if usage_ljust % 4 :
526- usage_ljust += 4 - (usage_ljust % 4 )
527- usage_ljust = max (usage_ljust , 16 )
528- cmd_help = [
529- " {:<{}} {}" .format (f"{ EXE_NAME } { cmd } " , usage_ljust , getattr (COMMANDS [cmd ], "HELP_LINE" , "" ))
530- for cmd in sorted (COMMANDS )
531- if cmd [:1 ].isalpha ()
532- ]
533- return fr"""
534- !G!Commands:!W!
535- { '\n ' .join (cmd_help )}
536- """ .lstrip ().replace ("\r \n " , "\n " )
537-
538566 @classmethod
539567 def help_text (cls ):
540568 return GLOBAL_OPTIONS_HELP_TEXT .replace ("\r \n " , "\n " )
541569
542570 def help (self ):
543571 if type (self ) is BaseCommand :
544572 LOGGER .print (self .usage_text ())
545- LOGGER .print (self .subcommands_list ())
546573 LOGGER .print (self .help_text ())
547574 try :
548575 LOGGER .print (self .HELP_TEXT .lstrip ())
@@ -616,7 +643,9 @@ def get_install_to_run(self, tag=None, script=None, *, windowed=False):
616643
617644class ListCommand (BaseCommand ):
618645 CMD = "list"
619- HELP_LINE = "Shows all installed Python runtimes"
646+ HELP_LINE = ("Shows installed Python runtimes, optionally filtering by " +
647+ "!B!<FILTER>!W!." )
648+ USAGE_LINE = "list !B![<FILTER>]!W!"
620649 HELP_TEXT = r"""!G!List command!W!
621650> py list !B![options] [<FILTER> ...]!W!
622651
@@ -691,7 +720,9 @@ class ListPathsLegacyCommand(ListLegacyCommand):
691720
692721class InstallCommand (BaseCommand ):
693722 CMD = "install"
694- HELP_LINE = "Download new Python runtimes"
723+ HELP_LINE = ("Download new Python runtimes, or pass !B!--update!W! to " +
724+ "update existing installs." )
725+ USAGE_LINE = "install !B!<TAG>!W!"
695726 HELP_TEXT = r"""!G!Install command!W!
696727> py install !B![options] <TAG> [<TAG>] ...!W!
697728
@@ -768,7 +799,9 @@ def execute(self):
768799
769800class UninstallCommand (BaseCommand ):
770801 CMD = "uninstall"
771- HELP_LINE = "Remove runtimes from your machine"
802+ HELP_LINE = ("Remove one or more runtimes from your machine. Pass " +
803+ "!B!--purge!W! to clean up all runtimes and cached files." )
804+ USAGE_LINE = "uninstall !B!<TAG>!W!"
772805 HELP_TEXT = r"""!G!Uninstall command!W!
773806> py uninstall !B![options] <TAG> [<TAG>] ...!W!
774807
@@ -809,6 +842,7 @@ def execute(self):
809842class HelpCommand (BaseCommand ):
810843 CMD = "help"
811844 HELP_LINE = "Show help for Python installation manager commands"
845+ USAGE_LINE = "help !B![<CMD>]!W!"
812846 HELP_TEXT = r"""!G!Help command!W!
813847> py help !B![<CMD>] ...!W!
814848
@@ -824,7 +858,6 @@ def execute(self):
824858 self .show_welcome (copyright = False )
825859 if not self .args :
826860 LOGGER .print (BaseCommand .usage_text ())
827- LOGGER .print (BaseCommand .subcommands_list ())
828861 LOGGER .print (BaseCommand .help_text ())
829862 for a in self .args :
830863 try :
@@ -846,7 +879,6 @@ def execute(self):
846879 LOGGER .print (COPYRIGHT )
847880 self .show_welcome (copyright = False )
848881 LOGGER .print (BaseCommand .usage_text ())
849- LOGGER .print (BaseCommand .subcommands_list ())
850882 LOGGER .print (f"The command !R!{ EXE_NAME } { ' ' .join (self .args )} !W! was not recognized." )
851883
852884
0 commit comments