mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-11 13:17:07 +00:00
The env generator runs on the bare bootstrap python, before any dependency is installed. utils/distros.py reached PyYAML through utils.cache.yaml, so `python -m cli.meta.env` died with ModuleNotFoundError on every dev-runtime image that ships python3 without PyYAML. utils/yaml_bootstrap.load_block reads the block-YAML subset the SPOT files use with stdlib only. It raises BootstrapYamlError on anything it cannot parse identically to yaml.safe_load, so meta/distros.yml fails loud instead of drifting. The per-line regex lint meant to guard this is gone: one hop of indirection defeated it and it stayed green while the invariant was broken. It is replaced by a test that runs the real generator with every non-stdlib import blocked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
168 lines
3.7 KiB
Python
168 lines
3.7 KiB
Python
"""The stdlib block reader agrees with PyYAML on its subset and rejects the rest."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
import yaml
|
|
|
|
from utils.cache import PROJECT_ROOT
|
|
from utils.cache.files import read_text
|
|
from utils.cache.yaml import load_yaml, load_yaml_str
|
|
from utils.distros import FILE_META_DISTROS
|
|
from utils.yaml_bootstrap import BootstrapYamlError, load_block
|
|
|
|
IN_SUBSET = (
|
|
"a: b\n",
|
|
"a:\n b: c\n d: e\n",
|
|
'b: "c:d"\n',
|
|
"x: quay.io/centos/centos:latest\n",
|
|
"a:\n - one\n - two\n",
|
|
"# leading comment\na: b # trailing comment\n",
|
|
"---\na: b\n",
|
|
'tpl: "example.test/{owner}/{slug}:{tag}"\n',
|
|
"a:\n b:\n - x\nc: d\n",
|
|
)
|
|
|
|
OUT_OF_SUBSET = (
|
|
"a: 1\n",
|
|
"a: true\n",
|
|
"a: null\n",
|
|
"a: [1, 2]\n",
|
|
"a: {b: c}\n",
|
|
"a: &anchor v\n",
|
|
"a: |\n block\n",
|
|
"a: 'single'\n",
|
|
"a:\nb: v\n",
|
|
"a: b\na: c\n",
|
|
"a:\n\tb: c\n",
|
|
"a: b\n---\nc: d\n",
|
|
"a:\n - one\n - two\n",
|
|
)
|
|
|
|
|
|
_UNPARSEABLE = object()
|
|
|
|
TOKENS = (
|
|
"1",
|
|
"1_000",
|
|
"0x10",
|
|
"0o17",
|
|
".5",
|
|
"-1",
|
|
"+1",
|
|
"1e3",
|
|
".inf",
|
|
".nan",
|
|
"12:30",
|
|
"2021-01-01",
|
|
"true",
|
|
"True",
|
|
"On",
|
|
"off",
|
|
"y",
|
|
"n",
|
|
"null",
|
|
"~",
|
|
"b",
|
|
"b c",
|
|
"b:c",
|
|
"b: c",
|
|
"b#c",
|
|
"b #c",
|
|
'"b"',
|
|
'"b: c"',
|
|
"'b'",
|
|
"[1]",
|
|
"{a: b}",
|
|
"&x v",
|
|
"*x",
|
|
"!!str v",
|
|
"a/b",
|
|
"quay.io/c/c:latest",
|
|
"-b",
|
|
"%y",
|
|
"@x",
|
|
">-",
|
|
"|",
|
|
"",
|
|
)
|
|
|
|
TEMPLATES = (
|
|
"k: {t}\n",
|
|
"{t}: v\n",
|
|
"k:\n - {t}\n",
|
|
"k:\n {t}: v\n",
|
|
"k:\n - {t}\n - z\n",
|
|
)
|
|
|
|
STRUCTURES = (
|
|
"",
|
|
"\n",
|
|
"# only a comment\n",
|
|
"---\n",
|
|
"matrix:\n - name: arch\n - name: debian\n",
|
|
"a:\n- x\n- y\n",
|
|
"- a\n- b\n",
|
|
"plain\n",
|
|
"a: b\nc:\n d: e\n",
|
|
"a:\n b: c\n d: e\n",
|
|
"a: b\n\n\nc: d\n",
|
|
'a: "b" trailing\n',
|
|
'a: "b\\nc"\n',
|
|
"a: b \n",
|
|
"a : b\n",
|
|
"a: see this: here\n",
|
|
"a:b\n",
|
|
"a: # note\n",
|
|
)
|
|
|
|
|
|
class TestNoSilentDivergence(unittest.TestCase):
|
|
"""Every accepted document parses exactly as PyYAML parses it."""
|
|
|
|
def test_reader_equals_pyyaml_or_raises(self) -> None:
|
|
documents = [tpl.format(t=token) for tpl in TEMPLATES for token in TOKENS]
|
|
documents.extend(STRUCTURES)
|
|
accepted = 0
|
|
for document in documents:
|
|
with self.subTest(document=document):
|
|
try:
|
|
expected = load_yaml_str(document)
|
|
except yaml.YAMLError:
|
|
expected = _UNPARSEABLE
|
|
try:
|
|
parsed = load_block(document)
|
|
except BootstrapYamlError:
|
|
continue
|
|
accepted += 1
|
|
self.assertEqual(parsed, expected)
|
|
self.assertGreater(accepted, 0)
|
|
|
|
|
|
class TestSpotStaysInSubset(unittest.TestCase):
|
|
def test_reader_matches_pyyaml_on_the_distro_spot(self) -> None:
|
|
path = str(PROJECT_ROOT / FILE_META_DISTROS)
|
|
parsed = load_block(read_text(path))
|
|
expected = load_yaml(path)
|
|
self.assertEqual(parsed, expected)
|
|
self.assertEqual(list(parsed["distros"]), list(expected["distros"]))
|
|
|
|
|
|
class TestSupportedSubset(unittest.TestCase):
|
|
def test_reader_matches_pyyaml(self) -> None:
|
|
for snippet in IN_SUBSET:
|
|
with self.subTest(snippet=snippet):
|
|
self.assertEqual(load_block(snippet), load_yaml_str(snippet))
|
|
|
|
|
|
class TestRejectedConstructs(unittest.TestCase):
|
|
def test_out_of_subset_fails_loud(self) -> None:
|
|
for snippet in OUT_OF_SUBSET:
|
|
with self.subTest(snippet=snippet), self.assertRaises(BootstrapYamlError):
|
|
load_block(snippet)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|