PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions | Variables
audit_docs_site Namespace Reference

Functions

dict load_contract ()
 Load the published-site integrity contract.
 
list[Path]|None git_tracked_files ()
 List tracked plus non-ignored untracked files, so the scan set is reproducible for a commit and generated scratch is excluded without excluding tracked docs.
 
 iter_scannable ()
 Yield repository files whose text may carry project-owned URLs.
 
Path require_built_html (dict contract)
 Resolve the generated HTML tree, failing loudly when it has not been built.
 
list[str] check_forbidden_urls (dict contract)
 Reject project-owned URL forms that are known not to resolve.
 
list[str] check_canonical_urls (dict contract, Path html_dir)
 Verify every canonical documentation URL names a page the build actually generates.
 
list[str] check_layout (dict contract, Path html_dir)
 Verify every Doxygen layout tab resolves to a generated page.
 
list[str] check_orphan_pages (dict contract, Path html_dir)
 Verify every page the build publishes is reachable from the navigation.
 
list[str] check_generated_fragment_links (Path html_dir)
 Verify every link emitted by a generated fragment resolves in the rendered page.
 
str strip_non_prose (str text)
 Blank out fenced code, inline code, and HTML comments.
 
list markdown_fragment_links (str text)
 Extract Markdown links carrying a fragment, from prose only.
 
list tracked_markdown ()
 Every Markdown file the current commit carries.
 
set rendered_ids (Path path)
 Anchor ids present in a rendered HTML page.
 
str heading_anchor (str text)
 GitHub-style anchor slug for a Markdown heading.
 
set markdown_anchors (Path path)
 Anchors a plain Markdown file offers.
 
list[str] check_page_cross_references (Path html_dir)
 Verify hand-written Markdown fragment links resolve, repository-wide.
 
int main ()
 Fail when a project-owned URL is dead or a navigation tab has no generated page.
 

Variables

 REPO_ROOT = Path(__file__).resolve().parents[2]
 
str CONTRACT_PATH = REPO_ROOT / "tests" / "tooling" / "docs_site_contract.json"
 
dict SKIP_DIRS = {".git", "docs_build", "obj", "bin", "stubs", "runs", "studies", "__pycache__", ".pytest_cache"}
 
dict SCAN_SUFFIXES = {".md", ".js", ".xml", ".html", ".yml", ".yaml"}
 

Detailed Description

Verify published-site integrity against the generated HTML, not against source declarations.

Function Documentation

◆ load_contract()

dict audit_docs_site.load_contract ( )

Load the published-site integrity contract.

Returns
Parsed contract mapping.

Definition at line 23 of file audit_docs_site.py.

23def load_contract() -> dict:
24 """!
25 @brief Load the published-site integrity contract.
26 @return Parsed contract mapping.
27 """
28 return json.loads(CONTRACT_PATH.read_text(encoding="utf-8"))
29
30
Here is the caller graph for this function:

◆ git_tracked_files()

list[Path] | None audit_docs_site.git_tracked_files ( )

List tracked plus non-ignored untracked files, so the scan set is reproducible for a commit and generated scratch is excluded without excluding tracked docs.

Returns
Sorted paths, or None when git enumeration is unavailable.

Definition at line 31 of file audit_docs_site.py.

31def git_tracked_files() -> list[Path] | None:
32 """!
33 @brief List tracked plus non-ignored untracked files, so the scan set is reproducible
34 for a commit and generated scratch is excluded without excluding tracked docs.
35 @return Sorted paths, or None when git enumeration is unavailable.
36 """
37 return enumerate_repository_files(REPO_ROOT)
38
39
Here is the caller graph for this function:

◆ iter_scannable()

audit_docs_site.iter_scannable ( )

Yield repository files whose text may carry project-owned URLs.

Returns
Generator of file paths.

Definition at line 40 of file audit_docs_site.py.

40def iter_scannable():
41 """!
42 @brief Yield repository files whose text may carry project-owned URLs.
43 @return Generator of file paths.
44 """
45 candidates = git_tracked_files()
46 if candidates is None:
47 candidates = sorted(REPO_ROOT.rglob("*"))
48 for path in candidates:
49 if not path.is_file() or path.suffix not in SCAN_SUFFIXES:
50 continue
51 if SKIP_DIRS.intersection(path.relative_to(REPO_ROOT).parts):
52 continue
53 yield path
54
55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ require_built_html()

