fix(lazy-deps): use version-aware check in active_features() to prevent unnecessary downgrades - #44416
fix(lazy-deps): use version-aware check in active_features() to prevent unnecessary downgrades#44416liuhao1024 wants to merge 1 commit into
Conversation
…nt unnecessary downgrades active_features() used _is_present() (presence-only, ignoring version) to detect which features the user has activated. If a dependency like aiohttp 3.14.1 is installed by another package but the lazy-dep spec pins aiohttp==3.13.4, the feature was reported as active and refresh_active_features() triggered a pip downgrade to 3.13.4. Switch to _is_satisfied() so only features whose installed versions actually match the spec range are considered active. This prevents hermes update from downgrading working newer versions to the pinned version.
|
Thanks for tracing the false-positive path. Current main does reproduce it: Problems
Suggested changes
Automated hermes-sweeper review. |
|
I ran this branch against a reproduction of the false-activation bug (#58458) alongside the other five open PRs on Linux / py3.12, The mechanism, printing Every installed version is newer than its pin, so That drift is not an odd venv state — it is the normal state, and it's the one that matters. Worth saying that the version-awareness idea is independently useful — "installed but stale" is real information. It just belongs on the refresh decision rather than the activation decision. Happy to be corrected if the intent was narrower than I've read it — and for transparency, I have an open PR on this same file (#60797, the |
|
Fixed on main by #72361 (anchor-gated active_features(), salvaged from earliest submitter #27878 by @paralegalia). Your version-aware variant identified the same false-activation mechanism — the anchor approach was chosen because _is_satisfied()-based activation would treat a legitimately-installed-but-newer backend as 'inactive' and skip refreshing it after a pin bump. The downgrade half of your report is covered by the new lockstep invariant (every shared LAZY_DEPS pin must equal uv.lock's version). Thanks @liuhao1024. |
What does this PR do?
Fixes
active_features()intools/lazy_deps.pyto use version-aware_is_satisfied()instead of presence-only_is_present(). This preventsrefresh_active_features()(triggered byhermes update) from unnecessarily downgrading packages that are installed at a working newer version than the pinned spec.Related Issue
Fixes #44404
Type of Change
Changes Made
tools/lazy_deps.py: Changedactive_features()to call_is_satisfied()instead of_is_present(). Updated docstring to reflect version-aware behavior.tests/tools/test_lazy_deps.py: Updated existingTestActiveFeaturestests to mock_is_satisfiedinstead of_is_present. Added two new regression tests verifying that packages at mismatched versions are not counted as active.How to Test
pytest tests/tools/test_lazy_deps.py -v— all 63 tests should passhermes update— without this fix,refresh_active_features()would downgrade aiohttp; with this fix, the feature is not reported as active and no downgrade occursChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/ACode Intelligence
active_features(),_is_satisfied(),_is_present()intools/lazy_deps.pyactive_features()which is only called byrefresh_active_features()duringhermes updaterefresh_active_features()consumesactive_features()output to decide which features to re-install. With_is_satisfied, only features with matching versions are refreshed, preventing the downgrade scenario described in fix(lazy-deps): active_features() presence-only check causes false-positive activation and unnecessary downgrades #44404.