Skip to content

symfonic.evals.fixtures.loader

loader

Load a committed book fixture and verify it against its truth manifest.

Loading always verifies: a fixture whose text has drifted from the checksum the manifest pins is not a weaker fixture, it is a different corpus, and every expected answer built on it is silently wrong.

Two properties make that verification mean what it says:

Bytes, not text. Checksums are taken over the bytes on disk and compared before anything is decoded. Reading text first would apply universal-newline translation, so rewriting a section from LF to CRLF would keep the recorded digest while changing every committed byte.

Containment, not trust. A manifest is data, and data does not get to name arbitrary files. Every path a manifest reaches for must be a single ordinary filename that still resolves inside the fixture directory once symlinks are followed.

FixtureChecksumError

Bases: FixtureError

A fixture file's bytes no longer match the checksum its manifest pins.

FixtureError

Bases: ValueError

A committed fixture is missing, malformed or no longer matches itself.

FixturePathError

Bases: FixtureError

A fixture identifier or manifest filename reaches outside its root.

checksum

checksum(data: bytes) -> str

The sha256 a manifest must record for the exact bytes data.

Takes bytes rather than text on purpose: b"a\n" and b"a\r\n" are different corpora and must have different digests, but they decode to the same string under universal newlines.

Source code in src/symfonic/evals/fixtures/loader.py
def checksum(data: bytes) -> str:
    """The sha256 a manifest must record for the exact bytes ``data``.

    Takes bytes rather than text on purpose: ``b"a\\n"`` and ``b"a\\r\\n"`` are
    different corpora and must have different digests, but they decode to the
    same string under universal newlines.
    """
    return hashlib.sha256(data).hexdigest()

load_book_fixture cached

load_book_fixture(fixture_id: str = GLASS_HARBOR) -> BookFixture

Load a fixture shipped inside this package by its identifier.

The identifier selects a directory, so it is checked as a filename before it is used as one: an absolute or traversing id names something that is not a packaged fixture, whether or not it exists.

Source code in src/symfonic/evals/fixtures/loader.py
@cache
def load_book_fixture(fixture_id: str = GLASS_HARBOR) -> BookFixture:
    """Load a fixture shipped inside this package by its identifier.

    The identifier selects a directory, so it is checked as a filename before
    it is used as one: an absolute or traversing id names something that is not
    a packaged fixture, whether or not it exists.
    """
    _safe_component(fixture_id, "fixture id")
    directory = _contained_child(_FIXTURE_ROOT, fixture_id.replace("-", "_"), "fixture id")
    if not directory.is_dir():
        raise FixtureError(f"no packaged fixture named {fixture_id!r}")
    fixture = load_fixture_from(directory)
    if fixture.id != fixture_id:
        raise FixtureError(f"fixture directory {fixture_id!r} declares id {fixture.id!r}")
    return fixture

load_fixture_from

load_fixture_from(directory: Path) -> BookFixture

Read and verify the fixture stored in directory.

directory is the trust boundary: nothing the manifest names may be read from outside it, and no failure reports the contents of a file it declined to accept.

Source code in src/symfonic/evals/fixtures/loader.py
def load_fixture_from(directory: Path) -> BookFixture:
    """Read and verify the fixture stored in ``directory``.

    ``directory`` is the trust boundary: nothing the manifest names may be read
    from outside it, and no failure reports the contents of a file it declined
    to accept.
    """
    root = Path(directory).resolve()
    manifest_path = _contained_child(root, MANIFEST_FILENAME, "manifest filename")
    try:
        manifest = json.loads(manifest_path.read_bytes().decode("utf-8"))
    except FileNotFoundError as exc:
        raise FixtureError(f"no truth manifest at {MANIFEST_FILENAME}") from exc
    except IsADirectoryError as exc:
        raise FixtureError(f"{MANIFEST_FILENAME} is a directory") from exc
    except UnicodeDecodeError as exc:
        raise FixtureError(f"{MANIFEST_FILENAME} is not valid UTF-8") from exc
    except json.JSONDecodeError as exc:
        raise FixtureError(f"{MANIFEST_FILENAME} is not valid JSON") from exc
    if not isinstance(manifest, dict):
        raise FixtureError(f"{MANIFEST_FILENAME} must hold a JSON object")
    try:
        return BookFixture(
            id=str(manifest["id"]),
            title=str(manifest["title"]),
            version=str(manifest["version"]),
            sections=tuple(_section(row, root) for row in manifest.get("sections", ())),
            claims=tuple(_claim(row) for row in manifest.get("claims", ())),
            relations=tuple(_relation(row) for row in manifest.get("relations", ())),
            negative_facts=tuple(_negative(row) for row in manifest.get("negative_facts", ())),
        )
    except KeyError as exc:
        raise FixtureError(f"{MANIFEST_FILENAME} is missing key {exc}") from exc
    except ValueError as exc:
        if isinstance(exc, FixtureError):
            raise
        raise FixtureError(str(exc)) from exc