Esempio n. 1
0
async def _call_later(
    timeout: float,
    callback: Callable,
    task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
) -> None:
    cancel_scope = trio.CancelScope()
    task_status.started(cancel_scope)
    with cancel_scope:
        await trio.sleep(timeout)
        cancel_scope.shield = True
        await callback()
Esempio n. 2
0
async def worker_serve(
    app: Type[ASGIFramework],
    config: Config,
    *,
    sockets: Optional[List[socket]] = None,
    shutdown_event: Optional[EventType] = None,
    task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
) -> None:
    lifespan = Lifespan(app, config)
    reload_ = False

    async with trio.open_nursery() as lifespan_nursery:
        await lifespan_nursery.start(lifespan.handle_lifespan)
        await lifespan.wait_for_startup()

        try:
            async with trio.open_nursery() as nursery:
                if config.use_reloader:
                    nursery.start_soon(observe_changes, trio.sleep)

                if shutdown_event is not None:
                    nursery.start_soon(check_shutdown, shutdown_event,
                                       trio.sleep)

                if sockets is None:
                    sockets = config.create_sockets()
                    for sock in sockets:
                        sock.listen(config.backlog)
                listeners = [
                    trio.SocketListener(trio.socket.from_stdlib_socket(sock))
                    for sock in sockets
                ]
                if config.ssl_enabled:
                    listeners = [
                        trio.ssl.SSLListener(tcp_listener,
                                             config.create_ssl_context(),
                                             https_compatible=True)
                        for tcp_listener in listeners
                    ]

                task_status.started()
                await trio.serve_listeners(partial(serve_stream, app, config),
                                           listeners)

        except MustReloadException:
            reload_ = True
        except (Shutdown, KeyboardInterrupt):
            pass
        finally:
            await lifespan.wait_for_shutdown()
            lifespan_nursery.cancel_scope.cancel()

    if reload_:
        restart()
Esempio n. 3
0
    async def run(
        self, task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED
    ) -> None:
        from ..protocol.quic import QuicProtocol  # h3/Quic is an optional part of Hypercorn

        task_status.started()
        server = parse_socket_addr(self.socket.family, self.socket.getsockname())
        async with TaskGroup() as task_group:
            self.protocol = QuicProtocol(
                self.app, self.config, self.context, task_group, server, self.protocol_send
            )

            while not self.context.terminated or not self.protocol.idle:
                data, address = await self.socket.recvfrom(MAX_RECV)
                await self.protocol.handle(RawData(data=data, address=address))
Esempio n. 4
0
 async def handle_lifespan(
     self, *, task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED
 ) -> None:
     task_status.started()
     scope = {"type": "lifespan", "asgi": {"spec_version": "2.0"}}
     try:
         await invoke_asgi(self.app, scope, self.asgi_receive, self.asgi_send)
     except LifespanFailure:
         # Lifespan failures should crash the server
         raise
     except Exception:
         self.supported = False
         await self.config.log.exception(
             "ASGI Framework Lifespan error, continuing without Lifespan support"
         )
     finally:
         await self.app_send_channel.aclose()
         await self.app_receive_channel.aclose()
Esempio n. 5
0
async def worker_serve(
    app: ASGIFramework,
    config: Config,
    *,
    sockets: Optional[Sockets] = None,
    shutdown_trigger: Optional[Callable[..., Awaitable[None]]] = None,
    task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
) -> None:
    config.set_statsd_logger_class(StatsdLogger)

    lifespan = Lifespan(app, config)
    reload_ = False

    async with trio.open_nursery() as lifespan_nursery:
        await lifespan_nursery.start(lifespan.handle_lifespan)
        await lifespan.wait_for_startup()

        try:
            async with trio.open_nursery() as nursery:
                if config.use_reloader:
                    nursery.start_soon(observe_changes, trio.sleep)

                if shutdown_trigger is not None:
                    nursery.start_soon(raise_shutdown, shutdown_trigger)

                if sockets is None:
                    sockets = config.create_sockets()
                    for sock in sockets.secure_sockets:
                        sock.listen(config.backlog)
                    for sock in sockets.insecure_sockets:
                        sock.listen(config.backlog)

                ssl_context = config.create_ssl_context()
                listeners = []
                binds = []
                for sock in sockets.secure_sockets:
                    listeners.append(
                        trio.SSLListener(
                            trio.SocketListener(
                                trio.socket.from_stdlib_socket(sock)),
                            ssl_context,
                            https_compatible=True,
                        ))
                    bind = repr_socket_addr(sock.family, sock.getsockname())
                    binds.append(f"https://{bind}")
                    await config.log.info(
                        f"Running on https://{bind} (CTRL + C to quit)")

                for sock in sockets.insecure_sockets:
                    listeners.append(
                        trio.SocketListener(
                            trio.socket.from_stdlib_socket(sock)))
                    bind = repr_socket_addr(sock.family, sock.getsockname())
                    binds.append(f"http://{bind}")
                    await config.log.info(
                        f"Running on http://{bind} (CTRL + C to quit)")

                for sock in sockets.quic_sockets:
                    await nursery.start(
                        UDPServer(app, config, sock, nursery).run)
                    bind = repr_socket_addr(sock.family, sock.getsockname())
                    await config.log.info(
                        f"Running on https://{bind} (QUIC) (CTRL + C to quit)")

                task_status.started(binds)
                await trio.serve_listeners(partial(TCPServer, app, config),
                                           listeners,
                                           handler_nursery=lifespan_nursery)

        except MustReloadException:
            reload_ = True
        except (Shutdown, KeyboardInterrupt):
            pass
        finally:
            try:
                await trio.sleep(config.graceful_timeout)
            except (Shutdown, KeyboardInterrupt):
                pass

            await lifespan.wait_for_shutdown()
            lifespan_nursery.cancel_scope.cancel()

    if reload_:
        restart()
