Exemplo n.º 1
0
    async def spawn_opts_chain():
        """Spawn an options chain UI in a new subactor.
        """
        from .option_chain import _async_main

        try:
            async with tractor.open_nursery() as tn:
                portal = await tn.run_in_actor(
                    'optschain',
                    _async_main,
                    symbol=table.last_clicked_row._last_record['symbol'],
                    brokername=brokermod.name,
                    loglevel=tractor.log.get_loglevel(),
                )
        except tractor.RemoteActorError:
            # don't allow option chain errors to crash this monitor
            # this is, like, the most basic of resliency policies
            log.exception(f"{portal.actor.name} crashed:")
Exemplo n.º 2
0
async def maybe_spawn_brokerd(
    brokername: str,
    sleep: float = 0.5,
    loglevel: Optional[str] = None,
    expose_mods: List = [],
    **tractor_kwargs,
) -> tractor._portal.Portal:
    """If no ``brokerd.{brokername}`` daemon-actor can be found,
    spawn one in a local subactor and return a portal to it.
    """
    if loglevel:
        get_console_log(loglevel)

    # disable debugger in brokerd?
    # tractor._state._runtime_vars['_debug_mode'] = False

    tractor_kwargs['loglevel'] = loglevel

    brokermod = get_brokermod(brokername)
    dname = f'brokerd.{brokername}'
    async with tractor.find_actor(dname) as portal:

        # WTF: why doesn't this work?
        if portal is not None:
            yield portal

        else:  # no daemon has been spawned yet

            log.info(f"Spawning {brokername} broker daemon")
            tractor_kwargs = getattr(brokermod, '_spawn_kwargs', {})
            async with tractor.open_nursery() as nursery:
                try:
                    # spawn new daemon
                    portal = await nursery.start_actor(
                        dname,
                        enable_modules=_data_mods + [brokermod.__name__],
                        loglevel=loglevel,
                        **tractor_kwargs)
                    async with tractor.wait_for_actor(dname) as portal:
                        yield portal
                finally:
                    # client code may block indefinitely so cancel when
                    # teardown is invoked
                    await nursery.cancel()
Exemplo n.º 3
0
async def spawn_and_error(breadth, depth) -> None:
    name = tractor.current_actor().name
    async with tractor.open_nursery() as nursery:
        for i in range(breadth):

            if depth > 0:

                args = (spawn_and_error, )
                kwargs = {
                    'name': f'spawner_{i}_depth_{depth}',
                    'breadth': breadth,
                    'depth': depth - 1,
                }
            else:
                args = (assert_err, )
                kwargs = {
                    'name': f'{name}_errorer_{i}',
                }
            await nursery.run_in_actor(*args, **kwargs)
Exemplo n.º 4
0
async def test_cancel_infinite_streamer(start_method):

    # stream for at most 1 seconds
    with trio.move_on_after(1) as cancel_scope:
        async with tractor.open_nursery() as n:
            portal = await n.start_actor(
                'donny',
                enable_modules=[__name__],
            )

            # this async for loop streams values from the above
            # async generator running in a separate process
            async with portal.open_stream_from(stream_forever) as stream:
                async for letter in stream:
                    print(letter)

    # we support trio's cancellation system
    assert cancel_scope.cancelled_caught
    assert n.cancelled
Exemplo n.º 5
0
async def test_movie_theatre_convo(start_method):
    """The main ``tractor`` routine.
    """
    async with tractor.open_nursery() as n:

        portal = await n.start_actor(
            'frank',
            # enable the actor to run funcs from this current module
            rpc_module_paths=[__name__],
        )

        print(await portal.run(__name__, 'movie_theatre_question'))
        # call the subactor a 2nd time
        print(await portal.run(__name__, 'movie_theatre_question'))

        # the async with will block here indefinitely waiting
        # for our actor "frank" to complete, we cancel 'frank'
        # to avoid blocking indefinitely
        await portal.cancel_actor()
