refactor: clean up httpx2 integration and remove pytest-httpx2 (#202) - #203
Merged
Conversation
|
🧙 Sourcery has finished reviewing your pull request! Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #203 +/- ##
=======================================
Coverage 94.06% 94.06%
=======================================
Files 10 10
Lines 1602 1602
=======================================
Hits 1507 1507
Misses 95 95 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The
httpx2.alias_httpx()invocation is duplicated in__init__.py,__main__.py, andsitecustomize.py; consider centralizing this in a single helper to avoid divergence and make the aliasing behavior easier to reason about. - Given that
sitecustomizealready aliaseshttpx/httpcoreat process startup, it may be worth reviewing whether the additional guarded alias calls insrc/ruff_sync/__init__.pyandsrc/ruff_sync/__main__.pyare still needed, or if they can be removed to reduce side effects on import.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `httpx2.alias_httpx()` invocation is duplicated in `__init__.py`, `__main__.py`, and `sitecustomize.py`; consider centralizing this in a single helper to avoid divergence and make the aliasing behavior easier to reason about.
- Given that `sitecustomize` already aliases `httpx`/`httpcore` at process startup, it may be worth reviewing whether the additional guarded alias calls in `src/ruff_sync/__init__.py` and `src/ruff_sync/__main__.py` are still needed, or if they can be removed to reduce side effects on import.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Contributor
Owner
Author
|
@sourcery-ai review |
Contributor
|
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.
Summary
Closes #202.
This PR cleans up and standardizes our
httpx2integration by:httpx2>=2.9.1(which introduceshttpx2.alias_httpx()) andrespx>=0.23.1.pytest-httpx2plugin dependency.src/sitecustomize.pymodule to handlerespxentrypoint aliasing during test execution.Architectural Decisions & Rationale
1. Removal of
pytest-httpx2We deliberately eliminated the
pytest-httpx2package dependency. Beyond introducing transport hook conflicts and obscure entrypoint ordering issues,pytest-httpx2caused significant developer confusion:pytest-httpx2is actually just a wrapper aroundrespx, but its naming causes developers to repeatedly land on documentation forpytest-httpx/httpx_pytest.Eliminating
pytest-httpx2removes this confusing wrapper layer, standardizes our test suite onrespxdirectly, and simplifies our dependency tree.2. No Third-Party HTTP Plugin Dependencies in Production
ruff-syncruntime code (src/ruff_sync/core.py) importshttpx2directly (import httpx2 as httpx) and does not rely on any third-partyhttpcoreorhttpxplugins at application runtime.Rationale: If our production runtime relied on third-party libraries or plugins that hard-coded
import httpxorimport httpcore, we would require process-wide aliasing at application startup in production. Becauseruff-syncmanages its HTTP transport directly viahttpx2, aliasing is only necessary during test suite execution whenrespxis loaded by Pytest.3. Packaging Exclusion (
src/sitecustomize.py)src/sitecustomize.pylives outsidesrc/ruff_sync/and is strictly excluded from wheel packages by Hatchling (packages = ["src/ruff_sync"])..whl) only includeruff_sync/*files.sitecustomize.pyis never included or shipped to end users.src/sitecustomize.py, an explicit runtime check (if any("pytest" in arg for arg in sys.argv) or "PYTEST_CURRENT_TEST" in os.environ:) guarantees thatalias_httpx()only fires during Pytest executions.Why Disabling
respxAutoloading (-p no:respx) Is a Gnarly Issue (Upstream Problem)We thoroughly evaluated attempting to disable
respxentrypoint autoloading (addopts = ["-p", "no:respx"]) and manually declaringpytest_plugins = ["respx"]inconftest.py. However, this approach reveals a gnarly entrypoint race condition:respxregisters apytest11entrypoint that importshttpx/httpcoreat plugin discovery time.respxis disabled via-p no:respxand re-enabled viapytest_plugins,respxinitializes itsMockRouterhooks after Python module resolution has settled. In end-to-end CLI tests wherehttpx2.AsyncClientis instantiated across sub-threads or CLI entrypoints,httpx2creates freshhttpcore2transport instances that bypassrespx's delayed transport hooks, causingRESPX: some routes were not called!errors.respx(by deferring transport binding until active router context rather than entrypoint import time) or inhttpx2(by providing nativehttpcore2transport interceptors forrespxwithout requiringsys.modulesaliasing). Until fixed upstream,sitecustomize.pyis the only mechanism that forcesalias_httpx()to run at Python interpreter launch before Pytest's entrypoint scanner initializesrespx.Technical Tradeoffs & Alternatives Evaluated
Approach A (Selected): Test-Guarded
src/sitecustomize.pyPython's built-in
sitemodule automatically executessitecustomize.pyat interpreter initialization time before Pytest loads entrypoints.sys.modules["httpcore"]points tohttpcore2whenrespxinitializes during test runs, without touching production execution. Excluded from wheel builds via Hatchling (packages = ["src/ruff_sync"]).sitecustomize.pyfile insrc/.Approach B: Explicit Transport Mocking (
respx.MockRouter(using="httpcore2"))Explicitly pass
using="httpcore2"torespxrouter fixtures.httpx2creates freshAsyncClientinstances without inherited router options.Verification
uv run ruff check . --fix: Passed (0 errors)uv run ruff format .: Passed (0 errors)uv run mypy .: Passed (0 errors across 53 source files)uv run pytest -vv: Passed (413 passed, 1 xfailed)