mirror of
https://github.com/NVIDIA/Model-Optimizer.git
synced 2026-10-02 03:14:52 +08:00
fix(deps): declare httpx, unbreaking partial-install (torch) for every PR (#2547)
## Summary `partial-install (torch)` has been failing on **every** PR since 2026-09-24 — including PRs whose branches predate the breakage — and because it is a *collection* error rather than a test failure, it aborts the entire run: ``` ImportError while importing test module '.../tests/unit/torch/speculative/plugins/test_hf_streaming_dataset.py' E ModuleNotFoundError: No module named 'httpx' collected 2243 items / 1 error / 45 skipped !!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! ``` `unit-pr-required-check` aggregates it, so nothing currently merges on a fresh run. ## What happened **No code changed.** `modelopt/torch/speculative/plugins/hf_streaming_dataset.py` has imported `httpx` at module scope since #1509 (2026-06-02), and `httpx` has never appeared in `pyproject.toml`. It arrived only transitively: `dev-test` → `timm` → `huggingface_hub` → `httpx`. **huggingface_hub 2.0.0**, published **2026-09-24T12:01:21Z**, replaced `httpx<1,>=0.23.0` with the separate **`httpx2<3,>=2.0.0`** distribution. Different package name, so `httpx` stopped being installed and the chain disappeared. The boundary is exact — every run *created* before that timestamp passes, every one after fails: | PR | run created | result | |---|---|---| | #2536 / #2535 | 09-23 22:02 | pass | | #2500 | 09-23 23:45 | pass — **merged 09-24 20:01 on this stale-green result** | | *hub 1.33.0 (still requires httpx)* | *09-24 09:49* | | | **hub 2.0.0 published** | **09-24 12:01** | ← | | #2539 | 09-24 16:57 | fail | | #2544 | 09-24 18:32 | fail | | #2216 | 09-25 11:58 | fail | #2500 merging afterwards is not a counterexample: GitHub does not re-run checks at merge time, so it merged on a result from ~20 hours earlier. That is also why this went unnoticed. ## The changes ### 1. Declare `httpx` in the `hf` extra `httpx` is not incidental to streaming — it is the only transport: - every fetch is HTTP: `POST /v1/completions` to the vLLM serve plus `GET /meta` and `/desc` against the connector's sidecar, all through `httpx.Client`; - there is no non-HTTP path — the base `StreamingDataset._fetch` is an abstract seam and `EagleVllmStreamingDataset._fetch` is its only implementation; - no other HTTP library appears in the module (`requests` / `urllib` / `aiohttp`: zero hits, and `requests` is not declared either); - even the retry predicate is built from it: `_TRANSIENT_FETCH_ERRORS = (httpx.HTTPError, OSError)`. It belongs in `hf` rather than in the core `dependencies`: the same module needs `transformers.trainer_pt_utils` at module scope, so one extra already gates the whole file, and a core install has no use for an HTTP client. The bound matches the 0.x API the code uses — `httpx` has no 1.0 release, and 2.x is a different distribution. This is the part that stops it recurring. `[hf]` currently gets `httpx` only because `datasets` happens to require it — the same accident with a different supplier, one release away from repeating. ### 2. Acquire `httpx` in the test through the existing skip guard The test file already intends to skip where the extra is absent — it has `pytest.importorskip("transformers")` and a comment explaining why, and `transformers` is absent in this job too. It broke only because `import httpx` sat **five lines above** that guard, where a missing module ends collection instead of skipping one file. ## Verification - With everything installed: **18 passed**, no behaviour change. - The import-order property is checked with an AST walk over the module's top-level statements: no `hf`-extra-only import precedes the first `importorskip` (which is now line 41). - A faithful local reproduction was attempted and abandoned honestly: hiding `httpx` locally also breaks `huggingface_hub` 1.28, which `modelopt.torch.opt.plugins.huggingface` imports, so the local failure is not the CI one. CI is the oracle for that half — this PR's own `partial-install (torch)` run is the check that matters. ## Scope Two files, five lines of declaration and four of test import order. Deliberately not folded into any feature PR: it blocks the whole repo, and burying a repo-wide fix inside unrelated work is how these stay invisible. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Optional Hugging Face installations now include `httpx`, supporting features that require HTTP communication without requiring it for all installations. * **Tests** * Hugging Face streaming dataset tests now skip when `httpx` is unavailable, allowing the remaining test suite to be collected and run without it. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,7 @@ hf = [
|
||||
"datasets>=3.0.0",
|
||||
"deepspeed>=0.9.6; platform_system != 'Darwin' and platform_system != 'Windows'",
|
||||
"diffusers>=0.32.2",
|
||||
"httpx>=0.23.0,<1",
|
||||
"huggingface_hub>=0.24.0",
|
||||
"nltk",
|
||||
"peft>=0.17.0",
|
||||
|
||||
@@ -30,12 +30,12 @@ import sys
|
||||
import types
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
# hf_streaming_dataset imports LabelSmoother at module scope.
|
||||
pytest.importorskip("transformers")
|
||||
httpx = pytest.importorskip("httpx")
|
||||
|
||||
from modelopt.torch.speculative.plugins import hf_streaming_dataset
|
||||
from modelopt.torch.speculative.plugins.hf_streaming_dataset import (
|
||||
|
||||
@@ -2620,6 +2620,7 @@ all = [
|
||||
{ name = "deepspeed", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" },
|
||||
{ name = "diffusers" },
|
||||
{ name = "fire" },
|
||||
{ name = "httpx" },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "hydra-core" },
|
||||
{ name = "immutabledict" },
|
||||
@@ -2658,6 +2659,7 @@ dev = [
|
||||
{ name = "deepspeed", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" },
|
||||
{ name = "diffusers" },
|
||||
{ name = "fire" },
|
||||
{ name = "httpx" },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "hydra-core" },
|
||||
{ name = "immutabledict" },
|
||||
@@ -2744,6 +2746,7 @@ hf = [
|
||||
{ name = "datasets" },
|
||||
{ name = "deepspeed", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" },
|
||||
{ name = "diffusers" },
|
||||
{ name = "httpx" },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "nltk" },
|
||||
{ name = "peft" },
|
||||
@@ -2794,6 +2797,7 @@ requires-dist = [
|
||||
{ name = "deepspeed", marker = "sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'hf'", specifier = ">=0.9.6" },
|
||||
{ name = "diffusers", marker = "extra == 'hf'", specifier = ">=0.32.2" },
|
||||
{ name = "fire", marker = "extra == 'puzzletron'" },
|
||||
{ name = "httpx", marker = "extra == 'hf'", specifier = ">=0.23.0,<1" },
|
||||
{ name = "huggingface-hub", marker = "extra == 'hf'", specifier = ">=0.24.0" },
|
||||
{ name = "hydra-core", marker = "extra == 'puzzletron'", specifier = "~=1.3.4" },
|
||||
{ name = "immutabledict", marker = "extra == 'puzzletron'" },
|
||||
|
||||
Reference in New Issue
Block a user