@@ -261,11 +261,9 @@ def get_items(self) -> list[CompletionItems]:
261261from argparse import ArgumentError
262262from collections .abc import (
263263 Callable ,
264- Iterable ,
265264 Iterator ,
266265 Sequence ,
267266)
268- from gettext import gettext
269267from typing import (
270268 TYPE_CHECKING ,
271269 Any ,
@@ -1126,177 +1124,22 @@ def __init__(
11261124 ** kwargs : Any ,
11271125 ) -> None :
11281126 """Initialize Cmd2HelpFormatter."""
1129- if console is None :
1130- console = Cmd2RichArgparseConsole ()
1131-
11321127 super ().__init__ (prog , indent_increment , max_help_position , width , console = console , ** kwargs )
11331128
1134- def _format_usage (
1135- self ,
1136- usage : str | None ,
1137- actions : Iterable [argparse .Action ],
1138- groups : Iterable [argparse ._ArgumentGroup ],
1139- prefix : str | None = None ,
1140- ) -> str :
1141- if prefix is None :
1142- prefix = gettext ('Usage: ' )
1143-
1144- # if usage is specified, use that
1145- if usage is not None :
1146- usage %= {"prog" : self ._prog }
1147-
1148- # if no optionals or positionals are available, usage is just prog
1149- elif not actions :
1150- usage = f'{ self ._prog } '
1151-
1152- # if optionals and positionals are available, calculate usage
1153- else :
1154- prog = f'{ self ._prog } '
1155-
1156- # split optionals from positionals
1157- optionals = []
1158- positionals = []
1159- # Begin cmd2 customization (separates required and optional, applies to all changes in this function)
1160- required_options = []
1161- for action in actions :
1162- if action .option_strings :
1163- if action .required :
1164- required_options .append (action )
1165- else :
1166- optionals .append (action )
1167- else :
1168- positionals .append (action )
1169- # End cmd2 customization
1170-
1171- # build full usage string
1172- format_actions = self ._format_actions_usage
1173- action_usage = format_actions (required_options + optionals + positionals , groups ) # type: ignore[arg-type]
1174- usage = ' ' .join ([s for s in [prog , action_usage ] if s ])
1175-
1176- # wrap the usage parts if it's too long
1177- text_width = self ._width - self ._current_indent
1178- if len (prefix ) + len (usage ) > text_width :
1179- # Begin cmd2 customization
1180-
1181- # break usage into wrappable parts
1182- part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
1183- req_usage = format_actions (required_options , groups ) # type: ignore[arg-type]
1184- opt_usage = format_actions (optionals , groups ) # type: ignore[arg-type]
1185- pos_usage = format_actions (positionals , groups ) # type: ignore[arg-type]
1186- req_parts = re .findall (part_regexp , req_usage )
1187- opt_parts = re .findall (part_regexp , opt_usage )
1188- pos_parts = re .findall (part_regexp , pos_usage )
1189-
1190- # End cmd2 customization
1191-
1192- # helper for wrapping lines
1193- def get_lines (parts : list [str ], indent : str , prefix : str | None = None ) -> list [str ]:
1194- lines : list [str ] = []
1195- line : list [str ] = []
1196- line_len = len (prefix ) - 1 if prefix is not None else len (indent ) - 1
1197- for part in parts :
1198- if line_len + 1 + len (part ) > text_width and line :
1199- lines .append (indent + ' ' .join (line ))
1200- line = []
1201- line_len = len (indent ) - 1
1202- line .append (part )
1203- line_len += len (part ) + 1
1204- if line :
1205- lines .append (indent + ' ' .join (line ))
1206- if prefix is not None :
1207- lines [0 ] = lines [0 ][len (indent ) :]
1208- return lines
1209-
1210- # if prog is short, follow it with optionals or positionals
1211- if len (prefix ) + len (prog ) <= 0.75 * text_width :
1212- indent = ' ' * (len (prefix ) + len (prog ) + 1 )
1213- # Begin cmd2 customization
1214- if req_parts :
1215- lines = get_lines ([prog , * req_parts ], indent , prefix )
1216- lines .extend (get_lines (opt_parts , indent ))
1217- lines .extend (get_lines (pos_parts , indent ))
1218- elif opt_parts :
1219- lines = get_lines ([prog , * opt_parts ], indent , prefix )
1220- lines .extend (get_lines (pos_parts , indent ))
1221- elif pos_parts :
1222- lines = get_lines ([prog , * pos_parts ], indent , prefix )
1223- else :
1224- lines = [prog ]
1225- # End cmd2 customization
1226-
1227- # if prog is long, put it on its own line
1228- else :
1229- indent = ' ' * len (prefix )
1230- # Begin cmd2 customization
1231- parts = req_parts + opt_parts + pos_parts
1232- lines = get_lines (parts , indent )
1233- if len (lines ) > 1 :
1234- lines = []
1235- lines .extend (get_lines (req_parts , indent ))
1236- lines .extend (get_lines (opt_parts , indent ))
1237- lines .extend (get_lines (pos_parts , indent ))
1238- # End cmd2 customization
1239- lines = [prog , * lines ]
1240-
1241- # join lines into usage
1242- usage = '\n ' .join (lines )
1243-
1244- # prefix with 'Usage:'
1245- return f'{ prefix } { usage } \n \n '
1246-
1247- def _format_action_invocation (self , action : argparse .Action ) -> str :
1248- if not action .option_strings :
1249- default = self ._get_default_metavar_for_positional (action )
1250- (metavar ,) = self ._metavar_formatter (action , default )(1 )
1251- return metavar
1252-
1253- parts : list [str ] = []
1254-
1255- # if the Optional doesn't take a value, format is:
1256- # -s, --long
1257- if action .nargs == 0 :
1258- parts .extend (action .option_strings )
1259- return ', ' .join (parts )
1260-
1261- # Begin cmd2 customization (less verbose)
1262- # if the Optional takes a value, format is:
1263- # -s, --long ARGS
1264- default = self ._get_default_metavar_for_optional (action )
1265- args_string = self ._format_args (action , default )
1266-
1267- return ', ' .join (action .option_strings ) + ' ' + args_string
1268- # End cmd2 customization
1269-
1270- def _determine_metavar (
1271- self ,
1272- action : argparse .Action ,
1273- default_metavar : str ,
1274- ) -> str | tuple [str , ...]:
1275- """Determine what to use as the metavar value of an action."""
1276- if action .metavar is not None :
1277- result = action .metavar
1278- elif action .choices is not None :
1279- choice_strs = [str (choice ) for choice in action .choices ]
1280- # Begin cmd2 customization (added space after comma)
1281- result = f'{ ", " .join (choice_strs )} '
1282- # End cmd2 customization
1283- else :
1284- result = default_metavar
1285- return result
1129+ # Recast to assist type checkers
1130+ self ._console : Cmd2RichArgparseConsole | None
12861131
1287- def _metavar_formatter (
1288- self ,
1289- action : argparse .Action ,
1290- default_metavar : str ,
1291- ) -> Callable [[int ], tuple [str , ...]]:
1292- metavar = self ._determine_metavar (action , default_metavar )
1293-
1294- def format_tuple (tuple_size : int ) -> tuple [str , ...]:
1295- if isinstance (metavar , tuple ):
1296- return metavar
1297- return (metavar ,) * tuple_size
1132+ @property # type: ignore[override]
1133+ def console (self ) -> Cmd2RichArgparseConsole :
1134+ """Return our console instance."""
1135+ if self ._console is None :
1136+ self ._console = Cmd2RichArgparseConsole ()
1137+ return self ._console
12981138
1299- return format_tuple
1139+ @console .setter
1140+ def console (self , console : Cmd2RichArgparseConsole ) -> None :
1141+ """Set our console instance."""
1142+ self ._console = console
13001143
13011144 def _build_nargs_range_str (self , nargs_range : tuple [int , int | float ]) -> str :
13021145 """Generate nargs range string for help text."""
@@ -1314,13 +1157,12 @@ def _format_args(self, action: argparse.Action, default_metavar: str) -> str:
13141157
13151158 All formats in this function need to be handled by _rich_metavar_parts().
13161159 """
1317- metavar = self ._determine_metavar (action , default_metavar )
1318- metavar_formatter = self ._metavar_formatter (action , default_metavar )
1160+ get_metavar = self ._metavar_formatter (action , default_metavar )
13191161
13201162 # Handle nargs specified as a range
13211163 nargs_range = action .get_nargs_range () # type: ignore[attr-defined]
13221164 if nargs_range is not None :
1323- arg_str = '%s' % metavar_formatter (1 ) # noqa: UP031
1165+ arg_str = '%s' % get_metavar (1 ) # noqa: UP031
13241166 range_str = self ._build_nargs_range_str (nargs_range )
13251167 return f"{ arg_str } { range_str } "
13261168
@@ -1329,8 +1171,8 @@ def _format_args(self, action: argparse.Action, default_metavar: str) -> str:
13291171 # To make this less verbose, format it like: 'command arg{5}'.
13301172 # Do not customize the output when metavar is a tuple of strings. Allow argparse's
13311173 # formatter to handle that instead.
1332- if isinstance (metavar , str ) and isinstance (action .nargs , int ) and action .nargs > 1 :
1333- arg_str = '%s' % metavar_formatter (1 ) # noqa: UP031
1174+ if not isinstance (action . metavar , tuple ) and isinstance (action .nargs , int ) and action .nargs > 1 :
1175+ arg_str = '%s' % get_metavar (1 ) # noqa: UP031
13341176 return f"{ arg_str } {{{ action .nargs } }}"
13351177
13361178 # Fallback to parent for all other cases
@@ -1342,19 +1184,18 @@ def _rich_metavar_parts(
13421184 default_metavar : str ,
13431185 ) -> Iterator [tuple [str , bool ]]:
13441186 """Override to handle all cmd2-specific formatting in _format_args()."""
1345- metavar = self ._determine_metavar (action , default_metavar )
1346- metavar_formatter = self ._metavar_formatter (action , default_metavar )
1187+ get_metavar = self ._metavar_formatter (action , default_metavar )
13471188
13481189 # Handle nargs specified as a range
13491190 nargs_range = action .get_nargs_range () # type: ignore[attr-defined]
13501191 if nargs_range is not None :
1351- yield "%s" % metavar_formatter (1 ), True # noqa: UP031
1192+ yield "%s" % get_metavar (1 ), True # noqa: UP031
13521193 yield self ._build_nargs_range_str (nargs_range ), False
13531194 return
13541195
13551196 # Handle specific integer nargs (e.g., nargs=5 -> arg{5})
1356- if isinstance (metavar , str ) and isinstance (action .nargs , int ) and action .nargs > 1 :
1357- yield "%s" % metavar_formatter (1 ), True # noqa: UP031
1197+ if not isinstance (action . metavar , tuple ) and isinstance (action .nargs , int ) and action .nargs > 1 :
1198+ yield "%s" % get_metavar (1 ), True # noqa: UP031
13581199 yield f"{{{ action .nargs } }}" , False
13591200 return
13601201
@@ -1490,15 +1331,15 @@ def __init__(
14901331 )
14911332
14921333 # Recast to assist type checkers since these can be Rich renderables in a Cmd2HelpFormatter.
1493- self .description : RenderableType | None = self . description # type: ignore[assignment]
1494- self .epilog : RenderableType | None = self . epilog # type: ignore[assignment]
1334+ self .description : RenderableType | None # type: ignore[assignment]
1335+ self .epilog : RenderableType | None # type: ignore[assignment]
14951336
14961337 self .set_ap_completer_type (ap_completer_type ) # type: ignore[attr-defined]
14971338
14981339 def add_subparsers (self , ** kwargs : Any ) -> argparse ._SubParsersAction : # type: ignore[type-arg]
14991340 """Add a subcommand parser.
15001341
1501- Set a default title if one was not given.f
1342+ Set a default title if one was not given.
15021343
15031344 :param kwargs: additional keyword arguments
15041345 :return: argparse Subparser Action
@@ -1509,10 +1350,7 @@ def add_subparsers(self, **kwargs: Any) -> argparse._SubParsersAction: # type:
15091350 return super ().add_subparsers (** kwargs )
15101351
15111352 def error (self , message : str ) -> NoReturn :
1512- """Print a usage message, including the message, to sys.stderr and terminates the program with a status code of 2.
1513-
1514- Custom override that applies custom formatting to the error message.
1515- """
1353+ """Override that applies custom formatting to the error message."""
15161354 lines = message .split ('\n ' )
15171355 formatted_message = ''
15181356 for linum , line in enumerate (lines ):
@@ -1532,62 +1370,12 @@ def error(self, message: str) -> NoReturn:
15321370 self .exit (2 , f'{ formatted_message } \n ' )
15331371
15341372 def _get_formatter (self ) -> Cmd2HelpFormatter :
1535- """Override _get_formatter with customizations for Cmd2HelpFormatter."""
1373+ """Override with customizations for Cmd2HelpFormatter."""
15361374 return cast (Cmd2HelpFormatter , super ()._get_formatter ())
15371375
15381376 def format_help (self ) -> str :
1539- """Return a string containing a help message, including the program usage and information about the arguments.
1540-
1541- Copy of format_help() from argparse.ArgumentParser with tweaks to separately display required parameters.
1542- """
1543- formatter = self ._get_formatter ()
1544-
1545- # usage
1546- formatter .add_usage (self .usage , self ._actions , self ._mutually_exclusive_groups )
1547-
1548- # description
1549- formatter .add_text (self .description )
1550-
1551- # Begin cmd2 customization (separate required and optional arguments)
1552-
1553- # positionals, optionals and user-defined groups
1554- for action_group in self ._action_groups :
1555- default_options_group = action_group .title == 'options'
1556-
1557- if default_options_group :
1558- # check if the arguments are required, group accordingly
1559- req_args = []
1560- opt_args = []
1561- for action in action_group ._group_actions :
1562- if action .required :
1563- req_args .append (action )
1564- else :
1565- opt_args .append (action )
1566-
1567- # separately display required arguments
1568- formatter .start_section ('required arguments' )
1569- formatter .add_text (action_group .description )
1570- formatter .add_arguments (req_args )
1571- formatter .end_section ()
1572-
1573- # now display truly optional arguments
1574- formatter .start_section ('optional arguments' )
1575- formatter .add_text (action_group .description )
1576- formatter .add_arguments (opt_args )
1577- formatter .end_section ()
1578- else :
1579- formatter .start_section (action_group .title )
1580- formatter .add_text (action_group .description )
1581- formatter .add_arguments (action_group ._group_actions )
1582- formatter .end_section ()
1583-
1584- # End cmd2 customization
1585-
1586- # epilog
1587- formatter .add_text (self .epilog )
1588-
1589- # determine help from format above
1590- return formatter .format_help () + '\n '
1377+ """Override to add a newline."""
1378+ return super ().format_help () + '\n '
15911379
15921380 def create_text_group (self , title : str , text : RenderableType ) -> TextGroup :
15931381 """Create a TextGroup using this parser's formatter creator."""
0 commit comments