Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 31 additions & 13 deletions httpcore/_backends/anyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,30 @@
from .._utils import is_socket_readable
from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream

# For compatibility with both AnyIO 2.x and 3.x
# (some functions and context managers were converted from async to sync in 3.0)
try:
from anyio import maybe_async, maybe_async_cm
except ImportError:

def maybe_async(x): # type: ignore
return x

def maybe_async_cm(x): # type: ignore
return x


try:
from anyio import Lock as create_lock, Semaphore as create_semaphore
except ImportError:
from anyio import create_lock, create_semaphore # type: ignore


class SocketStream(AsyncSocketStream):
def __init__(self, stream: ByteStream) -> None:
self.stream = stream
self.read_lock = anyio.create_lock()
self.write_lock = anyio.create_lock()
self.read_lock = create_lock()
self.write_lock = create_lock()

def get_http_version(self) -> str:
alpn_protocol = self.stream.extra(TLSAttribute.alpn_protocol, None)
Expand All @@ -39,7 +57,7 @@ async def start_tls(
) -> "SocketStream":
connect_timeout = timeout.get("connect")
try:
async with anyio.fail_after(connect_timeout):
async with maybe_async_cm(anyio.fail_after(connect_timeout)):
ssl_stream = await TLSStream.wrap(
self.stream,
ssl_context=ssl_context,
Expand All @@ -56,7 +74,7 @@ async def read(self, n: int, timeout: TimeoutDict) -> bytes:
read_timeout = timeout.get("read")
async with self.read_lock:
try:
async with anyio.fail_after(read_timeout):
async with maybe_async_cm(anyio.fail_after(read_timeout)):
return await self.stream.receive(n)
except TimeoutError:
raise ReadTimeout from None
Expand All @@ -72,7 +90,7 @@ async def write(self, data: bytes, timeout: TimeoutDict) -> None:
write_timeout = timeout.get("write")
async with self.write_lock:
try:
async with anyio.fail_after(write_timeout):
async with maybe_async_cm(anyio.fail_after(write_timeout)):
return await self.stream.send(data)
except TimeoutError:
raise WriteTimeout from None
Expand All @@ -93,10 +111,10 @@ def is_readable(self) -> bool:

class Lock(AsyncLock):
def __init__(self) -> None:
self._lock = anyio.create_lock()
self._lock = create_lock()

async def release(self) -> None:
await self._lock.release()
await maybe_async(self._lock.release())

async def acquire(self) -> None:
await self._lock.acquire()
Expand All @@ -110,18 +128,18 @@ def __init__(self, max_value: int, exc_class: type):
@property
def semaphore(self) -> anyio.abc.Semaphore:
if not hasattr(self, "_semaphore"):
self._semaphore = anyio.create_semaphore(self.max_value)
self._semaphore = create_semaphore(self.max_value)
return self._semaphore

async def acquire(self, timeout: float = None) -> None:
async with anyio.move_on_after(timeout):
async with maybe_async_cm(anyio.move_on_after(timeout)):
await self.semaphore.acquire()
return

raise self.exc_class()

async def release(self) -> None:
await self.semaphore.release()
await maybe_async(self.semaphore.release())


class AnyIOBackend(AsyncBackend):
Expand All @@ -143,7 +161,7 @@ async def open_tcp_stream(
}

with map_exceptions(exc_map):
async with anyio.fail_after(connect_timeout):
async with maybe_async_cm(anyio.fail_after(connect_timeout)):
stream: anyio.abc.ByteStream
stream = await anyio.connect_tcp(
unicode_host, port, local_host=local_address
Expand Down Expand Up @@ -174,7 +192,7 @@ async def open_uds_stream(
}

with map_exceptions(exc_map):
async with anyio.fail_after(connect_timeout):
async with maybe_async_cm(anyio.fail_after(connect_timeout)):
stream: anyio.abc.ByteStream = await anyio.connect_unix(path)
if ssl_context:
stream = await TLSStream.wrap(
Expand All @@ -193,7 +211,7 @@ def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
return Semaphore(max_value, exc_class=exc_class)

async def time(self) -> float:
return await anyio.current_time()
return await maybe_async(anyio.current_time())

async def sleep(self, seconds: float) -> None:
await anyio.sleep(seconds)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ twine==3.4.1
wheel==0.36.2

# Tests & Linting
anyio==2.2.0
anyio==3.0.0
autoflake==1.4
black==20.8b1
coverage==5.5
Expand Down