Skip to content

Commit 1fda2b2

Browse files
committed
Mypy
1 parent 8831d48 commit 1fda2b2

8 files changed

+114
-98
lines changed

sphinxarg/ext.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from sphinxarg.utils import command_pos_args, target_to_anchor_id
2929

3030
if TYPE_CHECKING:
31+
from collections.abc import Sequence
32+
3133
from docutils.nodes import Element
3234
from sphinx.addnodes import pending_xref
3335
from sphinx.application import Sphinx
@@ -152,7 +154,7 @@ class ArgParseDirective(SphinxDirective):
152154
'idxgroups': unchanged,
153155
}
154156
domain: Domain | None = None
155-
idxgroups: list[str] | None = None
157+
idxgroups: Sequence[str] = ()
156158

157159
def _construct_manpage_specific_structure(self, parser_info):
158160
"""
@@ -728,8 +730,10 @@ class SphinxArgParseDomain(Domain):
728730
name = 'commands'
729731
label = 'commands-label'
730732

731-
roles = {'command': XRefRole()}
732-
indices = {}
733+
roles = {
734+
'command': XRefRole(),
735+
}
736+
indices = []
733737
initial_data: dict[str, list | dict] = {
734738
'commands': [],
735739
'commands-by-group': defaultdict(list),
@@ -773,7 +777,7 @@ def resolve_xref(
773777
logger.warning(msg)
774778
return None
775779

776-
def add_command(self, result: dict, anchor: str, groups: list[str] = None):
780+
def add_command(self, result: dict, anchor: str, groups: Sequence[str] = ()):
777781
"""Add an argparse command to the domain."""
778782
full_command = command_pos_args(result)
779783
desc = 'No description.'
@@ -786,7 +790,7 @@ def add_command(self, result: dict, anchor: str, groups: list[str] = None):
786790
# A separate list is kept to avoid the edge case that a command is used
787791
# once as part of a group (with idxgroups) and another time without the
788792
# option.
789-
for group in groups or []:
793+
for group in groups:
790794
self.data['commands-by-group'][group].append(idx_entry)
791795

792796

test/conftest.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Test HTML output the same way that Sphinx does in test_build_html.py."""
22

3-
from itertools import chain, cycle
43
from pathlib import Path
54

65
import pytest
@@ -67,7 +66,3 @@ def parse(fname):
6766

6867
yield parse
6968
etree_cache.clear()
70-
71-
72-
def flat_dict(d):
73-
return chain.from_iterable([zip(cycle([fname]), values) for fname, values in d.items()])

test/test_commands_by_group_index.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,71 @@
44
import pytest
55

66
from sphinxarg.ext import CommandsByGroupIndex
7-
8-
from .conftest import check_xpath, flat_dict
7+
from test.utils.xpath import check_xpath
98

109

1110
@pytest.mark.parametrize(
1211
('fname', 'expect'),
13-
flat_dict({
14-
'index.html': [
12+
[
13+
(
14+
'index.html',
1515
(".//div[@role='navigation']//a[@class='reference internal']", 'Sample'),
16+
),
17+
(
18+
'index.html',
1619
(".//div[@role='navigation']//a[@class='reference internal']", 'Command A'),
20+
),
21+
(
22+
'index.html',
1723
(".//div[@role='navigation']//a[@class='reference internal']", 'Command B'),
24+
),
25+
(
26+
'index.html',
1827
(
1928
".//div[@role='navigation']//a[@class='reference internal']",
2029
'Commands by Group',
2130
),
22-
],
23-
'commands-by-group.html': [
24-
('.//h1', 'Commands by Group'),
25-
('.//tr/td[2]/strong', 'ham in a cone'),
31+
),
32+
('commands-by-group.html', ('.//h1', 'Commands by Group')),
33+
('commands-by-group.html', ('.//tr/td[2]/strong', 'ham in a cone')),
34+
(
35+
'commands-by-group.html',
2636
(
2737
".//tr[td[2]/strong/text()='ham in a cone']/following-sibling::tr[1]/td[2]/a/code", # NoQA: E501
2838
'sample-directive-opts',
2939
),
40+
),
41+
(
42+
'commands-by-group.html',
3043
(
3144
".//tr[td[2]/strong/text()='ham in a cone']/following-sibling::tr[2]/td[2]/a/code", # NoQA: E501
3245
'sample-directive-opts B',
3346
),
34-
('.//tr/td[2]/strong', 'spam'),
47+
),
48+
('commands-by-group.html', ('.//tr/td[2]/strong', 'spam')),
49+
(
50+
'commands-by-group.html',
3551
(
3652
".//tr[td[2]/strong/text()='spam on a stick']/following-sibling::tr[1]/td[2]/a/code", # NoQA: E501
3753
'sample-directive-opts',
3854
),
55+
),
56+
(
57+
'commands-by-group.html',
3958
(
4059
".//tr[td[2]/strong/text()='spam on a stick']/following-sibling::tr[2]/td[2]/a/code", # NoQA: E501
4160
'sample-directive-opts A',
4261
),
62+
),
63+
(
64+
'commands-by-group.html',
4365
(
4466
'.//tr/td[2]/em',
4567
'(other)',
4668
False,
47-
), # Other does not have idxgroups set at all and is not present.
48-
],
49-
}),
69+
),
70+
), # Other does not have idxgroups set at all and is not present.
71+
],
5072
)
5173
@pytest.mark.sphinx('html', testroot='command-by-group-index')
5274
def test_commands_by_group_index_html(app, cached_etree_parse, fname, expect):
@@ -56,18 +78,17 @@ def test_commands_by_group_index_html(app, cached_etree_parse, fname, expect):
5678

