mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Two leftovers of my own. The sweep that removed the reverse client entries appended a newline where it cut a block, which at end of file leaves a blank line after the last key in four services.yml. And the two cache-read nocheck comments in the bond editor were written past the line limit, so the formatter breaks their read_text calls across three lines and carries the comment to the closing paren. The wrap does not cost the suppression: test_no_uncached_file_reads matches the statement, not the line the directive sits on, and passes on this form. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
106 lines
3.5 KiB
Python
106 lines
3.5 KiB
Python
"""Write a single ``bond`` scalar back into a role's ``meta/services.yml``.
|
|
|
|
The file is edited line by line rather than parsed and re-dumped: these files
|
|
carry ``# nocheck:`` directives and hand-tuned ordering that a YAML round-trip
|
|
would drop.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import TYPE_CHECKING
|
|
|
|
from utils.roles.mapping import ROLE_FILE_META_SERVICES
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
_TOP_KEY = re.compile(r"^(?P<key>[^\s#][^:]*):\s*(?:#.*)?$")
|
|
_BOND = re.compile(r"^(?P<lead>\s*bond:\s*)(?P<value>[^#]*?)(?P<tail>\s*#.*)?$")
|
|
|
|
|
|
class EditError(Exception):
|
|
"""Raised when the requested bond cannot be written."""
|
|
|
|
|
|
def parse_bond(raw: str) -> float | None:
|
|
"""Return the bond a user typed, or None when they cleared the field."""
|
|
text = str(raw).strip()
|
|
if text in ("", "-", "·"):
|
|
return None
|
|
try:
|
|
value = float(text)
|
|
except ValueError as exc:
|
|
raise EditError(f"not a number: {raw!r}") from exc
|
|
if not 0.0 <= value <= 1.0:
|
|
raise EditError(f"bond must be between 0 and 1, got {value:g}")
|
|
return value
|
|
|
|
|
|
def _block(lines: list[str], key: str) -> tuple[int, int]:
|
|
"""Return the half-open line range of the top-level ``key`` mapping."""
|
|
start = -1
|
|
for index, line in enumerate(lines):
|
|
match = _TOP_KEY.match(line)
|
|
if match is None:
|
|
continue
|
|
if start < 0:
|
|
if match.group("key").strip() == key:
|
|
start = index
|
|
continue
|
|
return start, index
|
|
if start < 0:
|
|
raise EditError(f"no top-level entry {key!r}")
|
|
return start, len(lines)
|
|
|
|
|
|
def set_bond(roles_dir: Path, role: str, service_key: str, bond: float | None) -> str:
|
|
"""Set, or with ``bond=None`` remove, ``<service_key>.bond`` in ``role``.
|
|
|
|
Args:
|
|
roles_dir: directory holding the role directories.
|
|
role: the consumer role whose file declares the bond.
|
|
service_key: the top-level key inside that role's services file.
|
|
bond: the new value, or None to drop the declaration entirely.
|
|
|
|
Returns:
|
|
The value as it now stands in the file, or "" when it was removed.
|
|
"""
|
|
path = roles_dir / role / ROLE_FILE_META_SERVICES
|
|
if not path.is_file():
|
|
raise EditError(f"no such file: {path}")
|
|
|
|
raw = path.read_text(
|
|
encoding="utf-8"
|
|
) # nocheck: cache-read — a cached read would serve the text from before the previous edit and silently revert it
|
|
lines = raw.splitlines(keepends=True)
|
|
start, end = _block(lines, service_key)
|
|
|
|
target = next(
|
|
(i for i in range(start + 1, end) if _BOND.match(lines[i].rstrip("\n"))), -1
|
|
)
|
|
if bond is None:
|
|
if target < 0:
|
|
return ""
|
|
del lines[target]
|
|
else:
|
|
text = f"{bond:g}"
|
|
if target < 0:
|
|
indent = next(
|
|
(
|
|
line[: len(line) - len(line.lstrip())]
|
|
for line in lines[start + 1 : end]
|
|
if line.strip() and not line.lstrip().startswith("#")
|
|
),
|
|
" ",
|
|
)
|
|
lines.insert(start + 1, f"{indent}bond: {text}\n")
|
|
else:
|
|
match = _BOND.match(lines[target].rstrip("\n"))
|
|
if match is None:
|
|
raise EditError(f"cannot rewrite {lines[target]!r}")
|
|
lines[target] = f"{match['lead']}{text}{match['tail'] or ''}\n"
|
|
|
|
path.write_text("".join(lines), encoding="utf-8")
|
|
return "" if bond is None else f"{bond:g}"
|