Skip to content

Commit 5d65036

Browse files
committed
fix(viz): make local verification portable
Avoid Bash 4-only mapfile in the style compliance test and let the viz parser handle simple frontmatter when PyYAML is absent, so the rebased full suite can run in the local macOS environment.
1 parent 2031a74 commit 5d65036

2 files changed

Lines changed: 53 additions & 4 deletions

File tree

tests/test-style-compliance.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ _pass() { printf '\033[0;32mPASS\033[0m: %s\n' "$1"; PASS_COUNT=$((PASS_COUNT+1)
4141
_fail() { printf '\033[0;31mFAIL\033[0m: %s\n' "$1"; FAIL_COUNT=$((FAIL_COUNT+1)); }
4242

4343
# Step 1: every .sh and .py under viz/.
44-
mapfile -t CORE_FILES < <(
44+
CORE_FILES=()
45+
while IFS= read -r f; do
46+
CORE_FILES+=("$f")
47+
done < <(
4548
find "$PLUGIN_ROOT/viz" \
4649
-type f \( -name '*.sh' -o -name '*.py' \) \
4750
-not -path "*/__pycache__/*" \

viz/server/parser.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,60 @@
1111
import os
1212
import re
1313
import subprocess
14-
import yaml
1514
from datetime import datetime
1615

1716
import rlcr_sources
1817

18+
try:
19+
import yaml
20+
except ModuleNotFoundError: # pragma: no cover - exercised by shell tests
21+
yaml = None
22+
1923
logger = logging.getLogger(__name__)
2024

2125

26+
def _coerce_yaml_scalar(value):
27+
"""Parse the simple scalar values used in Humanize state frontmatter."""
28+
value = value.strip()
29+
if value == '':
30+
return ''
31+
if (value.startswith('"') and value.endswith('"')) or (
32+
value.startswith("'") and value.endswith("'")
33+
):
34+
return value[1:-1]
35+
lowered = value.lower()
36+
if lowered == 'true':
37+
return True
38+
if lowered == 'false':
39+
return False
40+
if lowered in {'null', 'none', '~'}:
41+
return None
42+
if re.fullmatch(r'-?[0-9]+', value):
43+
try:
44+
return int(value)
45+
except ValueError:
46+
return value
47+
return value
48+
49+
50+
def _safe_load_frontmatter(text):
51+
"""Load frontmatter, falling back to a small key/value parser without PyYAML."""
52+
if yaml is not None:
53+
return yaml.safe_load(text) or {}
54+
55+
meta = {}
56+
for raw_line in text.splitlines():
57+
line = raw_line.strip()
58+
if not line or line.startswith('#') or ':' not in line:
59+
continue
60+
key, value = line.split(':', 1)
61+
key = key.strip()
62+
if not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_-]*', key):
63+
continue
64+
meta[key] = _coerce_yaml_scalar(value)
65+
return meta
66+
67+
2268
def _derive_project_root(session_dir):
2369
"""Return the project root for a ``.humanize/rlcr/<session>`` path."""
2470
rlcr_dir = os.path.dirname(session_dir)
@@ -64,8 +110,8 @@ def parse_yaml_frontmatter(filepath):
64110
return {}, content
65111

66112
try:
67-
meta = yaml.safe_load(parts[1]) or {}
68-
except yaml.YAMLError:
113+
meta = _safe_load_frontmatter(parts[1])
114+
except Exception:
69115
meta = {}
70116

71117
body = parts[2].strip()

0 commit comments

Comments
 (0)