`pypandoc-binary==1.17` joins the `[extract]` extra, and the extra's contents are now pinned by a test -- `test_packaging.py` asserted `project.dependencies` only, so a second package could have arrived in the extra unnoticed, which is precisely where an unexamined transitive tree shows up. WHY VENDORED RATHER THAN FOUND ON PATH: the xlsx and pptx readers exist only from pandoc 3.8.3. Debian 12 ships 2.17.1.1 and Ubuntu 24.04 ships 3.1.3, so a PATH binary cannot deliver two of the five office formats on current stable distributions. The pin is exact rather than a range for the same reason pdfminer.six's is: extraction is deterministic within a converter version and not across one. The single-runtime-dependency rule is untouched -- it governs `project.dependencies`, which still names the guard alone. Measured after installing, on this host: bundled binary pandoc 3.9 (inside the wheel, as intended) pypandoc picks 3.10.2 (the host's PATH binary) That is the third independent measurement of the trap: pypandoc searches PATH before its own bundled binary and takes the highest version it finds, so "vendored" buys nothing until something resolves the path explicitly. The resolver is the next step; until it lands, the vendoring is a pin without an effect and should not be described as more than that. Suite 886 -> 887 (the extra is installed, so the packaging pin runs). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
87 lines
3.9 KiB
Python
87 lines
3.9 KiB
Python
"""Packaging contract: a PEP 561 typed package with exactly one dependency.
|
|
|
|
Consumers run mypy --strict against the inline annotations; without the
|
|
py.typed marker mypy degrades every imported symbol to Any.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import llm_ingestion_okf
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def test_package_ships_py_typed_marker() -> None:
|
|
package_dir = Path(llm_ingestion_okf.__file__).parent
|
|
assert (package_dir / "py.typed").is_file()
|
|
|
|
|
|
def test_the_only_runtime_dependency_is_the_security_boundary() -> None:
|
|
"""The stdlib-only rule, enforced rather than asserted in prose.
|
|
|
|
One dependency is permitted — the guard — because security is the one
|
|
thing this library must not implement. Everything else stays stdlib, so
|
|
a consumer vendoring this package takes on no transitive surface. The
|
|
version RANGE is the pin: it resolves against a package index, and is
|
|
satisfied by the git+https tag install until that index exists.
|
|
"""
|
|
tomllib = pytest.importorskip("tomllib") # stdlib from 3.11; the pin holds on 3.10 too
|
|
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
assert pyproject["project"]["dependencies"] == ["llm-ingestion-guard>=1.2,<2.0"]
|
|
|
|
|
|
def test_the_extract_extra_pins_exactly_what_it_ships() -> None:
|
|
"""`project.dependencies` was pinned; the extra's contents were not.
|
|
|
|
The single-dependency test above reads `project.dependencies` only, so a
|
|
second package could be added to `[extract]` and no test would notice --
|
|
and an extra is exactly where an unexamined transitive tree arrives. The
|
|
extra is opt-in, but "opt-in" is a statement about who installs it, not
|
|
about whether its contents were chosen.
|
|
|
|
Both entries are pins with a stated reason, not conveniences:
|
|
`pdfplumber` for the pdf reader, `pypandoc-binary` because the converter
|
|
BINARY travels with the wheel. Vendoring the binary is what makes the
|
|
output reproducible -- see the resolver, which refuses any version but the
|
|
pinned one.
|
|
"""
|
|
tomllib = pytest.importorskip("tomllib")
|
|
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
assert pyproject["project"]["optional-dependencies"]["extract"] == [
|
|
"pdfplumber>=0.11.10,<0.12",
|
|
"pypandoc-binary==1.17",
|
|
]
|
|
|
|
|
|
def test_the_declared_version_agrees_with_the_packaged_one() -> None:
|
|
"""The two places a version is written must not drift apart.
|
|
|
|
The install channel is a direct git reference, so a consumer pins a TAG
|
|
while pip records `project.version`. Nothing in the run path reads
|
|
`__version__` — which is exactly why a stale one survives a green suite,
|
|
and why a consumer installing at a pre-release tag can end up with a
|
|
package that reports the previous release. This is the only machine check
|
|
on that; the tag name itself remains a human step.
|
|
"""
|
|
tomllib = pytest.importorskip("tomllib")
|
|
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
assert llm_ingestion_okf.__version__ == pyproject["project"]["version"]
|
|
|
|
|
|
def test_operational_tooling_stays_out_of_the_wheel() -> None:
|
|
"""`tools/` is ours, not the consumer's.
|
|
|
|
The upstream watch drives git and the coord mailbox — machinery that is
|
|
meaningful on this machine and meaningless in a consumer's site-packages.
|
|
It lives outside `src/` so it cannot ship, and this test is what makes
|
|
that a promise instead of an accident of the current build config.
|
|
"""
|
|
tomllib = pytest.importorskip("tomllib")
|
|
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
packages = pyproject["tool"]["hatch"]["build"]["targets"]["wheel"]["packages"]
|
|
assert packages == ["src/llm_ingestion_okf"]
|
|
assert (PROJECT_ROOT / "tools" / "okf_watch.py").is_file(), "the test must have a subject"
|