Repository navigation
Fix four public-API bugs: json+data, retry=True on a view, wrong-kind options, paginate on a subclass - #54
Merged
Conversation
- Refuse json= with data=: httpx2 sent the form and dropped the JSON. - Default a verb's retry= to None (the view's), so a call's retry=True can undo a view's retry=False, as "a call's own options win" says. - Refuse a value of the wrong kind where it is written, with a TypeError naming the option: rate=10, TransportPolicy(retry=None) or breaker=3, and timeout=5.0 on HttpOptions, a view or a call. Each used to fail at the first request with an AttributeError. - Page through AsyncBaseClient's own get/post, so a subclass whose endpoint method is named get or post can still paginate. Also correct BudgetBacklogError's docstring, which called it public. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hishel 2.0.0 dropped the filter classes the cache layer imports, so `import headgate` failed wherever the newest hishel was resolved: the core-only install job and the lowest-dependencies job (whose `uv run` re-syncs at the highest resolution) both broke. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 tasks done
kelsayed17
added a commit
that referenced
this pull request
Nov 6, 2026
## What changes hishel 2.0.0 removed `FilterPolicy`, `BaseFilter` and `EntryMeta` and rewrote its storage interface, so `import headgate` fails wherever the newest hishel is installed. This ports the cache layer to hishel 2 and requires `hishel>=2.0.0`. (#54 caps hishel below 2 as a stopgap; this replaces that cap.) **Public API (breaking, pre-1.0, listed in the changelog):** - Removed `HttpCache(policy=...)`, `HttpCache.filter_policy()`, and `headgate.cache.MethodFilter`, `PathFragmentFilter`, `StatusCodeFilter`: they were built on hishel's filters. A POST can no longer be cached (`use_body_key` has no hishel 2 equivalent). - `methods`/`paths`/`statuses` keep working, now applied by headgate: a request they don't name goes straight past hishel, a status they don't name is never written. New `HttpCache.caches_request(method, path)` and `stores_status(status)` say what they decide. - `statuses` may name only 2xx other than 206 and raises `ValueError` otherwise, since hishel 2 stores nothing else (so a declared 404 answer is no longer cached). **Behavior kept, or fixed, against hishel 2:** - **No credential reaches storage.** hishel 2 records the stored request from the request object *after* the network send, by which point the layer below had restored the real URL — a query-string credential was being stored. The credential-free view is now put back as soon as the send returns. - **`Vary` is honored.** hishel 2 keys entries by URL alone and ignores `Vary`; headgate matches the varied headers itself (as keyed digests). A `Vary: *` response is no longer stored. Under `Partition.SHARED`, a response varying on a header the cache never stores (`Authorization`, a cookie, a declared credential) was served to every caller — this predates the port; such a response is now never stored. - Lifetimes and hits move to hishel 2's `caching` extension (`stamp_ttl`, `served_from_cache`). The `stored` flag reads `False` on a miss until the call accepts it. GETs asking for different `cache_ttl=` no longer share one download. **For reviewers:** - hishel 2's own storages log and absorb their faults instead of raising, so `cache_degradations()` now counts only faults a storage raises; documented in the caching guide. - hishel 2 cannot read a hishel 1 SQLite database (every lookup raises, so every call degrades to the network). The changelog tells users to point the storage at a new file. Its default file name is now `hishel_cache_v2.db`. - One test was removed: a cached 503 not charging the breaker, a state hishel 2 can no longer produce. `tests/cache/test_filters.py` became `test_rules.py`. - Two mutation-testing rows in `docs/mutation-testing.md` that described removed code were dropped; the surviving-mutant table has not been re-run with mutmut. - An independent review of the diff probed credential storage (query params, userinfo, `X-Api-Key`, `Set-Cookie`, HEAD, retries, 30 concurrent mixed-credential GETs) and found no leaks; its findings are all fixed here, each with a test that fails without the fix. ## Checklist - [x] `uv run ruff check`, `uv run ruff format --check` and `uv run ty check` pass, with no suppression comments - [x] `uv run python -m pytest --cov` passes at 100% line and branch coverage (2225 tests) - [x] Docs (README / docs/) and docstrings describe the new behaviour (`zensical build --strict` clean) - [x] `CHANGELOG.md` has an entry under "Unreleased" - [x] A change to the public API is reflected in the API-surface snapshot (`--snapshot-update`, diff reviewed) Also reproduced locally: the lowest-declared-dependencies job, the install without extras, and the benchmarks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gance-eval-47a0c1 # Conflicts: # CHANGELOG.md # pyproject.toml # uv.lock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
Four bugs that an adversarial review of the public API turned up, each reproduced by a test that failed before the fix.
post(json=..., data=...)raisesValueError. httpx2 sent the form alone and silently dropped the JSON;check_one_bodypromised to refuse exactly that and did not check this pair.retry=Trueundoes its view'sretry=False. A verb'sretry=defaulted toTrue, which doubled as "inherit", so a call could not override a view'sretry=False, contrary to "a call's own options win over the view's". The default is nowNone(the view's);Trueskips the view, leaving the route's policy or the client's (a route'sNO_RETRYstill holds, and a test pins that).TypeErrornaming the option and what it takes:rate=10,TransportPolicy(retry=None)(points atNO_RETRY),TransportPolicy(breaker=3), andtimeout=5.0onHttpOptions,with_optionsor a call (refused before anything is sent). Each used to pass construction and fail at the first request with anAttributeError.paginatefetches pages throughAsyncBaseClient's ownget/post, so an SDK subclass whose endpoint method is namedgetorpostcan still paginate; before, every walk raised aTypeErrorfrom the subclass's method.Also:
BudgetBacklogError's docstring no longer claims it is public (callers only ever see it asClientTimeoutError).For reviewers: the
retrydefault change is visible in the public signatures (verbs,stream,paginate), so the API-surface snapshot changed; the diff is onlyretry: 'bool | RetryPolicy' = True→retry: 'bool | RetryPolicy | None' = None. It is listed under Changed in the changelog. Not changed on purpose: using a closed client still raisesRuntimeError, which an existing test pins. Other late-failing wrong-kind values (e.g.paginate(limit=100)) are left for a follow-up.Checklist
uv run ruff check,uv run ruff format --checkanduv run ty checkpass, with no suppression commentsuv run python -m pytest --covpasses at 100% line and branch coverageCHANGELOG.mdhas an entry under "Unreleased"--snapshot-update, diff reviewed)🤖 Generated with Claude Code