sphinx-argparse-cli 1.22.0__tar.gz → 1.22.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sphinx-argparse-cli
3
- Version: 1.22.0
3
+ Version: 1.22.1
4
4
  Summary: render CLI arguments (sub-commands friendly) defined by argparse module
5
5
  Project-URL: Documentation, https://github.com/tox-dev/sphinx-argparse-cli#sphinx-argparse-cli
6
6
  Project-URL: Homepage, https://github.com/tox-dev/sphinx-argparse-cli
@@ -16,6 +16,7 @@ from argparse import (
16
16
  )
17
17
  from collections import defaultdict
18
18
  from contextlib import contextmanager
19
+ from functools import cached_property
19
20
  from pathlib import Path
20
21
  from typing import TYPE_CHECKING, Any, ClassVar, Final, cast
21
22
  from unittest.mock import patch
@@ -46,9 +47,8 @@ from sphinx.util.docutils import SphinxDirective
46
47
  from sphinx.util.logging import getLogger
47
48
 
48
49
  if TYPE_CHECKING:
49
- from collections.abc import Iterator
50
+ from collections.abc import Callable, Iterator
50
51
 
51
- from docutils.parsers.rst.states import RSTState, RSTStateMachine
52
52
  from sphinx.domains.std import StandardDomain
53
53
  from sphinx.util.logging import SphinxLoggerAdapter
54
54
 
@@ -77,66 +77,56 @@ class SphinxArgparseCli(SphinxDirective):
77
77
  "force_refs_lower": flag,
78
78
  }
79
79
 
80
- def __init__( # noqa: PLR0913
81
- self,
82
- name: str,
83
- arguments: list[str],
84
- options: dict[str, str | None],
85
- content: StringList,
86
- lineno: int,
87
- content_offset: int,
88
- block_text: str,
89
- state: RSTState,
90
- state_machine: RSTStateMachine,
91
- ) -> None:
92
- options.setdefault("group_title_prefix", None)
93
- options.setdefault("group_sub_title_prefix", None)
94
- super().__init__(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)
95
- self._parser: ArgumentParser | None = None
96
- self._std_domain: StandardDomain = cast("StandardDomain", self.env.get_domain("std"))
97
- self._raw_format: bool = False
98
- self._make_id = make_id_lower if "force_refs_lower" in self.options else make_id
99
-
100
- @property
80
+ @cached_property
101
81
  def parser(self) -> ArgumentParser:
102
- if self._parser is None:
103
- module_name, attr_name = self.options["module"], self.options["func"]
104
- try:
105
- module = __import__(module_name, fromlist=[attr_name])
106
- except ImportError:
107
- msg = f"Failed to import module {module_name!r}"
108
- raise self.error(msg) # noqa: B904
82
+ module_name, attr_name = self.options["module"], self.options["func"]
83
+ try:
84
+ module = __import__(module_name, fromlist=[attr_name])
85
+ except ImportError:
86
+ msg = f"Failed to import module {module_name!r}"
87
+ raise self.error(msg) # noqa: B904
88
+ try:
89
+ parser_creator = getattr(module, attr_name)
90
+ except AttributeError:
91
+ del sys.modules[module_name]
92
+ msg = f"Module {module_name!r} has no attribute {attr_name!r}"
93
+ raise self.error(msg) # noqa: B904
94
+ parser: ArgumentParser | None = None
95
+ if "hook" in self.options:
96
+ original_parse_known_args = ArgumentParser.parse_known_args
97
+ ArgumentParser.parse_known_args = _parse_known_args_hook # type: ignore[method-assign,assignment]
109
98
  try:
110
- parser_creator = getattr(module, attr_name)
111
- except AttributeError:
112
- del sys.modules[module_name]
113
- msg = f"Module {module_name!r} has no attribute {attr_name!r}"
114
- raise self.error(msg) # noqa: B904
115
- if "hook" in self.options:
116
- original_parse_known_args = ArgumentParser.parse_known_args
117
- ArgumentParser.parse_known_args = _parse_known_args_hook # type: ignore[method-assign,assignment]
118
- try:
119
- parser_creator()
120
- except HookError as hooked:
121
- self._parser = hooked.parser
122
- finally:
123
- ArgumentParser.parse_known_args = original_parse_known_args
124
- else:
125
- self._parser = parser_creator()
99
+ parser_creator()
100
+ except HookError as hooked:
101
+ parser = hooked.parser
102
+ finally:
103
+ ArgumentParser.parse_known_args = original_parse_known_args
104
+ else:
105
+ parser = parser_creator()
126
106
 
127
- del sys.modules[module_name]
128
- if self._parser is None:
129
- msg = "Failed to hook argparse to get ArgumentParser"
130
- raise self.error(msg)
107
+ del sys.modules[module_name]
108
+ if parser is None:
109
+ msg = "Failed to hook argparse to get ArgumentParser"
110
+ raise self.error(msg)
131
111
 
