2626
2727from __future__ import annotations
2828
29- import contextlib
3029import typing
30+ from functools import cache
3131from types import TracebackType
3232
3333if typing .TYPE_CHECKING :
6868
6969__all__ = ["AsyncHTTPTransport" , "HTTPTransport" ]
7070
71- HTTPCORE_EXC_MAP : dict [type [Exception ], type [httpx .HTTPError ]] = {}
72-
7371
72+ @cache
7473def _load_httpcore_exceptions () -> dict [type [Exception ], type [httpx .HTTPError ]]:
7574 import httpcore
7675
@@ -92,40 +91,38 @@ def _load_httpcore_exceptions() -> dict[type[Exception], type[httpx.HTTPError]]:
9291 }
9392
9493
95- @contextlib .contextmanager
96- def map_httpcore_exceptions () -> typing .Iterator [None ]:
97- global HTTPCORE_EXC_MAP
98- if len (HTTPCORE_EXC_MAP ) == 0 :
99- HTTPCORE_EXC_MAP = _load_httpcore_exceptions ()
100- try :
101- yield
102- except Exception as exc :
103- mapped_exc = None
94+ @cache
95+ def _get_httpcore_exception_types () -> tuple [type [Exception ], ...]:
96+ return tuple (_load_httpcore_exceptions ())
97+
10498
105- for from_exc , to_exc in HTTPCORE_EXC_MAP .items ():
106- if not isinstance (exc , from_exc ):
107- continue
108- # We want to map to the most specific exception we can find.
109- # Eg if `exc` is an `httpcore.ReadTimeout`, we want to map to
110- # `httpx.ReadTimeout`, not just `httpx.TimeoutException`.
111- if mapped_exc is None or issubclass (to_exc , mapped_exc ):
112- mapped_exc = to_exc
99+ def _map_httpcore_exception (exc : Exception ) -> httpx .HTTPError :
100+ mapped_exc = None
101+ for from_exc , to_exc in _load_httpcore_exceptions ().items ():
102+ if not isinstance (exc , from_exc ):
103+ continue
104+ # We want to map to the most specific exception we can find.
105+ # Eg if `exc` is an `httpcore.ReadTimeout`, we want to map to
106+ # `httpx.ReadTimeout`, not just `httpx.TimeoutException`.
107+ if mapped_exc is None or issubclass (to_exc , mapped_exc ):
108+ mapped_exc = to_exc
113109
114- if mapped_exc is None : # pragma: no cover
115- raise
110+ if mapped_exc is None : # pragma: no cover
111+ raise
116112
117- message = str (exc )
118- raise mapped_exc (message ) from exc
113+ return mapped_exc (str (exc ))
119114
120115
121116class ResponseStream (SyncByteStream ):
122117 def __init__ (self , httpcore_stream : typing .Iterable [bytes ]) -> None :
123118 self ._httpcore_stream = httpcore_stream
124119
125120 def __iter__ (self ) -> typing .Iterator [bytes ]:
126- with map_httpcore_exceptions () :
121+ try :
127122 for part in self ._httpcore_stream :
128123 yield part
124+ except _get_httpcore_exception_types () as exc :
125+ raise _map_httpcore_exception (exc ) from exc
129126
130127 def close (self ) -> None :
131128 if hasattr (self ._httpcore_stream , "close" ):
@@ -224,8 +221,10 @@ def __exit__(
224221 exc_value : BaseException | None = None ,
225222 traceback : TracebackType | None = None ,
226223 ) -> None :
227- with map_httpcore_exceptions () :
224+ try :
228225 self ._pool .__exit__ (exc_type , exc_value , traceback )
226+ except _get_httpcore_exception_types () as exc : # pragma: no cover
227+ raise _map_httpcore_exception (exc ) from exc
229228
230229 def handle_request (
231230 self ,
@@ -246,8 +245,10 @@ def handle_request(
246245 content = request .stream ,
247246 extensions = request .extensions ,
248247 )
249- with map_httpcore_exceptions () :
248+ try :
250249 resp = self ._pool .handle_request (req )
250+ except _get_httpcore_exception_types () as exc :
251+ raise _map_httpcore_exception (exc ) from exc
251252
252253 assert isinstance (resp .stream , typing .Iterable )
253254
@@ -267,9 +268,11 @@ def __init__(self, httpcore_stream: typing.AsyncIterable[bytes]) -> None:
267268 self ._httpcore_stream = httpcore_stream
268269
269270 async def __aiter__ (self ) -> typing .AsyncIterator [bytes ]:
270- with map_httpcore_exceptions () :
271+ try :
271272 async for part in self ._httpcore_stream :
272273 yield part
274+ except _get_httpcore_exception_types () as exc : # pragma: no cover
275+ raise _map_httpcore_exception (exc ) from exc
273276
274277 async def aclose (self ) -> None :
275278 if hasattr (self ._httpcore_stream , "aclose" ):
@@ -368,8 +371,10 @@ async def __aexit__(
368371 exc_value : BaseException | None = None ,
369372 traceback : TracebackType | None = None ,
370373 ) -> None :
371- with map_httpcore_exceptions () :
374+ try :
372375 await self ._pool .__aexit__ (exc_type , exc_value , traceback )
376+ except _get_httpcore_exception_types () as exc : # pragma: no cover
377+ raise _map_httpcore_exception (exc ) from exc
373378
374379 async def handle_async_request (
375380 self ,
@@ -390,8 +395,10 @@ async def handle_async_request(
390395 content = request .stream ,
391396 extensions = request .extensions ,
392397 )
393- with map_httpcore_exceptions () :
398+ try :
394399 resp = await self ._pool .handle_async_request (req )
400+ except _get_httpcore_exception_types () as exc :
401+ raise _map_httpcore_exception (exc ) from exc
395402
396403 assert isinstance (resp .stream , typing .AsyncIterable )
397404
0 commit comments