Skip to content

Commit ef5ecfa

Browse files
committed
Implement doc tree builder with toml, yaml and custom format
This is to help create fixtures, so I can write meaningfull tests without too much pain and then refactor this code without having to manually look at the pages.
1 parent 5ad6933 commit ef5ecfa

File tree

5 files changed

+179
-0
lines changed

5 files changed

+179
-0
lines changed

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ pulp_docs = ["data/**"]
3030
[tool.setuptools.packages.find]
3131
where = ["src"]
3232

33+
[tool.pytest.ini_options]
34+
pythonpath = "src"
35+
addopts = [
36+
"--import-mode=importlib",
37+
]
38+

src/pulp_docs/test_tools/__init__.py

Whitespace-only changes.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from pathlib import Path
2+
3+
import tomllib
4+
import yaml
5+
import re
6+
7+
8+
def parse_doctree_file(doctree_file: Path, target: Path, project_name: str = "foobar"):
9+
"""Create a whole documentation tree base on @doctree_file on @target.
10+
11+
The goal is to facilitate creating fixtures for testing complex build cases, such
12+
as pulp structure.
13+
14+
The declarative doctree file specifies a list of (path,content) tuples, with an semantic
15+
header separation..
16+
17+
The overall structure is:
18+
19+
```pseudo-format
20+
{
21+
projet-name-1: [{path: content}, ..., {path: content}],
22+
...
23+
projet-name-N: [{path: content}, ..., {path: content}],
24+
}
25+
```
26+
27+
See `test_doctree_writer` for samples.
28+
29+
Params:
30+
doctree_file: The file with a supported extenstion format. E.g: `.toml` `.yml` and `.doctree`
31+
target: The directory where the project should be writter to.
32+
"""
33+
34+
def custom_parser(file: Path):
35+
_data = file.read_text()
36+
section_match = r"\n*\[\[\s*[\w-]+\s*\]\]\n"
37+
item_match = r"----+\n"
38+
section_split = [
39+
section for section in re.split(section_match, _data) if section
40+
]
41+
item_split = [
42+
item
43+
for section in section_split
44+
for item in re.split(item_match, section)
45+
if section and item
46+
]
47+
item_partition = [t.partition("\n\n") for t in item_split if t]
48+
49+
def sanitize_path(s):
50+
return s.partition("\n")[0].strip(" ")
51+
52+
items = [{"path": sanitize_path(s[0]), "data": s[2]} for s in item_partition]
53+
return {"foobar": items}
54+
55+
# Open and parse doctree file
56+
if doctree_file.suffix in (".yml", ".yaml"):
57+
data = yaml.load(doctree_file.read_text(), Loader=yaml.SafeLoader)
58+
elif doctree_file.suffix in (".toml",):
59+
data = tomllib.loads(doctree_file.read_text())
60+
elif doctree_file.suffix in (".doctree",):
61+
data = custom_parser(doctree_file)
62+
# breakpoint()
63+
else:
64+
raise NotImplementedError(f"File type not supported: {doctree_file.name}")
65+
66+
# Create all directories
67+
for prj_name, contents in data.items():
68+
for item in contents:
69+
basedir, _, filename = item["path"].strip("/").rpartition("/")
70+
basedir = target / basedir
71+
basedir.mkdir(parents=True, exist_ok=True)
72+
Path(target / basedir / filename).write_text(item["data"])

src/pulp_docs/utils/__init__.py

Whitespace-only changes.

tests/test_doctree_writer.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
import textwrap
5+
6+
from pulp_docs.test_tools.doctree_writer import parse_doctree_file
7+
8+
file_sample = """\
9+
# check-title
10+
11+
check-content
12+
13+
---
14+
not/a/path (separator must have 4+ ---)
15+
16+
dont split."""
17+
18+
yaml_sample = f"""\
19+
project1:
20+
- path: docs/index.md
21+
data: |
22+
{textwrap.indent(file_sample, " " * 6)}
23+
- path: docs/guides/foo.md
24+
data: |
25+
{textwrap.indent(file_sample, " " * 6)}
26+
project2:
27+
- path: docs/guides/bar.md
28+
data: |
29+
{textwrap.indent(file_sample, " " * 6)}
30+
"""
31+
32+
toml_sample = f"""\
33+
[[project1]]
34+
path = 'docs/index.md'
35+
data = '''
36+
{file_sample}
37+
'''
38+
39+
[[project1]]
40+
path = 'docs/guides/foo.md'
41+
data = '''
42+
{file_sample}
43+
'''
44+
45+
[[project2]]
46+
path = 'docs/guides/bar.md'
47+
data = '''
48+
{file_sample}
49+
'''
50+
"""
51+
52+
# .doctree extenstion
53+
custom_sample = f"""\
54+
[[ project1 ]]
55+
------------------------
56+
docs/index.md
57+
58+
{file_sample}
59+
-----------------
60+
docs/guides/foo.md
61+
---------------#ignore
62+
63+
{file_sample}
64+
65+
[[ project2 ]]
66+
-----
67+
docs/guides/bar.md
68+
69+
{file_sample}
70+
"""
71+
72+
73+
@pytest.mark.parametrize(
74+
"file_ext,content",
75+
[
76+
pytest.param("toml", toml_sample, id="toml"),
77+
pytest.param("yaml", yaml_sample, id="yaml"),
78+
pytest.param("yml", yaml_sample, id="yml"),
79+
pytest.param("doctree", custom_sample, id="doctree"),
80+
],
81+
)
82+
def test_doctree_write(file_ext, content, tmp_path):
83+
sample_file = tmp_path / f"declarative_fixture.{file_ext}"
84+
sample_file.write_text(content)
85+
parse_doctree_file(sample_file, tmp_path)
86+
87+
pages = ("docs/index.md", "docs/guides/foo.md", "docs/guides/bar.md")
88+
for page_path in pages:
89+
assert Path(tmp_path / page_path).exists()
90+
91+
contents = []
92+
for page_path in pages:
93+
content = Path(tmp_path / page_path).read_text()
94+
contents.append(content)
95+
assert "# check-title" in content
96+
assert "check-content" in content
97+
assert "[[ project1 ]]" not in content
98+
assert "[[ project2 ]]" not in content
99+
100+
print()
101+
print(f"To check manually cd to:\n{tmp_path}")

0 commit comments

Comments
 (0)