Exemplo n.º 6
0
async def test_nested_multierrors(loglevel, start_method):
    """Test that failed actor sets are wrapped in `trio.MultiError`s.
    This test goes only 2 nurseries deep but we should eventually have tests
    for arbitrary n-depth actor trees.
    """
    if start_method == 'trio_run_in_process':
        depth = 3
        subactor_breadth = 2
    else:
        # XXX: multiprocessing can't seem to handle any more then 2 depth
        # process trees for whatever reason.
        # Any more process levels then this and we see bugs that cause
        # hangs and broken pipes all over the place...
        if start_method == 'forkserver':
            pytest.skip("Forksever sux hard at nested spawning...")
        depth = 2
        subactor_breadth = 2

    with trio.fail_after(120):
        try:
            async with tractor.open_nursery() as nursery:
                for i in range(subactor_breadth):
                    await nursery.run_in_actor(
                        f'spawner_{i}',
                        spawn_and_error,
                        breadth=subactor_breadth,
                        depth=depth,
                    )
        except trio.MultiError as err:
            assert len(err.exceptions) == subactor_breadth
            for subexc in err.exceptions:
                assert isinstance(subexc, tractor.RemoteActorError)
                if depth > 1 and subactor_breadth > 1:

                    # XXX not sure what's up with this..
                    if platform.system() == 'Windows':
                        assert (subexc.type is trio.MultiError) or (
                            subexc.type is tractor.RemoteActorError)
                    else:
                        assert subexc.type is trio.MultiError
                else:
                    assert (subexc.type is tractor.RemoteActorError) or (
                        subexc.type is trio.Cancelled)
Exemplo n.º 7
0
async def main():
    """The main ``tractor`` routine.
    """
    async with tractor.open_nursery() as n:

        portal = await n.start_actor(
            'frank',
            # enable the actor to run funcs from this current module
            rpc_module_paths=[__name__],
        )

        print(await portal.run(__name__, 'movie_theatre_question'))
        # call the subactor a 2nd time
        print(await portal.run(__name__, 'movie_theatre_question'))

        # the async with will block here indefinitely waiting
        # for our actor "frank" to complete, but since it's an
        # "outlive_main" actor it will never end until cancelled
        await portal.cancel_actor()
Exemplo n.º 8
0
async def worker_pool(workers=4):
    """Though it's a trivial special case for ``tractor``, the well
    known "worker pool" seems to be the defacto "but, I want this
    process pattern!" for most parallelism pilgrims.

    Yes, the workers stay alive (and ready for work) until you close
    the context.
    """
    async with tractor.open_nursery() as tn:

        portals = []
        snd_chan, recv_chan = trio.open_memory_channel(len(PRIMES))

        for i in range(workers):

            # this starts a new sub-actor (process + trio runtime) and
            # stores it's "portal" for later use to "submit jobs" (ugh).
            portals.append(await tn.start_actor(
                f'worker_{i}',
                enable_modules=[__name__],
            ))

        async def _map(worker_func: Callable[[int], bool],
                       sequence: List[int]) -> List[bool]:

            # define an async (local) task to collect results from workers
            async def send_result(func, value, portal):
                await snd_chan.send((value, await portal.run(func, n=value)))

            async with trio.open_nursery() as n:

                for value, portal in zip(sequence, itertools.cycle(portals)):
                    n.start_soon(send_result, worker_func, value, portal)

                # deliver results as they arrive
                for _ in range(len(sequence)):
                    yield await recv_chan.receive()

        # deliver the parallel "worker mapper" to user code
        yield _map

        # tear down all "workers" on pool close
        await tn.cancel()
Exemplo n.º 9
0
async def test_trynamic_trio(func, start_method):
    """Main tractor entry point, the "master" process (for now
    acts as the "director").
    """
    async with tractor.open_nursery() as n:
        print("Alright... Action!")

        donny = await n.run_in_actor(
            'donny',
            func,
            other_actor='gretchen',
        )
        gretchen = await n.run_in_actor(
            'gretchen',
            func,
            other_actor='donny',
        )
        print(await gretchen.result())
        print(await donny.result())
        print("CUTTTT CUUTT CUT!!?! Donny!! You're supposed to say...")
