|
9 | 9 | from docutils import nodes
|
10 | 10 |
|
11 | 11 | if TYPE_CHECKING:
|
| 12 | + from typing import Any |
| 13 | + |
12 | 14 | from sphinx.testing.util import SphinxTestApp
|
13 | 15 |
|
14 | 16 |
|
15 | 17 | @pytest.mark.sphinx('text', testroot='directive-only')
|
16 | 18 | def test_sectioning(app: SphinxTestApp) -> None:
|
17 |
| - def getsects(section): |
18 |
| - if not isinstance(section, nodes.section): |
19 |
| - return [getsects(n) for n in section.children] |
20 |
| - title = section.next_node(nodes.title).astext().strip() |
21 |
| - subsects = [] |
22 |
| - children = section.children[:] |
23 |
| - while children: |
24 |
| - node = children.pop(0) |
25 |
| - if isinstance(node, nodes.section): |
26 |
| - subsects.append(node) |
27 |
| - continue |
28 |
| - children = list(node.children) + children |
29 |
| - return [title, [getsects(subsect) for subsect in subsects]] |
30 |
| - |
31 |
| - def testsects(prefix, sects, indent=0): |
32 |
| - title = sects[0] |
33 |
| - parent_num = title.split()[0] |
34 |
| - assert prefix == parent_num, f'Section out of place: {title!r}' |
35 |
| - for i, subsect in enumerate(sects[1]): |
36 |
| - num = subsect[0].split()[0] |
37 |
| - assert re.match('[0-9]+[.0-9]*[.]', num), ( |
38 |
| - f'Unnumbered section: {subsect[0]!r}' |
39 |
| - ) |
40 |
| - testsects(prefix + str(i + 1) + '.', subsect, indent + 4) |
41 |
| - |
42 | 19 | app.build(filenames=[app.srcdir / 'only.rst'])
|
43 | 20 | doctree = app.env.get_doctree('only')
|
44 | 21 | app.env.apply_post_transforms(doctree, 'only')
|
45 | 22 |
|
46 |
| - parts = [getsects(n) for n in doctree.children if isinstance(n, nodes.section)] |
47 |
| - for i, s in enumerate(parts): |
48 |
| - testsects(str(i + 1) + '.', s, 4) |
49 |
| - actual_headings = '\n'.join(p[0] for p in parts) |
| 23 | + parts = [_get_sections(n) for n in doctree.children if isinstance(n, nodes.section)] |
| 24 | + for i, section in enumerate(parts): |
| 25 | + _test_sections(f'{i + 1}.', section, 4) |
| 26 | + actual_headings = '\n'.join(p[0] for p in parts) # type: ignore[misc] |
50 | 27 | assert len(parts) == 4, (
|
51 | 28 | f'Expected 4 document level headings, got:\n{actual_headings}'
|
52 | 29 | )
|
| 30 | + |
| 31 | + |
| 32 | +def _get_sections(section: nodes.Node) -> list[str | list[Any]]: |
| 33 | + if not isinstance(section, nodes.section): |
| 34 | + return list(map(_get_sections, section.children)) |
| 35 | + title = section.next_node(nodes.title).astext().strip() |
| 36 | + subsections = [] |
| 37 | + children = section.children.copy() |
| 38 | + while children: |
| 39 | + node = children.pop(0) |
| 40 | + if isinstance(node, nodes.section): |
| 41 | + subsections.append(node) |
| 42 | + continue |
| 43 | + children = list(node.children) + children |
| 44 | + return [title, list(map(_get_sections, subsections))] |
| 45 | + |
| 46 | + |
| 47 | +def _test_sections( |
| 48 | + prefix: str, sections: list[str | list[Any]], indent: int = 0 |
| 49 | +) -> None: |
| 50 | + title = sections[0] |
| 51 | + assert isinstance(title, str) |
| 52 | + parent_num = title.partition(' ')[0] |
| 53 | + assert prefix == parent_num, f'Section out of place: {title!r}' |
| 54 | + for i, subsection in enumerate(sections[1]): |
| 55 | + subsection_title = subsection[0] |
| 56 | + assert isinstance(subsection_title, str) |
| 57 | + num = subsection_title.partition(' ')[0] |
| 58 | + assert re.match('[0-9]+[.0-9]*[.]', num), ( |
| 59 | + f'Unnumbered section: {subsection[0]!r}' |
| 60 | + ) |
| 61 | + _test_sections(f'{prefix}{i + 1}.', subsection, indent + 4) |
0 commit comments