5779
@pytest.mark.parametrize(
5880
('fname', 'expect'),
59-
flat_dict({
60-
'index.html': [
81+
[
82+
(
83+
'index.html',
6184
(
6285
".//div[@role='navigation']//a[@class='reference internal']",
6386
'Commands grouped by SomeName',
6487
),
65-
],
66-
'commands-groupedby-somename.html': [
67-
('.//h1', 'Commands grouped by SomeName'),
68-
('.//h1', 'Commands by Group', False),
69-
],
70-
}),
88+
),
89+
('commands-groupedby-somename.html', ('.//h1', 'Commands grouped by SomeName')),
90+
('commands-groupedby-somename.html', ('.//h1', 'Commands by Group', False)),
91+
],
7192
)
7293
@pytest.mark.sphinx(
7394
'html',

test/test_commands_index.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
import pytest
22

3-
from .conftest import check_xpath, flat_dict
3+
from test.utils.xpath import check_xpath
44

55

66
@pytest.mark.parametrize(
77
('fname', 'expect'),
8-
flat_dict({
9-
'subcommand-a.html': [
10-
('.//h1', 'Sample', False),
11-
('.//h1', 'Command A'),
12-
],
13-
'subcommand-b.html': [
14-
('.//h1', 'Sample', False),
15-
('.//h1', 'Command B'),
16-
],
17-
'commands-index.html': [
18-
('.//h1', 'Commands Index'),
19-
('.//tr/td[2]/a/code', 'sample-directive-opts'),
20-
('.//tr/td[3]/em', 'Support SphinxArgParse HTML testing'),
8+
[
9+
('subcommand-a.html', ('.//h1', 'Sample', False)),
10+
('subcommand-a.html', ('.//h1', 'Command A')),
11+
('subcommand-b.html', ('.//h1', 'Sample', False)),
12+
('subcommand-b.html', ('.//h1', 'Command B')),
13+
('commands-index.html', ('.//h1', 'Commands Index')),
14+
('commands-index.html', ('.//tr/td[2]/a/code', 'sample-directive-opts')),
15+
('commands-index.html', ('.//tr/td[3]/em', 'Support SphinxArgParse HTML testing')),
16+
(
17+
'commands-index.html',
2118
(
2219
".//tr[td[2]/a/code/text()='sample-directive-opts']/td[3]/em",
2320
'Support SphinxArgParse HTML testing',
2421
),
25-
],
26-
}),
22+
),
23+
],
2724
)
2825
@pytest.mark.sphinx('html', testroot='command-index')
2926
def test_commands_index_html(app, cached_etree_parse, fname, expect):

test/test_conf_options_html.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@
22

33
import pytest
44

5-
from .conftest import check_xpath, flat_dict
5+
from test.utils.xpath import check_xpath
66

77

88
@pytest.mark.parametrize(
99
('fname', 'expect'),
10-
flat_dict({
11-
'index.html': [
12-
('.//h1', 'Sample'),
13-
('.//h2', 'Sub-commands'),
14-
('.//h3', 'sample-directive-opts A'), # By default, just "A".
15-
('.//h3', 'sample-directive-opts B'),
16-
],
17-
}),
10+
[
11+
('index.html', ('.//h1', 'Sample')),
12+
('index.html', ('.//h2', 'Sub-commands')),
13+
('index.html', ('.//h3', 'sample-directive-opts A')), # By default, just "A".
14+
('index.html', ('.//h3', 'sample-directive-opts B')),
15+
],
1816
)
1917
@pytest.mark.sphinx(
2018
'html',

test/test_default_html.py

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,11 @@
11
"""Test the HTML builder and check output against XPath."""
22

33
import posixpath
4-
import re
54

65
import pytest
7-
from sphinx.ext.intersphinx import INVENTORY_FILENAME
86
from sphinx.util.inventory import Inventory, InventoryFile
97

10-
11-
def check_xpath(etree, fname, path, check, be_found=True):
12-
nodes = list(etree.xpath(path))
13-
if check is None:
14-
assert nodes == [], f'found any nodes matching xpath {path!r} in file {fname}'
15-
return
16-
else:
17-
assert nodes != [], f'did not find any node matching xpath {path!r} in file {fname}'
18-
if callable(check):
19-
check(nodes)
20-
elif not check:
21-
# only check for node presence
22-
pass
23-
else:
24-
25-
def get_text(node):
26-
if node.text is not None:
27-
# the node has only one text
28-
return node.text
29-
else:
30-
# the node has tags and text; gather texts just under the node
31-
return ''.join(n.tail or '' for n in node)
32-
33-
rex = re.compile(check)
34-
if be_found:
35-
if any(rex.search(get_text(node)) for node in nodes):
36-
return
37-
msg = (
38-
f'{check!r} not found in any node matching path {path} in {fname}: '
39-
f'{[node.text for node in nodes]!r}'
40-
)
41-
else:
42-
if all(not rex.search(get_text(node)) for node in nodes):
43-
return
44-
msg = (
45-
f'Found {check!r} in a node matching path {path} in {fname}: '
46-
f'{[node.text for node in nodes]!r}'
47-
)
48-
49-
raise AssertionError(msg)
8+
from test.utils.xpath import check_xpath
509

5110

5211
@pytest.mark.parametrize(
@@ -134,7 +93,7 @@ def test_index_is_optional(app, cached_etree_parse):
13493
@pytest.mark.sphinx('html', testroot='default-html')
13594
def test_object_inventory(app, cached_etree_parse):
13695
app.build()
137-
inventory_file = app.outdir / INVENTORY_FILENAME
96+
inventory_file = app.outdir / 'objects.inv'
13897
assert inventory_file.exists() is True
13998

14099
with inventory_file.open('rb') as f:

test/utils/__init__.py

Whitespace-only changes.

test/utils/xpath.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import re
2+
3+
4+
def check_xpath(etree, fname, path, check, be_found=True):
5+
nodes = list(etree.xpath(path))
6+
if check is None:
7+
assert nodes == [], f'found any nodes matching xpath {path!r} in file {fname}'
8+
return
9+
else:
10+
assert nodes != [], f'did not find any node matching xpath {path!r} in file {fname}'
11+
if callable(check):
12+
check(nodes)
13+
elif not check:
14+
# only check for node presence
15+
pass
16+
else:
17+
18+
def get_text(node):
19+
if node.text is not None:
20+
# the node has only one text
21+
return node.text
22+
else:
23+
# the node has tags and text; gather texts just under the node
24+
return ''.join(n.tail or '' for n in node)
25+
26+
rex = re.compile(check)
27+
if be_found:
28+
if any(rex.search(get_text(node)) for node in nodes):
29+
return
30+
msg = (
31+
f'{check!r} not found in any node matching path {path} in {fname}: '
32+
f'{[node.text for node in nodes]!r}'
33+
)
34+
else:
35+
if all(not rex.search(get_text(node)) for node in nodes):
36+
return
37+
msg = (
38+
f'Found {check!r} in a node matching path {path} in {fname}: '
39+
f'{[node.text for node in nodes]!r}'
40+
)
41+
42+
raise AssertionError(msg)

0 commit comments

Comments
 (0)