Exemplo n.º 10
0
    async def main():
        async with tractor.open_nursery() as n:
            p = await n.start_actor(
                'aio_server',
                enable_modules=[__name__],
                infect_asyncio=True,
            )
            async with p.open_context(
                trio_to_aio_echo_server,
            ) as (ctx, first):

                assert first == 'start'

                async with ctx.open_stream() as stream:
                    for i in range(100):
                        await stream.send(i)
                        out = await stream.receive()
                        assert i == out

                        if raise_error_mid_stream and i == 50:
                            raise raise_error_mid_stream

                    # send terminate msg
                    await stream.send(None)
                    out = await stream.receive()
                    assert out is None

                    if out is None:
                        # ensure the stream is stopped
                        # with trio.fail_after(0.1):
                        try:
                            await stream.receive()
                        except trio.EndOfChannel:
                            pass
                        else:
                            pytest.fail(
                                "stream wasn't stopped after sentinel?!")

            # TODO: the case where this blocks and
            # is cancelled by kbi or out of task cancellation
            await p.cancel_actor()
Exemplo n.º 11
0
async def main():
    """Main tractor entry point, the "master" process (for now
    acts as the "director").
    """
    async with tractor.open_nursery() as n:
        print("Alright... Action!")

        donny = await n.run_in_actor(
            say_hello,
            name='donny',
            # arguments are always named
            other_actor='gretchen',
        )
        gretchen = await n.run_in_actor(
            say_hello,
            name='gretchen',
            other_actor='donny',
        )
        print(await gretchen.result())
        print(await donny.result())
        print("CUTTTT CUUTT CUT!!! Donny!! You're supposed to say...")
Exemplo n.º 12
0
    async def main():
        with trio.fail_after(timeout):
            async with tractor.open_nursery() as n:
                # name of this actor will be same as target func
                portal = await n.start_actor(
                    '2_way',
                    enable_modules=[__name__]
                )

                async with portal.open_context(echo_ctx_stream) as (ctx, _):
                    async with ctx.open_stream() as stream:
                        async with portal.open_stream_from(
                            async_gen_stream,
                            sequence=list(range(1)),
                        ) as gen_stream:

                            msg = await gen_stream.receive()
                            await stream.send(msg)
                            resp = await stream.receive()
                            assert resp == msg
                            raise KeyboardInterrupt
Exemplo n.º 13
0
async def main():
    """The main ``tractor`` routine.

    The process tree should look as approximately as follows:

    -python examples/debugging/multi_subactors.py
    |-python -m tractor._child --uid ('name_error', 'a7caf490 ...)
    |-python -m tractor._child --uid ('bp_forever', '1f787a7e ...)
    `-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
       `-python -m tractor._child --uid ('name_error', '3391222c ...)
    """
    async with tractor.open_nursery(
        debug_mode=True,
    ) as n:

        # Spawn both actors, don't bother with collecting results
        # (would result in a different debugger outcome due to parent's
        # cancellation).
        await n.run_in_actor(breakpoint_forever)
        await n.run_in_actor(name_error)
        await n.run_in_actor(spawn_error)
Exemplo n.º 14
0
async def spawn(
    is_arbiter: bool,
    data: Dict,
    arb_addr: Tuple[str, int],
):
    namespaces = [__name__]

    await trio.sleep(0.1)

    async with tractor.open_root_actor(
        arbiter_addr=arb_addr,
    ):

        actor = tractor.current_actor()
        assert actor.is_arbiter == is_arbiter
        data = data_to_pass_down

        if actor.is_arbiter:

            async with tractor.open_nursery(
            ) as nursery:

                # forks here
                portal = await nursery.run_in_actor(
                    spawn,
                    is_arbiter=False,
                    name='sub-actor',
                    data=data,
                    arb_addr=arb_addr,
                    enable_modules=namespaces,
                )

                assert len(nursery._children) == 1
                assert portal.channel.uid in tractor.current_actor()._peers
                # be sure we can still get the result
                result = await portal.result()
                assert result == 10
                return result
        else:
            return 10
