Factor transport-ep parser/loader into helper

For now def it `.cli.load_trans_eps()` just inside the pkg mod; only
loads the ep for `pikerd` which currently acts as the main service-actor
registrar per host. Delegate to this new `.load_trans_eps()`
as-it-was-used from the `pikerd` cmd body and add fresh support for
`piker chart --maddr <addr: str>` using the routine in the body of the
`piker.cli.cli` cmd group after loading the `conf.toml::network` section
B)

Also, toss in runtime debug mode wrapping around `piker chart` using the
new `tractor.devx.maybe_open_crash_handler()` and pull the switch from
a `--pdb` flag now factored into the `.cli.cli` click group.
distribute_dis
Tyler Goodlet 2023-10-03 10:00:01 -04:00
parent 9165515811
commit 00c046c280
2 changed files with 170 additions and 79 deletions

View File

@ -1,18 +1,20 @@
# piker: trading gear for hackers # piker: trading gear for hackers
# Copyright (C) 2018-present Tyler Goodlet (in stewardship of pikers) # Copyright (C) 2018-present Tyler Goodlet
# (in stewardship for pikers, everywhere.)
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or
# it under the terms of the GNU Affero General Public License as published by # modify it under the terms of the GNU Affero General Public
# the Free Software Foundation, either version 3 of the License, or # License as published by the Free Software Foundation, either
# (at your option) any later version. # version 3 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# GNU Affero General Public License for more details. # Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public
# along with this program. If not, see <https://www.gnu.org/licenses/>. # License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
''' '''
CLI commons. CLI commons.
@ -25,6 +27,7 @@ from types import ModuleType
import click import click
import trio import trio
import tractor import tractor
from tractor._multiaddr import parse_maddr
from ..log import ( from ..log import (
get_console_log, get_console_log,
@ -42,6 +45,50 @@ from .. import config
log = get_logger('piker.cli') log = get_logger('piker.cli')
def load_trans_eps(
network: dict | None = None,
maddrs: list[tuple] | None = None,
) -> dict[str, dict[str, dict]]:
# transport-oriented endpoint multi-addresses
eps: dict[
str, # service name, eg. `pikerd`, `emsd`..
# libp2p style multi-addresses parsed into prot layers
list[dict[str, str | int]]
] = {}
if (
network
and not maddrs
):
# load network section and (attempt to) connect all endpoints
# which are reachable B)
for key, maddrs in network.items():
match key:
# TODO: resolve table across multiple discov
# prots Bo
case 'resolv':
pass
case 'pikerd':
dname: str = key
for maddr in maddrs:
layers: dict = parse_maddr(maddr)
eps.setdefault(
dname,
[],
).append(layers)
elif maddrs:
# presume user is manually specifying the root actor ep.
eps['pikerd'] = [parse_maddr(maddr)]
return eps
@click.command() @click.command()
@click.option( @click.option(
'--loglevel', '--loglevel',
@ -76,7 +123,7 @@ log = get_logger('piker.cli')
# help='Enable local ``elasticsearch`` instance' # help='Enable local ``elasticsearch`` instance'
# ) # )
def pikerd( def pikerd(
maddr: str | None, maddr: list[str] | None,
loglevel: str, loglevel: str,
tl: bool, tl: bool,
pdb: bool, pdb: bool,
@ -100,63 +147,34 @@ def pikerd(
"\n" "\n"
)) ))
# service-actor registry endpoint socket-address # service-actor registry endpoint socket-address set
regaddrs: list[tuple[str, int]] | None = None regaddrs: list[tuple[str, int]] = []
conf, _ = config.load( conf, _ = config.load(
conf_name='conf', conf_name='conf',
) )
network: dict = conf.get('network') network: dict = conf.get('network')
if network is None: if (
network is None
and not maddr
):
regaddrs = [( regaddrs = [(
_default_registry_host, _default_registry_host,
_default_registry_port, _default_registry_port,
)] )]
from .. import service
from tractor._multiaddr import parse_maddr
# transport-oriented endpoint multi-addresses
eps: dict[
str, # service name, eg. `pikerd`, `emsd`..
# libp2p style multi-addresses parsed into prot layers
list[dict[str, str | int]]
] = {}
if (
not maddr
and network
):
# load network section and (attempt to) connect all endpoints
# which are reachable B)
for key, maddrs in network.items():
match key:
# TODO: resolve table across multiple discov
# prots Bo
case 'resolv':
pass
case 'pikerd':
dname: str = key
for maddr in maddrs:
layers: dict = parse_maddr(maddr)
eps.setdefault(
dname,
[],
).append(layers)
else: else:
# presume user is manually specifying the root actor ep. eps: dict = load_trans_eps(
eps['pikerd'] = [parse_maddr(maddr)] network,
maddr,
)
for layers in eps['pikerd']:
regaddrs.append((
layers['ipv4']['addr'],
layers['tcp']['port'],
))
regaddrs: list[tuple[str, int]] = [] from .. import service
for layers in eps['pikerd']:
regaddrs.append((
layers['ipv4']['addr'],
layers['tcp']['port'],
))
async def main(): async def main():
service_mngr: service.Services service_mngr: service.Services
@ -208,8 +226,24 @@ def pikerd(
@click.option('--loglevel', '-l', default='warning', help='Logging level') @click.option('--loglevel', '-l', default='warning', help='Logging level')
@click.option('--tl', is_flag=True, help='Enable tractor logging') @click.option('--tl', is_flag=True, help='Enable tractor logging')
@click.option('--configdir', '-c', help='Configuration directory') @click.option('--configdir', '-c', help='Configuration directory')
@click.option('--maddr', '-m', default=None, help='Multiaddr to bind') @click.option(
@click.option('--raddr', '-r', default=None, help='Registrar addr to contact') '--pdb',
is_flag=True,
help='Enable runtime debug mode ',
)
@click.option(
'--maddr',
'-m',
default=None,
multiple=True,
help='Multiaddr to bind',
)
@click.option(
'--regaddr',
'-r',
default=None,
help='Registrar addr to contact',
)
@click.pass_context @click.pass_context
def cli( def cli(
ctx: click.Context, ctx: click.Context,
@ -217,10 +251,11 @@ def cli(
loglevel: str, loglevel: str,
tl: bool, tl: bool,
configdir: str, configdir: str,
pdb: bool,
# TODO: make these list[str] with multiple -m maddr0 -m maddr1 # TODO: make these list[str] with multiple -m maddr0 -m maddr1
maddr: str, maddr: list[str],
raddr: str, regaddr: str,
) -> None: ) -> None:
if configdir is not None: if configdir is not None:
@ -245,11 +280,17 @@ def cli(
# - pikerd vs. regd, separate registry daemon? # - pikerd vs. regd, separate registry daemon?
# - expose datad vs. brokerd? # - expose datad vs. brokerd?
# - bind emsd with certain perms on public iface? # - bind emsd with certain perms on public iface?
regaddrs: list[tuple[str, int]] = [( regaddrs: list[tuple[str, int]] = regaddr or [(
_default_registry_host, _default_registry_host,
_default_registry_port, _default_registry_port,
)] )]
# TODO: factor [network] section parsing out from pikerd
# above and call it here as well.
# if maddr:
# for addr in maddr:
# layers: dict = parse_maddr(addr)
ctx.obj.update({ ctx.obj.update({
'brokers': brokers, 'brokers': brokers,
'brokermods': brokermods, 'brokermods': brokermods,
@ -259,6 +300,11 @@ def cli(
'confdir': config._config_dir, 'confdir': config._config_dir,
'wl_path': config._watchlists_data_path, 'wl_path': config._watchlists_data_path,
'registry_addrs': regaddrs, 'registry_addrs': regaddrs,
'pdb': pdb, # debug mode flag
# TODO: endpoint parsing, pinging and binding
# on no existing server.
# 'maddrs': maddr,
}) })
# allow enabling same loglevel in ``tractor`` machinery # allow enabling same loglevel in ``tractor`` machinery

View File

@ -96,9 +96,17 @@ def monitor(config, rate, name, dhost, test, tl):
@click.option('--rate', '-r', default=1, help='Logging level') @click.option('--rate', '-r', default=1, help='Logging level')
@click.argument('symbol', required=True) @click.argument('symbol', required=True)
@click.pass_obj @click.pass_obj
def optschain(config, symbol, date, rate, test): def optschain(
"""Start an option chain UI config,
""" symbol,
date,
rate,
test,
):
'''
Start an option chain UI
'''
# global opts # global opts
loglevel = config['loglevel'] loglevel = config['loglevel']
brokername = config['broker'] brokername = config['broker']
@ -132,18 +140,19 @@ def optschain(config, symbol, date, rate, test):
default=None, default=None,
help='Enable pyqtgraph profiling' help='Enable pyqtgraph profiling'
) )
@click.option( # @click.option(
'--pdb', # '--pdb',
is_flag=True, # is_flag=True,
help='Enable tractor debug mode' # help='Enable tractor debug mode'
) # )
@click.argument('symbols', nargs=-1, required=True) @click.argument('symbols', nargs=-1, required=True)
# @click.pass_context
@click.pass_obj @click.pass_obj
def chart( def chart(
config, config,
# ctx: click.Context,
symbols: list[str], symbols: list[str],
profile, profile,
pdb: bool,
): ):
''' '''
Run chart UI app, spawning service daemons dynamically as Run chart UI app, spawning service daemons dynamically as
@ -174,14 +183,50 @@ def chart(
tractorloglevel = config['tractorloglevel'] tractorloglevel = config['tractorloglevel']
pikerloglevel = config['loglevel'] pikerloglevel = config['loglevel']
_main( maddrs: list[tuple[str, int]] = config.get(
syms=symbols, 'maddrs',
brokermods=brokermods, [],
piker_loglevel=pikerloglevel,
tractor_kwargs={
'debug_mode': pdb,
'loglevel': tractorloglevel,
'name': 'chart',
'registry_addrs': config.get('registry_addrs'),
},
) )
# if maddrs:
# from tractor._multiaddr import parse_maddr
# for addr in maddrs:
# breakpoint()
# layers: dict = parse_maddr(addr)
regaddrs: list[tuple[str, int]] = config.get(
'registry_addrs',
[],
)
from ..config import load
conf, _ = load(
conf_name='conf',
)
network: dict = conf.get('network')
if network:
from ..cli import load_trans_eps
eps: dict = load_trans_eps(
network,
maddrs,
)
for layers in eps['pikerd']:
regaddrs.append((
layers['ipv4']['addr'],
layers['tcp']['port'],
))
from tractor.devx import maybe_open_crash_handler
pdb: bool = config['pdb']
with maybe_open_crash_handler(pdb=pdb):
_main(
syms=symbols,
brokermods=brokermods,
piker_loglevel=pikerloglevel,
tractor_kwargs={
'debug_mode': pdb,
'loglevel': tractorloglevel,
'name': 'chart',
'registry_addrs': list(set(regaddrs)),
},
)