Path audit_docs_site.require_built_html ( dict  contract)

Resolve the generated HTML tree, failing loudly when it has not been built.

Parameters
[in]contractParsed contract mapping.
Returns
Path to the generated HTML directory.
Exceptions
RuntimeErrorwhen the publication artifact is absent.

Definition at line 56 of file audit_docs_site.py.

56def require_built_html(contract: dict) -> Path:
57 """!
58 @brief Resolve the generated HTML tree, failing loudly when it has not been built.
59 @param[in] contract Parsed contract mapping.
60 @return Path to the generated HTML directory.
61 @throws RuntimeError when the publication artifact is absent.
62 """
63 html_dir = REPO_ROOT / contract["html_dir"]
64 if not (html_dir / "index.html").is_file():
65 raise RuntimeError(
66 f"{contract['html_dir']}/index.html is missing. This audit validates against the built "
67 "publication artifact; run 'make build-docs' first."
68 )
69 return html_dir
70
71
Here is the caller graph for this function:

◆ check_forbidden_urls()

list[str] audit_docs_site.check_forbidden_urls ( dict  contract)

Reject project-owned URL forms that are known not to resolve.

Parameters
[in]contractParsed contract mapping.
Returns
Violation lines.

Definition at line 72 of file audit_docs_site.py.

72def check_forbidden_urls(contract: dict) -> list[str]:
73 """!
74 @brief Reject project-owned URL forms that are known not to resolve.
75 @param[in] contract Parsed contract mapping.
76 @return Violation lines.
77 """
78 violations: list[str] = []
79 for rule in contract["forbidden_url_patterns"]:
80 pattern = re.compile(rule["pattern"])
81 for path in iter_scannable():
82 for match in pattern.findall(path.read_text(encoding="utf-8", errors="replace")):
83 violations.append(
84 f"{path.relative_to(REPO_ROOT)}: dead project URL '{match}'\n"
85 f" reason: {rule['reason']}\n"
86 f" use: {rule['replacement_hint']}"
87 )
88 return violations
89
90
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_canonical_urls()

list[str] audit_docs_site.check_canonical_urls ( dict  contract,
Path  html_dir 
)

Verify every canonical documentation URL names a page the build actually generates.

Parameters
[in]contractParsed contract mapping.
[in]html_dirGenerated HTML directory.
Returns
Violation lines.

Definition at line 91 of file audit_docs_site.py.

91def check_canonical_urls(contract: dict, html_dir: Path) -> list[str]:
92 """!
93 @brief Verify every canonical documentation URL names a page the build actually generates.
94 @param[in] contract Parsed contract mapping.
95 @param[in] html_dir Generated HTML directory.
96 @return Violation lines.
97 """
98 base = re.escape(contract["canonical_docs_base"])
99 pattern = re.compile(base + r"([A-Za-z0-9_.-]+\.html)")
100 violations: list[str] = []
101 for path in iter_scannable():
102 for page in sorted(set(pattern.findall(path.read_text(encoding="utf-8", errors="replace")))):
103 if not (html_dir / page).is_file():
104 violations.append(
105 f"{path.relative_to(REPO_ROOT)}: canonical URL points at '{page}', "
106 f"which the build does not generate.\n"
107 f" The page may be excluded from Doxyfile, renamed, or never to have existed."
108 )
109 return violations
110
111
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_layout()

list[str] audit_docs_site.check_layout ( dict  contract,
Path  html_dir 
)

Verify every Doxygen layout tab resolves to a generated page.

Parameters
[in]contractParsed contract mapping.
[in]html_dirGenerated HTML directory.
Returns
Violation lines.

Definition at line 112 of file audit_docs_site.py.

112def check_layout(contract: dict, html_dir: Path) -> list[str]:
113 """!
114 @brief Verify every Doxygen layout tab resolves to a generated page.
115 @param[in] contract Parsed contract mapping.
116 @param[in] html_dir Generated HTML directory.
117 @return Violation lines.
118 """
119 external = set(contract["external_layout_urls"])
120 violations: list[str] = []
121 for tab in ET.parse(REPO_ROOT / contract["layout_file"]).getroot().iter("tab"):
122 url = tab.get("url")
123 if not url or url in external or url.startswith(("http://", "https://", "/")):
124 continue
125 if not (html_dir / url).is_file():
126 violations.append(
127 f"{contract['layout_file']}: tab '{tab.get('title')}' -> {url}\n"
128 f" no such file in the generated site; the published tab will 404"
129 )
130 return violations
131
132
Here is the caller graph for this function:

