@@ -193,6 +193,7 @@ def run(self) -> list[Node]:
193193 def _pre_format (self , block : str | None , parser : ArgumentParser ) -> paragraph | literal_block | None :
194194 if block is None or not block .strip ():
195195 return None
196+ block = _expand_prog (block , parser .prog )
196197 formatter = parser .formatter_class
197198 if "\n " in block and isinstance (formatter , type ) and issubclass (formatter , RawDescriptionHelpFormatter ):
198199 lit = literal_block ("" , Text (block ), classes = ["sphinx-argparse-cli-wrap" ])
@@ -220,7 +221,7 @@ def _mk_option_group(
220221 self ._register_ref (ref_id , title_text , group_section )
221222 opt_group = bullet_list ()
222223 for action in actions :
223- opt_group += self ._mk_option_line (action , prefix )
224+ opt_group += self ._mk_option_line (parser , action , prefix )
224225 group_section += opt_group
225226 return group_section
226227
@@ -230,7 +231,7 @@ def _build_opt_grp_title(
230231 sub_cmd = prefix [len (prog ) :].strip () or None if prefix != prog else None
231232 return self ._resolve_prefix (prog , sub_cmd , prefix , title_prefix , sub_title_prefix ) + group_title
232233
233- def _mk_option_line (self , action : Action , prefix : str ) -> list_item :
234+ def _mk_option_line (self , parser : ArgumentParser , action : Action , prefix : str ) -> list_item :
234235 line = paragraph ()
235236 as_key = action .dest
236237 if action .metavar :
@@ -252,9 +253,9 @@ def _mk_option_line(self, action: Action, prefix: str) -> list_item:
252253 self ._mk_option_name (line , prefix , as_key )
253254
254255 extra : Sequence [Node ] = ()
255- if action . help :
256+ if help_text := _expand_help ( action , parser . prog ) :
256257 temp = paragraph ()
257- self .state .nested_parse (StringList (load_help_text (action . help ).split ("\n " )), 0 , temp )
258+ self .state .nested_parse (StringList (load_help_text (help_text ).split ("\n " )), 0 , temp )
258259 # only a leading paragraph can share the option's line; anything else becomes a block under it
259260 if temp .children and isinstance (temp .children [0 ], paragraph ):
260261 line += Text (" - " )
@@ -266,7 +267,7 @@ def _mk_option_line(self, action: Action, prefix: str) -> list_item:
266267 "no_default_values" not in self .options
267268 and action .default is not None
268269 and action .default != SUPPRESS
269- and not re . match ( r".*[ (]default[s]? .*" , ( action . help or "" ) )
270+ and not _DEFAULT_IN_HELP . search ( help_text )
270271 and not isinstance (action , _StoreTrueAction | _StoreFalseAction )
271272 ):
272273 line += Text (" (default: " )
@@ -426,6 +427,26 @@ def _visible_actions(group: _ArgumentGroup) -> list[Action]:
426427 ]
427428
428429
430+ def _expand_prog (text : str , prog : str ) -> str :
431+ # what argparse.HelpFormatter._format_text does for descriptions and epilogs
432+ return text % {"prog" : prog } if "%(prog)" in text else text
433+
434+
435+ def _expand_help (action : Action , prog : str ) -> str :
436+ # mirrors argparse.HelpFormatter._expand_help so the help reads as it does under --help
437+ help_text = action .help or ""
438+ if "%" not in help_text :
439+ return help_text
440+ params = {
441+ key : getattr (value , "__name__" , value ) for key , value in vars (action ).items () if value is not SUPPRESS
442+ } | {"prog" : prog }
443+ if action .choices is not None :
444+ params ["choices" ] = ", " .join (map (str , action .choices ))
445+ return help_text % params
446+
447+
448+ _DEFAULT_IN_HELP : Final [re .Pattern [str ]] = re .compile (r"\bdefaults?\b" , re .IGNORECASE )
449+
429450_HELP_SUBSTITUTIONS : Final [list [tuple [re .Pattern [str ], str ]]] = [
430451 # a quote glued to a word character is an apostrophe (don't, it's), not the edge of a quoted span
431452 (re .compile (r"(?<!\w)'([^']+?)'(?!\w)" ), "``'\\ 1'``" ),
0 commit comments