Skip to content

Commit 2aa99ca

Browse files
Add option to show usage before description (#148)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ddc99fa commit 2aa99ca

File tree

3 files changed

+38
-19
lines changed

3 files changed

+38
-19
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Within the reStructuredText files use the `sphinx_argparse_cli` directive that t
3939
| description | (optional) when provided, overwrites the description and when empty, will not be included |
4040
| epilog | (optional) when provided, overwrites the epilog and when empty, will not be included |
4141
| usage_width | (optional) how large should usage examples be - defaults to 100 character |
42+
| usage_first | (optional) show usage before description |
4243
| group_title_prefix | (optional) groups subsections title prefixes, accepts the string `{prog}` as a replacement for the program name - defaults to `{prog}` |
4344
| group_sub_title_prefix | (optional) subcommands groups subsections title prefixes, accepts replacement of `{prog}` and `{subcommand}` for program and subcommand name - defaults to `{prog} {subcommand}` |
4445
| no_default_values | (optional) suppresses generation of `default` entries |

src/sphinx_argparse_cli/_logic.py

+30-19
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class SphinxArgparseCli(SphinxDirective):
6767
"description": unchanged,
6868
"epilog": unchanged,
6969
"usage_width": positive_int,
70+
"usage_first": flag,
7071
"group_title_prefix": unchanged,
7172
"group_sub_title_prefix": unchanged,
7273
"no_default_values": unchanged,
@@ -89,6 +90,7 @@ def __init__( # noqa: PLR0913
8990
super().__init__(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)
9091
self._parser: ArgumentParser | None = None
9192
self._std_domain: StandardDomain = cast(StandardDomain, self.env.get_domain("std"))
93+
self._raw_format: bool = False
9294

9395
@property
9496
def parser(self) -> ArgumentParser:
@@ -114,6 +116,8 @@ def parser(self) -> ArgumentParser:
114116

115117
if "prog" in self.options:
116118
self._parser.prog = self.options["prog"]
119+
120+
self._raw_format = self._parser.formatter_class == RawDescriptionHelpFormatter
117121
return self._parser
118122

119123
def load_sub_parsers(self) -> Iterator[tuple[list[str], str, ArgumentParser]]:
@@ -148,18 +152,16 @@ def run(self) -> list[Node]:
148152
else:
149153
home_section = section("", title("", Text(title_text)), ids=[make_id(title_text)], names=[title_text])
150154

151-
raw_format = self.parser.formatter_class == RawDescriptionHelpFormatter
155+
if "usage_first" in self.options:
156+
home_section += self._mk_usage(self.parser)
157+
158+
if description := self._pre_format(self.options.get("description", self.parser.description)):
159+
home_section += description
160+
161+
if "usage_first" not in self.options:
162+
home_section += self._mk_usage(self.parser)
152163

153-
description = self.options.get("description", self.parser.description)
154-
if description:
155-
if raw_format and "\n" in description:
156-
lit = literal_block("", Text(description))
157-
lit["language"] = "none"
158-
home_section += lit
159-
else:
160-
home_section += paragraph("", Text(description))
161164
# construct groups excluding sub-parsers
162-
home_section += self._mk_usage(self.parser)
163165
for group in self.parser._action_groups: # noqa: SLF001
164166
if not group._group_actions or group is self.parser._subparsers: # noqa: SLF001
165167
continue
@@ -168,17 +170,20 @@ def run(self) -> list[Node]:
168170
for aliases, help_msg, parser in self.load_sub_parsers():
169171
home_section += self._mk_sub_command(aliases, help_msg, parser)
170172

171-
epilog = self.options.get("epilog", self.parser.epilog)
172-
if epilog:
173-
if raw_format and "\n" in epilog:
174-
lit = literal_block("", Text(epilog))
175-
lit["language"] = "none"
176-
home_section += lit
177-
else:
178-
home_section += paragraph("", Text(epilog))
173+
if epilog := self._pre_format(self.options.get("epilog", self.parser.epilog)):
174+
home_section += epilog
179175

180176
return [home_section]
181177

178+
def _pre_format(self, block: None | str) -> None | paragraph | literal_block:
179+
if block is None:
180+
return None
181+
if self._raw_format and "\n" in block:
182+
lit = literal_block("", Text(block))
183+
lit["language"] = "none"
184+
return lit
185+
return paragraph("", Text(block))
186+
182187
def _mk_option_group(self, group: _ArgumentGroup, prefix: str) -> section:
183188
sub_title_prefix: str = self.options["group_sub_title_prefix"]
184189
title_prefix = self.options["group_title_prefix"]
@@ -313,11 +318,17 @@ def _mk_sub_command(self, aliases: list[str], help_msg: str, parser: ArgumentPar
313318
group_section = section("", title("", Text(title_text)), ids=[ref_id], names=[title_ref])
314319
self._register_ref(ref_id, title_ref, group_section)
315320

321+
if "usage_first" in self.options:
322+
group_section += self._mk_usage(parser)
323+
316324
command_desc = (parser.description or help_msg or "").strip()
317325
if command_desc:
318326
desc_paragraph = paragraph("", Text(command_desc))
319327
group_section += desc_paragraph
320-
group_section += self._mk_usage(parser)
328+
329+
if "usage_first" not in self.options:
330+
group_section += self._mk_usage(parser)
331+
321332
for group in parser._action_groups: # noqa: SLF001
322333
if not group._group_actions: # do not show empty groups # noqa: SLF001
323334
continue

tests/test_logic.py

+7
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ def test_usage_width_custom(build_outcome: str) -> None:
139139
assert "complex second [-h] [--flag] [--root]\n" in build_outcome
140140

141141

142+
@pytest.mark.sphinx(buildername="text", testroot="complex")
143+
@pytest.mark.prepare(directive_args=[":usage_first:"])
144+
def test_set_usage_first(build_outcome: str) -> None:
145+
assert "complex [-h]" in build_outcome.split("argparse tester")[0]
146+
assert "complex first [-h]" in build_outcome.split("a-first-desc")[0]
147+
148+
142149
@pytest.mark.sphinx(buildername="text", testroot="suppressed-action")
143150
def test_suppressed_action(build_outcome: str) -> None:
144151
assert "--activities-since" not in build_outcome

0 commit comments

Comments
 (0)