Exemplo n.º 15
0
    async def main():

        # flat to make sure we get at least one pong
        got_pong: bool = False
        timeout: int = 2

        if is_win():  # smh
            timeout = 4

        with trio.move_on_after(timeout):
            async with tractor.open_nursery() as n:

                # name of this actor will be same as target func
                portal = await n.start_actor(
                    'dual_tasks',
                    enable_modules=[__name__]
                )

                async with portal.open_context(
                    one_task_streams_and_one_handles_reqresp,

                ) as (ctx, first):

                    assert first is None

                    async with ctx.open_stream() as stream:

                        await stream.send('ping')

                        async for msg in stream:
                            print(f'client received: {msg}')

                            assert msg in {'pong', 'yo'}

                            if msg == 'pong':
                                got_pong = True
                                await stream.send('ping')
                                print('client sent ping')

        assert got_pong
Exemplo n.º 16
0
async def stream_from_single_subactor():
    """Verify we can spawn a daemon actor and retrieve streamed data.
    """
    async with tractor.find_actor('brokerd') as portals:
        if not portals:
            # only one per host address, spawns an actor if None
            async with tractor.open_nursery() as nursery:
                # no brokerd actor found
                portal = await nursery.start_actor(
                    'streamerd',
                    rpc_module_paths=[__name__],
                    statespace={'global_dict': {}},
                )

                seq = range(10)

                agen = await portal.run(
                    __name__,
                    'stream_seq',  # the func above
                    sequence=list(seq),  # has to be msgpack serializable
                )
                # it'd sure be nice to have an asyncitertools here...
                iseq = iter(seq)
                ival = next(iseq)
                async for val in agen:
                    assert val == ival
                    try:
                        ival = next(iseq)
                    except StopIteration:
                        # should cancel far end task which will be
                        # caught and no error is raised
                        await agen.aclose()

                await trio.sleep(0.3)
                try:
                    await agen.__anext__()
                except StopAsyncIteration:
                    # stop all spawned subactors
                    await portal.cancel_actor()
Exemplo n.º 17
0
async def test_some_cancels_all(num_actors_and_errs, start_method):
    """Verify a subset of failed subactors causes all others in
    the nursery to be cancelled just like the strategy in trio.

    This is the first and only supervisory strategy at the moment.
    """
    num, first_err, err_type = num_actors_and_errs
    try:
        async with tractor.open_nursery() as n:
            real_actors = []
            for i in range(3):
                real_actors.append(await n.start_actor(
                    f'actor_{i}',
                    rpc_module_paths=[__name__],
                ))

            for i in range(num):
                # start actor(s) that will fail immediately
                await n.run_in_actor(f'extra_{i}', assert_err)

        # should error here with a ``RemoteActorError`` or ``MultiError``

    except first_err as err:
        if isinstance(err, tractor.MultiError):
            assert len(err.exceptions) == num
            for exc in err.exceptions:
                if isinstance(exc, tractor.RemoteActorError):
                    assert exc.type == err_type
                else:
                    assert isinstance(exc, trio.Cancelled)
        elif isinstance(err, tractor.RemoteActorError):
            assert err.type == err_type

        assert n.cancelled is True
        assert not n._children
    else:
        pytest.fail("Should have gotten a remote assertion error?")
