Skip to content

Commit

Permalink
Merge pull request #99 from vkottler/dev/2.5.0
Browse files Browse the repository at this point in the history
Add channel environment to JSON message connection
  • Loading branch information
vkottler authored Sep 6, 2023
2 parents 9487a52 + 6ed329b commit b0cee05
Show file tree
Hide file tree
Showing 26 changed files with 314 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=runtimepy version=2.4.1
repo=runtimepy version=2.5.0
if: |
matrix.python-version == '3.11'
&& matrix.system == 'ubuntu-latest'
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[DESIGN]
max-args=8
max-attributes=12
max-parents=9
max-parents=10

[MESSAGES CONTROL]
disable=too-few-public-methods
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ 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
+@echo "success"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.2
hash=da48a80970b792c3494552683c21ce3c
hash=b7f4f833830e6d02bb40fa8830398467
=====================================
-->

# runtimepy ([2.4.1](https://pypi.org/project/runtimepy/))
# runtimepy ([2.5.0](https://pypi.org/project/runtimepy/))

[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
Expand Down
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.

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


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

window = app.tui.window
while not app.stop.is_set():
data = window.getch()
if data != -1 and not await app.tui.handle_char(data):
# trigger this with 'q'
app.stop.set()

# do stuff

# make this updating a task
app.tui.tui_update()
await asyncio.sleep(0.1)

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.gui
4 changes: 2 additions & 2 deletions local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 2
minor: 4
patch: 1
minor: 5
patch: 0
entry: runtimepy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "runtimepy"
version = "2.4.1"
version = "2.5.0"
description = "A framework for implementing Python services."
readme = "README.md"
requires-python = ">=3.8"
Expand Down
4 changes: 2 additions & 2 deletions runtimepy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.2
# hash=33b3dfc332dc64b4caf209e69bd2e795
# hash=13cec9f3eb806319008b56d8dd4df98d
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "A framework for implementing Python services."
PKG_NAME = "runtimepy"
VERSION = "2.4.1"
VERSION = "2.5.0"
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
13 changes: 7 additions & 6 deletions runtimepy/channel/environment/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
"""

# built-in
from typing import Any as _Any
from typing import Union as _Union
from typing import cast as _cast

# third-party
from vcorelib.namespace import Namespace as _Namespace

from runtimepy.channel import FloatChannel as _FloatChannel

# internal
from runtimepy.channel import FloatChannel as _FloatChannel
from runtimepy.channel.environment.base import (
BaseChannelEnvironment as _BaseChannelEnvironment,
)
Expand All @@ -25,6 +25,7 @@
from runtimepy.enum import RuntimeEnum as _RuntimeEnum
from runtimepy.enum.type import EnumTypelike as _EnumTypelike
from runtimepy.mapping import EnumMappingData as _EnumMappingData
from runtimepy.primitives import Primitive
from runtimepy.primitives import Primitivelike as _Primitivelike
from runtimepy.registry.name import RegistryKey as _RegistryKey

Expand All @@ -35,7 +36,7 @@ class CreateChannelEnvironment(_BaseChannelEnvironment):
def channel(
self,
name: str,
kind: _Primitivelike,
kind: _Union[Primitive[_Any], _Primitivelike],
commandable: bool = False,
enum: _Union[_RegistryKey, _RuntimeEnum] = None,
namespace: _Namespace = None,
Expand All @@ -59,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 @@ -75,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 @@ -91,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
24 changes: 20 additions & 4 deletions runtimepy/channel/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any as _Any
from typing import Optional as _Optional
from typing import Type as _Type
from typing import cast as _cast
from typing import Union

# third-party
from vcorelib.io.types import JsonObject as _JsonObject
Expand All @@ -15,7 +15,9 @@
from runtimepy.channel import AnyChannel as _AnyChannel
from runtimepy.channel import Channel as _Channel
from runtimepy.mixins.regex import CHANNEL_PATTERN as _CHANNEL_PATTERN
from runtimepy.primitives import Primitive
from runtimepy.primitives import Primitivelike as _Primitivelike
from runtimepy.primitives import normalize
from runtimepy.registry import Registry as _Registry
from runtimepy.registry.name import NameRegistry as _NameRegistry
from runtimepy.registry.name import RegistryKey as _RegistryKey
Expand All @@ -40,17 +42,31 @@ def kind(self) -> _Type[_Channel[_Any]]:
def channel(
self,
name: str,
kind: _Primitivelike,
kind: Union[Primitive[_Any], _Primitivelike],
commandable: bool = False,
enum: _RegistryKey = None,
) -> _Optional[_AnyChannel]:
"""Create a new channel."""

if isinstance(kind, str):
kind = normalize(kind)

if isinstance(kind, Primitive):
primitive = kind
else:
primitive = kind()

data: _JsonObject = {
"type": _cast(str, kind),
"type": str(primitive.kind),
"commandable": commandable,
}
if enum is not None:
data["enum"] = enum

return self.register_dict(name, data)
result = self.register_dict(name, data)

# Replace the underlying primitive, in case it was direclty passed in.
if result is not None:
result.raw = primitive

return result
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
Loading

0 comments on commit b0cee05

Please sign in to comment.