Skip to content

Commit

Permalink
Raise a neater RuntimeError when the correct async deps are not insta…
Browse files Browse the repository at this point in the history
…lled. (#826)

* Raise a neater RuntimeError when the correct async deps are not installed

* Run scripts/unasync

* Update CHANGELOG
  • Loading branch information
tomchristie authored Nov 2, 2023
1 parent 66aa1c1 commit 08b3eee
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 62 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased

- Fix pool timeout to account for the total time spent retrying. (#823)
- Fix synchronous TLS-in-TLS streams. (#840)
- Raise a neater RuntimeError when the correct async deps are not installed. (#826)
- Add support for synchronous TLS-in-TLS streams. (#840)

## 1.0.0 (October 6th, 2023)

Expand Down
4 changes: 4 additions & 0 deletions httpcore/_async/connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ async def aclose(self) -> None:
self._requests = []

async def __aenter__(self) -> "AsyncConnectionPool":
# Acquiring the pool lock here ensures that we have the
# correct dependencies installed as early as possible.
async with self._pool_lock:
pass
return self

async def __aexit__(
Expand Down
4 changes: 4 additions & 0 deletions httpcore/_sync/connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ def close(self) -> None:
self._requests = []

def __enter__(self) -> "ConnectionPool":
# Acquiring the pool lock here ensures that we have the
# correct dependencies installed as early as possible.
with self._pool_lock:
pass
return self

def __exit__(
Expand Down
86 changes: 25 additions & 61 deletions httpcore/_synchronization.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ def current_async_library() -> str:
try:
import sniffio
except ImportError: # pragma: nocover
return "asyncio"

environment = sniffio.current_async_library()
environment = "asyncio"
else:
environment = sniffio.current_async_library()

if environment not in ("asyncio", "trio"): # pragma: nocover
raise RuntimeError("Running under an unsupported async environment.")

if environment == "asyncio" and anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)

if environment == "trio" and trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)

return environment


Expand All @@ -45,16 +55,8 @@ def setup(self) -> None:
"""
self._backend = current_async_library()
if self._backend == "trio":
if trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)
self._trio_lock = trio.Lock()
else:
if anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)
elif self._backend == "asyncio":
self._anyio_lock = anyio.Lock()

async def __aenter__(self) -> "AsyncLock":
Expand All @@ -63,7 +65,7 @@ async def __aenter__(self) -> "AsyncLock":

if self._backend == "trio":
await self._trio_lock.acquire()
else:
elif self._backend == "asyncio":
await self._anyio_lock.acquire()

return self
Expand All @@ -76,7 +78,7 @@ async def __aexit__(
) -> None:
if self._backend == "trio":
self._trio_lock.release()
else:
elif self._backend == "asyncio":
self._anyio_lock.release()


Expand All @@ -91,16 +93,8 @@ def setup(self) -> None:
"""
self._backend = current_async_library()
if self._backend == "trio":
if trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)
self._trio_event = trio.Event()
else:
if anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)
elif self._backend == "asyncio":
self._anyio_event = anyio.Event()

def set(self) -> None:
Expand All @@ -109,30 +103,20 @@ def set(self) -> None:

if self._backend == "trio":
self._trio_event.set()
else:
elif self._backend == "asyncio":
self._anyio_event.set()

async def wait(self, timeout: Optional[float] = None) -> None:
if not self._backend:
self.setup()

if self._backend == "trio":
if trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)

trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout}
timeout_or_inf = float("inf") if timeout is None else timeout
with map_exceptions(trio_exc_map):
with trio.fail_after(timeout_or_inf):
await self._trio_event.wait()
else:
if anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)

elif self._backend == "asyncio":
anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout}
with map_exceptions(anyio_exc_map):
with anyio.fail_after(timeout):
Expand All @@ -151,20 +135,10 @@ def setup(self) -> None:
"""
self._backend = current_async_library()
if self._backend == "trio":
if trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)

self._trio_semaphore = trio.Semaphore(
initial_value=self._bound, max_value=self._bound
)
else:
if anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)

elif self._backend == "asyncio":
self._anyio_semaphore = anyio.Semaphore(
initial_value=self._bound, max_value=self._bound
)
Expand All @@ -175,13 +149,13 @@ async def acquire(self) -> None:

if self._backend == "trio":
await self._trio_semaphore.acquire()
else:
elif self._backend == "asyncio":
await self._anyio_semaphore.acquire()

async def release(self) -> None:
if self._backend == "trio":
self._trio_semaphore.release()
else:
elif self._backend == "asyncio":
self._anyio_semaphore.release()


Expand All @@ -201,24 +175,14 @@ def __init__(self) -> None:
self._backend = current_async_library()

if self._backend == "trio":
if trio is None: # pragma: nocover
raise RuntimeError(
"Running with trio requires installation of 'httpcore[trio]'."
)

self._trio_shield = trio.CancelScope(shield=True)
else:
if anyio is None: # pragma: nocover
raise RuntimeError(
"Running with asyncio requires installation of 'httpcore[asyncio]'."
)

elif self._backend == "asyncio":
self._anyio_shield = anyio.CancelScope(shield=True)

def __enter__(self) -> "AsyncShieldCancellation":
if self._backend == "trio":
self._trio_shield.__enter__()
else:
elif self._backend == "asyncio":
self._anyio_shield.__enter__()
return self

Expand All @@ -230,7 +194,7 @@ def __exit__(
) -> None:
if self._backend == "trio":
self._trio_shield.__exit__(exc_type, exc_value, traceback)
else:
elif self._backend == "asyncio":
self._anyio_shield.__exit__(exc_type, exc_value, traceback)


Expand Down

0 comments on commit 08b3eee

Please sign in to comment.