refactor(okf): one frontmatter scanner behind both readers, contracts unchanged
Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
809fa04406
commit
ab2c509337
2 changed files with 242 additions and 19 deletions
|
|
@ -61,33 +61,72 @@ def unquote_scalar(raw: str) -> str:
|
|||
return raw.strip().strip('"').strip("'").strip()
|
||||
|
||||
|
||||
def parse_frontmatter(path: str | Path) -> dict[str, str]:
|
||||
"""Read the leading ``---``-delimited YAML frontmatter block as key:value strings.
|
||||
def _split_frontmatter(text: str) -> tuple[list[str], str, bool]:
|
||||
"""Scan the leading ``---``-delimited block ONCE: ``(frontmatter lines, body, terminated)``.
|
||||
|
||||
Minimal by design (no ``yaml`` dependency): enough for the one required ``type`` field and the
|
||||
verdict's scalar fields. List values (``tags: [...]``) are kept verbatim; unknown fields are
|
||||
preserved (OKF SPEC §4). Returns ``{}`` when there is no frontmatter block."""
|
||||
lines = Path(path).read_text(encoding="utf-8").splitlines()
|
||||
This is the module's ONLY place the delimiter is compared against. ``parse_frontmatter`` and
|
||||
``_read_body`` each had their own loop over the same delimiter, which is the kø-(p) shape — and
|
||||
here the two copies had already drifted, measured: given an opening ``---`` with no closing one,
|
||||
``parse_frontmatter`` consumed every remaining line as frontmatter while ``_read_body`` fell
|
||||
through and returned the WHOLE file, delimiter line included.
|
||||
|
||||
**That divergence is PINNED, not fixed.** Reconciling it would move the body-rendering path both
|
||||
nav-golden fasits read, which nothing asks for. So this function reports FACTS and decides
|
||||
nothing: ``terminated`` says whether a closing delimiter was found, and each caller keeps
|
||||
applying its own existing rule to it. ``body`` is the text after a CLOSED block and is ``""``
|
||||
whenever ``terminated`` is false — a caller that wants the whole-file fallback must say so,
|
||||
rather than receive it silently from a scanner that cannot know which rule applies.
|
||||
|
||||
Frontmatter lines are returned VERBATIM apart from their line ending: leading indentation is
|
||||
load-bearing for block-form values (``verified:`` as a sequence of mappings, SPEC §5.2), so the
|
||||
decoder that consumes these lines gets a second READER of one parse, never a second parser.
|
||||
|
||||
Gated by ``tests/test_provenance_decoder_loadbearing.py``."""
|
||||
lines = text.splitlines(keepends=True)
|
||||
if not lines or lines[0].strip() != "---":
|
||||
return {}
|
||||
return [], "", False
|
||||
for i in range(1, len(lines)):
|
||||
if lines[i].strip() == "---":
|
||||
return (
|
||||
[line.rstrip("\r\n") for line in lines[1:i]],
|
||||
"".join(lines[i + 1 :]).lstrip("\n"),
|
||||
True,
|
||||
)
|
||||
return [line.rstrip("\r\n") for line in lines[1:]], "", False
|
||||
|
||||
|
||||
def _frontmatter_from_text(text: str) -> dict[str, str]:
|
||||
"""``parse_frontmatter``'s rule, applied to already-read text: every frontmatter line that
|
||||
carries a colon becomes one ``key: value`` pair, last write winning. Unterminated blocks are
|
||||
parsed as if closed — the pre-split behaviour, preserved deliberately."""
|
||||
fm: dict[str, str] = {}
|
||||
for line in lines[1:]:
|
||||
if line.strip() == "---":
|
||||
break
|
||||
for line in _split_frontmatter(text)[0]:
|
||||
key, sep, val = line.partition(":")
|
||||
if sep:
|
||||
fm[key.strip()] = val.strip()
|
||||
return fm
|
||||
|
||||
|
||||
def _body_from_text(text: str) -> str:
|
||||
"""``_read_body``'s rule, applied to already-read text: the body after a CLOSED frontmatter
|
||||
block, and otherwise the whole file — which covers both "no block at all" and "opened but never
|
||||
closed". This is the caller-side rule ``_split_frontmatter`` deliberately refuses to apply."""
|
||||
_, body, terminated = _split_frontmatter(text)
|
||||
return body if terminated else text
|
||||
|
||||
|
||||
def parse_frontmatter(path: str | Path) -> dict[str, str]:
|
||||
"""Read the leading ``---``-delimited YAML frontmatter block as key:value strings.
|
||||
|
||||
Minimal by design (no ``yaml`` dependency): enough for the one required ``type`` field and the
|
||||
verdict's scalar fields. List values (``tags: [...]``) are kept verbatim; unknown fields are
|
||||
preserved (OKF SPEC §4). Returns ``{}`` when there is no frontmatter block."""
|
||||
return _frontmatter_from_text(Path(path).read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def _read_body(path: Path) -> str:
|
||||
"""The markdown body after the frontmatter block (or the whole file if there is none)."""
|
||||
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
|
||||
if lines and lines[0].strip() == "---":
|
||||
for i in range(1, len(lines)):
|
||||
if lines[i].strip() == "---":
|
||||
return "".join(lines[i + 1 :]).lstrip("\n")
|
||||
return "".join(lines)
|
||||
return _body_from_text(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -191,8 +230,11 @@ def _load_file(bundle_dir: str, name: str) -> BundleFile | None:
|
|||
return None
|
||||
if not resolved.is_file():
|
||||
return None
|
||||
fm = parse_frontmatter(resolved)
|
||||
return BundleFile(name=name, type=fm.get("type", ""), frontmatter=fm, body=_read_body(resolved))
|
||||
text = resolved.read_text(encoding="utf-8")
|
||||
fm = _frontmatter_from_text(text)
|
||||
return BundleFile(
|
||||
name=name, type=fm.get("type", ""), frontmatter=fm, body=_body_from_text(text)
|
||||
)
|
||||
|
||||
|
||||
def _resolve_target(bundle_dir: str, from_name: str, target: str) -> tuple[str, str] | None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue