The builder's imported modules could mark an empty dependency tree as installed. Probe every anchor in one isolated target process. Require all anchors for a multi-module extra, and process only the selected tree's .pth files so editable packages retain their launch behavior. The native builder passes its staged Python explicitly and stops before manifest publication when the inventory fails. Correct the Hindsight and Teams anchors using the namespaces in their locked wheels. Verified: 31 tests passed, 3 host skips. A real target child records its identity, and a caller mutation back to the builder Python fails the regression. Both downloaded SDK wheels match uv.lock and pass inventory and availability checks. Ruff and added-comment checks passed. No full native package or signing run is claimed.
75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
"""Feature inventory reads the selected dependency tree, not the builder."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
import packaging
|
|
import pytest
|
|
|
|
import pm.extras as extras
|
|
import pm.features as features
|
|
from hermes_cli.runtime_paths import site_packages
|
|
|
|
|
|
def test_inventory_uses_the_target_and_requires_every_anchor(tmp_path, monkeypatch):
|
|
target_root = tmp_path / "target"
|
|
subprocess.run(
|
|
[sys.executable, "-m", "venv", "--without-pip", str(target_root)],
|
|
capture_output=True, check=True, timeout=60,
|
|
)
|
|
target = target_root / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
|
|
dependencies = tmp_path / "dependencies"
|
|
site = site_packages(dependencies)
|
|
site.mkdir(parents=True)
|
|
witness = tmp_path / "child.json"
|
|
package = site / "inventory_anchor"
|
|
package.mkdir()
|
|
(package / "__init__.py").write_text(
|
|
"import json, pathlib, sys\n"
|
|
f"pathlib.Path({str(witness)!r}).write_text(json.dumps(sys.executable), encoding='utf-8')\n",
|
|
encoding="utf-8",
|
|
)
|
|
(package / "present.py").write_text("VALUE = 'target'\n", encoding="utf-8")
|
|
(site / "one_part.py").write_text("", encoding="utf-8")
|
|
editable = tmp_path / "editable"
|
|
editable.mkdir()
|
|
(editable / "editable_anchor.py").write_text("", encoding="utf-8")
|
|
(site / "editable.pth").write_text(str(editable) + "\n", encoding="utf-8")
|
|
launches = tmp_path / "launches.jsonl"
|
|
(site / "launches.pth").write_text(
|
|
f"import json, pathlib, sys; p = pathlib.Path({str(launches)!r}); "
|
|
"text = p.read_text(encoding='utf-8') if p.exists() else ''; "
|
|
"p.write_text(text + json.dumps(sys.executable) + '\\n', encoding='utf-8')\n",
|
|
encoding="utf-8",
|
|
)
|
|
mapping = {
|
|
"complete": "inventory_anchor.present",
|
|
"partial": ("one_part", "absent_part"),
|
|
"host-only": "packaging",
|
|
"editable": "editable_anchor",
|
|
}
|
|
repo = tmp_path / "source"
|
|
repo.mkdir()
|
|
(repo / "pyproject.toml").write_text(
|
|
"[project.optional-dependencies]\n" + "".join(f'"{name}"=[]\n' for name in mapping),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(extras, "ANCHORS", mapping)
|
|
before_path = list(sys.path)
|
|
before_env = dict(os.environ)
|
|
assert packaging.__file__ and "packaging" in sys.modules
|
|
result = features.installed_extras(repo, dependencies, python_exe=target)
|
|
assert result == ["complete", "editable"]
|
|
assert Path(json.loads(witness.read_text(encoding="utf-8"))) == target
|
|
assert [Path(json.loads(line)) for line in launches.read_text(encoding="utf-8").splitlines()] == [target]
|
|
assert "inventory_anchor" not in sys.modules
|
|
assert sys.path == before_path
|
|
assert dict(os.environ) == before_env
|
|
|
|
(site / "absent_part.py").write_text("", encoding="utf-8")
|
|
assert features.installed_extras(repo, dependencies, python_exe=target) == ["complete", "editable", "partial"]
|
|
with pytest.raises(features.FeatureProbeError, match="feature inventory failed"):
|
|
features.installed_extras(repo, dependencies, python_exe=tmp_path / "missing-python")
|