132
- if "prog" in self.options:
133
- old_prog, new_prog = self._parser.prog, self.options["prog"]
134
- self._parser.prog = new_prog
135
- _update_sub_parser_prog(self._parser, old_prog, new_prog)
112
+ if "prog" in self.options:
113
+ old_prog, new_prog = parser.prog, self.options["prog"]
114
+ parser.prog = new_prog
115
+ _update_sub_parser_prog(parser, old_prog, new_prog)
116
+ return parser
136
117
 
137
- formatter = self._parser.formatter_class
138
- self._raw_format = isinstance(formatter, type) and issubclass(formatter, RawDescriptionHelpFormatter)
139
- return self._parser
118
+ @cached_property
119
+ def _std_domain(self) -> StandardDomain:
120
+ return cast("StandardDomain", self.env.get_domain("std"))
121
+
122
+ @cached_property
123
+ def _make_id(self) -> Callable[[str], str]:
124
+ return make_id_lower if "force_refs_lower" in self.options else make_id
125
+
126
+ @property
127
+ def _raw_format(self) -> bool:
128
+ formatter = self.parser.formatter_class
129
+ return isinstance(formatter, type) and issubclass(formatter, RawDescriptionHelpFormatter)
140
130
 
141
131
  def _load_sub_parsers(
142
132
  self, sub_parser: _SubParsersAction[ArgumentParser]
@@ -159,7 +149,8 @@ class SphinxArgparseCli(SphinxDirective):
159
149
 
160
150
  if parser._subparsers: # noqa: SLF001
161
151
  sub_sub_parser: _SubParsersAction[ArgumentParser] = parser._subparsers._group_actions[0] # type: ignore[assignment] # noqa: SLF001
162
- yield from self._load_sub_parsers(sub_sub_parser)
152
+ if isinstance(sub_sub_parser, _SubParsersAction):
153
+ yield from self._load_sub_parsers(sub_sub_parser)
163
154
 
164
155
  def _iter_sub_commands(self) -> Iterator[tuple[list[str], str, ArgumentParser]]:
165
156
  top_sub_parser = self.parser._subparsers # noqa: SLF001
@@ -202,7 +193,7 @@ class SphinxArgparseCli(SphinxDirective):
202
193
 
203
194
  return [home_section]
204
195
 
205
- def _pre_format(self, block: None | str) -> None | paragraph | literal_block:
196
+ def _pre_format(self, block: str | None) -> paragraph | literal_block | None:
206
197
  if block is None:
207
198
  return None
208
199
  if self._raw_format and "\n" in block:
@@ -214,8 +205,8 @@ class SphinxArgparseCli(SphinxDirective):
214
205
  return para
215
206
 
216
207
  def _mk_option_group(self, group: _ArgumentGroup, prefix: str, prog: str) -> section:
217
- sub_title_prefix: str = self.options["group_sub_title_prefix"]
218
- title_prefix = self.options["group_title_prefix"]
208
+ sub_title_prefix: str = self.options.get("group_sub_title_prefix")
209
+ title_prefix = self.options.get("group_title_prefix")
219
210
  title_text = self._build_opt_grp_title(group, prefix, prog, sub_title_prefix, title_prefix)
220
211
  title_ref: str = f"{prefix}{' ' if prefix else ''}{group.title}"
221
212
  ref_id = self._make_id(title_ref)
@@ -318,8 +309,8 @@ class SphinxArgparseCli(SphinxDirective):
318
309
  self._std_domain.labels[name] = doc_name, ref_name, ref_title
319
310
 
320
311
  def _mk_sub_command(self, aliases: list[str], help_msg: str, parser: ArgumentParser) -> section:
321
- sub_title_prefix: str = self.options["group_sub_title_prefix"]
322
- title_prefix: str = self.options["group_title_prefix"]
312
+ sub_title_prefix: str = self.options.get("group_sub_title_prefix")
313
+ title_prefix: str = self.options.get("group_title_prefix")
323
314
 
324
315
  if sys.version_info >= (3, 14): # pragma: >=3.14 cover
325
316
  # https://github.com/python/cpython/issues/139809
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.22.0'
22
- __version_tuple__ = version_tuple = (1, 22, 0)
21
+ __version__ = version = '1.22.1'
22
+ __version_tuple__ = version_tuple = (1, 22, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -369,6 +369,13 @@ def test_subparsers(build_outcome: str) -> None:
369
369
  assert '<section id="test-no_child-options">' in build_outcome
370
370
 
371
371
 
372
+ @pytest.mark.sphinx(buildername="html", testroot="subparsers-storeaction")
373
+ def test_subparsers_stoeaction(build_outcome: str) -> None:
374
+ assert '<section id="test-options">' in build_outcome
375
+ assert '<section id="test-subparser">' in build_outcome
376
+ assert '<section id="test-subparser-positional-arguments">' in build_outcome
377
+
378
+
372
379
  @pytest.mark.sphinx(buildername="text", testroot="bad-module")
373
380
  def test_bad_module(app: SphinxTestApp, warning: StringIO) -> None:
374
381
  app.build()