Skip to content

Commit deccf31

Browse files
authored
Merge pull request #143 from braboj/bk3akj-codex/modify-script-to-generate-aggregated-markdown-files
Add aggregated docs generation
2 parents a3f7512 + 197255d commit deccf31

File tree

1 file changed

+50
-47
lines changed

1 file changed

+50
-47
lines changed

.scripts/examples_to_markdown_files.py

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,70 @@
11
#!/usr/bin/env python3
2-
"""Generate a Markdown file for each Python example.
2+
"""Generate aggregated Markdown files for each examples subfolder.
33
44
Usage:
55
python .scripts/examples_to_markdown_files.py \
66
--examples-dir examples \
7-
--template templates/example_file.mustache \
87
--output-dir docs
98
10-
The directory structure under ``examples`` is mirrored under the output
11-
directory. Each ``.py`` file becomes a ``.md`` file with the same name and
12-
relative path. Files named ``__init__.py`` are ignored and skipped.
9+
For every immediate subfolder in ``examples`` a single ``.md`` file is created
10+
in the output directory. The file name matches the subfolder name and contains
11+
all example files from that folder. Folder and file names are converted to
12+
title format: CamelCase words are split with spaces and acronyms remain in
13+
uppercase.
1314
"""
1415

1516
import argparse
1617
import re
1718
from pathlib import Path
1819

19-
try:
20-
import pystache
21-
except ModuleNotFoundError as exc: # pragma: no cover - import guard
22-
raise SystemExit(
23-
"pystache is required to run this script. Install it via `pip install pystache`."
24-
) from exc
2520

2621

27-
def parse_example(path: Path) -> dict:
28-
"""Return the file name and entire contents of the example."""
29-
with path.open("r", encoding="utf-8") as f:
30-
code = f.read()
31-
return {"name": path.stem, "code": code.rstrip()}
22+
def to_title(name: str) -> str:
23+
"""Return *name* in title format with spaces and preserved acronyms."""
24+
# Remove numeric prefixes like ``01_``
25+
name = re.sub(r"^\d+[_-]?", "", name)
26+
parts = re.split(r"[_\-\s]+", name)
27+
words = []
28+
for part in parts:
29+
if not part:
30+
continue
31+
if re.fullmatch(r"[A-Z0-9]+", part):
32+
words.append(part)
33+
elif re.fullmatch(r"[a-z]+", part) and len(part) <= 4:
34+
words.append(part.upper())
35+
else:
36+
words.append(part.capitalize())
37+
return " ".join(words)
3238

3339

34-
def render_markdown(template: str, context: dict) -> str:
35-
"""Render the Markdown using a Mustache template."""
36-
renderer = pystache.Renderer()
37-
return renderer.render(template, context)
3840

3941

40-
def to_camel_case(name: str) -> str:
41-
"""Return *name* converted to CamelCase without leading digits."""
42-
# Strip leading numeric prefixes like ``01_``
43-
name = re.sub(r"^\d+_?", "", name)
44-
parts = re.split(r"[_\-\s]+", name)
45-
return "".join(word.capitalize() for word in parts if word)
42+
def generate_aggregate(folder: Path, output_dir: Path) -> None:
43+
"""Create a single Markdown file aggregating all examples in *folder*."""
44+
examples = []
45+
for py_file in sorted(folder.glob("*.py")):
46+
if py_file.name == "__init__.py":
47+
continue
48+
with py_file.open("r", encoding="utf-8") as f:
49+
code = f.read().rstrip()
50+
examples.append((to_title(py_file.stem), code))
4651

52+
if not examples:
53+
return
4754

48-
def process_file(py_path: Path, template: str, base_dir: Path, output_dir: Path) -> None:
49-
"""Write a Markdown version of *py_path* under *output_dir*."""
50-
context = parse_example(py_path)
51-
markdown = render_markdown(template, context)
55+
title = to_title(folder.name)
56+
lines = [f"# {title}", ""]
57+
for name, code in examples:
58+
lines.append(f"## {name}")
59+
lines.append("")
60+
lines.append("```python")
61+
lines.append(code)
62+
lines.append("```")
63+
lines.append("")
5264

53-
relative = py_path.relative_to(base_dir).with_suffix(".md")
54-
md_path = output_dir / relative
55-
md_path.parent.mkdir(parents=True, exist_ok=True)
56-
with md_path.open("w", encoding="utf-8") as f:
57-
f.write(markdown)
65+
agg_path = output_dir / f"{folder.name}.md"
66+
with agg_path.open("w", encoding="utf-8") as f:
67+
f.write("\n".join(lines).rstrip() + "\n")
5868

5969

6070
def generate_aggregate(folder: Path, base_dir: Path, output_dir: Path) -> None:
@@ -87,18 +97,13 @@ def generate_aggregate(folder: Path, base_dir: Path, output_dir: Path) -> None:
8797

8898
def main() -> None:
8999
parser = argparse.ArgumentParser(
90-
description="Generate Markdown files mirroring the examples directory"
100+
description="Generate aggregated Markdown files from the examples"
91101
)
92102
parser.add_argument(
93103
"--examples-dir",
94104
default="examples",
95105
help="Directory containing example .py files",
96106
)
97-
parser.add_argument(
98-
"--template",
99-
default="templates/example_file.mustache",
100-
help="Mustache template file",
101-
)
102107
parser.add_argument(
103108
"--output-dir",
104109
default="docs",
@@ -113,13 +118,11 @@ def main() -> None:
113118
# previously generated documentation.
114119
output_dir.mkdir(parents=True, exist_ok=True)
115120

116-
with Path(args.template).open("r", encoding="utf-8") as f:
117-
template = f.read()
118121

119-
for py_file in sorted(examples_dir.rglob("*.py")):
120-
if py_file.name == "__init__.py":
121-
continue
122-
process_file(py_file, template, examples_dir, output_dir)
122+
123+
for folder in sorted(examples_dir.iterdir()):
124+
if folder.is_dir():
125+
generate_aggregate(folder, output_dir)
123126

124127
for folder in sorted(examples_dir.iterdir()):
125128
if folder.is_dir():

0 commit comments

Comments
 (0)