def close(self): """Close this receive channel object synchronously. All channel objects have an asynchronous `~.AsyncResource.aclose` method. Memory channels can also be closed synchronously. This has the same effect on the channel and other tasks using it, but `close` is not a trio checkpoint. This simplifies cleaning up in cancelled tasks. Using ``with receive_channel:`` will close the channel object on leaving the with block. """ if self._closed: return self._closed = True for task in self._tasks: trio.lowlevel.reschedule(task, Error(trio.ClosedResourceError())) del self._state.receive_tasks[task] self._tasks.clear() self._state.open_receive_channels -= 1 if self._state.open_receive_channels == 0: assert not self._state.receive_tasks for task in self._state.send_tasks: task.custom_sleep_data._tasks.remove(task) trio.lowlevel.reschedule(task, Error(trio.BrokenResourceError())) self._state.send_tasks.clear() self._state.data.clear()
async def _call_func(self, func, args, kwargs): try: retval = await func(*args, **kwargs) except Exception as exc: self._result_queue.append(Error(exc)) except BaseException as exc: self._result_queue.append(Error(exc)) raise else: self._result_queue.append(Value(retval))
def close(self): if self.closed: return self.closed = True for task in list(self._tasks): _core.reschedule(task, Error(ClosedResourceError())) self._buf.put_channels -= 1 if self._buf.put_channels == 0: assert not self._buf.put_tasks for task in list(self._buf.get_tasks): _core.reschedule(task, Error(EndOfChannel()))
async def __aexit__(self, exc_type, exc, tb): if self._recv_data_task: trio.hazmat.reschedule(self._recv_data_task, Error(Exception('Quit Context Manager'))) self._recv_data_task = None while self._pings: cancelled, task = self._pings.popleft() if not cancelled: trio.hazmat.reschedule(task, Error(Exception('Quit Context Manager'))) for msg_id in self._message_ids.ids: cancelled, task = self._message_ids.pop(msg_id) if not cancelled: trio.hazmat.reschedule(task, Error(Exception('Quit Context Manager'))) self._cancel_recv.cancel()
def close(self): if self.closed: return self.closed = True for task in list(self._tasks): _core.reschedule(task, Error(ClosedResourceError())) self._buf.get_channels -= 1 if self._buf.get_channels == 0: assert not self._buf.get_tasks for task in list(self._buf.put_tasks): _core.reschedule(task, Error(BrokenChannelError())) # XX: or if we're losing data, maybe we should raise a # BrokenChannelError here? self._buf.data.clear()
async def run_sync(self, sync_fn: Callable, *args) -> Optional[Outcome]: import trio try: job = dumps((sync_fn, args), protocol=HIGHEST_PROTOCOL) except BaseException as exc: return Error(exc) try: try: await self._send_chan.send(job) except trio.BrokenResourceError: with trio.CancelScope(shield=True): await self.wait() return None try: return loads(await self._receive_chan.receive()) except trio.EndOfChannel: self._send_pipe.close( ) # edge case: free proc spinning on recv_bytes with trio.CancelScope(shield=True): await self.wait() raise BrokenWorkerProcessError("Worker died unexpectedly:", self.proc) from None except BaseException: # cancellations require kill by contract # other exceptions will almost certainly leave us in an # unrecoverable state requiring kill as well self.kill() with trio.CancelScope(shield=True): await self.wait() raise
def test_Error_unwrap_does_not_create_reference_cycles(): # See comment in Error.unwrap for why reference cycles are tricky exc = ValueError() err = Error(exc) try: err.unwrap() except ValueError: pass # Top frame in the traceback is the current test function; we don't care # about its references assert exc.__traceback__.tb_frame is sys._getframe() # The next frame down is the 'unwrap' frame; we want to make sure it # doesn't reference the exception (or anything else for that matter, just # to be thorough) unwrap_frame = exc.__traceback__.tb_next.tb_frame assert unwrap_frame.f_code.co_name == "unwrap" assert unwrap_frame.f_locals == {}
async def _recv_error_undef(self) -> None: msg_id, = await self.stream.recv_struct('>L') try: cancelled, task = self._message_ids.pop(msg_id) except KeyError as ex: raise exception.UnexpectedAck(msg_id) from ex if not cancelled: trio.hazmat.reschedule(task, Error(exception.DataError(None, None)))
async def aclose(self): if self._closed: await trio.hazmat.checkpoint() return self._closed = True for task in self._tasks: trio.hazmat.reschedule(task, Error(trio.ClosedResourceError())) del self._state.send_tasks[task] self._tasks.clear() self._state.open_send_channels -= 1 if self._state.open_send_channels == 0: assert not self._state.send_tasks for task in self._state.receive_tasks: task.custom_sleep_data._tasks.remove(task) trio.hazmat.reschedule(task, Error(trio.EndOfChannel())) self._state.receive_tasks.clear() await trio.hazmat.checkpoint()
async def _call_func(self, func: Callable[..., Awaitable[object]], args: tuple, kwargs: dict) -> None: try: retval = await func(*args, **kwargs) except BaseException as exc: self._result_queue.append(Error(exc)) else: self._result_queue.append(Value(retval))
async def aclose(self): if self._closed: await _core.checkpoint() return self._closed = True for task in self._tasks: _core.reschedule(task, Error(_core.ClosedResourceError())) del self._state.receive_tasks[task] self._tasks.clear() self._state.open_receive_channels -= 1 if self._state.open_receive_channels == 0: assert not self._state.receive_tasks for task in self._state.send_tasks: task.custom_sleep_data._tasks.remove(task) _core.reschedule(task, Error(_core.BrokenResourceError())) self._state.send_tasks.clear() self._state.data.clear() await _core.checkpoint()
async def _recv_error(self) -> None: msg_id, error_type, plen = await self.stream.recv_struct('>LHL') if plen >= self.max_error: raise exception.LimitBreached('Peer tried to send us {} bytes, limit is {}'.format(plen, self.max_error)) payload = await self.stream.recv_exactly(plen) try: cancelled, task = self._message_ids.pop(msg_id) except KeyError as ex: raise exception.UnexpectedAck(msg_id) from ex if not cancelled: trio.hazmat.reschedule(task, Error(exception.DataError(error_type, payload)))
def test_Outcome_eq_hash(): v1 = Value(["hello"]) v2 = Value(["hello"]) v3 = Value("hello") v4 = Value("hello") assert v1 == v2 assert v1 != v3 with pytest.raises(TypeError): {v1} assert {v3, v4} == {v3} # exceptions in general compare by identity exc1 = RuntimeError("oops") exc2 = KeyError("foo") e1 = Error(exc1) e2 = Error(exc1) e3 = Error(exc2) e4 = Error(exc2) assert e1 == e2 assert e3 == e4 assert e1 != e3 assert {e1, e2, e3, e4} == {e1, e3}
class Future: """A very simple Future for trio based on `trio.Event`.""" def __init__(self): self._outcome = None self._event = trio.Event() def set_result(self, result): self._outcome = Value(result) self._event.set() def set_exception(self, exc): self._outcome = Error(exc) self._event.set() async def get(self): await self._event.wait() return self._outcome.unwrap()
async def test_asend(): async def my_agen_func(): assert (yield 1) == "value" with pytest.raises(KeyError): yield 2 yield 3 my_agen = my_agen_func().__aiter__() v = Value("value") e = Error(KeyError()) assert (await my_agen.asend(None)) == 1 assert (await v.asend(my_agen)) == 2 with pytest.raises(AlreadyUsedError): await v.asend(my_agen) assert (await e.asend(my_agen)) == 3 with pytest.raises(AlreadyUsedError): await e.asend(my_agen) with pytest.raises(StopAsyncIteration): await my_agen.asend(None)
async def test_asend(): @async_generator async def my_agen_func(): assert (await yield_(1)) == "value" with pytest.raises(KeyError): await yield_(2) await yield_(3) my_agen = my_agen_func().__aiter__() if sys.version_info < (3, 5, 2): my_agen = await my_agen v = Value("value") e = Error(KeyError()) assert (await my_agen.asend(None)) == 1 assert (await v.asend(my_agen)) == 2 with pytest.raises(AlreadyUsedError): await v.asend(my_agen) assert (await e.asend(my_agen)) == 3 with pytest.raises(AlreadyUsedError): await e.asend(my_agen) with pytest.raises(StopAsyncIteration): await my_agen.asend(None)
async def put_error(self, x): await self._s.send(Error(x))
def set_exception(self, exc): self._outcome = Error(exc) self._event.set()
def set_result(self, result): self._outcome = Value(result) self._event.set()
def test_Outcome(): v = Value(1) assert v.value == 1 assert v.unwrap() == 1 assert repr(v) == "Value(1)" with pytest.raises(AlreadyUsedError): v.unwrap() v = Value(1) exc = RuntimeError("oops") e = Error(exc) assert e.error is exc with pytest.raises(RuntimeError): e.unwrap() with pytest.raises(AlreadyUsedError): e.unwrap() assert repr(e) == f"Error({exc!r})" e = Error(exc) with pytest.raises(TypeError): Error("hello") with pytest.raises(TypeError): Error(RuntimeError) def expect_1(): assert (yield) == 1 yield "ok" it = iter(expect_1()) next(it) assert v.send(it) == "ok" with pytest.raises(AlreadyUsedError): v.send(it) def expect_RuntimeError(): with pytest.raises(RuntimeError): yield yield "ok" it = iter(expect_RuntimeError()) next(it) assert e.send(it) == "ok" with pytest.raises(AlreadyUsedError): e.send(it)
def safe_dumps(result): try: return dumps(result, protocol=HIGHEST_PROTOCOL) except BaseException as exc: return dumps(Error(exc), protocol=HIGHEST_PROTOCOL)