Fix bugs and remove footgun feature in AsyncChannel

This commit is contained in:
Nat Noordanus
2020-06-15 23:35:56 +02:00
parent c8229e53a7
commit 50bb67bf5d
2 changed files with 22 additions and 24 deletions

View File

@@ -81,17 +81,10 @@ class AsyncChannel(AsyncIterable[T]):
"""
def __init__(
self,
source: Union[Iterable[T], AsyncIterable[T]] = tuple(),
*,
buffer_limit: int = 0,
close: bool = False,
self, *, buffer_limit: int = 0, close: bool = False,
):
self._queue: asyncio.Queue[Union[T, object]] = asyncio.Queue(buffer_limit)
self._closed = False
self._sending_task = (
asyncio.ensure_future(self.send_from(source, close)) if source else None
)
self._waiting_recievers: int = 0
# Track whether flush has been invoked so it can only happen once
self._flushed = False
@@ -100,13 +93,14 @@ class AsyncChannel(AsyncIterable[T]):
return self
async def __anext__(self) -> T:
if self.done:
if self.done():
raise StopAsyncIteration
self._waiting_recievers += 1
try:
result = await self._queue.get()
if result is self.__flush:
raise StopAsyncIteration
return result
finally:
self._waiting_recievers -= 1
self._queue.task_done()
@@ -131,7 +125,7 @@ class AsyncChannel(AsyncIterable[T]):
async def send_from(
self, source: Union[Iterable[T], AsyncIterable[T]], close: bool = False
):
) -> "AsyncChannel[T]":
"""
Iterates the given [Async]Iterable and sends all the resulting items.
If close is set to True then subsequent send calls will be rejected with a
@@ -151,9 +145,10 @@ class AsyncChannel(AsyncIterable[T]):
await self._queue.put(item)
if close:
# Complete the closing process
await self.close()
self.close()
return self
async def send(self, item: T):
async def send(self, item: T) -> "AsyncChannel[T]":
"""
Send a single item over this channel.
:param item: The item to send
@@ -161,6 +156,7 @@ class AsyncChannel(AsyncIterable[T]):
if self._closed:
raise ChannelClosed("Cannot send through a closed channel")
await self._queue.put(item)
return self
async def recieve(self) -> Optional[T]:
"""
@@ -168,7 +164,7 @@ class AsyncChannel(AsyncIterable[T]):
or None if the channel is closed before another item is sent.
:return: An item from the channel
"""
if self.done:
if self.done():
raise ChannelDone("Cannot recieve from a closed channel")
self._waiting_recievers += 1
try:
@@ -184,8 +180,6 @@ class AsyncChannel(AsyncIterable[T]):
"""
Close this channel to new items
"""
if self._sending_task is not None:
self._sending_task.cancel()
self._closed = True
asyncio.ensure_future(self._flush_queue())