Skip to content

Commit a880bf7

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: decompose parse_frontmatter into focused helpers to flatten nesting
Extract _extract_frontmatter_block (delimiter scanning) and _parse_kv_lines (key-value parsing) from parse_frontmatter, reducing max nesting from 7 to 3 levels. Type coercion is now data-driven via _FIELD_COERCIONS dict — adding a new typed field is a one-line dict entry instead of an elif branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 81724c8 commit a880bf7

2 files changed

Lines changed: 49 additions & 34 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Tests are in `tests/` with one file per module. Docs are in `docs/` using MkDocs
4141
## Traps
4242

4343
- Primitive marker filenames (`CHECK.md`, `CONTEXT.md`, `INSTRUCTION.md`, `PROMPT.md`) are defined as constants in `_frontmatter.py` (`CHECK_MARKER`, `CONTEXT_MARKER`, etc.). All modules import from there — change the constant to rename everywhere.
44-
- `timeout` and `enabled` frontmatter fields have special type coercion in `_frontmatter.py:parse_frontmatter()`. New typed fields need coercion logic added there.
44+
- `timeout` and `enabled` frontmatter fields have special type coercion via `_FIELD_COERCIONS` in `_frontmatter.py`. To add a new typed field, add an entry to that dict.
4545
- Both contexts and instructions share `resolver.py:resolve_placeholders()`. Changes affect both.
4646
- Output is truncated to 5000 chars in `_output.py`. This is intentional.
4747
- Commands in frontmatter run via `shlex.split()` — no shell features (pipes, redirections, `&&`). Scripts (`run.*`) are the escape hatch.

src/ralphify/_frontmatter.py

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,49 @@
2121
PROMPT_MARKER = "PROMPT.md"
2222

2323

24+
# Type coercion for known frontmatter fields.
25+
# To add a new typed field, add an entry here — no other changes needed.
26+
_FIELD_COERCIONS: dict[str, object] = {
27+
"timeout": int,
28+
"enabled": lambda v: v.lower() in ("true", "yes", "1"),
29+
}
30+
31+
32+
def _parse_kv_lines(lines: list[str]) -> dict:
33+
"""Parse flat ``key: value`` lines with type coercion for known fields."""
34+
result: dict = {}
35+
for line in lines:
36+
line = line.strip()
37+
if not line or line.startswith("#"):
38+
continue
39+
if ":" not in line:
40+
continue
41+
key, _, value = line.partition(":")
42+
key = key.strip()
43+
value = value.strip()
44+
coerce = _FIELD_COERCIONS.get(key)
45+
result[key] = coerce(value) if coerce else value
46+
return result
47+
48+
49+
def _extract_frontmatter_block(text: str) -> tuple[list[str], str]:
50+
"""Split text into frontmatter lines and body at ``---`` delimiters.
51+
52+
Returns ``([], text)`` when no valid frontmatter block is found.
53+
"""
54+
lines = text.split("\n")
55+
start = None
56+
for i, line in enumerate(lines):
57+
if line.strip() == "---":
58+
if start is None:
59+
start = i
60+
else:
61+
fm_lines = lines[start + 1 : i]
62+
body = "\n".join(lines[i + 1 :]).strip()
63+
return fm_lines, body
64+
return [], text
65+
66+
2467
def parse_frontmatter(text: str) -> tuple[dict, str]:
2568
"""Parse a markdown file with optional YAML-like frontmatter.
2669
@@ -31,39 +74,11 @@ def parse_frontmatter(text: str) -> tuple[dict, str]:
3174
3275
Returns ``(frontmatter_dict, body_text)``.
3376
"""
34-
frontmatter: dict = {}
35-
body = text
36-
37-
stripped = text.strip()
38-
if stripped.startswith("---"):
39-
lines = text.split("\n")
40-
# Find the opening ---
41-
start = None
42-
for i, line in enumerate(lines):
43-
if line.strip() == "---":
44-
if start is None:
45-
start = i
46-
else:
47-
# Found closing ---
48-
fm_lines = lines[start + 1 : i]
49-
body = "\n".join(lines[i + 1 :]).strip()
50-
for fm_line in fm_lines:
51-
fm_line = fm_line.strip()
52-
if not fm_line or fm_line.startswith("#"):
53-
continue
54-
if ":" not in fm_line:
55-
continue
56-
key, _, value = fm_line.partition(":")
57-
key = key.strip()
58-
value = value.strip()
59-
# Type coercion
60-
if key == "timeout":
61-
frontmatter[key] = int(value)
62-
elif key == "enabled":
63-
frontmatter[key] = value.lower() in ("true", "yes", "1")
64-
else:
65-
frontmatter[key] = value
66-
break
77+
if text.strip().startswith("---"):
78+
fm_lines, body = _extract_frontmatter_block(text)
79+
frontmatter = _parse_kv_lines(fm_lines)
80+
else:
81+
frontmatter, body = {}, text
6782

6883
body = re.sub(r"<!--.*?-->", "", body, flags=re.DOTALL).strip()
6984
return frontmatter, body

0 commit comments

Comments
 (0)