Skip to content

Commit

Permalink
Initial TUI integration with arbiter
Browse files Browse the repository at this point in the history
  • Loading branch information
vkottler committed Sep 6, 2023
1 parent d9c14f2 commit fd81f4e
Show file tree
Hide file tree
Showing 15 changed files with 226 additions and 119 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ edit: $(PY_PREFIX)edit
clean: $(PY_PREFIX)clean $(DZ_PREFIX)clean

s:
./venv$(PYTHON_VERSION)/bin/runtimepy arbiter ./local/arbiter/test.yaml
./venv$(PYTHON_VERSION)/bin/runtimepy arbiter --curses \
./local/arbiter/test.yaml
13 changes: 0 additions & 13 deletions local/arbiter/factories.yaml

This file was deleted.

5 changes: 0 additions & 5 deletions local/arbiter/ports.yaml

This file was deleted.

12 changes: 10 additions & 2 deletions local/arbiter/tasks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ async def noop2(app: AppInfo) -> int:
return 1


async def gui(app: AppInfo) -> int:
"""Run a channel GUI for application connections."""

print(app)

return 0


async def test(app: AppInfo) -> int:
"""A network application that doesn't do anything."""

while not app.stop.is_set():
for _ in range(100):
for conn in app.search(pattern="client", kind=Connection):
for conn in app.search(pattern="client", kind=Connection):
for _ in range(100):
conn.send_text("Hello, world!")

await asyncio.sleep(0.1)
Expand Down
16 changes: 10 additions & 6 deletions local/arbiter/test.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
---
includes:
- factories.yaml
- ports.yaml
- servers.yaml
- clients.yaml
# - servers.yaml
# - clients.yaml
- ../../tests/data/valid/connection_arbiter/json.yaml
- ../../tests/data/valid/connection_arbiter/echo_factories.yaml
- ../../tests/data/valid/connection_arbiter/ports.yaml

factories:
- {name: runtimepy.net.arbiter.housekeeping.ConnectionMetricsLoggerFactory}

tasks:
- name: log_metrics
factory: ConnectionMetricsLoggerFactory
period_s: 1.0
period_s: 10.0

app:
- [tasks.test, tasks.noop1, tasks.noop2]
# - [tasks.test, tasks.noop1, tasks.noop2]
- tasks.noop1
5 changes: 4 additions & 1 deletion runtimepy/channel/environment/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from typing import cast as _cast

# third-party
from vcorelib.namespace import DEFAULT_DELIM, Namespace
from vcorelib.namespace import NamespaceMixin as _NamespaceMixin

# internal
Expand Down Expand Up @@ -47,10 +48,12 @@ def __init__(
enums: _EnumRegistry = None,
values: ValueMap = None,
fields: _Iterable[_BitFields] = None,
namespace: Namespace = None,
namespace_delim: str = DEFAULT_DELIM,
) -> None:
"""Initialize this channel environment."""

super().__init__()
super().__init__(namespace=namespace, namespace_delim=namespace_delim)

