def _heartbeat_loop(gw: 'VoiceGateway', heartbeat_interval: float): """ Heartbeat looper that loops and sends heartbeats to the gateway. :param gw: The gateway to handle. """ # async threads! logger.debug("Sending initial heartbeat.") AWAIT(gw.send_heartbeat()) while True: # this is similar to the normal threaded event waiter # it will time out after heartbeat_interval seconds. try: AWAIT( curio.timeout_after(heartbeat_interval, gw._stop_heartbeating.wait())) except curio.TaskTimeout: pass else: break try: AWAIT(gw.send_heartbeat()) except ReconnectWebsocket: break
def readinto(self, memory): with memoryview(memory).cast('B') as view: remaining = len(view) total_read = 0 # It's possible that there is data already buffered on this stream. # If so, we have to copy into the memory buffer first. buffered = len(self._buffer) tocopy = remaining if (remaining < buffered) else buffered if tocopy: view[:tocopy] = self._buffer[:tocopy] del self._buffer[:tocopy] remaining -= tocopy total_read += tocopy # To emulate behavior of synchronous readinto(), we read all available # bytes up to the buffer size. while remaining > 0: try: nrecv = self._readinto_impl(view[total_read:total_read + remaining]) # On proper file objects, None might be returned to indicate blocking if nrecv is None: AWAIT(_read_wait, self._fileno) elif nrecv == 0: break else: total_read += nrecv remaining -= nrecv except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno) return total_read
def recv(self, maxsize, flags=0): while True: try: return self._socket_recv(maxsize, flags) except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def do_handshake(self): while True: try: return self._socket.do_handshake() except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def recvfrom(self, buffersize, flags=0): while True: try: return self._socket.recvfrom(buffersize, flags) except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def send(self, data, flags=0): while True: try: return self._socket_send(data, flags) except WantWrite: AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno)
def recv_into(self, buffer, nbytes=0, flags=0): while True: try: return self._socket.recv_into(buffer, nbytes, flags) except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def task(): task1 = AWAIT(spawn(add, 1, 1)) task2 = AWAIT(spawn(add, 2, 2)) task3 = AWAIT(spawn(add, 3, 3)) w = TaskGroup([task1, task2, task3]) with w: for task in w: result = AWAIT(task.join()) results.append(result)
def _read(self, maxbytes=-1): while True: try: data = self._socket_recv( maxbytes if maxbytes > 0 else MAX_READ) return data except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def flush(self): if not self._file: return while True: try: return self._file.flush() except WantWrite: AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno)
def func(): with disable_cancellation(): AWAIT(sleep(1)) assert True with enable_cancellation(): AWAIT(sleep(2)) assert isinstance(AWAIT(check_cancellation()), TaskTimeout) with pytest.raises(TaskTimeout): AWAIT(sleep(2))
def sendto(self, bytes, flags_or_address, address=None): if address: flags = flags_or_address else: address = flags_or_address flags = 0 while True: try: return self._socket.sendto(bytes, flags, address) except WantWrite: AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno)
def _read(self, maxbytes=-1): while True: # In non-blocking mode, a file-like object might return None if no data is # available. Alternatively, we'll catch the usual blocking exceptions just to be safe try: data = self._file_read(maxbytes) if data is None: AWAIT(_read_wait, self._fileno) else: return data except WantRead: AWAIT(_read_wait, self._fileno) except WantWrite: AWAIT(_write_wait, self._fileno)
def sendall(self, data, flags=0): with memoryview(data).cast('B') as buffer: total_sent = 0 try: while buffer: try: nsent = self._socket_send(buffer, flags) total_sent += nsent buffer = buffer[nsent:] except WantWrite: AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno) except curio.errors.CancelledError as e: e.bytes_sent = total_sent raise
def test_errors(kernel): # spawn_thread used on a coroutine async def main(): with pytest.raises(TypeError): t = await spawn_thread(simple_coro, 2, 3) kernel.run(main) # AWAIT used on coroutine outside of async-thread with pytest.raises(AsyncOnlyError): AWAIT(simple_coro(2,3)) # Premature result async def f(): t = await spawn_thread(simple_func, 2, 3) assert t.state != 'TERMINATED' with pytest.raises(RuntimeError): r = t.result with pytest.raises(RuntimeError): e = t.exception kernel.run(f) # Launching a thread with no target async def g(): from curio.thread import AsyncThread t = AsyncThread() with pytest.raises(RuntimeError): await t.start() kernel.run(g)
def write(self, data): nwritten = 0 view = memoryview(data).cast('B') try: while view: try: nbytes = self._socket_send(view) nwritten += nbytes view = view[nbytes:] except WantWrite: AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno) return nwritten except curio.errors.CancelledError as e: e.bytes_written = nwritten raise
def TAWAIT(coro, *args, **kwargs): ''' Ensure that the caller is an async thread (promoting if necessary), then await for a coroutine ''' if not is_async_thread(): enable_async() return AWAIT(coro, *args, **kwargs)
def accept(self): while True: try: client, addr = self._socket.accept() client = Socket(client) client.__class__ = type(self) return client, addr except WantRead: AWAIT(_read_wait, self._fileno)
async def run_in_main(func_, *args, **kwargs): """ Run and return `func_(*args, **kwargs)` in the kernel's thread. Note that this should only be restricted to tkinter calls. """ # Note: the argument is `func_` to prevent name clashes in `kwargs`. # This can become a positional only parameter (introduced in Python # 3.8) once curio deprecates its usage in Python 3.6 and 3.7. async with spawn_thread(): return AWAIT(_call(func_, args, kwargs))
def connect(self, address): try: result = self._socket.connect(address) if getattr(self, 'do_handshake_on_connect', False): self.do_handshake() return result except WantWrite: AWAIT(_write_wait, self._fileno) err = self._socket.getsockopt(SOL_SOCKET, SO_ERROR) if err != 0: raise OSError(err, 'Connect call failed %s' % (address, )) if getattr(self, 'do_handshake_on_connect', False): self.do_handshake()
def write(self, data): nwritten = 0 view = memoryview(data).cast('B') try: while view: try: nbytes = self._file_write(view) if nbytes is None: raise BlockingIOError() nwritten += nbytes view = view[nbytes:] except WantWrite as e: if hasattr(e, 'characters_written'): nwritten += e.characters_written view = view[e.characters_written:] AWAIT(_write_wait, self._fileno) except WantRead: AWAIT(_read_wait, self._fileno) return nwritten except curio.errors.CancelledError as e: e.bytes_written = nwritten raise
def close(self, code: int = 1000, reason: str = "Client disconnect", reconnect: bool = False): """ Cancels and closes this websocket. """ # if reconnecting, don't close this as this will kill the websocket prematurely if not reconnect: self._cancelled.set() AWAIT(self._task.cancel( blocking=False)) # don't block because it closes by itself self._ws.close(code=code, reason=reason)
async def test(): toplevel = await current_toplevel() canvas = tkinter.Canvas(toplevel, highlightthickness=0) canvas.pack(expand=True, fill="both") task = await curio.current_task() task.next_event = 0 # Make current task an event task assert iseventtask(task) x = y = None lastx = lasty = None try: async for i, event in aenumerate(aevents()): if str(event.type) in {"Enter", "Motion"}: x, y = event.x, event.y if lastx is None: lastx, lasty = x, y canvas.create_line(lastx, lasty, x, y, width=5) canvas.create_oval(x - 2, y - 2, x + 2, y + 2, fill="black") lastx, lasty = x, y except CloseWindow: pass if False: # Toggle this to try out `run_in_main`'s power async with curio.spawn_thread(): try: print("not in main thread") print(toplevel.winfo_exists()) except RuntimeError as e: print(repr(e)) AWAIT( run_in_main(lambda: ( print("in main thread"), print(toplevel.winfo_exists()), ))) # Note that `CloseWindow` exceptions don't pop up here await curio.sleep(5)
def func(): with pytest.raises(TaskCancelled): result = AWAIT(simple_coro(2, 3))
def coro(): with pytest.raises(TypeError): result = AWAIT(simple_coro(2, '3'))
def coro(): result = AWAIT(simple_coro(2, 3)) return result
def func2(): results.append('func2') # Awaiting on an async-thread function should work, but it should stay in the same thread AWAIT(func1, threading.currentThread())
async def worker2(): async with spawn_thread(): AWAIT(evt.wait) results.append('worker2')
def worker1(): AWAIT(evt.wait) results.append('worker1')
async def func(x, y): async with spawn_thread(): time.sleep(0.5) results.append(AWAIT(simple_coro(x, y)))