Exemplo n.º 18
0
async def main() -> None:

    async with tractor.open_nursery() as n:

        portal = await n.start_actor(
            'rpc_server',
            enable_modules=[__name__],
        )

        # XXX: syntax requires py3.9
        async with (

            portal.open_context(
                simple_rpc,  # taken from pytest parameterization
                data=10,

            ) as (ctx, sent),

            ctx.open_stream() as stream,
        ):

            assert sent == 11

            count = 0
            # receive msgs using async for style
            await stream.send('ping')

            async for msg in stream:
                assert msg == 'pong'
                await stream.send('ping')
                count += 1

                if count >= 9:
                    break

        # explicitly teardown the daemon-actor
        await portal.cancel_actor()
Exemplo n.º 19
0
async def a_quadruple_example():
    # a nursery which spawns "actors"
    async with tractor.open_nursery() as nursery:

        seed = int(1e3)
        pre_start = time.time()

        portal = await nursery.run_in_actor(
            'aggregator',
            aggregate,
            seed=seed,
        )

        start = time.time()
        # the portal call returns exactly what you'd expect
        # as if the remote "aggregate" function was called locally
        result_stream = []
        async for value in await portal.result():
            result_stream.append(value)

        print(f"STREAM TIME = {time.time() - start}")
        print(f"STREAM + SPAWN TIME = {time.time() - pre_start}")
        assert result_stream == list(range(seed))
        return result_stream
Exemplo n.º 20
0
async def test_most_beautiful_word(
    start_method,
    return_value
):
    '''
    The main ``tractor`` routine.

    '''
    with trio.fail_after(1):
        async with tractor.open_nursery() as n:

            portal = await n.run_in_actor(
                cellar_door,
                return_value=return_value,
                name='some_linguist',
            )

            print(await portal.result())
    # The ``async with`` will unblock here since the 'some_linguist'
    # actor has completed its main task ``cellar_door``.

    # this should pull the cached final result already captured during
    # the nursery block exit.
    print(await portal.result())
async def main():
    """The main ``tractor`` routine.

    The process tree should look as approximately as follows:

    python examples/debugging/multi_subactors.py
    ├─ python -m tractor._child --uid ('name_error', 'a7caf490 ...)
    `-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
       `-python -m tractor._child --uid ('name_error', '3391222c ...)

    Order of failure:
        - nested name_error sub-sub-actor
        - root actor should then fail on assert
        - program termination
    """
    async with tractor.open_nursery(
        debug_mode=True,
    ) as n:

        # spawn both actors
        portal = await n.run_in_actor(
            name_error,
            name='name_error',
        )
        portal1 = await n.run_in_actor(
            spawn_error,
            name='spawn_error',
        )

        # trigger a root actor error
        assert 0

        # attempt to collect results (which raises error in parent)
        # still has some issues where the parent seems to get stuck
        await portal.result()
        await portal1.result()
Exemplo n.º 22
0
async def main():

    async with tractor.open_nursery(debug_mode=True, ) as n:

        portal = await n.run_in_actor(breakpoint_forever, )
        await portal.result()
Exemplo n.º 23
0
async def test_some_cancels_all(num_actors_and_errs, start_method):
    """Verify a subset of failed subactors causes all others in
    the nursery to be cancelled just like the strategy in trio.

    This is the first and only supervisory strategy at the moment.
    """
    num_actors, first_err, err_type, ria_func, da_func = num_actors_and_errs
    try:
        async with tractor.open_nursery() as n:

            # spawn the same number of deamon actors which should be cancelled
            dactor_portals = []
            for i in range(num_actors):
                dactor_portals.append(await n.start_actor(
                    f'deamon_{i}',
                    rpc_module_paths=[__name__],
                ))

            func, kwargs = ria_func
            riactor_portals = []
            for i in range(num_actors):
                # start actor(s) that will fail immediately
                riactor_portals.append(await
                                       n.run_in_actor(f'actor_{i}', func,
                                                      **kwargs))

            if da_func:
                func, kwargs, expect_error = da_func
                for portal in dactor_portals:
                    # if this function fails then we should error here
                    # and the nursery should teardown all other actors
                    try:
                        await portal.run(__name__, func.__name__, **kwargs)
                    except tractor.RemoteActorError as err:
                        assert err.type == err_type
                        # we only expect this first error to propogate
                        # (all other daemons are cancelled before they
                        # can be scheduled)
                        num_actors = 1
                        # reraise so nursery teardown is triggered
                        raise
                    else:
                        if expect_error:
                            pytest.fail(
                                "Deamon call should fail at checkpoint?")

        # should error here with a ``RemoteActorError`` or ``MultiError``

    except first_err as err:
        if isinstance(err, tractor.MultiError):
            assert len(err.exceptions) == num_actors
            for exc in err.exceptions:
                if isinstance(exc, tractor.RemoteActorError):
                    assert exc.type == err_type
                else:
                    assert isinstance(exc, trio.Cancelled)
        elif isinstance(err, tractor.RemoteActorError):
            assert err.type == err_type

        assert n.cancelled is True
        assert not n._children
    else:
        pytest.fail("Should have gotten a remote assertion error?")
