Refactor `map_httpcore_exceptions` to not be a context manager

AK
8mo ago
+37-30

Summary

contextlib.contextmanagers are much slower than try:except: (where the no-except path is free in new Pythons), and here they occur in very hot paths.

Sibling of encode/httpcore#1044.

This benchmarks to be slightly faster than the original, like in the httpcore sibling PR:

$ hyperfine --warmup=3 'git checkout master && pytest --benchmark-autosave test_bench1.py' 'git checkout retire-map-exceptions-2 && pytest --benchmark-autosave test_bench1.py'
Benchmark 1: git checkout master && pytest --benchmark-autosave test_bench1.py
  Time (mean ± σ):      2.292 s ±  0.076 s    [User: 1.700 s, System: 0.440 s]
  Range (min … max):    2.163 s …  2.408 s    10 runs

Benchmark 2: git checkout retire-map-exceptions-2 && pytest --benchmark-autosave test_bench1.py
  Time (mean ± σ):      2.255 s ±  0.086 s    [User: 1.683 s, System: 0.433 s]
  Range (min … max):    2.126 s …  2.431 s    10 runs

Summary
  git checkout retire-map-exceptions-2 && pytest --benchmark-autosave test_bench1.py ran
    1.02 ± 0.05 times faster than git checkout master && pytest --benchmark-autosave test_bench1.py

where test_bench1 is

from httpx import Client


def test_bench(benchmark):
    with Client() as c:
        def b():
            resp = c.request(
                method="GET",
                url="http://127.0.0.1:8080/bigfile2",
            )
            assert len(resp.content) == 10 * 1024 * 1024
        benchmark(b)

Checklist

  • I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
    • No test changes needed. Like in the httpcore sibling, some pragma: no covers were added to the exception branches that were actually not covered originally either.
  • I've updated the documentation accordingly.
    • No documentation changes needed; internal change. (Unless map_httpcore_exceptions() was meant to be a public API.)