feat(okf): add okf_common frontmatter/link parser with tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
78
custom-skills/97-ourdigital-okf/code/scripts/okf_common.py
Normal file
78
custom-skills/97-ourdigital-okf/code/scripts/okf_common.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Shared OKF v0.1 parsing utilities (Python standard library only)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
RESERVED = {"index.md", "log.md"}
|
||||
_LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
|
||||
|
||||
|
||||
class FrontmatterError(ValueError):
|
||||
"""Raised when a frontmatter block is present but cannot be parsed."""
|
||||
|
||||
|
||||
def split_frontmatter(text):
|
||||
"""Return (raw_frontmatter, body). raw is None if there is no leading '---' block."""
|
||||
if not text.startswith("---"):
|
||||
return None, text
|
||||
lines = text.splitlines()
|
||||
if lines[0].strip() != "---":
|
||||
return None, text
|
||||
for i in range(1, len(lines)):
|
||||
if lines[i].strip() == "---":
|
||||
return "\n".join(lines[1:i]), "\n".join(lines[i + 1:])
|
||||
raise FrontmatterError("opening '---' without a closing '---'")
|
||||
|
||||
|
||||
def _scalar(value):
|
||||
value = value.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
|
||||
return value[1:-1]
|
||||
return value
|
||||
|
||||
|
||||
def parse_yaml_subset(raw):
|
||||
"""Parse the small YAML subset OKF uses: `key: value` and `key: [a, b]`."""
|
||||
meta = {}
|
||||
for line in raw.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
if ":" not in line:
|
||||
raise FrontmatterError("unparseable frontmatter line: %r" % line)
|
||||
key, _, value = line.partition(":")
|
||||
key, value = key.strip(), value.strip()
|
||||
if value.startswith("[") and value.endswith("]"):
|
||||
inner = value[1:-1].strip()
|
||||
meta[key] = [s for s in (_scalar(x) for x in inner.split(",")) if s] if inner else []
|
||||
else:
|
||||
meta[key] = _scalar(value)
|
||||
return meta
|
||||
|
||||
|
||||
def parse_frontmatter(text):
|
||||
"""Return (meta, body). meta is None when no frontmatter block is present."""
|
||||
raw, body = split_frontmatter(text)
|
||||
if raw is None:
|
||||
return None, text
|
||||
return parse_yaml_subset(raw), body
|
||||
|
||||
|
||||
def iter_concepts(bundle_dir):
|
||||
"""Yield Path for every non-reserved .md file under bundle_dir, sorted."""
|
||||
root = Path(bundle_dir)
|
||||
for path in sorted(root.rglob("*.md")):
|
||||
if path.name not in RESERVED:
|
||||
yield path
|
||||
|
||||
|
||||
def concept_id(bundle_dir, path):
|
||||
"""Concept ID = bundle-relative path with the .md suffix removed."""
|
||||
rel = Path(path).relative_to(Path(bundle_dir)).as_posix()
|
||||
return rel[:-3] if rel.endswith(".md") else rel
|
||||
|
||||
|
||||
def extract_links(body):
|
||||
"""Return the list of link targets from markdown links in body."""
|
||||
return _LINK_RE.findall(body)
|
||||
@@ -0,0 +1,47 @@
|
||||
import sys, unittest
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from okf_common import (parse_frontmatter, parse_yaml_subset,
|
||||
extract_links, concept_id, FrontmatterError)
|
||||
|
||||
|
||||
class TestFrontmatter(unittest.TestCase):
|
||||
def test_parse_basic(self):
|
||||
text = "---\ntype: BigQuery Table\ntitle: Orders\ntags: [sales, revenue]\n---\n\n# Body\n"
|
||||
meta, body = parse_frontmatter(text)
|
||||
self.assertEqual(meta["type"], "BigQuery Table")
|
||||
self.assertEqual(meta["title"], "Orders")
|
||||
self.assertEqual(meta["tags"], ["sales", "revenue"])
|
||||
self.assertIn("# Body", body)
|
||||
|
||||
def test_url_value_with_colons(self):
|
||||
meta, _ = parse_frontmatter("---\ntype: X\nresource: https://e.com/a?b=c\n---\nbody")
|
||||
self.assertEqual(meta["resource"], "https://e.com/a?b=c")
|
||||
|
||||
def test_quoted_value(self):
|
||||
self.assertEqual(parse_yaml_subset('okf_version: "0.1"')["okf_version"], "0.1")
|
||||
|
||||
def test_empty_list(self):
|
||||
self.assertEqual(parse_yaml_subset("tags: []")["tags"], [])
|
||||
|
||||
def test_no_frontmatter(self):
|
||||
meta, body = parse_frontmatter("# Just markdown\n")
|
||||
self.assertIsNone(meta)
|
||||
self.assertEqual(body, "# Just markdown\n")
|
||||
|
||||
def test_unclosed_raises(self):
|
||||
with self.assertRaises(FrontmatterError):
|
||||
parse_frontmatter("---\ntype: X\n\nbody without close")
|
||||
|
||||
|
||||
class TestLinksAndIds(unittest.TestCase):
|
||||
def test_extract_links(self):
|
||||
body = "See [a](/tables/a.md) and [b](./b.md) and [ext](https://x.com)."
|
||||
self.assertEqual(extract_links(body), ["/tables/a.md", "./b.md", "https://x.com"])
|
||||
|
||||
def test_concept_id(self):
|
||||
self.assertEqual(concept_id("/bundle", "/bundle/tables/users.md"), "tables/users")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user