Exemplo n.º 24
0
 async def main():
     async with tractor.open_nursery() as nursery:
         for i in range(num_subactors):
             await nursery.run_in_actor(f'errorer{i}',
                                        assert_err,
                                        delay=delay)
Exemplo n.º 25
0
async def test_caller_closes_ctx_after_callee_opens_stream(
    use_ctx_cancel_method: bool,
):
    'caller context closes without using stream'

    async with tractor.open_nursery() as n:

        portal = await n.start_actor(
            'ctx_cancelled',
            enable_modules=[__name__],
        )

        async with portal.open_context(
            expect_cancelled,
        ) as (ctx, sent):
            await portal.run(assert_state, value=True)

            assert sent is None

            # call cancel explicitly
            if use_ctx_cancel_method:

                await ctx.cancel()

                try:
                    async with ctx.open_stream() as stream:
                        async for msg in stream:
                            pass

                except tractor.ContextCancelled:
                    raise  # XXX: must be propagated to __aexit__

                else:
                    assert 0, "Should have context cancelled?"

                # channel should still be up
                assert portal.channel.connected()

                # ctx is closed here
                await portal.run(assert_state, value=False)

            else:
                try:
                    with trio.fail_after(0.2):
                        await ctx.result()
                        assert 0, "Callee should have blocked!?"
                except trio.TooSlowError:
                    await ctx.cancel()
        try:
            async with ctx.open_stream() as stream:
                async for msg in stream:
                    pass
        except tractor.ContextCancelled:
            pass
        else:
            assert 0, "Should have received closed resource error?"

        # ctx is closed here
        await portal.run(assert_state, value=False)

        # channel should not have been destroyed yet, only the
        # inter-actor-task context
        assert portal.channel.connected()

        # teardown the actor
        await portal.cancel_actor()
Exemplo n.º 26
0
async def spawn_router_stream_alerts(
    order_mode,
    symbol: Symbol,
    # lines: 'LinesEditor',
    task_status: TaskStatus[str] = trio.TASK_STATUS_IGNORED,
) -> None:
    """Spawn an EMS daemon and begin sending orders and receiving
    alerts.

    """

    actor = tractor.current_actor()
    subactor_name = 'emsd'

    # TODO: add ``maybe_spawn_emsd()`` for this
    async with tractor.open_nursery() as n:

        portal = await n.start_actor(
            subactor_name,
            enable_modules=[__name__],
        )
        stream = await portal.run(stream_and_route, ui_name=actor.name)

        async with tractor.wait_for_actor(subactor_name):
            # let parent task continue
            task_status.started(_to_ems)

        # begin the trigger-alert stream
        # this is where we receive **back** messages
        # about executions **from** the EMS actor
        async for msg in stream:

            # delete the line from view
            oid = msg['oid']
            resp = msg['msg']

            if resp in ('active', ):
                print(f"order accepted: {msg}")

                # show line label once order is live
                order_mode.lines.commit_line(oid)

                continue

            elif resp in ('cancelled', ):

                # delete level from view
                order_mode.lines.remove_line(uuid=oid)
                print(f'deleting line with oid: {oid}')

            elif resp in ('executed', ):

                order_mode.lines.remove_line(uuid=oid)
                print(f'deleting line with oid: {oid}')

                order_mode.arrows.add(
                    oid,
                    msg['index'],
                    msg['price'],
                    pointing='up' if msg['name'] == 'up' else 'down')

                # DESKTOP NOTIFICATIONS
                #
                # TODO: this in another task?
                # not sure if this will ever be a bottleneck,
                # we probably could do graphics stuff first tho?

                # XXX: linux only for now
                result = await trio.run_process([
                    'notify-send',
                    '-u',
                    'normal',
                    '-t',
                    '10000',
                    'piker',
                    f'alert: {msg}',
                ], )
                log.runtime(result)
