def optschain(config, symbol, date, tl, rate, test): """Start an option chain UI """ # global opts loglevel = config['loglevel'] brokername = config['broker'] _kivy_import_hack() from .kivy.option_chain import _async_main async def main(tries): async with maybe_spawn_brokerd(tries=tries, loglevel=loglevel): # run app "main" await _async_main( symbol, brokername, rate=rate, loglevel=loglevel, test=test, ) tractor.run( partial(main, tries=1), name='kivy-options-chain', loglevel=loglevel if tl else None, start_method='forkserver', )
def optschain(config, symbol, date, rate, test): """Start an option chain UI """ # global opts loglevel = config['loglevel'] brokername = config['broker'] _kivy_import_hack() from .kivy.option_chain import _async_main async def main(): async with maybe_spawn_brokerd(loglevel=loglevel): # run app "main" await _async_main( symbol, brokername, rate=rate, loglevel=loglevel, test=test, ) tractor.run( main, name='kivy-options-chain', )
def ingest(config, name, test_file, tl, url): """Ingest real-time broker quotes and ticks to a marketstore instance. """ # global opts brokermod = config['brokermod'] loglevel = config['loglevel'] tractorloglevel = config['tractorloglevel'] # log = config['log'] watchlist_from_file = wl.ensure_watchlists(config['wl_path']) watchlists = wl.merge_watchlist(watchlist_from_file, wl._builtins) symbols = watchlists[name] tractor.run( partial( ingest_quote_stream, symbols, brokermod.name, tries=1, loglevel=loglevel, ), name='ingest_marketstore', loglevel=tractorloglevel, debug_mode=True, )
def test_remote_error(arb_addr, args_err): """Verify an error raised in a subactor that is propagated to the parent nursery, contains the underlying boxed builtin error type info and causes cancellation and reraising all the way up the stack. """ args, errtype = args_err async def main(): async with tractor.open_nursery() as nursery: portal = await nursery.run_in_actor('errorer', assert_err, **args) # get result(s) from main task try: await portal.result() except tractor.RemoteActorError as err: assert err.type == errtype print("Look Maa that actor failed hard, hehh") raise with pytest.raises(tractor.RemoteActorError) as excinfo: tractor.run(main, arbiter_addr=arb_addr) # ensure boxed error is correct assert excinfo.value.type == errtype
def ms_destroy(config: dict, names: List[str], url: str) -> None: """Destroy symbol entries in the local marketstore instance. """ async def main(): nonlocal names async with get_client(url) as client: if not names: names = await client.list_symbols() # default is to wipe db entirely. answer = input( "This will entirely wipe you local marketstore db @ " f"{url} of the following symbols:\n {pformat(names)}" "\n\nDelete [N/y]?\n") if answer == 'y': for sym in names: # tbk = _tick_tbk.format(sym) tbk = tuple(sym, *_tick_tbk_ids) print(f"Destroying {tbk}..") await client.destroy(mk_tbk(tbk)) else: print("Nothing deleted.") tractor.run(main)
def test_loglevel_propagated_to_subactor( start_method, capfd, arb_addr, ): if start_method == 'forkserver': pytest.skip( "a bug with `capfd` seems to make forkserver capture not work?") level = 'critical' async def main(): async with tractor.open_nursery() as tn: await tn.run_in_actor( 'log_checker', check_loglevel, level=level, ) tractor.run( main, name='arbiter', loglevel=level, start_method=start_method, arbiter_addr=arb_addr, ) # ensure subactor spits log message on stderr captured = capfd.readouterr() assert 'yoyoyo' in captured.err
def optschain(config, symbol, date, tl, rate, test): """Start the real-time option chain UI. """ # global opts loglevel = config['loglevel'] brokername = config['broker'] from .ui.option_chain import _async_main async def main(tries): async with maybe_spawn_brokerd_as_subactor( tries=tries, loglevel=loglevel) as portal: # run app "main" await _async_main( symbol, brokername, rate=rate, loglevel=loglevel, test=test, ) tractor.run( partial(main, tries=1), name='kivy-options-chain', loglevel=loglevel if tl else None, )
def test_stream_from_single_subactor(arb_addr, start_method): """Verify streaming from a spawned async generator. """ tractor.run( stream_from_single_subactor, arbiter_addr=arb_addr, start_method=start_method, )
def ms_shell(config, name, tl, url): """Start an IPython shell ready to query the local marketstore db. """ async def main(): async with get_client(url) as client: query = client.query # noqa # TODO: write magics to query marketstore from IPython import embed embed() tractor.run(main)
def test_register_duplicate_name(daemon, arb_addr): async def main(): assert not tractor.current_actor().is_arbiter async with tractor.open_nursery() as n: p1 = await n.start_actor('doggy') p2 = await n.start_actor('doggy') async with tractor.wait_for_actor('doggy') as portal: assert portal.channel.uid in (p2.channel.uid, p1.channel.uid) await n.cancel() # run it manually since we want to start **after** # the other "daemon" program tractor.run(main, arbiter_addr=arb_addr)
def record(config, rate, name, dhost, filename): """Record client side quotes to a file on disk """ # global opts brokermod = config['brokermod'] loglevel = config['loglevel'] log = config['log'] watchlist_from_file = wl.ensure_watchlists(_watchlists_data_path) watchlists = wl.merge_watchlist(watchlist_from_file, wl._builtins) tickers = watchlists[name] if not tickers: log.error(f"No symbols found for watchlist `{name}`?") return async def main(tries): async with maybe_spawn_brokerd( tries=tries, loglevel=loglevel ) as portal: # run app "main" return await data.stream_to_file( name, filename, portal, tickers, brokermod, rate, ) filename = tractor.run(partial(main, tries=1), name='data-feed-recorder') click.echo(f"Data feed recording saved to {filename}")
def time_quad_ex(arb_addr): timeout = 7 if platform.system() == 'Windows' else 3 start = time.time() results = tractor.run(cancel_after, timeout, arbiter_addr=arb_addr) diff = time.time() - start assert results return results, diff
def test_not_fast_enough_quad(arb_addr, time_quad_ex, cancel_delay): """Verify we can cancel midway through the quad example and all actors cancel gracefully. """ results, diff = time_quad_ex delay = max(diff - cancel_delay, 0) results = tractor.run(cancel_after, delay, arbiter_addr=arb_addr) assert results is None
def services(config, tl, names): async def list_services(): async with tractor.get_arbiter( *tractor.current_actor()._arb_addr) as portal: registry = await portal.run('self', 'get_registry') json_d = {} for uid, socket in registry.items(): name, uuid = uid host, port = socket json_d[f'{name}.{uuid}'] = f'{host}:{port}' click.echo(f"Available `piker` services:\n{colorize_json(json_d)}") tractor.run( list_services, name='service_query', loglevel=config['loglevel'] if tl else None, )
def test_local_arbiter_subactor_global_state(arb_addr): result = tractor.run( spawn, True, name='arbiter', statespace=statespace, arbiter_addr=arb_addr, ) assert result == 10
def test_cancel_single_subactor(arb_addr): """Ensure a ``ActorNursery.start_actor()`` spawned subactor cancels when the nursery is cancelled. """ async def spawn_actor(): """Spawn an actor that blocks indefinitely. """ async with tractor.open_nursery() as nursery: portal = await nursery.start_actor( 'nothin', rpc_module_paths=[__name__], ) assert (await portal.run(__name__, 'do_nothing')) is None # would hang otherwise await nursery.cancel() tractor.run(spawn_actor, arbiter_addr=arb_addr)
def test_local_actor_async_func(arb_addr): """Verify a simple async function in-process. """ nums = [] async def print_loop(): # arbiter is started in-proc if dne assert tractor.current_actor().is_arbiter for i in range(10): nums.append(i) await trio.sleep(0.1) start = time.time() tractor.run(print_loop, arbiter_addr=arb_addr) # ensure the sleeps were actually awaited assert time.time() - start >= 1 assert nums == list(range(10))
def services(config, tl, names): async def list_services(): async with tractor.get_arbiter( *_tractor_kwargs['arbiter_addr'] ) as portal: registry = await portal.run_from_ns('self', 'get_registry') json_d = {} for key, socket in registry.items(): # name, uuid = uid host, port = socket json_d[key] = f'{host}:{port}' click.echo(f"{colorize_json(json_d)}") tractor.run( list_services, name='service_query', loglevel=config['loglevel'] if tl else None, arbiter_addr=_tractor_kwargs['arbiter_addr'], )
def test_multierror_fast_nursery(arb_addr, start_method, num_subactors, delay): """Verify we raise a ``trio.MultiError`` out of a nursery where more then one actor errors and also with a delay before failure to test failure during an ongoing spawning. """ 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) with pytest.raises(trio.MultiError) as exc_info: tractor.run(main, arbiter_addr=arb_addr) assert exc_info.type == tractor.MultiError err = exc_info.value assert len(err.exceptions) == num_subactors for exc in err.exceptions: assert isinstance(exc, tractor.RemoteActorError) assert exc.type == AssertionError
def test_single_subactor_pub_multitask_subs( loglevel, arb_addr, ): async def main(): async with tractor.open_nursery() as n: portal = await n.start_actor( 'streamer', rpc_module_paths=[__name__], ) async with tractor.wait_for_actor('streamer'): # block until 2nd actor is initialized pass async with trio.open_nursery() as tn: agen = await tn.start(subs, ['even'], 'streamer') await trio.sleep(0.1) tn.start_soon(subs, ['even'], 'streamer') # XXX this will trigger the python bug: # https://bugs.python.org/issue32526 # if using async generators to wrap tractor channels await agen.aclose() await trio.sleep(0.1) tn.start_soon(subs, ['even'], 'streamer') await trio.sleep(0.1) tn.start_soon(subs, ['even'], 'streamer') await portal.cancel_actor() tractor.run( main, arbiter_addr=arb_addr, rpc_module_paths=[__name__], )
def monitor(config, rate, name, dhost, test, tl): """Start a real-time watchlist UI """ # global opts brokermod = config['brokermod'] loglevel = config['loglevel'] log = config['log'] watchlist_from_file = wl.ensure_watchlists(_watchlists_data_path) watchlists = wl.merge_watchlist(watchlist_from_file, wl._builtins) tickers = watchlists[name] if not tickers: log.error(f"No symbols found for watchlist `{name}`?") return _kivy_import_hack() from .kivy.monitor import _async_main async def main(tries): async with maybe_spawn_brokerd(brokername=brokermod.name, tries=tries, loglevel=loglevel) as portal: # run app "main" await _async_main( name, portal, tickers, brokermod, rate, test=test, ) tractor.run( partial(main, tries=1), name='monitor', loglevel=loglevel if tl else None, rpc_module_paths=['piker.ui.kivy.monitor'], start_method='forkserver', )
def test_multierror(arb_addr): """Verify we raise a ``trio.MultiError`` out of a nursery where more then one actor errors. """ async def main(): async with tractor.open_nursery() as nursery: await nursery.run_in_actor('errorer1', assert_err) portal2 = await nursery.run_in_actor('errorer2', assert_err) # get result(s) from main task try: await portal2.result() except tractor.RemoteActorError as err: assert err.type == AssertionError print("Look Maa that first actor failed hard, hehh") raise # here we should get a `trio.MultiError` containing exceptions # from both subactors with pytest.raises(trio.MultiError): tractor.run(main, arbiter_addr=arb_addr)
def test_not_fast_enough_quad(arb_addr, time_quad_ex, cancel_delay): """Verify we can cancel midway through the quad example and all actors cancel gracefully. """ results, diff = time_quad_ex delay = max(diff - cancel_delay, 0) results = tractor.run(cancel_after, delay, arbiter_addr=arb_addr) if platform.system() == 'Windows' and results is not None: # In Windows CI it seems later runs are quicker then the first # so just ignore these print("Woa there windows caught your breath eh?") else: # should be cancelled mid-streaming assert results is None
def ingest(config, name, test_file, tl): ''' Ingest real-time broker quotes and ticks to a marketstore instance. ''' # global opts loglevel = config['loglevel'] tractorloglevel = config['tractorloglevel'] # log = config['log'] watchlist_from_file = wl.ensure_watchlists(config['wl_path']) watchlists = wl.merge_watchlist(watchlist_from_file, wl._builtins) symbols = watchlists[name] grouped_syms = {} for sym in symbols: symbol, _, provider = sym.rpartition('.') if provider not in grouped_syms: grouped_syms[provider] = [] grouped_syms[provider].append(symbol) async def entry_point(): async with tractor.open_nursery() as n: for provider, symbols in grouped_syms.items(): await n.run_in_actor( ingest_quote_stream, name='ingest_marketstore', symbols=symbols, brokername=provider, tries=1, actorloglevel=loglevel, loglevel=tractorloglevel ) tractor.run(entry_point)
def search(config, pattern): """Search for symbols from broker backend(s). """ # global opts brokermod = config['brokermod'] quotes = tractor.run( partial(core.symbol_search, brokermod, pattern), start_method='forkserver', loglevel='info', ) if not quotes: log.error(f"No matches could be found for {pattern}?") return click.echo(colorize_json(quotes))
# this is the main actor and *arbiter* async def main(): # a nursery which spawns "actors" async with tractor.open_nursery() as nursery: seed = int(1e3) import time 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 if __name__ == '__main__': final_stream = tractor.run(main, arbiter_addr=('127.0.0.1', 1616))
def test_multi_actor_subs_arbiter_pub( loglevel, arb_addr, pub_actor, ): """Try out the neato @pub decorator system. """ 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() tractor.run( main, arbiter_addr=arb_addr, rpc_module_paths=[__name__], )
def run(): tractor.run( main, arbiter_addr=arb_addr, rpc_module_paths=exposed_mods, )
def test_no_main(): """An async function **must** be passed to ``tractor.run()``. """ with pytest.raises(TypeError): tractor.run(None)
import tractor tractor.log.get_console_log("INFO") async def main(service_name): async with tractor.open_nursery() as an: await an.start_actor(service_name) async with tractor.get_arbiter('127.0.0.1', 1616) as portal: print(f"Arbiter is listening on {portal.channel}") async with tractor.wait_for_actor(service_name) as sockaddr: print(f"my_service is found at {sockaddr}") await an.cancel() if __name__ == '__main__': tractor.run(main, 'some_actor_name')