◆ check_orphan_pages()

list[str] audit_docs_site.check_orphan_pages ( dict  contract,
Path  html_dir 
)

Verify every page the build publishes is reachable from the navigation.

A page is reachable when another page adopts it with a subpage directive, or when a layout tab points at it directly. A page that is generated but adopted by nothing renders in the site with no route to it except search.

Parameters
[in]contractParsed contract mapping.
[in]html_dirGenerated HTML directory.
Returns
Violation lines.

Definition at line 133 of file audit_docs_site.py.

133def check_orphan_pages(contract: dict, html_dir: Path) -> list[str]:
134 """!
135 @brief Verify every page the build publishes is reachable from the navigation.
136
137 A page is reachable when another page adopts it with a subpage directive, or when a layout tab
138 points at it directly. A page that is generated but adopted by nothing renders in the
139 site with no route to it except search.
140 @param[in] contract Parsed contract mapping.
141 @param[in] html_dir Generated HTML directory.
142 @return Violation lines.
143 """
144 declared: dict[str, Path] = {}
145 adopted: set[str] = set()
146 for directory in ("docs/pages", "docs"):
147 for markdown in sorted((REPO_ROOT / directory).glob("*.md")):
148 text = markdown.read_text(encoding="utf-8", errors="replace")
149 match = re.search(r"@page\s+([A-Za-z0-9_]+)", text)
150 if match:
151 declared.setdefault(match.group(1), markdown)
152 adopted.update(re.findall(r"@subpage\s+([A-Za-z0-9_]+)", text))
153 tabs = {
154 (tab.get("url") or "")[: -len(".html")]
155 for tab in ET.parse(REPO_ROOT / contract["layout_file"]).getroot().iter("tab")
156 if (tab.get("url") or "").endswith(".html")
157 }
158 violations = []
159 for page_id, source in sorted(declared.items()):
160 if not (html_dir / f"{page_id}.html").is_file():
161 continue # excluded from the build; not published, so not an orphan
162 if page_id in adopted or page_id in tabs:
163 continue
164 violations.append(
165 f"{source.relative_to(REPO_ROOT)}: page '{page_id}' is published but orphaned\n"
166 f" no page adopts it with a subpage directive and no layout tab points at it"
167 )
168 return violations
169
170
Here is the caller graph for this function:

◆ check_generated_fragment_links()

list[str] audit_docs_site.check_generated_fragment_links ( Path  html_dir)

Verify every link emitted by a generated fragment resolves in the rendered page.

Generated tables link into Tier-2 entries. Doxygen does not validate raw HTML inserted through an HTML include, so a fragment pointing at an anchor that was never written renders as a dead link and passes every other gate.

Parameters
[in]html_dirGenerated HTML directory.
Returns
Violation lines.

Definition at line 171 of file audit_docs_site.py.

