diff options
| author | Danilo M. <danix@danix.xyz> | 2026-05-01 12:51:54 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-05-01 12:51:54 +0200 |
| commit | b4d3b526e232804e06b84d93628244cecd035df5 (patch) | |
| tree | 9725d93a5818630afd4bbfeed9845518bfb012c1 | |
| parent | ba5438abaa430358eeb69b1376737ce61a3d68dc (diff) | |
| download | publisher-b4d3b526e232804e06b84d93628244cecd035df5.tar.gz publisher-b4d3b526e232804e06b84d93628244cecd035df5.zip | |
feat: TOML frontmatter parse/write with tomlkit
| -rw-r--r-- | core/frontmatter.py | 28 | ||||
| -rw-r--r-- | tests/test_frontmatter.py | 49 |
2 files changed, 77 insertions, 0 deletions
diff --git a/core/frontmatter.py b/core/frontmatter.py new file mode 100644 index 0000000..4e9eff3 --- /dev/null +++ b/core/frontmatter.py @@ -0,0 +1,28 @@ +from __future__ import annotations +from pathlib import Path +import tomlkit + +def parse_frontmatter(path: Path) -> tuple[dict, str]: + """Parse TOML frontmatter delimited by +++. Returns (frontmatter_dict, body_str).""" + text = path.read_text(encoding="utf-8") + if not text.startswith("+++"): + raise ValueError(f"No TOML frontmatter found in {path}") + parts = text.split("+++", 2) + if len(parts) < 3: + raise ValueError(f"Malformed frontmatter in {path}") + _, toml_block, body = parts + fm = dict(tomlkit.loads(toml_block)) + return fm, body.lstrip("\n") + +def write_frontmatter(path: Path, frontmatter: dict, body: str) -> None: + """Write frontmatter + body back to file, preserving TOML format.""" + text = path.read_text(encoding="utf-8") + parts = text.split("+++", 2) + if len(parts) < 3: + raise ValueError(f"Malformed frontmatter in {path}") + _, original_toml, _ = parts + doc = tomlkit.loads(original_toml) + for k, v in frontmatter.items(): + doc[k] = v + new_text = f"+++\n{tomlkit.dumps(doc)}+++\n\n{body}" + path.write_text(new_text, encoding="utf-8") diff --git a/tests/test_frontmatter.py b/tests/test_frontmatter.py new file mode 100644 index 0000000..2257a8a --- /dev/null +++ b/tests/test_frontmatter.py @@ -0,0 +1,49 @@ +import pytest +from pathlib import Path +import tempfile +from core.frontmatter import parse_frontmatter, write_frontmatter + +SAMPLE_MD = """\ ++++ +title = "Test Article" +type = "Tech" +draft = false +tags = ["linux", "python"] ++++ + +# Body + +Some content here. +""" + +def test_parse_returns_frontmatter_dict(tmp_path): + f = tmp_path / "index.md" + f.write_text(SAMPLE_MD) + fm, body = parse_frontmatter(f) + assert fm["title"] == "Test Article" + assert fm["type"] == "Tech" + assert fm["draft"] is False + assert "linux" in fm["tags"] + +def test_parse_returns_body(tmp_path): + f = tmp_path / "index.md" + f.write_text(SAMPLE_MD) + fm, body = parse_frontmatter(f) + assert "# Body" in body + assert "Some content here." in body + +def test_write_preserves_format(tmp_path): + f = tmp_path / "index.md" + f.write_text(SAMPLE_MD) + fm, body = parse_frontmatter(f) + fm["title"] = "Updated Title" + write_frontmatter(f, fm, body) + fm2, _ = parse_frontmatter(f) + assert fm2["title"] == "Updated Title" + assert fm2["type"] == "Tech" + +def test_parse_raises_on_missing_delimiters(tmp_path): + f = tmp_path / "index.md" + f.write_text("# No frontmatter\n\nJust body.") + with pytest.raises(ValueError, match="frontmatter"): + parse_frontmatter(f) |
