From ba29ae6480dfc966ec0b1c267e56fed3d92c2a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Thu, 27 Aug 2026 08:06:47 -0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20register=20arguments=20as?= =?UTF-8?q?=20Sphinx=20program=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linking to a flag required the :ref: role with a hand-assembled anchor (`tox-run---magic`), and mixed-case names also forced :force_refs_lower: on. Sphinx resolves the :option: role through a separate `progoptions` table keyed on program and option name, which the directive did not fill. Each rendered flag and positional now also goes through `StandardDomain.add_program_option`, under the same anchor id the :ref: labels use, so `:option:`tox run --magic`` and a `.. program::` scope both resolve without changing any existing URL. Resolves #345. --- CHANGELOG.md | 1 + README.md | 17 +++++++++++++++++ roots/test-option/conf.py | 8 ++++++++ roots/test-option/index.rst | 12 ++++++++++++ roots/test-option/parser.py | 12 ++++++++++++ src/sphinx_argparse_cli/_logic.py | 2 ++ tests/test_logic.py | 15 +++++++++++++++ 7 files changed, 67 insertions(+) create mode 100644 roots/test-option/conf.py create mode 100644 roots/test-option/index.rst create mode 100644 roots/test-option/parser.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 209ae69..3628423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Fix Sphinx warnings about parallel reads. - Add `force_args_lower` to enable `:ref:` links with mixed-case program names and arguments. - Fix Sphinx smart quotes rewriting `--` to an en dash in `--option` names within descriptions, epilogs, and help text. +- Register flags and positional arguments as Sphinx program options so the `:option:` role links to them. ## 1.13.1 diff --git a/README.md b/README.md index fe992bf..b000985 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,23 @@ With `sphinx_argparse_cli_prefix_document = True` (anchors prefixed by document The anchor text is visible after the `#` in the URL when you click a heading. +Flags and positional arguments are also registered as Sphinx program options, so the `:option:` role works with the +program (and sub-command) name followed by the argument, or scoped through a `.. program::` directive: + +```rst +:option:`tox --magic` +:option:`tox run --magic` + +.. program:: tox run + +:option:`--magic` +:option:`--magic=value` +``` + +`:option:` targets keep their original case, so they do not need `:force_refs_lower:`. They ignore +`sphinx_argparse_cli_prefix_document`; when two documents render the same program, the role links to the first one +Sphinx reads. + ### Handle mixed-case references Sphinx `:ref:` only supports lower-case targets. When your program name or flags contain capital letters, set diff --git a/roots/test-option/conf.py b/roots/test-option/conf.py new file mode 100644 index 0000000..9f2a54a --- /dev/null +++ b/roots/test-option/conf.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) +extensions = ["sphinx_argparse_cli"] +nitpicky = True diff --git a/roots/test-option/index.rst b/roots/test-option/index.rst new file mode 100644 index 0000000..59d9145 --- /dev/null +++ b/roots/test-option/index.rst @@ -0,0 +1,12 @@ +.. sphinx_argparse_cli:: + :module: parser + :func: make + +Option role test +---------------- +Flag :option:`prog --root`, alias :option:`prog -r`, sub-command :option:`prog run --magic`, positional +:option:`prog run target`. + +.. program:: prog run + +Scoped :option:`--magic` and :option:`--magic=value`. diff --git a/roots/test-option/parser.py b/roots/test-option/parser.py new file mode 100644 index 0000000..2463495 --- /dev/null +++ b/roots/test-option/parser.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from argparse import ArgumentParser + + +def make() -> ArgumentParser: + parser = ArgumentParser(description="argparse tester", prog="prog") + parser.add_argument("--root", "-r", action="store_true", help="root flag") + run = parser.add_subparsers().add_parser("run", help="run it") + run.add_argument("target") + run.add_argument("--magic", help="magic") + return parser diff --git a/src/sphinx_argparse_cli/_logic.py b/src/sphinx_argparse_cli/_logic.py index ded07f5..21f3bef 100644 --- a/src/sphinx_argparse_cli/_logic.py +++ b/src/sphinx_argparse_cli/_logic.py @@ -43,6 +43,7 @@ from docutils.parsers.rst.directives import flag, positive_int, unchanged, unchanged_required from docutils.statemachine import StringList from sphinx.locale import __ +from sphinx.util import ws_re from sphinx.util.docutils import SphinxDirective from sphinx.util.logging import getLogger @@ -281,6 +282,7 @@ def _mk_option_name(self, line: paragraph, prefix: str, opt: str) -> None: line.attributes["ids"].append(ref_id) ref += strong("", "", literal(text=opt)) self._register_ref(ref_id, ref_title, ref, is_cli_option=True) + self._std_domain.add_program_option(ws_re.sub("-", prefix), opt, self.env.docname, ref_id) line += ref def _register_ref( diff --git a/tests/test_logic.py b/tests/test_logic.py index 26de84e..a64c1ab 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import re import sys from pathlib import Path from typing import TYPE_CHECKING @@ -215,6 +216,20 @@ def test_ref_prefix_doc(build_outcome: str) -> None: assert ref in build_outcome +@pytest.mark.sphinx(buildername="html", testroot="option") +def test_option_role_as_html(build_outcome: str, warning: StringIO) -> None: + hrefs = re.findall(r' None: assert build_outcome