(text: str)
| 490 | |
| 491 | |
| 492 | def read_md_frontmatter(text: str) -> tuple[dict | None, str, str | None]: |
| 493 | lines = text.splitlines(keepends=True) |
| 494 | if not lines or not FRONTMATTER_RE.match(lines[0]): |
| 495 | return None, text, None |
| 496 | |
| 497 | end_idx = None |
| 498 | for i in range(1, min(len(lines), 5000)): |
| 499 | if FRONTMATTER_RE.match(lines[i]): |
| 500 | end_idx = i |
| 501 | break |
| 502 | |
| 503 | if end_idx is None: |
| 504 | return None, text, "unterminated_markdown_frontmatter" |
| 505 | |
| 506 | fm_text = "".join(lines[1:end_idx]) |
| 507 | body = "".join(lines[end_idx + 1 :]) |
| 508 | |
| 509 | if yaml is None: |
| 510 | raise RuntimeError("PyYAML is required to parse Markdown frontmatter") |
| 511 | |
| 512 | fm = yaml.safe_load(fm_text) if fm_text.strip() else {} |
| 513 | if not isinstance(fm, dict): |
| 514 | return None, text, "markdown_frontmatter_not_mapping" |
| 515 | |
| 516 | return fm, body, None |
| 517 | |
| 518 | |
| 519 | def dump_md_frontmatter(frontmatter: dict, body: str) -> str: |
no test coverage detected