Skip to content

Commit 7bc2756

Browse files
authored
🐛 fix(titles): handle multi-word prog names in group titles (#289)
1 parent d7c5092 commit 7bc2756

5 files changed

Lines changed: 40 additions & 10 deletions

File tree

roots/test-multiword-prog/conf.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from __future__ import annotations
2+
3+
import sys
4+
from pathlib import Path
5+
6+
sys.path.insert(0, str(Path(__file__).parent))
7+
extensions = ["sphinx_argparse_cli"]
8+
nitpicky = True
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.. sphinx_argparse_cli::
2+
:module: parser
3+
:func: make
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from argparse import ArgumentParser
4+
5+
6+
def make() -> ArgumentParser:
7+
parser = ArgumentParser(prog="python -m build")
8+
parser.add_argument("srcdir", help="source directory")
9+
return parser

src/sphinx_argparse_cli/_logic.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ def run(self) -> list[Node]:
204204
for group in self.parser._action_groups: # noqa: SLF001
205205
if not group._group_actions or group is self.parser._subparsers: # noqa: SLF001
206206
continue
207-
home_section += self._mk_option_group(group, prefix=self.parser.prog.split("/")[-1])
207+
home_section += self._mk_option_group(
208+
group, prefix=self.parser.prog.split("/")[-1], prog=self.parser.prog.split("/")[-1]
209+
)
208210
# construct sub-parser
209211
for aliases, help_msg, parser in self.load_sub_parsers():
210212
home_section += self._mk_sub_command(aliases, help_msg, parser)
@@ -226,10 +228,10 @@ def _pre_format(self, block: None | str) -> None | paragraph | literal_block:
226228
return lit
227229
return paragraph("", Text(block))
228230

229-
def _mk_option_group(self, group: _ArgumentGroup, prefix: str) -> section:
231+
def _mk_option_group(self, group: _ArgumentGroup, prefix: str, prog: str) -> section:
230232
sub_title_prefix: str = self.options["group_sub_title_prefix"]
231233
title_prefix = self.options["group_title_prefix"]
232-
title_text = self._build_opt_grp_title(group, prefix, sub_title_prefix, title_prefix)
234+
title_text = self._build_opt_grp_title(group, prefix, prog, sub_title_prefix, title_prefix)
233235
title_ref: str = f"{prefix}{' ' if prefix else ''}{group.title}"
234236
ref_id = self.make_id(title_ref)
235237
# the text sadly needs to be prefixed, because otherwise the autosectionlabel will conflict
@@ -247,10 +249,11 @@ def _mk_option_group(self, group: _ArgumentGroup, prefix: str) -> section:
247249
group_section += opt_group
248250
return group_section
249251

250-
def _build_opt_grp_title(self, group: _ArgumentGroup, prefix: str, sub_title_prefix: str, title_prefix: str) -> str:
251-
elements = prefix.split(" ")
252-
sub_cmd = " ".join(elements[1:]) if " " in prefix else None
253-
title_text = self._resolve_prefix(elements[0], sub_cmd, prefix, title_prefix, sub_title_prefix)
252+
def _build_opt_grp_title(
253+
self, group: _ArgumentGroup, prefix: str, prog: str, sub_title_prefix: str, title_prefix: str
254+
) -> str:
255+
sub_cmd = prefix[len(prog) :].strip() or None if prefix != prog else None
256+
title_text = self._resolve_prefix(prog, sub_cmd, prefix, title_prefix, sub_title_prefix)
254257
title_text += group.title or ""
255258
return title_text
256259

@@ -371,12 +374,13 @@ def _mk_sub_command(self, aliases: list[str], help_msg: str, parser: ArgumentPar
371374
if isinstance(group._group_actions[0], _SubParsersAction): # noqa: SLF001
372375
# If this is a subparser, ignore it
373376
continue
374-
group_section += self._mk_option_group(group, prefix=parser.prog)
377+
group_section += self._mk_option_group(group, prefix=parser.prog, prog=self.parser.prog.split("/")[-1])
375378
return group_section
376379

377380
def _build_sub_cmd_title(self, parser: ArgumentParser, sub_title_prefix: str, title_prefix: str) -> str:
378-
elements = parser.prog.split(" ")
379-
return self._resolve_prefix(elements[0], elements[1], parser.prog, title_prefix, sub_title_prefix).rstrip()
381+
root_prog = self.parser.prog.split("/")[-1]
382+
sub_cmd = parser.prog[len(root_prog) :].strip().split(" ", maxsplit=1)[0]
383+
return self._resolve_prefix(root_prog, sub_cmd, parser.prog, title_prefix, sub_title_prefix).rstrip()
380384

381385
def _resolve_prefix(
382386
self,

tests/test_logic.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,9 @@ def test_prog_subcommands(build_outcome: str) -> None:
397397
assert "my-tool" in build_outcome
398398
assert "original-name" not in build_outcome
399399
assert "my-tool foo" in build_outcome
400+
401+
402+
@pytest.mark.sphinx(buildername="text", testroot="multiword-prog")
403+
def test_multiword_prog(build_outcome: str) -> None:
404+
assert "python -m build positional arguments" in build_outcome
405+
assert "python -m build options" in build_outcome

0 commit comments

Comments
 (0)