sphinx-argparse-cli 1.21.3__tar.gz → 1.21.4__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.
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/PKG-INFO +4 -4
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/README.md +3 -3
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/pyproject.toml +10 -10
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/src/sphinx_argparse_cli/_logic.py +71 -63
- sphinx_argparse_cli-1.21.4/src/sphinx_argparse_cli/version.py +24 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/tests/test_logic.py +15 -5
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/tests/test_sphinx_argparse_cli.py +2 -2
- sphinx_argparse_cli-1.21.3/src/sphinx_argparse_cli/version.py +0 -34
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/.gitignore +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/LICENSE.txt +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/src/sphinx_argparse_cli/__init__.py +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/src/sphinx_argparse_cli/py.typed +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/tests/complex.txt +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/tests/complex_pre_310.txt +0 -0
- {sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/tests/conftest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-argparse-cli
|
|
3
|
-
Version: 1.21.
|
|
3
|
+
Version: 1.21.4
|
|
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
|
|
@@ -63,7 +63,7 @@ Use the directive in any reStructuredText file:
|
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
`:module:` points to the Python module containing the parser, and `:func:` names a zero-argument function that returns
|
|
66
|
-
an `ArgumentParser`. Build your docs and the full CLI reference appears
|
|
66
|
+
an `ArgumentParser`. Build your docs and the full CLI reference appears.
|
|
67
67
|
|
|
68
68
|
## How-to guides
|
|
69
69
|
|
|
@@ -129,7 +129,7 @@ Set the character width for usage lines and optionally show usage before the des
|
|
|
129
129
|
|
|
130
130
|
### Override title, description, or epilog
|
|
131
131
|
|
|
132
|
-
Replace auto-detected values, or pass an empty string to suppress them
|
|
132
|
+
Replace auto-detected values, or pass an empty string to suppress them:
|
|
133
133
|
|
|
134
134
|
```rst
|
|
135
135
|
.. sphinx_argparse_cli::
|
|
@@ -166,7 +166,7 @@ The anchor text is visible after the `#` in the URL when you click a heading.
|
|
|
166
166
|
### Handle mixed-case references
|
|
167
167
|
|
|
168
168
|
Sphinx `:ref:` only supports lower-case targets. When your program name or flags contain capital letters, set
|
|
169
|
-
`:force_refs_lower:` to convert them
|
|
169
|
+
`:force_refs_lower:` to convert them. Each upper-case letter becomes its lower-case form prefixed with `_` (e.g. `A`
|
|
170
170
|
becomes `_a`):
|
|
171
171
|
|
|
172
172
|
```rst
|
|
@@ -32,7 +32,7 @@ Use the directive in any reStructuredText file:
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
`:module:` points to the Python module containing the parser, and `:func:` names a zero-argument function that returns
|
|
35
|
-
an `ArgumentParser`. Build your docs and the full CLI reference appears
|
|
35
|
+
an `ArgumentParser`. Build your docs and the full CLI reference appears.
|
|
36
36
|
|
|
37
37
|
## How-to guides
|
|
38
38
|
|
|
@@ -98,7 +98,7 @@ Set the character width for usage lines and optionally show usage before the des
|
|
|
98
98
|
|
|
99
99
|
### Override title, description, or epilog
|
|
100
100
|
|
|
101
|
-
Replace auto-detected values, or pass an empty string to suppress them
|
|
101
|
+
Replace auto-detected values, or pass an empty string to suppress them:
|
|
102
102
|
|
|
103
103
|
```rst
|
|
104
104
|
.. sphinx_argparse_cli::
|
|
@@ -135,7 +135,7 @@ The anchor text is visible after the `#` in the URL when you click a heading.
|
|
|
135
135
|
### Handle mixed-case references
|
|
136
136
|
|
|
137
137
|
Sphinx `:ref:` only supports lower-case targets. When your program name or flags contain capital letters, set
|
|
138
|
-
`:force_refs_lower:` to convert them
|
|
138
|
+
`:force_refs_lower:` to convert them. Each upper-case letter becomes its lower-case form prefixed with `_` (e.g. `A`
|
|
139
139
|
becomes `_a`):
|
|
140
140
|
|
|
141
141
|
```rst
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
build-backend = "hatchling.build"
|
|
3
3
|
requires = [
|
|
4
4
|
"hatch-vcs>=0.5",
|
|
5
|
-
"hatchling>=1.
|
|
5
|
+
"hatchling>=1.30.1",
|
|
6
6
|
]
|
|
7
7
|
|
|
8
8
|
[project]
|
|
@@ -56,29 +56,29 @@ dev = [
|
|
|
56
56
|
test = [
|
|
57
57
|
"covdefaults>=2.3",
|
|
58
58
|
"defusedxml>=0.7.1",
|
|
59
|
-
"pytest>=9.
|
|
60
|
-
"pytest-cov>=7",
|
|
59
|
+
"pytest>=9.1.1",
|
|
60
|
+
"pytest-cov>=7.1",
|
|
61
61
|
]
|
|
62
62
|
type = [
|
|
63
|
-
"ty
|
|
63
|
+
"ty<0.0.25,>=0.0.24",
|
|
64
64
|
{ include-group = "test" },
|
|
65
65
|
]
|
|
66
66
|
lint = [
|
|
67
|
-
"pre-commit-uv>=4.2",
|
|
67
|
+
"pre-commit-uv>=4.2.2",
|
|
68
68
|
]
|
|
69
69
|
pkg-meta = [
|
|
70
70
|
"check-wheel-contents>=0.6.3",
|
|
71
71
|
"twine>=6.2",
|
|
72
|
-
"uv>=0.
|
|
72
|
+
"uv>=0.11.26",
|
|
73
73
|
]
|
|
74
74
|
|
|
75
75
|
[tool.hatch]
|
|
76
|
+
version.source = "vcs"
|
|
76
77
|
build.hooks.vcs.version-file = "src/sphinx_argparse_cli/version.py"
|
|
77
78
|
build.targets.sdist.include = [
|
|
78
79
|
"/src",
|
|
79
80
|
"/tests",
|
|
80
81
|
]
|
|
81
|
-
version.source = "vcs"
|
|
82
82
|
|
|
83
83
|
[tool.black]
|
|
84
84
|
line-length = 120
|
|
@@ -130,6 +130,9 @@ count = true
|
|
|
130
130
|
[tool.pyproject-fmt]
|
|
131
131
|
max_supported_python = "3.14"
|
|
132
132
|
|
|
133
|
+
[tool.ty]
|
|
134
|
+
environment.python-version = "3.14"
|
|
135
|
+
|
|
133
136
|
[tool.coverage]
|
|
134
137
|
run.parallel = true
|
|
135
138
|
run.plugins = [
|
|
@@ -143,6 +146,3 @@ paths.source = [
|
|
|
143
146
|
report.fail_under = 100
|
|
144
147
|
html.show_contexts = true
|
|
145
148
|
html.skip_covered = false
|
|
146
|
-
|
|
147
|
-
[tool.ty]
|
|
148
|
-
environment.python-version = "3.14"
|
|
@@ -17,16 +17,18 @@ from argparse import (
|
|
|
17
17
|
from collections import defaultdict
|
|
18
18
|
from contextlib import contextmanager
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import TYPE_CHECKING, Any, ClassVar,
|
|
20
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Final, cast
|
|
21
21
|
from unittest.mock import patch
|
|
22
22
|
|
|
23
23
|
from docutils.nodes import (
|
|
24
24
|
Element,
|
|
25
|
+
FixedTextElement,
|
|
25
26
|
Node,
|
|
26
27
|
Text,
|
|
27
28
|
bullet_list,
|
|
28
29
|
container,
|
|
29
30
|
fully_normalize_name,
|
|
31
|
+
inline,
|
|
30
32
|
list_item,
|
|
31
33
|
literal,
|
|
32
34
|
literal_block,
|
|
@@ -48,22 +50,10 @@ if TYPE_CHECKING:
|
|
|
48
50
|
|
|
49
51
|
from docutils.parsers.rst.states import RSTState, RSTStateMachine
|
|
50
52
|
from sphinx.domains.std import StandardDomain
|
|
53
|
+
from sphinx.util.logging import SphinxLoggerAdapter
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
text: str
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def make_id(key: str) -> str:
|
|
58
|
-
return "-".join(key.split()).rstrip("-")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def make_id_lower(key: str) -> str:
|
|
62
|
-
# replace all capital letters "X" with "_lower(X)"
|
|
63
|
-
return re.sub("[A-Z]", lambda m: "_" + m.group(0).lower(), make_id(key))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
logger = getLogger(__name__)
|
|
56
|
+
_LOGGER: Final[SphinxLoggerAdapter] = getLogger(__name__)
|
|
67
57
|
|
|
68
58
|
|
|
69
59
|
class SphinxArgparseCli(SphinxDirective):
|
|
@@ -82,9 +72,8 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
82
72
|
"group_title_prefix": unchanged,
|
|
83
73
|
"group_sub_title_prefix": unchanged,
|
|
84
74
|
"no_default_values": unchanged,
|
|
85
|
-
# :ref: only supports lower-case
|
|
86
|
-
#
|
|
87
|
-
# this is backwards incompatible for URL's, this is opt-in.
|
|
75
|
+
# :ref: only supports lower-case, so this prefixes would-be-upper-case chars with _;
|
|
76
|
+
# opt-in because it breaks existing URLs.
|
|
88
77
|
"force_refs_lower": flag,
|
|
89
78
|
}
|
|
90
79
|
|
|
@@ -106,7 +95,7 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
106
95
|
self._parser: ArgumentParser | None = None
|
|
107
96
|
self._std_domain: StandardDomain = cast("StandardDomain", self.env.get_domain("std"))
|
|
108
97
|
self._raw_format: bool = False
|
|
109
|
-
self.
|
|
98
|
+
self._make_id = make_id_lower if "force_refs_lower" in self.options else make_id
|
|
110
99
|
|
|
111
100
|
@property
|
|
112
101
|
def parser(self) -> ArgumentParser:
|
|
@@ -153,10 +142,8 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
153
142
|
self, sub_parser: _SubParsersAction[ArgumentParser]
|
|
154
143
|
) -> Iterator[tuple[list[str], str, ArgumentParser]]:
|
|
155
144
|
parser_to_args: dict[int, list[str]] = defaultdict(list)
|
|
156
|
-
str_to_parser: dict[str, ArgumentParser] = {}
|
|
157
145
|
for key, parser in sub_parser._name_parser_map.items(): # noqa: SLF001
|
|
158
146
|
parser_to_args[id(parser)].append(key)
|
|
159
|
-
str_to_parser[key] = parser
|
|
160
147
|
done_parser: set[int] = set()
|
|
161
148
|
|
|
162
149
|
for name, parser in sub_parser.choices.items():
|
|
@@ -170,28 +157,24 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
170
157
|
help_msg = next((a.help for a in sub_parser._choices_actions if a.dest == name), None) or "" # noqa: SLF001
|
|
171
158
|
yield aliases, help_msg, parser
|
|
172
159
|
|
|
173
|
-
# If this parser has a subparser, recurse into it
|
|
174
160
|
if parser._subparsers: # noqa: SLF001
|
|
175
161
|
sub_sub_parser: _SubParsersAction[ArgumentParser] = parser._subparsers._group_actions[0] # type: ignore[assignment] # noqa: SLF001
|
|
176
162
|
yield from self._load_sub_parsers(sub_sub_parser)
|
|
177
163
|
|
|
178
|
-
def
|
|
164
|
+
def _iter_sub_commands(self) -> Iterator[tuple[list[str], str, ArgumentParser]]:
|
|
179
165
|
top_sub_parser = self.parser._subparsers # noqa: SLF001
|
|
180
166
|
if not top_sub_parser:
|
|
181
167
|
return
|
|
182
|
-
sub_parser: _SubParsersAction[ArgumentParser]
|
|
183
|
-
sub_parser = top_sub_parser._group_actions[0] # type: ignore[assignment] # noqa: SLF001
|
|
184
|
-
|
|
168
|
+
sub_parser: _SubParsersAction[ArgumentParser] = top_sub_parser._group_actions[0] # type: ignore[assignment] # noqa: SLF001
|
|
185
169
|
yield from self._load_sub_parsers(sub_parser)
|
|
186
170
|
|
|
187
171
|
def run(self) -> list[Node]:
|
|
188
|
-
#
|
|
189
|
-
self.env.note_reread() # this document needs to be always updated
|
|
172
|
+
self.env.note_reread() # this document needs to always be rebuilt
|
|
190
173
|
title_text = self.options.get("title", f"{self.parser.prog} - CLI interface").strip()
|
|
191
174
|
if not title_text:
|
|
192
175
|
home_section: Element = container("")
|
|
193
176
|
else:
|
|
194
|
-
home_section = section("", title("", Text(title_text)), ids=[self.
|
|
177
|
+
home_section = section("", title("", Text(title_text)), ids=[self._make_id(title_text)], names=[title_text])
|
|
195
178
|
|
|
196
179
|
if "usage_first" in self.options:
|
|
197
180
|
home_section += self._mk_usage(self.parser)
|
|
@@ -202,15 +185,13 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
202
185
|
if "usage_first" not in self.options:
|
|
203
186
|
home_section += self._mk_usage(self.parser)
|
|
204
187
|
|
|
205
|
-
# construct groups excluding sub-parsers
|
|
206
188
|
for group in self.parser._action_groups: # noqa: SLF001
|
|
207
189
|
if not group._group_actions or group is self.parser._subparsers: # noqa: SLF001
|
|
208
190
|
continue
|
|
209
191
|
home_section += self._mk_option_group(
|
|
210
192
|
group, prefix=self.parser.prog.split("/")[-1], prog=self.parser.prog.split("/")[-1]
|
|
211
193
|
)
|
|
212
|
-
|
|
213
|
-
for aliases, help_msg, parser in self.load_sub_parsers():
|
|
194
|
+
for aliases, help_msg, parser in self._iter_sub_commands():
|
|
214
195
|
home_section += self._mk_sub_command(aliases, help_msg, parser)
|
|
215
196
|
|
|
216
197
|
if epilog := self._pre_format(self.options.get("epilog", self.parser.epilog)):
|
|
@@ -228,14 +209,16 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
228
209
|
lit = literal_block("", Text(block), classes=["sphinx-argparse-cli-wrap"])
|
|
229
210
|
lit["language"] = "none"
|
|
230
211
|
return lit
|
|
231
|
-
|
|
212
|
+
para = paragraph("", Text(block))
|
|
213
|
+
_protect_option_dashes(para)
|
|
214
|
+
return para
|
|
232
215
|
|
|
233
216
|
def _mk_option_group(self, group: _ArgumentGroup, prefix: str, prog: str) -> section:
|
|
234
217
|
sub_title_prefix: str = self.options["group_sub_title_prefix"]
|
|
235
218
|
title_prefix = self.options["group_title_prefix"]
|
|
236
219
|
title_text = self._build_opt_grp_title(group, prefix, prog, sub_title_prefix, title_prefix)
|
|
237
220
|
title_ref: str = f"{prefix}{' ' if prefix else ''}{group.title}"
|
|
238
|
-
ref_id = self.
|
|
221
|
+
ref_id = self._make_id(title_ref)
|
|
239
222
|
# the text sadly needs to be prefixed, because otherwise the autosectionlabel will conflict
|
|
240
223
|
header = title("", Text(title_text))
|
|
241
224
|
group_section = section("", header, ids=[ref_id], names=[ref_id])
|
|
@@ -265,15 +248,11 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
265
248
|
if action.metavar:
|
|
266
249
|
as_key = action.metavar if isinstance(action.metavar, str) else action.metavar[0]
|
|
267
250
|
if action.option_strings:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
for opt in action.option_strings:
|
|
271
|
-
if first:
|
|
272
|
-
first = False
|
|
273
|
-
else:
|
|
251
|
+
for at, opt in enumerate(action.option_strings):
|
|
252
|
+
if at:
|
|
274
253
|
line += Text(", ")
|
|
275
254
|
self._mk_option_name(line, prefix, opt)
|
|
276
|
-
if
|
|
255
|
+
if action.nargs != 0:
|
|
277
256
|
line += Text(" ")
|
|
278
257
|
metavar_text = (
|
|
279
258
|
" ".join(meta.upper() for meta in action.metavar)
|
|
@@ -284,7 +263,6 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
284
263
|
else:
|
|
285
264
|
self._mk_option_name(line, prefix, as_key)
|
|
286
265
|
|
|
287
|
-
point = list_item("", line, ids=[])
|
|
288
266
|
if action.help:
|
|
289
267
|
help_text = load_help_text(action.help)
|
|
290
268
|
temp = paragraph()
|
|
@@ -302,16 +280,15 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
302
280
|
line += Text(" (default: ")
|
|
303
281
|
line += literal(text=str(action.default).replace(str(Path.cwd()), "{cwd}"))
|
|
304
282
|
line += Text(")")
|
|
305
|
-
|
|
283
|
+
_protect_option_dashes(line)
|
|
284
|
+
return list_item("", line, ids=[])
|
|
306
285
|
|
|
307
286
|
def _mk_option_name(self, line: paragraph, prefix: str, opt: str) -> None:
|
|
308
|
-
ref_id = self.
|
|
287
|
+
ref_id = self._make_id(f"{prefix}-{opt}")
|
|
309
288
|
ref_title = f"{prefix} {opt}"
|
|
310
289
|
ref = reference("", refid=ref_id, reftitle=ref_title)
|
|
311
290
|
line.attributes["ids"].append(ref_id)
|
|
312
|
-
|
|
313
|
-
st += literal(text=opt)
|
|
314
|
-
ref += st
|
|
291
|
+
ref += strong("", "", literal(text=opt))
|
|
315
292
|
self._register_ref(ref_id, ref_title, ref, is_cli_option=True)
|
|
316
293
|
line += ref
|
|
317
294
|
|
|
@@ -329,7 +306,7 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
329
306
|
else:
|
|
330
307
|
name = normalize_name(ref_name)
|
|
331
308
|
if name in self._std_domain.labels:
|
|
332
|
-
|
|
309
|
+
_LOGGER.warning(
|
|
333
310
|
__("duplicate label %s, other instance in %s"),
|
|
334
311
|
name,
|
|
335
312
|
self.env.doc2path(self._std_domain.labels[name][0]),
|
|
@@ -355,7 +332,7 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
355
332
|
title_text += aliases_text
|
|
356
333
|
title_ref += aliases_text
|
|
357
334
|
title_text = title_text.strip()
|
|
358
|
-
ref_id = self.
|
|
335
|
+
ref_id = self._make_id(title_ref)
|
|
359
336
|
group_section = section("", title("", Text(title_text)), ids=[ref_id], names=[title_ref])
|
|
360
337
|
self._register_ref(ref_id, title_ref, group_section)
|
|
361
338
|
|
|
@@ -365,16 +342,16 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
365
342
|
command_desc = (parser.description or help_msg or "").strip()
|
|
366
343
|
if command_desc:
|
|
367
344
|
desc_paragraph = paragraph("", Text(command_desc))
|
|
345
|
+
_protect_option_dashes(desc_paragraph)
|
|
368
346
|
group_section += desc_paragraph
|
|
369
347
|
|
|
370
348
|
if "usage_first" not in self.options:
|
|
371
349
|
group_section += self._mk_usage(parser)
|
|
372
350
|
|
|
373
351
|
for group in parser._action_groups: # noqa: SLF001
|
|
374
|
-
if not group._group_actions: #
|
|
352
|
+
if not group._group_actions: # noqa: SLF001
|
|
375
353
|
continue
|
|
376
354
|
if isinstance(group._group_actions[0], _SubParsersAction): # noqa: SLF001
|
|
377
|
-
# If this is a subparser, ignore it
|
|
378
355
|
continue
|
|
379
356
|
group_section += self._mk_option_group(group, prefix=parser.prog, prog=self.parser.prog.split("/")[-1])
|
|
380
357
|
return group_section
|
|
@@ -415,33 +392,64 @@ class SphinxArgparseCli(SphinxDirective):
|
|
|
415
392
|
@staticmethod
|
|
416
393
|
def _apply_sub_title(title_text: str, sub_title_prefix: str, prog: str, sub_cmd: str) -> str:
|
|
417
394
|
if sub_title_prefix:
|
|
418
|
-
sub_title_prefix = sub_title_prefix.replace("{prog}", prog)
|
|
419
|
-
sub_title_prefix = sub_title_prefix.replace("{subcommand}", sub_cmd)
|
|
395
|
+
sub_title_prefix = sub_title_prefix.replace("{prog}", prog).replace("{subcommand}", sub_cmd)
|
|
420
396
|
title_text += f"{sub_title_prefix} "
|
|
421
397
|
return title_text
|
|
422
398
|
|
|
423
399
|
def _mk_usage(self, parser: ArgumentParser) -> literal_block:
|
|
424
400
|
parser.formatter_class = lambda prog: HelpFormatter(prog, width=self.options.get("usage_width", 100))
|
|
425
|
-
with self.
|
|
401
|
+
with self._no_color():
|
|
426
402
|
texts = parser.format_usage()[len("usage: ") :].splitlines()
|
|
427
403
|
texts = [line if at == 0 else f"{' ' * (len(parser.prog) + 1)}{line.lstrip()}" for at, line in enumerate(texts)]
|
|
428
404
|
return literal_block("", Text("\n".join(texts)), classes=["sphinx-argparse-cli-wrap"])
|
|
429
405
|
|
|
430
406
|
@contextmanager
|
|
431
|
-
def
|
|
407
|
+
def _no_color(self) -> Iterator[None]:
|
|
432
408
|
with patch.dict(os.environ, {"NO_COLOR": "1"}, clear=False):
|
|
433
|
-
yield
|
|
409
|
+
yield
|
|
434
410
|
|
|
435
411
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
412
|
+
def make_id_lower(key: str) -> str:
|
|
413
|
+
return re.sub("[A-Z]", lambda m: f"_{m.group(0).lower()}", make_id(key))
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def make_id(key: str) -> str:
|
|
417
|
+
return "-".join(key.split()).rstrip("-")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
_HELP_SUBSTITUTIONS: Final[list[tuple[re.Pattern[str], str]]] = [
|
|
421
|
+
(re.compile(r"[']+(.+?)[']+"), "``'\\1'``"),
|
|
422
|
+
(re.compile(r'["]+(.+?)["]+'), '``"\\1"``'),
|
|
423
|
+
(re.compile(r"[{](.+?)[}]"), "``{\\1}``"),
|
|
424
|
+
]
|
|
439
425
|
|
|
440
426
|
|
|
441
427
|
def load_help_text(help_text: str) -> str:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
return
|
|
428
|
+
for pattern, replacement in _HELP_SUBSTITUTIONS:
|
|
429
|
+
help_text = pattern.sub(replacement, help_text)
|
|
430
|
+
return help_text
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
_OPTION_TOKEN: Final[re.Pattern[str]] = re.compile(r"((?<!\w)--[a-zA-Z0-9][\w-]*)")
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _protect_option_dashes(node: Element) -> None:
|
|
437
|
+
"""Wrap each ``--option`` in a smartquotes-exempt inline so its ``--`` survives Sphinx's en-dash transform."""
|
|
438
|
+
for text in list(node.findall(Text)):
|
|
439
|
+
if isinstance(text.parent, (literal, FixedTextElement)):
|
|
440
|
+
continue
|
|
441
|
+
# A capturing group makes split() interleave the option tokens (odd indices) with the surrounding text.
|
|
442
|
+
parts = _OPTION_TOKEN.split(text)
|
|
443
|
+
if len(parts) == 1:
|
|
444
|
+
continue
|
|
445
|
+
text.parent.replace(
|
|
446
|
+
text,
|
|
447
|
+
[
|
|
448
|
+
inline("", part, support_smartquotes=False) if index % 2 else Text(part)
|
|
449
|
+
for index, part in enumerate(parts)
|
|
450
|
+
if part
|
|
451
|
+
],
|
|
452
|
+
)
|
|
445
453
|
|
|
446
454
|
|
|
447
455
|
class HookError(Exception):
|
|
@@ -453,7 +461,7 @@ def _parse_known_args_hook(self: ArgumentParser, *args: Any, **kwargs: Any) -> N
|
|
|
453
461
|
raise HookError(self)
|
|
454
462
|
|
|
455
463
|
|
|
456
|
-
_ANSI_COLOR_RE = re.compile(r"\x1b\[[0-9;]*m")
|
|
464
|
+
_ANSI_COLOR_RE: Final[re.Pattern[str]] = re.compile(r"\x1b\[[0-9;]*m")
|
|
457
465
|
|
|
458
466
|
|
|
459
467
|
def _strip_ansi_colors(text: str) -> str: # pragma: >=3.14 cover
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '1.21.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 21, 4)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
-
from sphinx_argparse_cli._logic import make_id, make_id_lower
|
|
10
|
+
from sphinx_argparse_cli._logic import load_help_text, make_id, make_id_lower
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from io import StringIO
|
|
@@ -142,6 +142,19 @@ def test_multiline_epilog_subclass_formatter_as_html(build_outcome: str) -> None
|
|
|
142
142
|
assert ref in build_outcome
|
|
143
143
|
|
|
144
144
|
|
|
145
|
+
@pytest.mark.sphinx(buildername="html", testroot="smartquotes")
|
|
146
|
+
def test_option_dashes_survive_smartquotes(build_outcome: str) -> None:
|
|
147
|
+
# option names mentioned in parser-supplied text keep their double hyphen
|
|
148
|
+
assert "Pass input via <span>--text</span> or stdin." in build_outcome
|
|
149
|
+
assert "see also <span>--2fa</span> and <span>--dry_run</span>." in build_outcome
|
|
150
|
+
assert "combine with <span>--output</span> for files" in build_outcome
|
|
151
|
+
assert "build things with <span>--flair</span>" in build_outcome
|
|
152
|
+
for mangled in ("\u2013text", "\u2013output", "\u20132fa", "\u2013dry_run", "\u2013flair"):
|
|
153
|
+
assert mangled not in build_outcome
|
|
154
|
+
# the surrounding text keeps normal typography
|
|
155
|
+
assert "north\u2013south and 10\u201320" in build_outcome
|
|
156
|
+
|
|
157
|
+
|
|
145
158
|
@pytest.mark.sphinx(buildername="text", testroot="complex")
|
|
146
159
|
@pytest.mark.prepare(directive_args=[":usage_width: 100"])
|
|
147
160
|
def test_usage_width_default(build_outcome: str) -> None:
|
|
@@ -179,10 +192,7 @@ def test_suppressed_action(build_outcome: str) -> None:
|
|
|
179
192
|
],
|
|
180
193
|
)
|
|
181
194
|
def test_help_loader(example: str, output: str) -> None:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
result = load_help_text(example)
|
|
185
|
-
assert result == output
|
|
195
|
+
assert load_help_text(example) == output
|
|
186
196
|
|
|
187
197
|
|
|
188
198
|
@pytest.mark.sphinx(buildername="html", testroot="ref")
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# file generated by setuptools-scm
|
|
2
|
-
# don't change, don't track in version control
|
|
3
|
-
|
|
4
|
-
__all__ = [
|
|
5
|
-
"__version__",
|
|
6
|
-
"__version_tuple__",
|
|
7
|
-
"version",
|
|
8
|
-
"version_tuple",
|
|
9
|
-
"__commit_id__",
|
|
10
|
-
"commit_id",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
TYPE_CHECKING = False
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from typing import Tuple
|
|
16
|
-
from typing import Union
|
|
17
|
-
|
|
18
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
-
COMMIT_ID = Union[str, None]
|
|
20
|
-
else:
|
|
21
|
-
VERSION_TUPLE = object
|
|
22
|
-
COMMIT_ID = object
|
|
23
|
-
|
|
24
|
-
version: str
|
|
25
|
-
__version__: str
|
|
26
|
-
__version_tuple__: VERSION_TUPLE
|
|
27
|
-
version_tuple: VERSION_TUPLE
|
|
28
|
-
commit_id: COMMIT_ID
|
|
29
|
-
__commit_id__: COMMIT_ID
|
|
30
|
-
|
|
31
|
-
__version__ = version = '1.21.3'
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 21, 3)
|
|
33
|
-
|
|
34
|
-
__commit_id__ = commit_id = None
|
|
File without changes
|
|
File without changes
|
{sphinx_argparse_cli-1.21.3 → sphinx_argparse_cli-1.21.4}/src/sphinx_argparse_cli/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|