import json
import re
from pathlib import Path
import yaml
SCRIPTS_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = SCRIPTS_DIR.parent
_JAVASCRIPT_COPYRIGHT = (
"// ----------------------------------------------------------------------------------------------------------\n"
"// Copyright (c) 2026 Huawei Technologies Co., Ltd.\n"
"// This program is free software, you can redistribute it and/or modify it under the terms and conditions of\n"
"// CANN Open Software License Agreement Version 2.0 (the \"License\").\n"
"// Please refer to the License for details. You may not use this file except in compliance with the License.\n"
"// THIS SOFTWARE IS PROVIDED ON AN \"AS IS\" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,\n"
"// INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.\n"
"// See LICENSE in the root of the software repository for the full text of the License.\n"
"// ----------------------------------------------------------------------------------------------------------\n\n"
)
def parse_readme_toc():
readme = PROJECT_ROOT / "README.md"
with open(readme, "r", encoding="utf-8") as f:
lines = f.readlines()
root_entries = []
stack = [(0, None)]
link_re = re.compile(r"^(\s*)-\s*\[(.+?)\]\((.+)\)")
for line in lines:
m = link_re.match(line)
if not m:
continue
indent_str, text, link = m.groups()
if any(link.lower().endswith(ext) for ext in (".png", ".jpg", ".gif", ".svg", ".pdf")):
continue
ref = re.sub(r"\.md$", "", link)
indent = len(indent_str)
level = indent // 4
entry = {"file": ref, "_text": text}
while len(stack) > 1 and stack[-1][0] >= level:
stack.pop()
parent_level, parent = stack[-1]
if level == 0 or parent is None:
root_entries.append(entry)
else:
parent.setdefault("entries", []).append(entry)
stack.append((level, entry))
return root_entries
def add_numbering(entries, prefix):
for i, entry in enumerate(entries, 1):
num = ".".join(str(p) for p in prefix) + "." + str(i) if prefix else str(i)
entry["title"] = num + " " + entry["_text"]
if entry.get("entries"):
add_numbering(entry["entries"], prefix + (i,))
def remove_temp_keys(entries):
for entry in entries:
entry.pop("_text", None)
if "entries" in entry:
remove_temp_keys(entry["entries"])
def main():
toc_entries = parse_readme_toc()
for entry in toc_entries:
entry["title"] = entry["_text"]
if entry.get("entries"):
add_numbering(entry["entries"], ())
remove_temp_keys(toc_entries)
toc = {"root": "index", "entries": toc_entries}
toc_path = SCRIPTS_DIR / "_toc.yml"
with open(toc_path, "w", encoding="utf-8") as f:
yaml.dump(toc, f, allow_unicode=True, default_flow_style=False, sort_keys=False, width=200)
_unescape_yaml(toc_path)
json_data = [_compact(e) for e in toc_entries]
json_str = json.dumps(json_data, ensure_ascii=False, separators=(',', ':'))
json_path = PROJECT_ROOT / "_static" / "toc_data.json"
json_path.write_text(json_str, encoding="utf-8")
js_path = PROJECT_ROOT / "_static" / "js"
js_path.mkdir(parents=True, exist_ok=True)
js_path = js_path / "toc-data.js"
js_path.write_text(_JAVASCRIPT_COPYRIGHT + "var TOC_DATA_EMBEDDED = " + json_str + ";", encoding="utf-8")
def _compact(entry):
obj = {"t": entry.get("title", ""), "f": entry.get("file", "")}
if "entries" in entry:
obj["e"] = [_compact(e) for e in entry["entries"]]
return obj
def _unescape_yaml(path):
"""Strip PyYAML's unnecessary backslash-escapes from a YAML file.
PyYAML 6.0 escapes _, (, ), [, ] etc. in large nested documents,
producing title values with spurious backslash characters before
underscores, parentheses, and brackets. These are not real YAML escape
sequences and cause literal backslashes to appear in the sidebar
when sphinx_external_toc parses the file.
"""
import re as _re
content = path.read_text(encoding="utf-8")
cleaned = _re.sub(r"\\(" + "[]_[(){}])", r"\1", content)
path.write_text(cleaned, encoding="utf-8")
if __name__ == "__main__":
main()