Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:

- run: |
mk python-release owner=libre-embedded \
repo=runtimepy version=5.15.3
repo=runtimepy version=5.15.4
if: |
matrix.python-version == '3.12'
&& 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=10
max-positional-arguments=10
max-attributes=15
max-attributes=16
max-parents=14
max-public-methods=22
max-branches=13
Expand Down
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.2.3
hash=ed2f5b3731ba189c4a3ec2f2252b622e
hash=99eea1986d2105483ebb2fe566128f5f
=====================================
-->

# runtimepy ([5.15.3](https://pypi.org/project/runtimepy/))
# runtimepy ([5.15.4](https://pypi.org/project/runtimepy/))

[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
![Build Status](https://github.com/libre-embedded/runtimepy/workflows/Python%20Package/badge.svg)
Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 5
minor: 15
patch: 3
patch: 4
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 = "5.15.3"
version = "5.15.4"
description = "A framework for implementing Python services."
readme = "README.md"
requires-python = ">=3.12"
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.2.3
# hash=f5155e85b2694b012648fdbe9c12b1cd
# hash=b0f0463a508c9a1312a071f5c0b06f3b
# =====================================

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

DESCRIPTION = "A framework for implementing Python services."
PKG_NAME = "runtimepy"
VERSION = "5.15.3"
VERSION = "5.15.4"

# runtimepy-specific content.
METRICS_NAME = "metrics"
Expand Down
5 changes: 5 additions & 0 deletions runtimepy/channel/environment/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def __init__(
fields: _Iterable[_BitFields] = None,
namespace: Namespace = None,
namespace_delim: str = DEFAULT_DELIM,
views: dict[str, str] = None,
) -> None:
"""Initialize this channel environment."""

Expand Down Expand Up @@ -105,6 +106,10 @@ def __init__(
if values is not None:
self.apply(values)

if not views:
views = {}
self.views = views

def __setitem__(self, key: _RegistryKey, value: ChannelValue) -> None:
"""Mapping-set interface."""
return self.set(key, value)
Expand Down
4 changes: 4 additions & 0 deletions runtimepy/channel/environment/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
VALUES_FILE = f"{VALUES_KEY}.json"
FIELDS_FILE = f"{FIELDS_KEY}.json"
NAMES_FILE = f"{NAMES_KEY}.json"
VIEWS_KEY = "views"
# VIEWS_FILE = f"{VIEWS_KEY}.json"


class FileChannelEnvironment(_BaseChannelEnvironment):
Expand All @@ -64,6 +66,7 @@ def export_json(self, resolve_enum: bool = True) -> dict[str, _JsonObject]:
VALUES_KEY: _cast(
_JsonObject, self.values(resolve_enum=resolve_enum)
),
VIEWS_KEY: _cast(_JsonObject, self.views),
}

def export(
Expand Down Expand Up @@ -143,6 +146,7 @@ def load_json(
enums=enum_reg,
values=_cast(_Optional[_ValueMap], data.get(VALUES_KEY)),
fields=_fields_from_dict(data[FIELDS_KEY]),
views=_cast(dict[str, str], data.get(VIEWS_KEY, {})),
)

# Typically, externally loaded environments should be final at load
Expand Down
10 changes: 10 additions & 0 deletions runtimepy/data/dummy_load.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ tasks:
a: 1
b: 2
c: 3
views:
- [zero, "\\.0\\."]
- [one, "\\.1\\."]
markdown: |
# This is a Test

Expand All @@ -29,6 +32,9 @@ tasks:
- name: wave3
factory: sinusoid
period_s: 0.03
config:
views:
- [trig, sin cos]
markdown: |
# Markdown for wave3

Expand All @@ -50,6 +56,10 @@ clients:
defer: true
kwargs:
remote_addr: [localhost, "$udp_json"]
views:
- [metrics, metrics]
- [transmit, tx]
- [receive, rx]
markdown: |
# `udp_json_client`

Expand Down
4 changes: 4 additions & 0 deletions runtimepy/data/js/classes/ChannelTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class ChannelTable {

if (Number.isInteger(val)) {
/* Handle integer formatting. */
} else if (typeof val == "boolean") {
/* Use glyphs for booleans. */
val = val ? '<i class="bi bi-circle-fill"></i>'
: '<i class="bi bi-circle"></i>';
} else {
/* Handle floating-point numbers. */
let checkFloat = Number.parseFloat(val);
Expand Down
13 changes: 13 additions & 0 deletions runtimepy/data/js/classes/TabInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,19 @@ class TabInterface {
this.updateChannelStyles(this.channelFilter.value);
};
}

/* Initialize channel view dropdown. */
let channelView = this.query("#filter-view");
if (channelView) {
channelView.onchange = () => {
if (!channelView.value || channelView.value == '-') {
this.channelFilter.value = "";
} else {
this.channelFilter.value = channelView.value;
}
this.updateChannelStyles(this.channelFilter.value);
};
}
}

setHandler(elem) {
Expand Down
6 changes: 4 additions & 2 deletions runtimepy/data/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ function isModifierKeyEvent(event) {
}

function ignoreFilterKeyEvent(event) {
// home end pg up pg down delete f keys
return isModifierKeyEvent(event) || event.key == "Tab";
return isModifierKeyEvent(event) || event.key == "Tab" ||
(event.key.startsWith("F") && event.key.length > 1) ||
event.key.startsWith("Del") || event.key == "Home" ||
event.key == "End" || event.key == "PageUp" || event.key == "PageDown";
}

function globalKeyEvent(event) {
Expand Down
1 change: 1 addition & 0 deletions runtimepy/data/schemas/ClientConnectionConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ includes:
- has_factory.yaml
- has_name.yaml
- has_markdown.yaml
- has_views.yaml

properties:
defer:
Expand Down
9 changes: 1 addition & 8 deletions runtimepy/data/schemas/ConnectionArbiterConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,7 @@ properties:
$ref: package://runtimepy/schemas/StructConfig.yaml

commands:
type: array
items:
type: array
minItems: 2
maxItems: 2
items:
- type: string
- type: string
$ref: package://runtimepy/schemas/TwoTupleArray.yaml

tasks:
type: array
Expand Down
1 change: 1 addition & 0 deletions runtimepy/data/schemas/PeerProcessConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ includes:
- has_name.yaml
- has_config.yaml
- has_markdown.yaml
- has_views.yaml

properties:
program:
Expand Down
9 changes: 9 additions & 0 deletions runtimepy/data/schemas/TwoTupleArray.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
type: array
items:
type: array
minItems: 2
maxItems: 2
items:
- type: string
- type: string
4 changes: 4 additions & 0 deletions runtimepy/data/schemas/has_views.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
properties:
views:
$ref: package://runtimepy/schemas/TwoTupleArray.yaml
8 changes: 8 additions & 0 deletions runtimepy/net/arbiter/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def __init__(
self._ports: dict[str, int] = {}

self._commands: list[tuple[str, str]] = []
self._views: dict[str, dict[str, str]] = {}

self._init()

Expand All @@ -156,6 +157,9 @@ def _init(self) -> None:
def _register_connection(self, connection: _Connection, name: str) -> None:
"""Perform connection registration."""

if name in self._views:
connection.env.views.update(self._views[name])
del self._views[name]
self._connections[name] = connection
self.manager.queue.put_nowait(connection)
connection.logger.info("Registered as '%s'.", name)
Expand All @@ -165,6 +169,7 @@ def register_connection(
connection: _Union[_Connection, _Awaitable[_Connection]],
*names: str,
delim: str = None,
views: dict[str, str] = None,
) -> bool:
"""Attempt to register a connection object."""

Expand All @@ -173,6 +178,9 @@ def register_connection(
with self.names_pushed(*names):
name = self.namespace(delim=delim)

if views:
self._views[name] = views

if (
name not in self._connections
and name not in self._deferred_connections
Expand Down
3 changes: 2 additions & 1 deletion runtimepy/net/arbiter/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ async def process_config(
kwargs = dict_resolve_env_vars(
client.get("kwargs", {}), env=self._ports # type: ignore
)
kwargs.setdefault("markdown", client.get("markdown"))

assert await self.factory_client(
factory,
Expand All @@ -179,6 +178,8 @@ async def process_config(
defer=client["defer"],
# Perform some known fixes for common keyword arguments.
**fix_kwargs(kwargs),
views=client.get("views"),
markdown=client.get("markdown"),
), f"Couldn't register client '{name}' ({factory})!"

# Register servers.
Expand Down
7 changes: 6 additions & 1 deletion runtimepy/net/arbiter/factory/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,17 @@ async def factory_client(
if factory in self._conn_factories:
factory_inst = self._conn_factories[factory]

views = kwargs.pop("views", {})

conn = factory_inst.client(name, *args, **kwargs)
if not defer:
conn = await conn # type: ignore

result = self.register_connection(
conn, *self._conn_names[factory_inst], name
conn,
*self._conn_names[factory_inst],
name,
views=views,
)

return result
Expand Down
24 changes: 20 additions & 4 deletions runtimepy/net/server/app/env/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ def enum_dropdown(
TABLE_BUTTON_CLASSES = ("border-top-0", "border-bottom-0")


def views_dropdown(parent: Element, command: ChannelCommandProcessor) -> None:
"""Dropdown menu for channel environment views."""

select = select_element(
parent=div(tag="th", parent=parent, class_str="p-0"),
id="filter-view",
title="Canonical channel filters.",
).add_class("border-end-0", "w-100")
div(tag="option", value="", text="-", parent=select)
for text, value in command.env.views.items():
div(tag="option", value=value, text=text, parent=select)
if not command.env.views:
select.booleans.add("disabled")


def channel_table_header(
parent: Element, command: ChannelCommandProcessor
) -> None:
Expand All @@ -105,7 +120,7 @@ def channel_table_header(
).add_class(*TABLE_BUTTON_CLASSES)

_, label, box = input_box(
div(tag="th", parent=ctl_row, colspan="2", class_str="p-0"),
div(tag="th", parent=ctl_row, class_str="p-0 border-end-0"),
description="Channel name filter.",
pattern=".* ! @ $",
label="filter",
Expand All @@ -114,17 +129,18 @@ def channel_table_header(
spellcheck="false",
)
label.add_class("border-top-0", "border-bottom-0")
box.add_class("border-top-0", "border-bottom-0")
box.add_class("border-top-0", "border-bottom-0", "border-end-0")

views_dropdown(ctl_row, command)

cell = flex(
parent=div(tag="th", parent=ctl_row, colspan="2", class_str="p-0")
)

# Add a selection menu for custom commands.
# Add a selection menu for custom commands. (need a data source for this)
select = select_element(
parent=cell, id="custom-commands", title="Custom command selector."
)
select.add_class("border-start-0")

if command.custom_commands:
for key in command.custom_commands:
Expand Down
1 change: 1 addition & 0 deletions runtimepy/struct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(
self.set_markdown(config=config, markdown=markdown, package=PKG_NAME)
LoggerMixinLevelControl.__init__(self, logger=_getLogger(self.name))
ChannelEnvironmentMixin.__init__(self)
self.env.views.update(config.get("views", {})) # type: ignore
if self.log_level_channel:
self.setup_level_channel(self.env)
self.command = ChannelCommandProcessor(self.env, self.logger)
Expand Down
1 change: 1 addition & 0 deletions runtimepy/task/basic/periodic.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(
self.metrics = metrics

ChannelEnvironmentMixin.__init__(self, env=env)
self.env.views.update(self.config.get("views", {})) # type: ignore
self.setup_level_channel(self.env)
self.command = ChannelCommandProcessor(self.env, self.logger)
self.register_task_metrics(self.metrics)
Expand Down