Exemplo n.º 27
0
async def chart_from_fsp(
    linked_charts,
    fsp_func_name,
    sym,
    src_shm,
    brokermod,
    loglevel,
) -> None:
    """Start financial signal processing in subactor.

    Pass target entrypoint and historical data.
    """
    name = f'fsp.{fsp_func_name}'

    # TODO: load function here and introspect
    # return stream type(s)

    # TODO: should `index` be a required internal field?
    fsp_dtype = np.dtype([('index', int), (fsp_func_name, float)])

    async with tractor.open_nursery() as n:
        key = f'{sym}.' + name

        shm, opened = maybe_open_shm_array(
            key,
            # TODO: create entry for each time frame
            dtype=fsp_dtype,
            readonly=True,
        )

        # XXX: fsp may have been opened by a duplicate chart. Error for
        # now until we figure out how to wrap fsps as "feeds".
        assert opened, f"A chart for {key} likely already exists?"

        # start fsp sub-actor
        portal = await n.run_in_actor(

            # name as title of sub-chart
            name,

            # subactor entrypoint
            fsp.cascade,
            brokername=brokermod.name,
            src_shm_token=src_shm.token,
            dst_shm_token=shm.token,
            symbol=sym,
            fsp_func_name=fsp_func_name,

            # tractor config
            loglevel=loglevel,
        )

        stream = await portal.result()

        # receive last index for processed historical
        # data-array as first msg
        _ = await stream.receive()

        chart = linked_charts.add_plot(
            name=fsp_func_name,
            array=shm.array,

            # curve by default
            ohlc=False,

            # settings passed down to ``ChartPlotWidget``
            static_yrange=(0, 100),
        )

        # display contents labels asap
        chart.update_contents_labels(len(shm.array) - 1)

        array = shm.array
        value = array[fsp_func_name][-1]

        last_val_sticky = chart._ysticks[chart.name]
        last_val_sticky.update_from_data(-1, value)

        chart.update_curve_from_array(fsp_func_name, array)
        chart.default_view()

        # TODO: figure out if we can roll our own `FillToThreshold` to
        # get brush filled polygons for OS/OB conditions.
        # ``pg.FillBetweenItems`` seems to be one technique using
        # generic fills between curve types while ``PlotCurveItem`` has
        # logic inside ``.paint()`` for ``self.opts['fillLevel']`` which
        # might be the best solution?
        # graphics = chart.update_from_array(chart.name, array[fsp_func_name])
        # graphics.curve.setBrush(50, 50, 200, 100)
        # graphics.curve.setFillLevel(50)

        # add moveable over-[sold/bought] lines
        level_line(chart, 30)
        level_line(chart, 70, orient_v='top')

        chart._shm = shm
        chart._set_yrange()

        # update chart graphics
        async for value in stream:
            # p = pg.debug.Profiler(disabled=False, delayed=False)
            array = shm.array
            value = array[-1][fsp_func_name]
            last_val_sticky.update_from_data(-1, value)
            chart.update_curve_from_array(fsp_func_name, array)
