fix(deps): declare packaging as a core dependency so it ships everywhere (#40522)

* fix(deps): declare packaging as a core dependency so it ships everywhere

packaging is imported directly on three production paths but was never
declared in [project.dependencies], so it only reached users transitively
(pip/uv pull it for other tools). The slim official Docker image ships
without it, where each try/except-ImportError fallback silently degrades:

- plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns
  False when packaging is absent, disabling update_mode='append' so every
  session leaks separate Hindsight documents (the reported #40503 symptom).
- tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as
  satisfied", defeating every version-constraint check on lazy extras.
- hermes_cli/main.py drops to naive name==version requirement parsing.

Promote it to a declared core dep pinned to packaging==26.0 — the exact
version already resolved in uv.lock, so there is zero resolution churn (the
lock change is two edge annotations marking it transitive->direct). It is a
pure-Python py3-none-any wheel with no compiled extensions, safe to ship on
every platform. Declaring it also wires it into the
_verify_core_dependencies_installed() update-repair guard, which reinstalls
missing [project.dependencies] on hermes update.

Adds a hermetic tomllib-parse regression test that fails before the
declaration and passes after.

Fixes #40503

* test(deps): make packaging dep-name extraction PEP 508-robust

Address Copilot review on #40522: the inline name-extraction only handled
==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=,
!=, <, > or a direct reference (name @ url). Factor a _distribution_name
helper that drops markers, direct-reference URLs and extras, then strips any
version operator via regex, so a future dep declared with any PEP 508
specifier shape is matched correctly.

---------

Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
This commit is contained in:
Brian D. Evans
2026-06-09 11:11:48 -04:00
committed by GitHub
co-authored by briandevans
parent d046169646
commit b5421f4ba6
3 changed files with 50 additions and 0 deletions
+38
View File
@@ -1,4 +1,5 @@
from pathlib import Path
import re
import tomllib
import pytest
@@ -14,6 +15,22 @@ find_packages = pytest.importorskip("setuptools", exc_type=ImportError).find_pac
REPO_ROOT = Path(__file__).resolve().parents[1]
def _distribution_name(requirement: str) -> str:
"""Extract the PEP 508 distribution name from a requirement string.
Robust to markers (``; python_version < '3.12'``), direct references
(``name @ https://...``), extras (``name[extra]``) and every version
operator (``==``, ``>=``, ``<=``, ``~=``, ``!=``, ``<``, ``>``), so a
future dep declared with any valid specifier shape doesn't silently
mis-parse here.
"""
spec = requirement.split(";", 1)[0] # drop environment markers
spec = spec.split("@", 1)[0] # drop direct-reference URLs
spec = spec.split("[", 1)[0] # drop extras
spec = re.split(r"[=<>!~]", spec, maxsplit=1)[0] # drop any version operator
return spec.strip().lower()
def _packages_find_include():
data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
return data["tool"]["setuptools"]["packages"]["find"]["include"]
@@ -61,6 +78,27 @@ def test_every_on_disk_subpackage_is_covered_by_packages_find():
)
def test_packaging_declared_as_core_dependency():
"""Regression for #40503.
``packaging`` is imported directly on three production paths
(plugins/memory/hindsight/__init__.py, tools/lazy_deps.py,
hermes_cli/main.py) yet was undeclared, so it only reached users
transitively. The slim Docker image shipped without it, silently
disabling Hindsight append-mode and version-constraint checks. It must
be a declared core dependency so it installs everywhere and the
update-repair step (``_verify_core_dependencies_installed``) guards it.
"""
data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
core = data["project"]["dependencies"]
names = {_distribution_name(dep) for dep in core}
assert "packaging" in names, (
"packaging is imported on production paths (hindsight version compare, "
"lazy_deps version constraints, requirement parsing) and must be a "
"declared core dependency, not a transitive — see #40503"
)
def test_faster_whisper_is_not_a_base_dependency():
data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
deps = data["project"]["dependencies"]