if channels is None:
channels = _ChannelRegistry()
Expand Down
6 changes: 3 additions & 3 deletions runtimepy/channel/environment/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def channel(
def int_channel(
self,
name: str,
kind: _Primitivelike = "uint32",
kind: _Union[Primitive[_Any], _Primitivelike] = "uint32",
commandable: bool = False,
enum: _Union[_RegistryKey, _RuntimeEnum] = None,
namespace: _Namespace = None,
Expand All @@ -76,7 +76,7 @@ def int_channel(
def bool_channel(
self,
name: str,
kind: _Primitivelike = "bool",
kind: _Union[Primitive[_Any], _Primitivelike] = "bool",
commandable: bool = False,
enum: _Union[_RegistryKey, _RuntimeEnum] = None,
namespace: _Namespace = None,
Expand All @@ -92,7 +92,7 @@ def bool_channel(
def float_channel(
self,
name: str,
kind: _Primitivelike = "float",
kind: _Union[Primitive[_Any], _Primitivelike] = "float",
commandable: bool = False,
namespace: _Namespace = None,
) -> _FloatChannel:
Expand Down
33 changes: 29 additions & 4 deletions runtimepy/commands/arbiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,58 @@
from argparse import Namespace as _Namespace
import asyncio as _asyncio
from pathlib import Path as _Path
from typing import Optional

# third-party
from vcorelib.args import CommandFunction as _CommandFunction
from vcorelib.asyncio import run_handle_stop as _run_handle_stop

from runtimepy.commands.tui import curses_wrap_if

# internal
from runtimepy.net.arbiter import ConnectionArbiter
from runtimepy.tui.channels import CursesWindow as _CursesWindow


async def entry(stop_sig: _asyncio.Event, args: _Namespace) -> int:
async def entry(
stop_sig: _asyncio.Event, args: _Namespace, window: _CursesWindow = None
) -> int:
"""The async command entry."""

arbiter = ConnectionArbiter(stop_sig=stop_sig)
arbiter = ConnectionArbiter(stop_sig=stop_sig, window=window)
await arbiter.load_configs(args.configs)
return await arbiter.app()


EXIT_CODE = 0


def app(window: Optional[_CursesWindow], args: _Namespace) -> None:
"""Start the application with an optional TUI."""

stop_sig = _asyncio.Event()
global EXIT_CODE # pylint: disable=global-statement
EXIT_CODE = _run_handle_stop(
stop_sig, entry(stop_sig, args, window=window)
)


def arbiter_cmd(args: _Namespace) -> int:
"""Execute the arbiter command."""

stop_sig = _asyncio.Event()
return _run_handle_stop(stop_sig, entry(stop_sig, args))
curses_wrap_if(args.curses, app, args)
return EXIT_CODE


def add_arbiter_cmd(parser: _ArgumentParser) -> _CommandFunction:
"""Add arbiter-command arguments to its parser."""

parser.add_argument(
"--curses",
action="store_true",
help="whether or not to use curses.wrapper when starting",
)

parser.add_argument(
"configs", type=_Path, nargs="+", help="the configuration to load"
)
Expand Down
18 changes: 16 additions & 2 deletions runtimepy/commands/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from argparse import Namespace as _Namespace
import asyncio as _asyncio
import curses as _curses
from typing import Callable, Optional

# third-party
from vcorelib.args import CommandFunction as _CommandFunction
Expand All @@ -19,10 +20,14 @@
from runtimepy.tui.channels import CursesWindow as _CursesWindow
from runtimepy.tui.task import TuiTask as _TuiTask

CursesApp = Callable[[Optional[_CursesWindow], _Namespace], None]

def start(window: _CursesWindow, args: _Namespace) -> None:

def start(window: Optional[_CursesWindow], args: _Namespace) -> None:
"""Start the user interface."""

assert window is not None

task = _TuiTask(
"ui",
1 / args.rate,
Expand All @@ -33,10 +38,19 @@ def start(window: _CursesWindow, args: _Namespace) -> None:
return _run_handle_stop(stop_sig, task.run(window, stop_sig=stop_sig))


def curses_wrap_if(cond: bool, method: CursesApp, args: _Namespace) -> None:
"""Run a method in TUI mode if a condition is met."""

if cond:
getattr(_curses, "wrapper")(method, args)
else:
method(None, args)


def tui_cmd(args: _Namespace) -> int:
"""Execute the tui command."""

getattr(_curses, "wrapper")(start, args)
curses_wrap_if(True, start, args)
return 0


Expand Down
6 changes: 5 additions & 1 deletion runtimepy/net/arbiter/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Iterable as _Iterable
from typing import List as _List
from typing import MutableMapping as _MutableMapping
from typing import Optional
from typing import Union as _Union

# third-party
Expand All @@ -29,6 +30,7 @@
)
from runtimepy.net.connection import Connection as _Connection
from runtimepy.net.manager import ConnectionManager as _ConnectionManager
from runtimepy.tui.mixin import CursesWindow, TuiMixin

NetworkApplication = _Callable[[AppInfo], _Awaitable[int]]
NetworkApplicationlike = _Union[NetworkApplication, _List[NetworkApplication]]
Expand Down Expand Up @@ -58,7 +60,7 @@ def normalize_app(
return [app]


class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin):
class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
"""
A class implementing a base connection-manager for a broader application.
"""
Expand All @@ -71,10 +73,12 @@ def __init__(
logger: _LoggerType = None,
app: NetworkApplicationlike = None,
config: _JsonObject = None,
window: Optional[CursesWindow] = None,
) -> None:
"""Initialize this connection arbiter."""

_LoggerMixin.__init__(self, logger=logger)
TuiMixin.__init__(self, window=window)

if manager is None:
manager = _ConnectionManager()
Expand Down
Loading

0 comments on commit fd81f4e

Please sign in to comment.