Exemplo n.º 28
0
async def spawn():
    async with tractor.open_nursery() as tn:
        await tn.run_in_actor(
            spin_for,
            name='sleeper',
        )
Exemplo n.º 29
0
    async def main():
        ss = tractor.current_actor().statespace

        async with tractor.open_nursery() as n:

            name = 'arbiter'

            if pub_actor == 'streamer':
                # start the publisher as a daemon
                master_portal = await n.start_actor(
                    'streamer',
                    rpc_module_paths=[__name__],
                )

            even_portal = await n.run_in_actor('evens',
                                               subs,
                                               which=['even'],
                                               pub_actor_name=name)
            odd_portal = await n.run_in_actor('odds',
                                              subs,
                                              which=['odd'],
                                              pub_actor_name=name)

            async with tractor.wait_for_actor('evens'):
                # block until 2nd actor is initialized
                pass

            if pub_actor == 'arbiter':
                # wait for publisher task to be spawned in a local RPC task
                while not ss.get('get_topics'):
                    await trio.sleep(0.1)

                get_topics = ss.get('get_topics')

                assert 'even' in get_topics()

            async with tractor.wait_for_actor('odds'):
                # block until 2nd actor is initialized
                pass

            if pub_actor == 'arbiter':
                start = time.time()
                while 'odd' not in get_topics():
                    await trio.sleep(0.1)
                    if time.time() - start > 1:
                        pytest.fail("odds subscription never arrived?")

            # TODO: how to make this work when the arbiter gets
            # a portal to itself? Currently this causes a hang
            # when the channel server is torn down due to a lingering
            # loopback channel
            #     with trio.move_on_after(1):
            #         await subs(['even', 'odd'])

            # XXX: this would cause infinite
            # blocking due to actor never terminating loop
            # await even_portal.result()

            await trio.sleep(0.5)
            await even_portal.cancel_actor()
            await trio.sleep(0.5)

            if pub_actor == 'arbiter':
                assert 'even' not in get_topics()

            await odd_portal.cancel_actor()
            await trio.sleep(1)

            if pub_actor == 'arbiter':
                while get_topics():
                    await trio.sleep(0.1)
                    if time.time() - start > 1:
                        pytest.fail("odds subscription never dropped?")
            else:
                await master_portal.cancel_actor()
Exemplo n.º 30
0
async def test_respawn_consumer_task(
    arb_addr,
    spawn_backend,
    loglevel,
):
    """Verify that ``._portal.ReceiveStream.shield()``
    sucessfully protects the underlying IPC channel from being closed
    when cancelling and respawning a consumer task.

    This also serves to verify that all values from the stream can be
    received despite the respawns.

    """
    stream = None

    async with tractor.open_nursery() as n:

        portal = await n.start_actor(name='streamer',
                                     enable_modules=[__name__])
        async with portal.open_stream_from(
                stream_data,
                seed=11,
        ) as stream:

            expect = set(range(11))
            received = []

            # this is the re-spawn task routine
            async def consume(task_status=trio.TASK_STATUS_IGNORED):
                print('starting consume task..')
                nonlocal stream

                with trio.CancelScope() as cs:
                    task_status.started(cs)

                    # shield stream's underlying channel from cancellation
                    # with stream.shield():
                    async for v in stream:
                        print(f'from stream: {v}')
                        expect.remove(v)
                        received.append(v)

                    print('exited consume')

            async with trio.open_nursery() as ln:
                cs = await ln.start(consume)

                while True:

                    await trio.sleep(0.1)

                    if received[-1] % 2 == 0:

                        print('cancelling consume task..')
                        cs.cancel()

                        # respawn
                        cs = await ln.start(consume)

                    if not expect:
                        print("all values streamed, BREAKING")
                        break

                cs.cancel()

        # TODO: this is justification for a
        # ``ActorNursery.stream_from_actor()`` helper?
        await portal.cancel_actor()