"""Regression tests for packaging metadata in pyproject.toml.""" from pathlib import Path import tomllib def _load_optional_dependencies(): pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml" with pyproject_path.open("rb") as handle: project = tomllib.load(handle)["project"] return project["optional-dependencies"] def test_wake_dependencies_and_runtime_gate_agree_on_supported_targets(): from packaging.markers import Marker, default_environment from packaging.requirements import Requirement root = Path(__file__).resolve().parents[1] metadata = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8-sig")) optional = metadata["project"]["optional-dependencies"] gates = metadata["tool"]["hermes"]["extras-platforms"] targets = [ ("darwin", "Darwin", "x86_64", False), ("darwin", "Darwin", "arm64", True), ("linux", "Linux", "x86_64", True), ("linux", "Linux", "aarch64", True), ("win32", "Windows", "AMD64", True), ("win32", "Windows", "ARM64", False), ] for system, platform_system, machine, supported in targets: environment = {**default_environment(), "sys_platform": system, "platform_system": platform_system, "platform_machine": machine} for extra in ("wake", "wake-openwakeword"): requirement = next(req for spec in optional[extra] if (req := Requirement(spec)).name == "pyopen-wakeword") assert requirement.marker.evaluate(environment) is supported, (extra, system, machine) assert Marker(gates["wake-openwakeword"]).evaluate(environment) is supported selected = {req.name for spec in optional["wake"] if (req := Requirement(spec)).marker is None or req.marker.evaluate(environment)} assert {"sherpa-onnx", "pvporcupine", "sentencepiece", "pypinyin"} <= selected sherpa_deps = {req.name for spec in optional["wake-sherpa"] if (req := Requirement(spec)).marker is None or req.marker.evaluate(environment)} assert {"sherpa-onnx", "sentencepiece", "pypinyin"} <= sherpa_deps for extra in ("wake-sherpa", "wake-porcupine"): assert all(req.marker is None or req.marker.evaluate(environment) for req in map(Requirement, optional[extra])) gate = gates.get(extra) assert gate is None or Marker(gate).evaluate(environment) def test_direct_overrides_preserve_the_declared_exact_version(): from packaging.requirements import Requirement root = Path(__file__).resolve().parents[1] metadata = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8-sig")) locked = tomllib.loads((root / "uv.lock").read_text(encoding="utf-8-sig")) direct = {req.name: req for spec in metadata["project"]["dependencies"] if (req := Requirement(spec)).specifier} for spec in metadata["tool"]["uv"].get("override-dependencies", []): override = Requirement(spec) requirement = direct.get(override.name) if requirement is None or not any(s.operator == "==" for s in requirement.specifier): continue assert override.specifier == requirement.specifier, ( f"{override.name}: override {override.specifier} contradicts exact requirement {requirement.specifier}" ) versions = {row["version"] for row in locked["package"] if row["name"] == override.name} assert versions and all(version in requirement.specifier for version in versions) def test_matrix_extra_not_in_all(): """The [matrix] extra pulls `mautrix[encryption]` -> `python-olm`, which has Linux-only wheels and no native build path on Windows or modern macOS (archived libolm, C++ errors with Clang 21+). With matrix in [all], `uv sync --locked` on Windows tried to build python-olm from sdist and failed on `make`. As of 2026-05-12 the [matrix] extra is excluded from [all] entirely and installs on first use (pm.ensure_import("matrix")), where the user is expected to have a toolchain. """ optional_dependencies = _load_optional_dependencies() assert "matrix" in optional_dependencies, "[matrix] extra must still exist for `uv sync --extra matrix`" # Must NOT appear in [all] in any form — neither unconditional nor # platform-gated. Lazy-install handles it. matrix_in_all = [ dep for dep in optional_dependencies["all"] if "matrix" in dep ] assert not matrix_in_all, ( "matrix must not appear in [all] — it installs on first use via " f"pm.ensure_import('matrix'). Found: {matrix_in_all}" ) def test_lazy_installable_extras_excluded_from_all(): """Policy (2026-05-12): opt-in backends stay out of [all]. On-demand install exists so one quarantined PyPI release (e.g. mistralai 2.4.6) can't break every fresh install. Putting a backend in [all] defeats that — fresh installs eager-install it and inherit whatever's broken upstream. Opt-in backends are extras that install at first use via pm.ensure_import(extra). """ optional_dependencies = _load_optional_dependencies() # The on-demand backends as of 2026-05-12. Deliberately a literal # list so the test stays a contract — adding a new opt-in backend # means updating this list AND verifying [all] doesn't contain it. lazy_covered_extras = { "anthropic", "bedrock", "exa", "firecrawl", "parallel-web", "fal", "edge-tts", "tts-premium", "voice", # faster-whisper / sounddevice / numpy (composes stt-whisper + audio-io) "stt-whisper", "modal", "daytona", "vercel", "messaging", "slack", "matrix", "dingtalk", "feishu", "telegram", "discord", "wake", "wake-openwakeword", "wake-sherpa", "wake-porcupine", "google-chat", "mistral", # mistralai — Voxtral STT/TTS, lazy-installed (stt.mistral / tts.mistral) } all_extra_specs = optional_dependencies["all"] for extra in lazy_covered_extras: offending = [ spec for spec in all_extra_specs if f"hermes-agent[{extra}]" in spec ] assert not offending, ( f"[{extra}] is in [all] but also in LAZY_DEPS. " f"Remove it from [all] in pyproject.toml — it lazy-installs " f"at first use. Found in [all]: {offending}" ) def _exact_pins(specs): pins = {} for spec in specs: requirement = spec.split(";", 1)[0].strip() if "==" not in requirement: continue package, version = requirement.split("==", 1) package = package.split("[", 1)[0].lower().replace("_", "-") pins[package] = version return pins def test_extras_pin_each_package_at_one_version(): """One package, one version, across every extra. tools/lazy_deps.py is gone — pyproject.toml is the single authority for optional-dependency pins (pm syncs the venv from uv.lock, which resolves from here). The drift class that killed us before (#31817: two documents pinning the same package differently, update ping-ponging the version) is now only possible BETWEEN extras — so pin consistency across extras is the whole remaining contract. """ optional_dependencies = _load_optional_dependencies() pins: dict[str, dict[str, set[str]]] = {} for extra, specs in optional_dependencies.items(): for package, version in _exact_pins(specs).items(): pins.setdefault(package, {}).setdefault(version, set()).add(extra) drift = { package: {v: sorted(extras) for v, extras in versions.items()} for package, versions in pins.items() if len(versions) > 1 } assert not drift, ( "extras pin the same package at different versions — uv sync would " f"resolve whichever wins and silently downgrade the other: {drift}" ) def test_dingtalk_extra_includes_qrcode_for_qr_auth(): """DingTalk's QR-code device-flow auth (hermes_cli/dingtalk_auth.py) needs the qrcode package.""" optional_dependencies = _load_optional_dependencies() dingtalk_extra = optional_dependencies["dingtalk"] assert any(dep.startswith("qrcode") for dep in dingtalk_extra)