mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
- Declare `__all__` in every package `__init__.py` whose `PROJECT_ROOT` is imported by its modules (163 packages), so the constant counts as exported; list every public name where the package defines more. - Empty the 15 package `__init__.py` files whose `PROJECT_ROOT` no module imports. - Declare the public API of `utils.github.variant.axes` in `__all__`, re-exports included, instead of silencing them with `noqa: F401`; the list matches the one `feature/store-i18n` already carries. - Use `import unittest.mock as mock` in `test_applications.py`, as the rest of the suite does. - Spell `_NON_PRINTABLE` in `utils/yaml_bootstrap.py` as the equivalent BMP-only positive class; it matches exactly the same 2111 code points as the old negated class across U+0000..U+10FFFF. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
136 lines
5.3 KiB
Python
136 lines
5.3 KiB
Python
"""Stdlib reader for the block-YAML subset the bootstrap SPOT files use.
|
|
|
|
This is a deliberate second YAML touchpoint outside ``utils.cache.yaml``:
|
|
the modules that read the distro SPOT must import on the bare bootstrap
|
|
python, which runs the ``.env`` generator before any dependency is
|
|
installed, so they cannot reach the PyYAML-backed cache.
|
|
|
|
Supported: nested block mappings, block sequences of scalars, plain and
|
|
double-quoted string scalars, full-line and trailing comments, a single
|
|
leading document marker. Every other construct raises
|
|
:class:`BootstrapYamlError`, so ``load_block(text)`` either equals
|
|
``yaml.safe_load(text)`` or fails loud.
|
|
|
|
The plain-scalar rule is deliberately narrower than YAML's: a plain scalar
|
|
must start with a letter, an underscore or a slash, and may then carry only
|
|
``[A-Za-z0-9_./+@:-]`` and spaces. That excludes every leading character
|
|
PyYAML's implicit resolvers key off, so no accepted plain scalar can come
|
|
back as an int, float, bool, timestamp or null.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
_PLAIN = re.compile(r"^[A-Za-z_/][A-Za-z0-9_./+@:\- ]*$")
|
|
_RESERVED = re.compile(r"^(?:true|false|yes|no|on|off|y|n|null)$", re.IGNORECASE)
|
|
_NON_PRINTABLE = re.compile(
|
|
r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff]"
|
|
)
|
|
|
|
|
|
class BootstrapYamlError(ValueError):
|
|
"""Raised for a construct outside the supported block subset."""
|
|
|
|
|
|
def _plain(text: str, lineno: int) -> str:
|
|
if not _PLAIN.match(text) or ": " in text or text.endswith(":"):
|
|
raise BootstrapYamlError(f"line {lineno}: unsupported plain scalar {text!r}")
|
|
if _RESERVED.match(text):
|
|
raise BootstrapYamlError(f"line {lineno}: {text!r} is not a string, quote it")
|
|
return text
|
|
|
|
|
|
def _scalar(raw: str, lineno: int) -> str:
|
|
if raw.startswith('"'):
|
|
end = raw.find('"', 1)
|
|
if end < 0 or "\\" in raw[1:end]:
|
|
raise BootstrapYamlError(
|
|
f"line {lineno}: unsupported quoted scalar {raw!r}"
|
|
)
|
|
trailer = raw[end + 1 :].strip()
|
|
if trailer and not trailer.startswith("#"):
|
|
raise BootstrapYamlError(f"line {lineno}: trailing text after {raw!r}")
|
|
return raw[1:end]
|
|
return _plain(raw.split(" #", 1)[0].strip(), lineno)
|
|
|
|
|
|
def load_block(text: str) -> dict:
|
|
"""Parse *text* as a block mapping and return it.
|
|
|
|
Args:
|
|
text: YAML source restricted to the supported block subset.
|
|
|
|
Returns:
|
|
The root mapping, with every scalar as a ``str``.
|
|
|
|
Raises:
|
|
BootstrapYamlError: for any construct outside the subset.
|
|
"""
|
|
non_printable = _NON_PRINTABLE.search(text)
|
|
if non_printable:
|
|
raise BootstrapYamlError(
|
|
f"non-printable character {non_printable.group()!r} at offset {non_printable.start()}"
|
|
)
|
|
root: dict = {}
|
|
stack: list[tuple[int, object]] = [(0, root)]
|
|
pending: tuple[dict, str, int] | None = None
|
|
for lineno, line in enumerate(text.splitlines(), 1):
|
|
if "\t" in line:
|
|
raise BootstrapYamlError(f"line {lineno}: tab indentation")
|
|
body = line.strip()
|
|
if not body or body.startswith("#"):
|
|
continue
|
|
if body == "---":
|
|
if lineno != 1:
|
|
raise BootstrapYamlError(f"line {lineno}: second document")
|
|
if line.startswith(" "):
|
|
raise BootstrapYamlError(f"line {lineno}: indented document marker")
|
|
continue
|
|
indent = len(line) - len(line.lstrip(" "))
|
|
if pending is not None:
|
|
parent, key, parent_indent = pending
|
|
if indent <= parent_indent:
|
|
raise BootstrapYamlError(
|
|
f"line {lineno}: {key!r} has neither a value nor a block"
|
|
)
|
|
child: object = [] if body.startswith("- ") else {}
|
|
parent[key] = child
|
|
stack.append((indent, child))
|
|
pending = None
|
|
else:
|
|
while indent < stack[-1][0]:
|
|
stack.pop()
|
|
if indent != stack[-1][0]:
|
|
raise BootstrapYamlError(f"line {lineno}: unexpected indent")
|
|
container = stack[-1][1]
|
|
if body.startswith("- "):
|
|
if not isinstance(container, list):
|
|
raise BootstrapYamlError(f"line {lineno}: sequence entry in a mapping")
|
|
container.append(_scalar(body[2:], lineno))
|
|
continue
|
|
if isinstance(container, list):
|
|
raise BootstrapYamlError(f"line {lineno}: mapping entry in a sequence")
|
|
key, sep, rest = body.partition(": ")
|
|
if not sep:
|
|
if not body.endswith(":"):
|
|
raise BootstrapYamlError(
|
|
f"line {lineno}: not a mapping entry: {body!r}"
|
|
)
|
|
key, rest = body[:-1], ""
|
|
if key != key.strip():
|
|
raise BootstrapYamlError(f"line {lineno}: not a mapping entry: {body!r}")
|
|
_plain(key, lineno)
|
|
if key in container:
|
|
raise BootstrapYamlError(f"line {lineno}: duplicate key {key!r}")
|
|
rest = rest.strip()
|
|
if not rest or rest.startswith("#"):
|
|
pending = (container, key, indent)
|
|
continue
|
|
container[key] = _scalar(rest, lineno)
|
|
if pending is not None:
|
|
raise BootstrapYamlError(f"{pending[1]!r} has neither a value nor a block")
|
|
if not root:
|
|
raise BootstrapYamlError("empty document")
|
|
return root
|