Esempio n. 6
0
async def update_quotes(
    nursery: trio._core._run.Nursery,
    formatter: Callable,
    widgets: dict,
    agen: AsyncGeneratorType,
    symbol_data: dict,
    first_quotes: dict,
    task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
):
    """Process live quotes by updating ticker rows.
    """
    log.debug("Initializing UI update loop")
    table = widgets['table']
    flash_keys = {'low', 'high'}

    async def revert_cells_color(cells):
        await trio.sleep(0.3)
        for cell in cells:
            cell.background_color = _black_rgba

    def color_row(row, record, cells):
        hdrcell = row.get_cell('symbol')
        chngcell = row.get_cell('%')

        # determine daily change color
        percent_change = record.get('%')
        if percent_change is not None and percent_change != chngcell:
            daychange = float(percent_change)
            if daychange < 0.:
                color = colorcode('red2')
            elif daychange > 0.:
                color = colorcode('forestgreen')
            else:
                color = colorcode('gray')

            # if the cell has been "highlighted" make sure to change its color
            if hdrcell.background_color != [0]*4:
                hdrcell.background_color = color

            # update row header and '%' cell text color
            chngcell.color = color
            hdrcell.color = color

        # briefly highlight bg of certain cells on each trade execution
        unflash = set()
        tick_color = None
        last = cells.get('last')
        if not last:
            vol = cells.get('volume')
            if not vol:
                return  # no trade exec took place

            # flash gray on volume tick
            # (means trade exec @ current price)
            last = row.get_cell('last')
            tick_color = colorcode('gray')
        else:
            tick_color = last.color

        last.background_color = tick_color
        unflash.add(last)
        # flash the size cell
        size = row.get_cell('size')
        size.background_color = tick_color
        unflash.add(size)

        # flash all other cells
        for key in flash_keys:
            cell = cells.get(key)
            if cell:
                cell.background_color = cell.color
                unflash.add(cell)

        # revert flash state momentarily
        nursery.start_soon(revert_cells_color, unflash)

    # initial coloring
    to_sort = set()
    for quote in first_quotes:
        row = table.get_row(quote['symbol'])
        row.update(quote)
        color_row(row, quote, {})
        to_sort.add(row.widget)

    table.render_rows(to_sort)

    log.debug("Finished initializing update loop")
    task_status.started()

    # real-time cell update loop
    async for quotes in agen:  # new quotes data only
        to_sort = set()
        for symbol, quote in quotes.items():
            row = table.get_row(symbol)

            # don't red/green the header cell in ``row.update()``
            quote.pop('symbol')
            quote.pop('key')

            # determine if sorting should happen
            sort_key = table.sort_key
            last = row.get_field(sort_key)
            new = quote.get(sort_key, last)
            if new != last:
                to_sort.add(row.widget)

            # update and color
            cells = row.update(quote)
            color_row(row, quote, cells)

        if to_sort:
            table.render_rows(to_sort)

        log.debug("Waiting on quotes")

    log.warn("Data feed connection dropped")
