Skip to content

Replace httpx with httpx2 - #71

Open
skupriienko-mailgun wants to merge 15 commits into
mainfrom
fix/switch-to-httpx2
Open

skupriienko-mailgun wants to merge 15 commits into
mainfrom
fix/switch-to-httpx2

Conversation

@skupriienko-mailgun

@skupriienko-mailgun skupriienko-mailgun commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator
Links:

Jira

Actions:
  • Core Engine Modernization (httpx2):

    • Async Engine Migration: Transitioned the asynchronous transport layer exclusively to httpx2 >=2.7.0, dropping the unmaintained httpx >=0.24.0 dependency across pyproject.toml, environment.yaml, and environment-dev.yaml.
    • Direct Core Imports: Updated mailgun.client, mailgun.endpoints, mailgun.security, and mailgun.types to interface natively with httpx2 for AsyncClient, Request, Response, Timeout, and exception classes.
    • Compatibility Layer Decommissioning: Removed mailgun/_httpx_compat.py and its runtime detection flag (HAS_HTTPX2), eliminating dynamic import branching, dual-engine shims, and obsolete tests (tests/unit/test_httpx_compat.py).
    • Security Policy Alignment: Updated SECURITY.md supported release targets to designate 1.10.x as active and deprecate < 1.10.0.
  • Bug Fixes & Network Resilience:

    • Cyclic & Stagnant Cursor Guard in Streams: Hardened Endpoint.stream() and AsyncEndpoint.stream() to track prev_url, terminating the generator if Mailgun's pagination API returns duplicate or stagnant cursor tokens (next_url == prev_url) to prevent infinite iteration loops.
    • Stream Error Contract Parity: Wrapped synchronous requests.HTTPError into ApiError(exc.response) in Endpoint.stream(), matching the defensive exception contract implemented in AsyncEndpoint.stream() for httpx2.HTTPStatusError.
    • Dead Code Elimination: Removed the redundant secondary if not next_url or not items: break check in Endpoint.stream().
    • Telemetry Log Index Boundary Guard: Added an explicit items existence check in test_post_query_get_account_logs across test_integration_async.py and test_integration_sync.py to prevent IndexError: list index out of range on accounts with empty log retention windows.
    • Test Suite Error Handling & Log Format: Refactored verbose exception re-raising (raise e to raise) and corrected format string parameter alignment in test_integration_coverage.py.
  • Linting, Tooling & Developer Experience (DX):

    • Ruff Formatter Conflict Resolution: Added missing-trailing-comma (COM812) to [tool.ruff.lint.ignore] in pyproject.toml to eliminate formatting rule collisions with ruff format.
    • Test Suite Rule Scoping: Configured targeted per-file-ignores for tests/** in pyproject.toml to suppress non-library false positives (such as assert, magic-value-comparison, pytest-unittest-assertion, and private test helper imports).
    • Pre-Commit Dependency Synchronization: Bumped commitizen to v4.19.2, semgrep to v1.180.0, typos to v1.51.1, check-jsonschema to 0.38.2, ruff to v0.16.10, and mypy to v2.4.0 in .pre-commit-config.yaml.
    • Codebase-Wide Formatting: Applied ruff check --fix --unsafe-fixes and ruff format across all unit, integration, fuzz, property, and regression test suites.
  • Documentation & Release Preparation:


Verification & Testing:

1. Run the Unit Test Suite:
Validates core client lifecycle, builders, security guards, and httpx2 lazy client instantiation.

pytest tests/unit/ -v

2. Verify Stream Pagination & Endpoints:
Confirms cyclic cursor detection, type drift casting, and HTTP status handling across sync and async streams.

pytest tests/unit/test_endpoint.py tests/unit/test_pagination.py -v

3. Run the Live Integration Suite:
Executes end-to-end flows against sandbox domains verifying async/sync parity under httpx2.

pytest tests/integration/test_integration_sync.py -v
pytest tests/integration/test_integration_async.py -v

4. Execute Live Routing Meta-Tests:
Verifies URL formulation across all supported endpoints against live Mailgun servers.

pytest tests/integration/test_routing_meta_live.py -v -s

5. Performance & Concurrency Benchmarks:
Proves connection pooling throughput under httpx2.Limits(max_connections=100).

pytest tests/test_perf.py --benchmark-only

6. Static Analysis & Lint Gates:
Ensures complete Ruff, Mypy, and Pyright compliance across all library and test modules.

ruff check tests/ mailgun/
ruff format --check tests/ mailgun/
mypy mailgun/

- Relax strict production linter rules across tests/** (assert, magic values, unittest assertions, etc.).
- Ignore COM812 (missing-trailing-comma) to avoid conflicts with ruff format.
- Convert tuple membership tests to set literals in conftest.py.
- Remove redundant dict.keys() calls in assertions.
- Combine nested with statements and format files with ruff format.
- Convert membership checks in evil server and pagination fuzzers to set literals.
- Simplify conditional branching in webhook signature fuzzer.
- Replace manual iteration loops with enumerate in pagination stream harness.
- Standardize import ordering and code formatting across all fuzz targets.
- Optimize dict key lookups in sync and async integration test assertions.
- Inline unnecessary lambdas in live routing meta tests.
- Add raw string prefixes to regex docstrings in regression tests.
- Apply consistent ruff format styling across all suites.
…lign error handling

- Track prev_url in Endpoint.stream() and AsyncEndpoint.stream() to prevent infinite loops on stagnant cursors.
- Wrap requests.HTTPError into ApiError in Endpoint.stream() for contract parity with the async client.
- Remove redundant loop break condition in Endpoint.stream().
- Update message tags from httpx-sdk to httpx2-sdk across unit and integration suites.
- Append new tokens to tests/fuzz/fuzz.dict.
…otes

- Update README.md runtime dependencies to mandate httpx2 >=2.7.0 exclusively.
- Add stream pagination hardening, httpx2 engine migration, and merged PRs to CHANGELOG.md.
Comment thread mailgun/types.py Dismissed
Comment thread mailgun/types.py Dismissed
Comment thread tests/fuzz/replay_corpus.py Fixed
@skupriienko-mailgun
skupriienko-mailgun marked this pull request as ready for review October 8, 2026 11:01
skupriienko-mailgun and others added 4 commits October 8, 2026 18:47
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
…errors

- Short-circuit retries on unrecoverable requests URL and schema validation exceptions.

- Route httpx2 UnsupportedProtocol and DecodingError directly to API error handler.
…t harness

- Mock time.sleep to eliminate execution delays during fuzzing runs.

- Configure Client with zero-retry policy and safely catch expected URL validation errors.
…latency fixes

- Add Unreleased section entries covering retry suppression for requests and httpx2 errors.

- Document test execution optimizations in fuzz_audit_events.
Comment thread tests/fuzz/fuzz_audit_events.py Fixed
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: mailgunpython should switch to httpx2

1 participant