PICurv
0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
tests
tooling
audit_page_types.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
"""Enforce that every published documentation page has a declared page type."""
3
4
from
__future__
import
annotations
5
6
import
json
7
import
re
8
import
sys
9
from
pathlib
import
Path
10
11
12
REPO_ROOT = Path(__file__).resolve().parents[2]
13
REGISTRY = REPO_ROOT /
"tests"
/
"tooling"
/
"page_types.json"
14
HTML_DIR = REPO_ROOT /
"docs_build"
/
"html"
15
PAGE_DIRS = (
"docs/pages"
,
"docs"
)
16
# Authoring templates use a literal placeholder rather than a real page id.
17
PLACEHOLDER_IDS = {
"<ID>"
,
"<id>"
}
18
19
20
def
declared_pages
() -> dict:
21
"""!
22
@brief Every `@page` id in the documentation sources, with its file.
23
@return Mapping of page id to source path.
24
"""
25
pages: dict = {}
26
for
directory
in
PAGE_DIRS:
27
for
markdown
in
sorted((REPO_ROOT / directory).glob(
"*.md"
)):
28
match = re.search(
r"^@page\s+(\S+)"
, markdown.read_text(encoding=
"utf-8"
), re.M)
29
if
match
and
match.group(1)
not
in
PLACEHOLDER_IDS:
30
pages.setdefault(match.group(1), markdown)
31
return
pages
32
33
34
def
inline_type
(path: Path) -> str:
35
"""!
36
@brief The page type a page declares inline, if any.
37
@param[in] path Page source.
38
@return Declared type, or an empty string.
39
"""
40
match = re.search(
r"@pagemeta\{([^,}]+)"
, path.read_text(encoding=
"utf-8"
))
41
return
match.group(1).strip()
if
match
else
""
42
43
44
def
main
() -> int:
45
"""!
46
@brief Fail when a published page is untyped or its declarations disagree.
47
48
@details Coverage is enforced against the pages the build actually publishes. A
49
central registry types all of them without adding repetitive chrome to each
50
page; where a page also declares its type inline, the two must agree.
51
@return Process status code.
52
"""
53
registry = json.loads(REGISTRY.read_text(encoding=
"utf-8"
))
54
valid = set(registry[
"valid_types"
])
55
assignments = registry[
"assignments"
]
56
pages =
declared_pages
()
57
58
published = {
59
page_id
for
page_id
in
pages
if
(HTML_DIR / f
"{page_id}.html"
).is_file()
60
}
or
set(pages)
61
62
problems: list = []
63
for
page_id
in
sorted(published):
64
assigned = assignments.get(page_id)
65
if
not
assigned:
66
problems.append(f
"{pages[page_id].name}: page '{page_id}' has no type assignment"
)
67
continue
68
if
assigned
not
in
valid:
69
problems.append(f
"{page_id}: type '{assigned}' is not one of {sorted(valid)}"
)
70
continue
71
declared =
inline_type
(pages[page_id])
72
if
declared
and
declared != assigned:
73
problems.append(
74
f
"{pages[page_id].name}: inline @pagemeta says '{declared}' but the registry "
75
f
"says '{assigned}'"
76
)
77
for
stale
in
sorted(set(assignments) - set(pages)):
78
problems.append(f
"registry assigns a type to '{stale}', which is not a declared page"
)
79
80
if
problems:
81
print(
"Page-type coverage violations:"
, file=sys.stderr)
82
for
problem
in
problems:
83
print(f
" {problem}"
, file=sys.stderr)
84
print(
85
"\nAssign a type in tests/tooling/page_types.json. See 63_Page_Type_Contract for\n"
86
"what each type owes the reader."
,
87
file=sys.stderr,
88
)
89
return
1
90
91
from
collections
import
Counter
92
spread = Counter(assignments[p]
for
p
in
published)
93
summary =
", "
.join(f
"{count} {kind}"
for
kind, count
in
sorted(spread.items()))
94
print(f
"Page-type audit passed: {len(published)} published pages typed ({summary})."
)
95
return
0
96
97
98
if
__name__ ==
"__main__"
:
99
raise
SystemExit(
main
())
audit_page_types.declared_pages
dict declared_pages()
Every @page id in the documentation sources, with its file.
Definition
audit_page_types.py:20
audit_page_types.main
int main()
Fail when a published page is untyped or its declarations disagree.
Definition
audit_page_types.py:44
audit_page_types.inline_type
str inline_type(Path path)
The page type a page declares inline, if any.
Definition
audit_page_types.py:34
Generated by
1.9.8