171def check_generated_fragment_links(html_dir: Path) -> list[str]:
172 """!
173 @brief Verify every link emitted by a generated fragment resolves in the rendered page.
174
175 Generated tables link into Tier-2 entries. Doxygen does not validate raw HTML
176 inserted through an HTML include, so a fragment pointing at an anchor that was
177 never written renders as a dead link and passes every other gate.
178 @param[in] html_dir Generated HTML directory.
179 @return Violation lines.
180 """
181 generated = REPO_ROOT / "docs" / "generated"
182 if not generated.is_dir():
183 return []
184 violations: list[str] = []
185 for fragment in sorted(generated.glob("*.html")):
186 text = fragment.read_text(encoding="utf-8")
187 hosts = [
188 page
189 for page in sorted((REPO_ROOT / "docs" / "pages").glob("*.md"))
190 if fragment.name in page.read_text(encoding="utf-8")
191 ]
192 if not hosts:
193 violations.append(f"docs/generated/{fragment.name}: generated but no page includes it")
194 continue
195 for host in hosts:
196 match = re.search(r"@page\s+([A-Za-z0-9_]+)", host.read_text(encoding="utf-8"))
197 if not match:
198 continue
199 rendered = html_dir / f"{match.group(1)}.html"
200 if not rendered.is_file():
201 continue
202 markup = rendered.read_text(encoding="utf-8")
203 ids = set(re.findall(r'id="([A-Za-z0-9_]+)"', markup))
204 for anchor in sorted(set(re.findall(r'href="#([A-Za-z0-9_]+)"', text))):
205 if anchor not in ids:
206 violations.append(
207 f"docs/generated/{fragment.name}: links to #{anchor}, which does not "
208 f"exist in the rendered {rendered.name}"
209 )
210 for page_ref, anchor in sorted(set(re.findall(r'href="([A-Za-z0-9_]+)\.html#([A-Za-z0-9_]+)"', text))):
211 target = html_dir / f"{page_ref}.html"
212 if not target.is_file():
213 violations.append(f"docs/generated/{fragment.name}: links to {page_ref}.html, which is not generated")
214 elif f'id="{anchor}"' not in target.read_text(encoding="utf-8"):
215 violations.append(
216 f"docs/generated/{fragment.name}: links to {page_ref}.html#{anchor}, "
217 f"which does not exist in the rendered page"
218 )
219 return violations
220
221
Here is the caller graph for this function:

◆ strip_non_prose()

str audit_docs_site.strip_non_prose ( str  text)

Blank out fenced code, inline code, and HTML comments.

A link shown inside an example is documentation of a link, not a link. Replacing the spans with blanks rather than deleting them keeps line numbers meaningful for any future line-accurate reporting.

Parameters
[in]textRaw Markdown text.
Returns
Text with non-prose spans blanked.

Definition at line 222 of file audit_docs_site.py.

222def strip_non_prose(text: str) -> str:
223 """!
224 @brief Blank out fenced code, inline code, and HTML comments.
225
226 @details A link shown inside an example is documentation of a link, not a link.
227 Replacing the spans with blanks rather than deleting them keeps line
228 numbers meaningful for any future line-accurate reporting.
229 @param[in] text Raw Markdown text.
230 @return Text with non-prose spans blanked.
231 """
232 def blank(match: "re.Match") -> str:
233 """!
234 @brief Replace a matched span with spaces, preserving newlines.
235 @param[in] match Regular-expression match to blank out.
236 @return Whitespace of the same shape as the matched text.
237 """
238 return re.sub(r"[^\n]", " ", match.group(0))
239
240 text = re.sub(r"<!--.*?-->", blank, text, flags=re.S)
241 text = re.sub(r"^```.*?^```", blank, text, flags=re.S | re.M)
242 text = re.sub(r"~~~.*?~~~", blank, text, flags=re.S)
243 text = re.sub(r"`[^`\n]*`", blank, text)
244 return text
245
246
Here is the caller graph for this function:

◆ markdown_fragment_links()

list audit_docs_site.markdown_fragment_links ( str  text)

Extract Markdown links carrying a fragment, from prose only.

