Skip to content

Commit 5572d9d

Browse files
committed
Generate documentation for proto files
A new function is added to `frequenz.repo.config.mkdocs` to generate documentation pages for protobuf files with `pseudomuto/protoc-gen-doc`. The cookiecutter template uses the new function to generate the pages for API-type projects automatically, adding a new top-level tab to the generated site called "Protobuf API Reference". Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 1169c3b commit 5572d9d

File tree

5 files changed

+88
-5
lines changed

5 files changed

+88
-5
lines changed

cookiecutter/hooks/post_gen_project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def main() -> None:
6868
print("nox")
6969
print("# To generate and serve the documentation:")
7070
print("pip install .[dev-mkdocs]")
71+
if cookiecutter.type == "api":
72+
print("# Requires docker")
7173
print("mkdocs serve")
7274
print()
7375
if warnings := do_sanity_checks():

cookiecutter/{{cookiecutter.github_repo_name}}/docs/SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
* [Home](index.md)
22
{%- if cookiecutter.type == "api" %}
3-
* [Python API Reference](reference/)
3+
* [Protobuf API Reference](protobuf-reference/)
4+
* [Python API Reference](python-reference/)
45
{%- else %}
56
* [API Reference](reference/)
67
{%- endif %}

cookiecutter/{{cookiecutter.github_repo_name}}/docs/mkdocstrings_autoapi.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55

66
from frequenz.repo.config import mkdocs
77

8-
mkdocs.generate_api_pages("{{cookiecutter | src_path}}")
8+
mkdocs.generate_python_api_pages("{{cookiecutter | src_path}}", "{{'python-' if cookiecutter.type == 'api'}}reference")
9+
{%- if cookiecutter.type == 'api' %}
10+
mkdocs.generate_protobuf_api_pages()
11+
{%- endif %}

src/frequenz/repo/config/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@
180180
```
181181
182182
By default this script will look for files in the `src/` directory and generate the
183-
documentation files in the `reference/` directory inside `mkdocs` output directory
183+
documentation files in the `python-reference/` directory inside `mkdocs` output directory
184184
(`site` by defaul).
185185
186186
If you need to customize the above paths, you can create a new script to use with the
@@ -189,7 +189,7 @@
189189
```python
190190
from frequenz.repo.config import mkdocs
191191
192-
mkdocs.generate_api_pages("my_sources", "API")
192+
mkdocs.generate_python_api_pages("my_sources", "API")
193193
```
194194
195195
Where `my_sources` is the directory containing the source files and `API` is the
@@ -205,6 +205,16 @@
205205
- path/to/my/custom/script.py
206206
```
207207
208+
If your project also provides *protobuf* files, you can also generate the API
209+
documentation for them adding one more line to the previous script:
210+
211+
```python
212+
from frequenz.repo.config import mkdocs
213+
214+
mkdocs.generate_python_api_pages("my_sources", "API-py")
215+
mkdocs.generate_protobuf_api_pages("my_protos", "API-proto")
216+
```
217+
208218
# APIs
209219
210220
## `setuptools` gRPC support

src/frequenz/repo/config/mkdocs.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
https://mkdocstrings.github.io/recipes/#automatic-code-reference-pages
1313
"""
1414

15+
import subprocess
16+
import tempfile
1517
from pathlib import Path
1618
from typing import Tuple
1719

@@ -34,7 +36,9 @@ def with_underscore_not_init(part: str) -> bool:
3436
return any(p for p in path_parts if with_underscore_not_init(p))
3537

3638

37-
def generate_api_pages(src_path: str = "src", dst_path: str = "reference") -> None:
39+
def generate_python_api_pages(
40+
src_path: str = "src", dst_path: str = "python-reference"
41+
) -> None:
3842
"""Generate API documentation pages for the code.
3943
4044
Internal modules (those starting with an underscore except from `__init__`) are
@@ -74,3 +78,66 @@ def generate_api_pages(src_path: str = "src", dst_path: str = "reference") -> No
7478

7579
with mkdocs_gen_files.open(Path(dst_path) / "SUMMARY.md", "w") as nav_file:
7680
nav_file.writelines(nav.build_literate_nav())
81+
82+
83+
def generate_protobuf_api_pages(
84+
src_path: str = "proto", dst_path: str = "protobuf-reference"
85+
) -> None:
86+
"""Generate API documentation pages for the code.
87+
88+
Internal modules (those starting with an underscore except from `__init__`) are
89+
not included.
90+
91+
A summary page is generated as `SUMMARY.md` which is compatible with the
92+
`mkdocs-literary-nav` plugin.
93+
94+
Args:
95+
src_path: Path where the code is located.
96+
dst_path: Path where the documentation should be generated. This is relative
97+
to the output directory of mkdocs.
98+
"""
99+
# type ignore because mkdocs_gen_files uses a very weird module-level
100+
# __getattr__() which messes up the type system
101+
nav = mkdocs_gen_files.Nav() # type: ignore
102+
103+
cwd = Path.cwd()
104+
105+
with tempfile.TemporaryDirectory(prefix="mkdocs-protobuf-reference-") as tmp_path:
106+
for path in sorted(Path(src_path).rglob("*.proto")):
107+
doc_path = path.relative_to(src_path).with_suffix(".md")
108+
full_doc_path = Path(dst_path, doc_path)
109+
parts = tuple(path.relative_to(src_path).parts)
110+
nav[parts] = doc_path.as_posix()
111+
doc_tmp_path = tmp_path / doc_path
112+
doc_tmp_path.parent.mkdir(parents=True, exist_ok=True)
113+
try:
114+
# TODO: Get arguments from setuptools.grpc
115+
subprocess.run(
116+
[
117+
"docker",
118+
"run",
119+
"--rm",
120+
f"-v{cwd}:{cwd}",
121+
f"-v{tmp_path}:{tmp_path}",
122+
"pseudomuto/protoc-gen-doc",
123+
f"-I{cwd / src_path}",
124+
f"-I{cwd}/submodules/api-common-protos",
125+
f"-I{cwd}/submodules/frequenz-api-common/proto",
126+
f"--doc_opt=markdown,{doc_path.name}",
127+
f"--doc_out={tmp_path / doc_path.parent}",
128+
str(cwd / path),
129+
],
130+
check=True,
131+
)
132+
except subprocess.CalledProcessError as error:
133+
print(f"Error generating protobuf reference page: {error}")
134+
135+
with doc_tmp_path.open() as input_file, mkdocs_gen_files.open(
136+
full_doc_path, "w"
137+
) as output_file:
138+
output_file.write(input_file.read())
139+
140+
mkdocs_gen_files.set_edit_path(full_doc_path, Path("..") / path)
141+
142+
with mkdocs_gen_files.open(Path(dst_path) / "SUMMARY.md", "w") as nav_file:
143+
nav_file.writelines(nav.build_literate_nav())

0 commit comments

Comments
 (0)