Esempio n. 7
0
    async def _async_main(
        self,
        accept_addr: Tuple[str, int],
        arbiter_addr: Optional[Tuple[str, int]] = None,
        parent_addr: Optional[Tuple[str, int]] = None,
        task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
    ) -> None:
        """Start the channel server, maybe connect back to the parent, and
        start the main task.

        A "root-most" (or "top-level") nursery for this actor is opened here
        and when cancelled effectively cancels the actor.
        """
        arbiter_addr = arbiter_addr or self._arb_addr
        registered_with_arbiter = False
        try:
            async with trio.open_nursery() as nursery:
                self._root_nursery = nursery

                # Startup up channel server
                host, port = accept_addr
                await nursery.start(
                    partial(self._serve_forever,
                            accept_host=host,
                            accept_port=port))

                if parent_addr is not None:
                    try:
                        # Connect back to the parent actor and conduct initial
                        # handshake (From this point on if we error, ship the
                        # exception back to the parent actor)
                        chan = self._parent_chan = Channel(
                            destaddr=parent_addr, )
                        await chan.connect()
                        # initial handshake, report who we are, who they are
                        await _do_handshake(self, chan)
                    except OSError:  # failed to connect
                        log.warning(
                            f"Failed to connect to parent @ {parent_addr},"
                            " closing server")
                        await self.cancel()
                        self._parent_chan = None

                    # handle new connection back to parent
                    nursery.start_soon(self._process_messages,
                                       self._parent_chan)

                # load exposed/allowed RPC modules
                # XXX: do this **after** establishing connection to parent
                # so that import errors are properly propagated upwards
                self.load_modules()

                # register with the arbiter if we're told its addr
                log.debug(f"Registering {self} for role `{self.name}`")
                async with get_arbiter(*arbiter_addr) as arb_portal:
                    await arb_portal.run('self',
                                         'register_actor',
                                         uid=self.uid,
                                         sockaddr=self.accept_addr)
                    registered_with_arbiter = True

                task_status.started()
                log.debug("Waiting on root nursery to complete")

            # blocks here as expected until the channel server is
            # killed (i.e. this actor is cancelled or signalled by the parent)
        except Exception as err:
            if not registered_with_arbiter:
                log.exception(
                    f"Actor errored and failed to register with arbiter "
                    f"@ {arbiter_addr}")

            if self._parent_chan:
                try:
                    # internal error so ship to parent without cid
                    await self._parent_chan.send(pack_error(err))
                except trio.ClosedResourceError:
                    log.error(f"Failed to ship error to parent "
                              f"{self._parent_chan.uid}, channel was closed")
                    log.exception("Actor errored:")
            else:
                # XXX wait, why?
                # causes a hang if I always raise..
                raise

        finally:
            if registered_with_arbiter:
                await self._do_unreg(arbiter_addr)
            # terminate actor once all it's peers (actors that connected
            # to it as clients) have disappeared
            if not self._no_more_peers.is_set():
                if any(chan.connected()
                       for chan in chain(*self._peers.values())):
                    log.debug(
                        f"Waiting for remaining peers {self._peers} to clear")
                    await self._no_more_peers.wait()
            log.debug(f"All peer channels are complete")

            # tear down channel server no matter what since we errored
            # or completed
            self.cancel_server()
Esempio n. 8
0
async def worker_serve(
    app: ASGIFramework,
    config: Config,
    *,
    sockets: Optional[Sockets] = None,
    shutdown_event: Optional[EventType] = None,
    task_status: trio._core._run._TaskStatus = trio.TASK_STATUS_IGNORED,
) -> None:
    lifespan = Lifespan(app, config)
    reload_ = False

    async with trio.open_nursery() as lifespan_nursery:
        await lifespan_nursery.start(lifespan.handle_lifespan)
        await lifespan.wait_for_startup()

        try:
            async with trio.open_nursery() as nursery:
                metrics_send_channel, metrics_receive_channel = trio.open_memory_channel(
                    100)
                metrics = {'total_requests': 0}
                async with metrics_receive_channel, metrics_send_channel:
                    nursery.start_soon(collect_metrics,
                                       metrics_receive_channel.clone(),
                                       metrics)
                    nursery.start_soon(log_metrics, metrics)

                    if config.use_reloader:
                        nursery.start_soon(observe_changes, trio.sleep)

                    if shutdown_event is not None:
                        nursery.start_soon(check_shutdown, shutdown_event,
                                           trio.sleep)

                    if sockets is None:
                        sockets = config.create_sockets()
                        for sock in sockets.secure_sockets:
                            sock.listen(config.backlog)
                        for sock in sockets.insecure_sockets:
                            sock.listen(config.backlog)

                    ssl_context = config.create_ssl_context()
                    listeners = [
                        trio.ssl.SSLListener(
                            trio.SocketListener(
                                trio.socket.from_stdlib_socket(sock)),
                            ssl_context,
                            https_compatible=True,
                        ) for sock in sockets.secure_sockets
                    ]
                    listeners.extend([
                        trio.SocketListener(
                            trio.socket.from_stdlib_socket(sock))
                        for sock in sockets.insecure_sockets
                    ])
                    task_status.started()
                    await trio.serve_listeners(
                        partial(serve_stream, app, config,
                                metrics_send_channel), listeners)

        except MustReloadException:
            reload_ = True
        except (Shutdown, KeyboardInterrupt):
            pass
        finally:
            await lifespan.wait_for_shutdown()
            lifespan_nursery.cancel_scope.cancel()

    if reload_:
        restart()