Covers [t](#frag), [t](#frag "title"), and [t](page.md#frag), and accepts every legal fragment character rather than only word characters - a hyphenated anchor is the common Markdown style and was previously invisible to this check.

Parameters
[in]textRaw Markdown text.
Returns
List of (target_page_or_None, fragment) pairs.

Definition at line 247 of file audit_docs_site.py.

247def markdown_fragment_links(text: str) -> list:
248 """!
249 @brief Extract Markdown links carrying a fragment, from prose only.
250
251 @details Covers `[t](#frag)`, `[t](#frag "title")`, and `[t](page.md#frag)`, and
252 accepts every legal fragment character rather than only word characters -
253 a hyphenated anchor is the common Markdown style and was previously
254 invisible to this check.
255 @param[in] text Raw Markdown text.
256 @return List of (target_page_or_None, fragment) pairs.
257 """
258 prose = strip_non_prose(text)
259 pattern = re.compile(
260 r"\]\‍(\s*"
261 r"(?P<path>[^)\s#]*)"
262 r"#(?P<fragment>[^)\s\"']+)"
263 r"(?:\s+[\"'][^)]*[\"'])?"
264 r"\s*\‍)"
265 )
266 results = []
267 for match in pattern.finditer(prose):
268 target = match.group("path") or None
269 results.append((target, match.group("fragment")))
270 return results
271
272
Here is the call graph for this function:
Here is the caller graph for this function:

◆ tracked_markdown()

list audit_docs_site.tracked_markdown ( )

Every Markdown file the current commit carries.

Uses the same git-backed enumeration as the link checker, so fragment validation covers README, example documentation, and every guide.md rather than only the Doxygen page tree.

Returns
Sorted Markdown paths.

Definition at line 273 of file audit_docs_site.py.

273def tracked_markdown() -> list:
274 """!
275 @brief Every Markdown file the current commit carries.
276
277 @details Uses the same git-backed enumeration as the link checker, so fragment
278 validation covers README, example documentation, and every `guide.md`
279 rather than only the Doxygen page tree.
280 @return Sorted Markdown paths.
281 """
282 found = enumerate_repository_files(REPO_ROOT, ".md", frozenset(SKIP_DIRS))
283 if found is None:
284 return sorted(path for path in REPO_ROOT.rglob("*.md") if path.is_file())
285 return found
286
287
Here is the caller graph for this function:

◆ rendered_ids()

set audit_docs_site.rendered_ids ( Path  path)

Anchor ids present in a rendered HTML page.

Parameters
[in]pathRendered page.
Returns
Set of ids.

Definition at line 288 of file audit_docs_site.py.

288def rendered_ids(path: Path) -> set:
289 """!
290 @brief Anchor ids present in a rendered HTML page.
291 @param[in] path Rendered page.
292 @return Set of ids.
293 """
294 if not path.is_file():
295 return set()
296 markup = path.read_text(encoding="utf-8", errors="replace")
297 ids = set(re.findall(r'id="([^"]+)"', markup))
298 ids.update(re.findall(r'name="([^"]+)"', markup))
299 return ids
300
301
Here is the caller graph for this function:

◆ heading_anchor()

str audit_docs_site.heading_anchor ( str  text)

GitHub-style anchor slug for a Markdown heading.

Parameters
[in]textHeading text without its leading hashes.
Returns
Anchor slug.

Definition at line 302 of file audit_docs_site.py.

302def heading_anchor(text: str) -> str:
303 """!
304 @brief GitHub-style anchor slug for a Markdown heading.
305 @param[in] text Heading text without its leading hashes.
306 @return Anchor slug.
307 """
308 slug = text.strip().lower()
309 slug = re.sub(r"[`*_~\[\]()]", "", slug)
310 slug = re.sub(r"[^\w\s-]", "", slug)
311 return re.sub(r"\s+", "-", slug).strip("-")
312
313
Here is the caller graph for this function:

◆ markdown_anchors()

set audit_docs_site.markdown_anchors ( Path  path)

Anchors a plain Markdown file offers.

A file that Doxygen does not render still has targets: explicit HTML anchors, and the heading slugs a Markdown viewer generates.

Parameters
[in]pathMarkdown file.
Returns
Set of available anchor names.

Definition at line 314 of file audit_docs_site.py.

314def markdown_anchors(path: Path) -> set:
315 """!
316 @brief Anchors a plain Markdown file offers.
317
318 @details A file that Doxygen does not render still has targets: explicit HTML
319 anchors, and the heading slugs a Markdown viewer generates.
320 @param[in] path Markdown file.
321 @return Set of available anchor names.
322 """
323 text = path.read_text(encoding="utf-8", errors="replace")
324 anchors = set(re.findall(r'<a\s+(?:id|name)="([^"]+)"', text))
325 anchors.update(re.findall(r"^@anchor\s+(\S+)\s*$", text, re.M))
326 for heading in re.findall(r"^#{1,6}\s+(.+?)\s*$", strip_non_prose(text), re.M):
327 anchors.add(heading_anchor(heading))
328 return anchors
329
330
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_page_cross_references()

list[str] audit_docs_site.check_page_cross_references ( Path  html_dir)

Verify hand-written Markdown fragment links resolve, repository-wide.

Doxygen validates its own reference graph, but a Markdown fragment link is checked by nothing. Cross-page targets are resolved relative to the linking file, so the many guide.md files in this repository are not conflated by basename.

Parameters
[in]html_dirGenerated HTML directory.
Returns
Violation lines.

Definition at line 331 of file audit_docs_site.py.

331def check_page_cross_references(html_dir: Path) -> list[str]:
332 """!
333 @brief Verify hand-written Markdown fragment links resolve, repository-wide.
334
335 @details Doxygen validates its own reference graph, but a Markdown fragment link is
336 checked by nothing. Cross-page targets are resolved **relative to the
337 linking file**, so the many `guide.md` files in this repository are not
338 conflated by basename.
339 @param[in] html_dir Generated HTML directory.
340 @return Violation lines.
341 """
342 page_ids: dict = {}
343 for markdown in tracked_markdown():
344 match = re.search(r"^@page\s+([A-Za-z0-9_]+)", markdown.read_text(encoding="utf-8"), re.M)
345 if match:
346 page_ids[markdown.resolve()] = match.group(1)
347
348 def anchors_for(path: Path) -> set:
349 """!
350 @brief Anchors available in one Markdown file, rendered or not.
351 @param[in] path Markdown file.
352 @return Set of anchor names.
353 """
354 page_id = page_ids.get(path.resolve())
355 if page_id:
356 return rendered_ids(html_dir / f"{page_id}.html")
357 return markdown_anchors(path)
358
359 violations: list[str] = []
360 for markdown in tracked_markdown():
361 text = markdown.read_text(encoding="utf-8", errors="replace")
362 for target, fragment in markdown_fragment_links(text):
363 if target is None:
364 resolved = markdown
365 else:
366 resolved = (markdown.parent / target).resolve()
367 if not resolved.is_file() or resolved.suffix != ".md":
368 continue # the link checker owns missing-file reporting
369 available = anchors_for(resolved)
370 if not available:
371 continue # nothing to check against; not evidence of a broken link
372 if fragment not in available:
373 where = markdown.name if target is None else target
374 violations.append(
375 f"{markdown.relative_to(REPO_ROOT)}: fragment link '#{fragment}' does not "
376 f"resolve in {where}"
377 )
378 return violations
379
380
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int audit_docs_site.main ( )

Fail when a project-owned URL is dead or a navigation tab has no generated page.

Returns
Process status code.

Definition at line 381 of file audit_docs_site.py.

381def main() -> int:
382 """!
383 @brief Fail when a project-owned URL is dead or a navigation tab has no generated page.
384 @return Process status code.
385 """
386 contract = load_contract()
387 try:
388 html_dir = require_built_html(contract)
389 except RuntimeError as error:
390 print(f"Docs-site audit could not run: {error}", file=sys.stderr)
391 return 1
392 violations = (
393 check_forbidden_urls(contract)
394 + check_canonical_urls(contract, html_dir)
395 + check_layout(contract, html_dir)
396 + check_orphan_pages(contract, html_dir)
397 + check_generated_fragment_links(html_dir)
398 + check_page_cross_references(html_dir)
399 )
400 if violations:
401 print("Published-site integrity violations:", file=sys.stderr)
402 for violation in violations:
403 print(f" {violation}", file=sys.stderr)
404 return 1
405 print(
406 f"Docs-site audit passed: canonical URLs and navigation tabs all resolve to pages "
407 f"generated under {contract['html_dir']}; no published page is orphaned; generated fragment links resolve."
408 )
409 return 0
410
411
int main(int argc, char **argv)
Entry point for the postprocessor executable.
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ REPO_ROOT

audit_docs_site.REPO_ROOT = Path(__file__).resolve().parents[2]

Definition at line 17 of file audit_docs_site.py.

◆ CONTRACT_PATH

str audit_docs_site.CONTRACT_PATH = REPO_ROOT / "tests" / "tooling" / "docs_site_contract.json"

Definition at line 18 of file audit_docs_site.py.

◆ SKIP_DIRS

dict audit_docs_site.SKIP_DIRS = {".git", "docs_build", "obj", "bin", "stubs", "runs", "studies", "__pycache__", ".pytest_cache"}

Definition at line 19 of file audit_docs_site.py.

◆ SCAN_SUFFIXES

dict audit_docs_site.SCAN_SUFFIXES = {".md", ".js", ".xml", ".html", ".yml", ".yaml"}

Definition at line 20 of file audit_docs_site.py.