def copy_future(future: asyncio.Future, new_future: asyncio.Future = None): new_future = new_future or asyncio.Future(loop=future._loop) handler = partial(_on_result, new_future=new_future) future.add_done_callback(handler) return new_future
def wrap_exc(f: asyncio.Future, logger): def check_exception(f: asyncio.Future): if not f.cancelled() and f.exception() is not None: logger.error('Exception! {}'.format(f.exception())) f.add_done_callback(check_exception) return f
def run_until_complete(self, future: asyncio.Future) -> Any: self._check_running() new_task = False if not asyncio.isfuture(future): future = asyncio.ensure_future(future, loop=self) # We wrapped `future` in a new Task since it was not a Future. new_task = True # An exception is raised if the new task doesn't complete, so there is no need to log the "destroy # pending task" message. future._log_destroy_pending = False else: if _helpers.get_future_loop(future) is not self: raise ValueError("Future does not belong to this loop") future.add_done_callback(_run_until_complete_cb) try: self._run_mainloop() except: if new_task and future.done() and not future.cancelled(): # The coroutine raised a BaseException. Consume the exception to not log a warning (Future # will log a warning if its exception is not retrieved), the caller doesn't have access to the # task wrapper we made. future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) if not future.done(): raise RuntimeError('Event loop stopped before Future completed.') return future.result()
def start_tls(self, server_side, ssl_options=None, server_hostname=None, connect_timeout=None): if not self._transport or self._read_future: raise ValueError("IOStream is not idle; cannot convert to SSL") self._connect_ssl_future = connect_ssl_future = Future(loop=self._loop) waiter = Future(loop=self._loop) def on_connected(future): if self._loop_connect_timeout: self._loop_connect_timeout.cancel() self._loop_connect_timeout = None if connect_ssl_future._exception is not None: self.on_closed(future.exception()) self._connect_ssl_future = None else: self._connect_ssl_future = None connect_ssl_future.set_result(self) waiter.add_done_callback(on_connected) if connect_timeout: def on_timeout(): self._loop_connect_timeout = None if not waiter.done(): self.close((None, IOError("Connect timeout"), None)) self._loop_connect_timeout = self._loop.call_later(connect_timeout, on_timeout) self._transport.pause_reading() sock, self._transport._sock = self._transport._sock, None self._transport = self._loop._make_ssl_transport( sock, self, ssl_options, waiter, server_side=False, server_hostname=server_hostname) return connect_ssl_future
def start_tls(self, server_side, ssl_options=None, server_hostname=None, connect_timeout=None): if not self._transport or self._read_future: raise ValueError("IOStream is not idle; cannot convert to SSL") self._connect_ssl_future = connect_ssl_future = Future(loop=self._loop) waiter = Future(loop=self._loop) def on_connected(future): if self._loop_connect_timeout: self._loop_connect_timeout.cancel() self._loop_connect_timeout = None if connect_ssl_future._exception is not None: self.on_closed(future.exception()) self._connect_ssl_future = None else: self._connect_ssl_future = None connect_ssl_future.set_result(self) waiter.add_done_callback(on_connected) if connect_timeout: def on_timeout(): self._loop_connect_timeout = None if not waiter.done(): self.close((None, IOError("Connect timeout"), None)) self._loop_connect_timeout = self._loop.call_later(connect_timeout, on_timeout) self._transport.pause_reading() sock, self._transport._sock = self._transport._sock, None self._transport = self._loop._make_ssl_transport( sock, self, ssl_options, waiter, server_side=False, server_hostname=server_hostname) return connect_ssl_future
async def _waiter(cls, future: asyncio.Future) -> T_Result: wait_future = asyncio.Future() # type: asyncio.Future future.add_done_callback( partial(cls._set_wait_future_result, wait_future=wait_future)) return await wait_future
def unpack_future(src: asyncio.Future, num_items: int) -> List[asyncio.Future]: """Unpack the result of source future to num_items futures. This function takes in a Future and splits its result into many futures. If the result of the source future is an exception, then all destination futures will have the same exception. """ dest_futures = [ asyncio.get_event_loop().create_future() for _ in range(num_items) ] def unwrap_callback(fut: asyncio.Future): exception = fut.exception() if exception is not None: [f.set_exception(exception) for f in dest_futures] return result = fut.result() assert len(result) == num_items for item, future in zip(result, dest_futures): future.set_result(item) src.add_done_callback(unwrap_callback) return dest_futures
def queue_request(self, aRequest): if aRequest not in self._request_queue: future = Future() future.add_done_callback(functools.partial(self.cleanup_future, aRequest=aRequest)) self._request_queue[aRequest] = future return self._request_queue[aRequest]
def propagate(from_: Future, to: Future) -> None: """ Copy the value that ``from_`` is completed with to ``to``, whenever ``from_`` is completed. """ if from_.done(): copy_result(from_, to) else: from_.add_done_callback(lambda _: copy_result(from_, to))
def addFuture(self, future: Future, caller, iomode: str): def onComplete(fut): try: # self.future.discard(fut) # logger.debug(f"discarding {self.name} {iomode} future") self.future = None fut.result() except asyncio.CancelledError: logger.info(f"{caller} {iomode} was cancelled") except Exception as ex: logger.warn(f"{caller} {iomode} errored : {ex}") future.add_done_callback(onComplete) self.future = future
def copy_future(future: asyncio.Future, new_future: asyncio.Future = None): """ Creates a copy of passed future instance. Actually another future will be created but result or exception of original future will be passed to created future. :param future: :class:`asyncio.Future` instance :param new_future: Target future (:class:`None` by default) :return: :class:`asyncio.Future` """ new_future = new_future or create_future(loop=future._loop) handler = partial(_on_result, new_future=new_future) future.add_done_callback(handler) return new_future
def assertReactorWorksWithAsyncioFuture(self, reactor): """ Ensure that C{reactor} has an event loop that works properly with L{asyncio.Future}. """ future = Future() result = [] def completed(future): result.append(future.result()) reactor.stop() future.add_done_callback(completed) future.set_result(True) self.assertEqual(result, []) self.runReactor(reactor, timeout=1) self.assertEqual(result, [True])
def _complete_callback(self, result_future: asyncio.Future = None ): ''' Obtaining Cloud Function results is complete. If the acquisition has not started, a coroutine acquisition task is created. ''' if not result_future: result_future: asyncio.Future = asyncio.run_coroutine_threadsafe( self.__function_client.get_function_result_by_request_id_async( region_id = self.__function_metadata['region_id'], namespace_name = self.__function_metadata['namespace_name'], function_name = self.__function_metadata['function_name'], function_request_id = self.__function_request_id, function_version = self.__function_metadata['function_version'] ), self.__function_client.get_event_loop()) result_future.add_done_callback(self._complete_callback) else: if result_future.exception(): if isinstance(result_future.exception(), errors.errors.NotFoundError): self.__function_client.get_event_loop().call_later(1, self._complete_callback) else: self.set_exception(result_future.exception()) return function_result: dict = result_future.result() if not function_result['is_successful']: self.set_exception(errors.InvokeError( error_message = function_result['return_result'], request_id = self.__function_request_id )) self.set_result(helper.inferred_return_result( function_result['return_result'])) self.done()
def chain_future(fut: Future, other: Future) -> Future: """Chains a future with other future. Returns the first future. """ def done(fut): if other.done(): return if fut.cancelled(): other.cancel() return if fut.exception() is not None: other.set_exception(fut.exception()) return other.set_result(fut.result()) fut.add_done_callback(done) return fut
async def set_reader(self, reader: asyncio.Future): """ Cancel the currently running reader and register the new one. A reader is a coroutine that calls this transports 'read' function. The 'read' function calls can be paused by calling pause_reading of this transport. :param reader: future reader """ if self._read_thread is not None: # cancel currently running reader if self._read_thread.cancel(): try: await self._read_thread except asyncio.CancelledError: pass # Create callback for debugging in case the reader is failing err_callback = utils.create_error_check_callback( ignore=asyncio.CancelledError) reader.add_done_callback(err_callback) self._read_thread = reader
def log_task_exceptions(task: asyncio.Future, logger: Union[logging.Logger, logging.LoggerAdapter], msg: str): """Add a done callback to a task that logs any exception it raised. Parameters ---------- task Task (or any future) on which the callback will be added logger Logger to which the warning will be written msg Message that will be logged """ def done_callback(task): if not task.cancelled(): try: task.result() except Exception: logger.exception('%s', msg) task.add_done_callback(done_callback)
def aio_future_chain_thread(aio_future: asyncio.Future, future: ThreadFuture): """Chain an asyncio future to a thread future. If the result of the asyncio future is another aio future this will also be chained so the client only sees thread futures """ def done(done_future: asyncio.Future): # Here we're on the aio thread # Copy over the future try: result = done_future.result() if asyncio.isfuture(result): # Change the aio future to a thread future fut = ThreadFuture() aio_future_chain_thread(result, fut) result = fut future.set_result(result) except asyncio.CancelledError: future.cancel() except Exception as exception: # pylint: disable=broad-except future.set_exception(exception) aio_future.add_done_callback(done) return future
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ log = txaio.make_logger() peer: Optional[str] = None is_server: Optional[bool] = None def connection_made(self, transport): # asyncio networking framework entry point, called by asyncio # when the connection is established (either a client or a server) self.log.info('{func}(transport={transport})', func=hltype(self.connection_made), transport=transport) self.transport = transport # determine preliminary transport details (what is know at this point) self._transport_details = create_transport_details( self.transport, self.is_server) # backward compatibility self.peer = self._transport_details.peer self.receive_queue = deque() self._consume() self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) # according to asyncio docs, connection_lost(None) is called # if something else called transport.close() if exc is not None: self.transport.close() self.transport = None def _consume(self): self.waiter = Future(loop=self.factory.loop or txaio.config.loop) def process(_): while self.receive_queue: data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) def _closeConnection(self, abort=False): if abort and hasattr(self.transport, 'abort'): self.transport.abort() else: self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): asyncio.ensure_future(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): asyncio.ensure_future(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): asyncio.ensure_future(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): asyncio.ensure_future(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): asyncio.ensure_future(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): asyncio.ensure_future(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): asyncio.ensure_future(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): asyncio.ensure_future(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): asyncio.ensure_future(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): asyncio.ensure_future(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): asyncio.ensure_future(res) def registerProducer(self, producer, streaming): raise Exception("not implemented") def unregisterProducer(self): # note that generic websocket/protocol.py code calls # .unregisterProducer whenever we dropConnection -- that's # correct behavior on Twisted so either we'd have to # try/except there, or special-case Twisted, ..or just make # this "not an error" pass
def add(self, future: asyncio.Future): if self.__main_store: self.__main_store.add(future) self.__collection.add(future) future.add_done_callback(self._on_future_done)
async def register_callbacks(all_done: asyncio.Future): print("registering callbacks on future") all_done.add_done_callback(functools.partial(callback, n=1)) all_done.add_done_callback(functools.partial(callback, n=2))
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() try: peer = transport.get_extra_info('peername') try: # FIXME: tcp4 vs tcp6 self.peer = u"tcp:%s:%d" % (peer[0], peer[1]) except: # e.g. Unix Domain sockets don't have host/port self.peer = u"unix:{0}".format(peer) except: self.peer = u"?" self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) # according to asyncio docs, connection_lost(None) is called # if something else called transport.close() if exc is not None: self.transport.close() self.transport = None def _consume(self): self.waiter = Future(loop=self.factory.loop or txaio.config.loop) def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) # noinspection PyUnusedLocal def _closeConnection(self, abort=False): self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): ensure_future(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): ensure_future(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): ensure_future(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): ensure_future(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): ensure_future(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): ensure_future(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): ensure_future(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): ensure_future(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): ensure_future(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): ensure_future(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): ensure_future(res) def get_channel_id(self): """ Implements :func:`autobahn.wamp.interfaces.ITransport.get_channel_id` """ self.log.debug( 'FIXME: transport channel binding not implemented for asyncio (autobahn-python issue #729)' ) return None def registerProducer(self, producer, streaming): raise Exception("not implemented")
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() try: peer = transport.get_extra_info('peername') try: # FIXME: tcp4 vs tcp6 self.peer = u"tcp:%s:%d" % (peer[0], peer[1]) except: # e.g. Unix Domain sockets don't have host/port self.peer = u"unix:{0}".format(peer) except: self.peer = u"?" self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) # according to asyncio docs, connection_lost(None) is called # if something else called transport.close() if exc is not None: self.transport.close() self.transport = None def _consume(self): self.waiter = Future(loop=self.factory.loop or txaio.config.loop) def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) # noinspection PyUnusedLocal def _closeConnection(self, abort=False): self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): ensure_future(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): ensure_future(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): ensure_future(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): ensure_future(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): ensure_future(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): ensure_future(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): ensure_future(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): ensure_future(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): ensure_future(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): ensure_future(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): ensure_future(res) def get_channel_id(self): """ Implements :func:`autobahn.wamp.interfaces.ITransport.get_channel_id` """ self.log.debug('FIXME: transport channel binding not implemented for asyncio (autobahn-python issue #729)') return None def registerProducer(self, producer, streaming): raise Exception("not implemented")
def cancellable(self): def on_done(fut): self._cancelled.set_result(fut.cancelled()) fut = Future(loop=self._lsession.conn.loop) fut.add_done_callback(on_done) return fut
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ peer = None peer_transport = None def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() # the peer we are connected to try: self.peer = peer2str(transport.get_extra_info('peername')) except: self.peer = 'process:{}'.format(self.transport.pid) self.peer_transport = 'websocket' self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) # according to asyncio docs, connection_lost(None) is called # if something else called transport.close() if exc is not None: self.transport.close() self.transport = None def _consume(self): self.waiter = Future(loop=self.factory.loop or txaio.config.loop) def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) def _closeConnection(self, abort=False): if abort and hasattr(self.transport, 'abort'): self.transport.abort() else: self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): asyncio.ensure_future(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): asyncio.ensure_future(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): asyncio.ensure_future(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): asyncio.ensure_future(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): asyncio.ensure_future(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): asyncio.ensure_future(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): asyncio.ensure_future(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): asyncio.ensure_future(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): asyncio.ensure_future(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): asyncio.ensure_future(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): asyncio.ensure_future(res) def registerProducer(self, producer, streaming): raise Exception("not implemented") def unregisterProducer(self): # note that generic websocket/protocol.py code calls # .unregisterProducer whenever we dropConnection -- that's # correct behavior on Twisted so either we'd have to # try/except there, or special-case Twisted, ..or just make # this "not an error" pass
async def register_callbacks(future: asyncio.Future): print('registering callbacks on future') future.add_done_callback(functools.partial(callback, n=1)) future.add_done_callback(functools.partial(callback, n=2))
class Lock(object): def __init__(self, db, lock_name, timeout=5, expried=8, lock_id=None, max_count=1, reentrant_count=0): self._db = db self._db_id = db.id self._lock_name = lock_name self._lock_id = lock_id or self.generate() self._timeout = timeout self._expried = expried self._max_count = max_count self._reentrant_count = reentrant_count self._lock_future = None self._unlock_future = None def generate(self): return UniqId().to_bytes() def acquire(self, flag=0): if self._lock_future: raise LockIsLockingError(Result(b'\x56\x01' + b'\x00' * 62)) self._lock_future = Future() command = Command(Command.COMMAND_TYPE.LOCK, self._lock_id, self._db_id, self._lock_name, self._timeout, self._expried, flag, max(self._max_count - 1, 0), max(self._reentrant_count - 1, 0)) def finish(future): self._lock_future = None self._lock_future.add_done_callback(finish) self._db.command(self, command, self._lock_future) return self._lock_future def release(self, flag=0): if self._unlock_future: raise LockIsUnlockingError(Result(b'\x56\x01' + b'\x00' * 62)) self._unlock_future = Future() command = Command(Command.COMMAND_TYPE.UNLOCK, self._lock_id, self._db_id, self._lock_name, self._timeout, self._expried, flag, max(self._max_count - 1, 0), max(self._reentrant_count - 1, 0)) def finish(future): self._unlock_future = None self._unlock_future.add_done_callback(finish) self._db.command(self, command, self._unlock_future) return self._unlock_future def on_result(self, result): if result.command == Command.COMMAND_TYPE.LOCK: self.on_lock_result(result) elif result.command == Command.COMMAND_TYPE.UNLOCK: self.on_unlock_result(result) def on_lock_result(self, result): if result.result == RESULT_SUCCED: self._lock_future.set_result(result) else: if result.result in ERRORS: e = ERRORS[result.result](result) else: e = LockException(result) self._lock_future.set_exception(e) def on_unlock_result(self, result): if result.result == RESULT_SUCCED: self._unlock_future.set_result(result) else: if result.result in ERRORS: e = ERRORS[result.result](result) else: e = LockException(result) self._unlock_future.set_exception(e) async def __aenter__(self): await self.acquire() return self async def __aexit__(self, exc_type, exc_val, exc_tb): await self.release()
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() try: peer = transport.get_extra_info('peername') try: # FIXME: tcp4 vs tcp6 self.peer = "tcp:%s:%d" % (peer[0], peer[1]) except: # e.g. Unix Domain sockets don't have host/port self.peer = "unix:{0}".format(peer) except: self.peer = "?" self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) self.transport = None def _consume(self): self.waiter = Future() def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) else: print("WebSocketAdapterProtocol._consume: no transport") self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) # noinspection PyUnusedLocal def _closeConnection(self, abort=False): self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): asyncio.async(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): asyncio.async(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): asyncio.async(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): asyncio.async(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): asyncio.async(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): asyncio.async(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): asyncio.async(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): asyncio.async(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): asyncio.async(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): asyncio.async(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): asyncio.async(res) def registerProducer(self, producer, streaming): raise Exception("not implemented")
await asyncsleep(0.1) future_.set_result('\n\tFuture completed.') def got(future_): print(future_.result()) loop.stop() loop = get_event_loop() # get the current event loop future = Future() # promise of a result ensure_future( slow(future) ) # wrap a coroutine or an awaitable in a future (if the arg is a future, return it directly) future.add_done_callback(got) # add callback for Future try: loop.run_forever() finally: loop.close() async def speller(): for letter in '\tHI!': await asyncsleep(0.25) print(letter, end='') run(speller()) print()
def prepare(self, future: asyncio.Future, default: Any, pack_single: bool) -> Callable[..., None]: on_ready = functools.partial(self._on_ready, future=future, default=default, pack_single=pack_single) future.add_done_callback(lambda _future: self._ready_callbacks.discard(on_ready)) self._ready_callbacks.add(on_ready) return on_ready
class _Application(Tk): '''Ringmaster main application window.''' def __init__(self, parent=None): super().__init__(parent) # Internal. self._sub = self._req1 = self._rep1 = self._req2 = self._rep2 = None self._running = True self._toplevel = None # GUI. self._font = font.Font(family='Helvetica', size=12) # See [A1]. self._master = ttk.Frame(self, name='master', padding=(10, 10, 10, 10)) self._title = ttk.Label(self._master, text='Watchers', font='-size 16') self._frame = ttk.Frame(self._master, name='frame') self._button = ttk.Button(self._master, text='Quit') self._grid = self._frame.children self.createcommand('::tk::mac::Quit', self._quit) # See [A2]. self.resizable(False, False) self.minsize(320, 160) self.title(_TITLE) self._master.grid(row=0, column=0, sticky='NSEW') self._title.grid(row=0, column=0, pady=(0, 10)) self._frame.grid(row=1, column=0, sticky='EW') self._button.grid(row=2, column=0, sticky='EW', pady=(10, 0)) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self._master.columnconfigure(0, weight=1) self._frame.columnconfigure(0, weight=1) self._button.bind('<Button-1>', lambda x: self._quit()) @coroutine def setup(self): factory1 = lambda: _CircusSubProtocol(self) factory2 = lambda: _CircusDealerProtocol(self, '_rep1') factory3 = lambda: _CircusDealerProtocol(self, '_rep2') self._sub, __ = yield from create_zmq_connection(factory1, SUB) self._req1, _ = yield from create_zmq_connection(factory2, DEALER) self._req2, _ = yield from create_zmq_connection(factory3, DEALER) self._sub.subscribe(b'') self._sub.connect(_CIRCUS_STATS_ADDR) self._req1.setsockopt(LINGER, 0) self._req1.setsockopt(IDENTITY, uuid4().hex.encode()) self._req1.connect(_CIRCUS_CONTROL_ADDR) self._req2.setsockopt(LINGER, 0) self._req2.setsockopt(IDENTITY, uuid4().hex.encode()) self._req2.connect(_CIRCUS_CONTROL_ADDR) @coroutine def paint(self): set1 = set((yield from self._do_request('list')).get('watchers', [])) set2 = set() set3 = set() ttk.Style().map('TLabel', foreground=[('disabled', 'gray')]) # [A3]. ttk.Style().map('TButton', foreground=[('disabled', 'gray')]) # Pass one, classify watchers: all, singletons and forgotten. for watcher in set1: conf = yield from self._do_request('options', watcher) if conf['options']['singleton']: set2.add(watcher) if 'forget' in conf['options']: set3.update(conf['options']['forget'].split()) # Pass two, assemble the watcher grid. for i, name in enumerate(sorted(set1 - set3)): lb1 = ttk.Label(self._frame, name=name + '+l', text=_DOT(name)) lb2 = ttk.Label(self._frame, name=name + '+c1', text='–') lb1.grid(row=i, column=0, sticky='EW') lb2.grid(row=i, column=1) lb2.config(anchor='center', width=25, foreground='grey') if name in set2: bt1 = ttk.Button(self._frame, name=name + '+c2', text='Start') bt2 = ttk.Button(self._frame, name=name + '+r', text=' +') bt1.grid(row=i, column=2, columnspan=2, sticky='EW') bt2.grid(row=i, column=4) bt1.bind('<Button-1>', partial(self._start_watcher, name)) bt2.config(state='disabled', width=3) else: bt1 = ttk.Button(self._frame, name=name + '+c2', text='Incr') bt2 = ttk.Button(self._frame, name=name + '+c3', text='Decr') bt3 = ttk.Button(self._frame, name=name + '+r', text=' +') bt1.grid(row=i, column=2) bt2.grid(row=i, column=3) bt3.grid(row=i, column=4) bt1.bind('<Button-1>', partial(self._incr_process, name)) bt2.config(state='disabled') bt3.config(state='disabled', width=3) # Put metadata in label one. lb1._w_procs = [] lb1._w_state = 'stopped' lb1._w_singleton = name in set2 # Pass three, continuously update watcher state. while self._running: for name in set1 - set3: state = yield from self._do_request('status', name) stats = yield from self._do_request('stats', name) procs = list(stats.get('info', {}).keys()) label = self._grid[name + '+l'] label._w_state = state if sorted(label._w_procs) != sorted(procs): # See [LEAK2]. label._w_procs = [int(x) for x in procs] self._update_watcher_state_a(name) yield from sleep(0.5) # Taken from "http://www.reddit.com/r/Python/comments/33ecpl". # # For a threaded approach, see the ActiveState recipe mentioned by Guido # van Rossum at the "Tkinter-discuss" list ("http://goo.gl/VJI1oJ"). # @coroutine def mainloop(self, interval=0.05): '''Run a tkinter app in an asyncio event loop.''' try: while self._running: self.update() yield from sleep(interval) except TclError as e: if 'application has been destroyed' not in e.args[0]: raise # This method: # # 1. Is a standard coroutine. Cannot run concurrently, otherwise a # second request may happen before a reply arrives. It is called # only from "_paint, so concurrency is not an issue. But be # aware when calling this method from other methods or functions. # # 2. Writes to socket "_req1". This is a REQ/REP socket that will only # receive monitoring commands. # # 3. Does not return error messages. Monitoring commands are passive, # automatically issued by the program. A fail can simply be discarded, # and the GUI updates that would arise from it, bypassed. A common # error is caused by the process stopping while Circus is executing a # command on it, like "stats". # @coroutine def _do_request(self, action, name=''): query = {'id': uuid4().hex, 'command': action} reply = {} if name: query['properties'] = {'name': name} self._rep1 = Future() self._req1.write([dumps(query).encode()]) reply = yield from self._rep1 if query['id'] == reply['id'] and action == 'status': return reply['status'] elif query['id'] == reply['id'] and reply['status'] == 'ok': return reply elif query['id'] == reply['id']: return {} else: raise Exception() # This method: # # 1. Is a coroutine, mostly because it needs to block to prevent that # more than one request is written on the REQ/REP socket before a # reply arrives. It can run concurrently, and often will, since it # will be spawned from event handlers attached to buttons and other # GUI widgets. That is also why it adopts a callback approach to # provide a reply. Tkinter event handlers cannot yield on the result # of a coroutine. # # 2. Writes to socker "_req2". This socket should be used to send # management commands to wachers, like stop, increment a process etc. # # 3. Returns error messages. Here, actions sent to Circus come from # direct interaction with the GUI, and errors messages are expected. # @coroutine def _on_reply(self, action, name, on_reply_ok, on_reply_error): def callback(future): reply = future.result() if query['id'] == reply['id'] and reply['status'] == 'ok': on_reply_ok(reply) elif query['id'] == reply['id']: on_reply_error(reply['reason'].capitalize() + '.') else: raise Exception() query = {'id': uuid4().hex, 'command': action} query['properties'] = {'name': name} if action == 'incr' or action == 'decr': query['properties'].update({'waiting': False, 'nb': 1}) elif action == 'start' or action == 'stop': query['properties'].update({'waiting': False, 'match': 'glob'}) # Block to prevent writing multiple requests before reading a reply. if self._rep2: yield from self._rep2 self._rep2 = Future() self._rep2.add_done_callback(callback) self._req2.write([dumps(query).encode()]) def _update_watcher_state_a(self, name): # See [LEAK]. lb1 = self._grid[name + '+l'] lb2 = self._grid[name + '+c1'] if lb1._w_singleton: bt1 = self._grid[name + '+c2'] bt2 = self._grid[name + '+r'] if lb1._w_procs: lb2.config(foreground='darkgreen') bt1.config(text='Stop') bt2.config(state='normal') bt1.bind('<Button-1>', partial(self._stop_watcher, name)) bt2.bind('<Button-1>', partial(self._more_watcher, name)) else: lb2.config(foreground='grey', text='–') bt1.config(text='Start') bt2.config(state='disabled') bt1.bind('<Button-1>', partial(self._start_watcher, name)) bt2.unbind('<Button-1>') else: bt1 = self._grid[name + '+c2'] bt2 = self._grid[name + '+c3'] bt3 = self._grid[name + '+r'] if lb1._w_procs: lb2.config(foreground='darkgreen') bt2.config(state='normal') bt3.config(state='normal') bt1.bind('<Button-1>', partial(self._incr_process, name)) bt2.bind('<Button-1>', partial(self._decr_process, name)) bt3.bind('<Button-1>', partial(self._more_watcher, name)) else: lb2.config(foreground='grey', text='–') bt2.config(state='disabled') bt3.config(state='disabled') bt1.bind('<Button-1>', partial(self._incr_process, name)) bt2.unbind('<Button-1>') bt3.unbind('<Button-1>') def _update_watcher_state_b(self, watcher, stats): # See [LEAK]. if watcher + '+l' in self._grid and 'pid' in stats: lb1 = self._grid[watcher + '+l'] lb1._w_procs = [int(x) for x in stats['pid']] if stats['pid']: lb2 = self._frame.children[watcher + '+c1'] tpl = '{}: {cpu:.1%} cpu, {mem:.1%} mem' if stats['cpu'] == 'N/A': stats['cpu'] = 0 else: stats['cpu'] /= 100.0 if stats['mem'] == 'N/A': stats['mem'] = 0 else: stats['mem'] /= 100.0 # http://docs.python.org/3/library/string.html#format-examples. lb2.config(text=tpl.format(len(stats['pid']), **stats)) if self._toplevel: self._toplevel._painter.send(None) def _start_watcher(self, name, event): def ok(reply): lbl._w_procs.append(0) # Request actual PIDs here (FIXME). self._update_watcher_state_a(name) def error(message): btn.bind('<Button-1>', partial(self._start_watcher, name)) messagebox.showerror('Error', message + '.') lbl = self._grid[name + '+l'] btn = self._grid[name + '+c2'] btn.unbind('<Button-1>') async(self._on_reply('start', name, ok, error)) def _stop_watcher(self, name, event): def ok(reply): lbl._w_procs.pop() # Request actual PIDs here (FIXME). self._update_watcher_state_a(name) def error(message): btn.bind('<Button-1>', partial(self._start_watcher, name)) messagebox.showerror('Error', message + '.') lbl = self._frame.children[name + '+l'] btn = self._frame.children[name + '+c2'] btn.unbind('<Button-1>') async(self._on_reply('stop', name, ok, error)) def _more_watcher(self, name, event): self._toplevel = _Dialog(self, name) def _incr_process(self, watcher, event): def ok1(reply): lbl._w_procs.append(0) # Request actual PIDs here (FIXME). lbl._w_state = 'active' self._update_watcher_state_a(watcher) def ok2(reply): lbl._w_procs.append(0) # Request actual PIDs here (FIXME). self._update_watcher_state_a(watcher) def error(message): btn.bind('<Button-1>', partial(self._incr_process, watcher)) messagebox.showerror('Error', message + '.') lbl = self._grid[watcher + '+l'] btn = self._grid[watcher + '+c2'] btn.unbind('<Button-1>') if lbl._w_state == 'stopped': async(self._on_reply('start', watcher, ok1, error)) else: async(self._on_reply('incr', watcher, ok2, error)) def _decr_process(self, watcher, event): def ok(reply): lbl._w_procs.pop() # Request actual PIDs here (FIXME). self._update_watcher_state_a(watcher) def error(message): btn.bind('<Button-1>', partial(self._decr_process, watcher)) messagebox.showerror('Error', message + '.') lbl = self._grid[watcher + '+l'] btn = self._grid[watcher + '+c3'] btn.unbind('<Button-1>') async(self._on_reply('decr', watcher, ok, error)) def _quit(self): self._running = False
def _add_cleanup(self, task: asyncio.Future) -> None: self._cleanups.add(task) task.add_done_callback(log_exception) task.add_done_callback(self._cleanups.discard)
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() try: peer = transport.get_extra_info('peername') try: # FIXME: tcp4 vs tcp6 self.peer = "tcp:%s:%d" % (peer[0], peer[1]) except: # e.g. Unix Domain sockets don't have host/port self.peer = "unix:{0}".format(peer) except: self.peer = "?" self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) self.transport = None def _consume(self): self.waiter = Future() def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) else: print("WebSocketAdapterProtocol._consume: no transport") self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) # noinspection PyUnusedLocal def _closeConnection(self, abort=False): self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): asyncio. async (res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): asyncio. async (res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): asyncio. async (res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): asyncio. async (res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): asyncio. async (res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): asyncio. async (res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): asyncio. async (res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): asyncio. async (res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): asyncio. async (res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): asyncio. async (res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): asyncio. async (res) def registerProducer(self, producer, streaming): raise Exception("not implemented")
class WebSocketAdapterProtocol(asyncio.Protocol): """ Adapter class for asyncio-based WebSocket client and server protocols. """ def connection_made(self, transport): self.transport = transport self.receive_queue = deque() self._consume() try: self.peer = peer2str(transport.get_extra_info('peername')) except: self.peer = u"?" self._connectionMade() def connection_lost(self, exc): self._connectionLost(exc) # according to asyncio docs, connection_lost(None) is called # if something else called transport.close() if exc is not None: self.transport.close() self.transport = None def _consume(self): self.waiter = Future(loop=self.factory.loop or txaio.config.loop) def process(_): while len(self.receive_queue): data = self.receive_queue.popleft() if self.transport: self._dataReceived(data) self._consume() self.waiter.add_done_callback(process) def data_received(self, data): self.receive_queue.append(data) if not self.waiter.done(): self.waiter.set_result(None) def _closeConnection(self, abort=False): if abort and hasattr(self.transport, 'abort'): self.transport.abort() else: self.transport.close() def _onOpen(self): res = self.onOpen() if yields(res): ensure_future(res) def _onMessageBegin(self, isBinary): res = self.onMessageBegin(isBinary) if yields(res): ensure_future(res) def _onMessageFrameBegin(self, length): res = self.onMessageFrameBegin(length) if yields(res): ensure_future(res) def _onMessageFrameData(self, payload): res = self.onMessageFrameData(payload) if yields(res): ensure_future(res) def _onMessageFrameEnd(self): res = self.onMessageFrameEnd() if yields(res): ensure_future(res) def _onMessageFrame(self, payload): res = self.onMessageFrame(payload) if yields(res): ensure_future(res) def _onMessageEnd(self): res = self.onMessageEnd() if yields(res): ensure_future(res) def _onMessage(self, payload, isBinary): res = self.onMessage(payload, isBinary) if yields(res): ensure_future(res) def _onPing(self, payload): res = self.onPing(payload) if yields(res): ensure_future(res) def _onPong(self, payload): res = self.onPong(payload) if yields(res): ensure_future(res) def _onClose(self, wasClean, code, reason): res = self.onClose(wasClean, code, reason) if yields(res): ensure_future(res) def registerProducer(self, producer, streaming): raise Exception("not implemented") def unregisterProducer(self): # note that generic websocket/protocol.py code calls # .unregisterProducer whenever we dropConnection -- that's # correct behavior on Twisted so either we'd have to # try/except there, or special-case Twisted, ..or just make # this "not an error" pass