llm-ingestion-okf/tests/test_okf_consume_shim.py
Kjell Tore Guttormsen 9d1f4b14ed test(fixtures): replace sector-specific example material with generic, fictitious examples — green
Every fixture, test document, tool example and document now uses an invented
kitchen-and-baking handbook series, written in this repository. The package's
behaviour is unchanged; src/ changes are comments and help text only.

- Generated fixtures are regenerated from their generators. Their structural
  counts are identical before and after: elements, images, rows, cells,
  headings, bookmarks and the witness inventory's per-document totals. The
  image-inbox and accounting documents are renamed kapittel-84-*.
- tools/okf_accounting_gate.py: the two options that named one real corpus
  each are replaced by a generic, repeatable --corpus PATH with no default.
  Row 5 compares the PDF pair alone. Gate verdict unchanged: RED rows 2, 3, 6.
- tools/okf_witness.py: the STS JSON reader for one publisher's delivery is
  removed, along with its three twins and five tests. The mutation harness
  loses W09.
- docs/: 13 dated reports that documented runs on a retired reference corpus
  are removed, and 40 are neutralized. Dead links are removed, and no new
  dangling path is introduced.
- The synthetic MCP-gate corpus and the residual probe words are neutral.

Valgt: keep the `okf quality --fasit` bar value (the measured fraction, one corpus) and
rewrite only its provenance, because the verdict stays unchanged and the
number names nothing.

Term check with the local list: 0 of 411 tracked files, 0 file names, 0 of
27 binary fixtures. Suite after git add: 2457 passed, 1 skipped. The base
tree had 2460 passed and 2 skipped; five tests went with the JSON reader and
four were added by the term check. ruff, ruff format and mypy --strict src/
are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 14:52:02 +02:00

105 lines
4.1 KiB
Python

"""A path importer holds the module object, and `sys.modules` is not it.
WHAT WAS REPORTED, AND BY WHOM. A downstream consumer repository reported
after v0.7.0 that the shim broke a caller importing it with
`importlib.util.spec_from_file_location`. Reproduced here, and it is not a spelling mistake: `sys.modules[__name__] =
_impl` replaces the REGISTRY entry, and a path importer already holds a
different module object -- the one `module_from_spec` made and `exec_module`
ran. That object keeps whatever the file's own globals ended up with, which is
four public names, while the registry entry has ninety.
TWO COUNTING METHODS, BOTH IN THE TEST. `vars()` gives 4 against 90 and `dir()`
gives 3 against 75; the numbers differ because `dir()` on a module is sorted
and de-duplicated over a different set. Asserting only that ONE name appears
would be green over a nearly empty set -- which is how the defect survived a
release -- so the test asserts the COUNT under both methods.
WHAT THE FIX IS AND WHAT IT IS NOT. One line, copying the implementation's
public names into this module's globals BEFORE the alias. It restores attribute
ACCESS. It does NOT restore patch-through: a caller who monkeypatches the copy
patches a binding the implementation never reads, and that is exactly why the
alias exists and why it stays. The dunder filter is load-bearing -- an
unfiltered `vars(_impl)` overwrites `__name__` with
`llm_ingestion_okf.consume` before the next line reads it, and the module is
then aliased under the wrong key.
"""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
from types import ModuleType
PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "tools"))
SHIM = PROJECT_ROOT / "tools" / "okf_consume.py"
def _load_by_path(name: str) -> ModuleType:
"""Exactly what the reporting caller does, and nothing else."""
spec = importlib.util.spec_from_file_location(name, SHIM)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
saved = sys.modules.get(name)
try:
spec.loader.exec_module(module)
finally:
if saved is None:
sys.modules.pop(name, None)
else:
sys.modules[name] = saved
return module
def _public_by_vars(module: ModuleType) -> set[str]:
return {name for name in vars(module) if not name.startswith("_")}
def _public_by_dir(module: ModuleType) -> set[str]:
return {name for name in dir(module) if not name.startswith("_")}
def test_a_path_imported_shim_carries_the_implementations_public_names() -> None:
"""The defect, stated as the count rather than as one name."""
from llm_ingestion_okf import consume
module = _load_by_path("okf_consume_path_imported")
for method in (_public_by_vars, _public_by_dir):
held = method(module)
registry = method(consume)
missing = registry - held
assert not missing, (
f"{method.__name__}: the path-imported object is missing "
f"{len(missing)} of {len(registry)} public names"
)
def test_build_payload_is_reachable_on_the_path_imported_object() -> None:
"""The specific call the reporting caller makes."""
from llm_ingestion_okf import consume
module = _load_by_path("okf_consume_path_imported_two")
assert module.build_payload is consume.build_payload
def test_the_dunder_filter_leaves_the_modules_own_name_alone() -> None:
"""The known-negative for the filter: without it the alias key is wrong.
An unfiltered copy would set `__name__` to `llm_ingestion_okf.consume`,
and the very next line uses `__name__` as the `sys.modules` key.
"""
name = "okf_consume_path_imported_three"
module = _load_by_path(name)
assert module.__name__ == name
assert module.__file__ is not None and module.__file__.endswith("okf_consume.py")
def test_importing_it_by_name_still_hands_back_the_packaged_module() -> None:
"""The alias is unchanged: patch-through is what it buys and it stays."""
import okf_consume
from llm_ingestion_okf import consume
assert okf_consume is consume