runtimepy 5.15.3__py3-none-any.whl → 5.15.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runtimepy/__init__.py +2 -2
- runtimepy/channel/environment/base.py +5 -0
- runtimepy/channel/environment/command/processor.py +11 -1
- runtimepy/channel/environment/file.py +4 -0
- runtimepy/data/css/bootstrap_extra.css +4 -0
- runtimepy/data/css/main.css +8 -0
- runtimepy/data/dummy_load.yaml +47 -17
- runtimepy/data/js/classes/ChannelTable.js +4 -0
- runtimepy/data/js/classes/TabInterface.js +13 -0
- runtimepy/data/js/classes/WorkerInterface.js +1 -1
- runtimepy/data/js/init.js +4 -2
- runtimepy/data/js/sample.js +1 -1
- runtimepy/data/schemas/Button.yaml +17 -0
- runtimepy/data/schemas/ClientConnectionConfig.yaml +2 -0
- runtimepy/data/schemas/ConnectionArbiterConfig.yaml +1 -8
- runtimepy/data/schemas/PeerProcessConfig.yaml +1 -0
- runtimepy/data/schemas/TwoTupleArray.yaml +9 -0
- runtimepy/data/schemas/has_buttons.yaml +6 -0
- runtimepy/data/schemas/has_views.yaml +4 -0
- runtimepy/net/arbiter/base.py +20 -0
- runtimepy/net/arbiter/config/__init__.py +3 -1
- runtimepy/net/arbiter/factory/connection.py +9 -1
- runtimepy/net/server/app/env/tab/base.py +11 -0
- runtimepy/net/server/app/env/tab/html.py +10 -0
- runtimepy/net/server/app/env/widgets.py +25 -9
- runtimepy/net/server/websocket/__init__.py +1 -1
- runtimepy/requirements.txt +1 -1
- runtimepy/struct/__init__.py +3 -0
- runtimepy/task/basic/periodic.py +3 -0
- runtimepy/ui/button.py +76 -0
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/METADATA +6 -6
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/RECORD +36 -31
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/WHEEL +0 -0
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/entry_points.txt +0 -0
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/licenses/LICENSE +0 -0
- {runtimepy-5.15.3.dist-info → runtimepy-5.15.5.dist-info}/top_level.txt +0 -0
runtimepy/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# =====================================
|
|
2
2
|
# generator=datazen
|
|
3
3
|
# version=3.2.3
|
|
4
|
-
# hash=
|
|
4
|
+
# hash=8ce7abe0f8c0e64d9afc0b5e4de6e2ad
|
|
5
5
|
# =====================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
@@ -10,7 +10,7 @@ Useful defaults and other package metadata.
|
|
|
10
10
|
|
|
11
11
|
DESCRIPTION = "A framework for implementing Python services."
|
|
12
12
|
PKG_NAME = "runtimepy"
|
|
13
|
-
VERSION = "5.15.
|
|
13
|
+
VERSION = "5.15.5"
|
|
14
14
|
|
|
15
15
|
# runtimepy-specific content.
|
|
16
16
|
METRICS_NAME = "metrics"
|
|
@@ -54,6 +54,7 @@ class BaseChannelEnvironment(_NamespaceMixin, FinalizeMixin):
|
|
|
54
54
|
fields: _Iterable[_BitFields] = None,
|
|
55
55
|
namespace: Namespace = None,
|
|
56
56
|
namespace_delim: str = DEFAULT_DELIM,
|
|
57
|
+
views: dict[str, str] = None,
|
|
57
58
|
) -> None:
|
|
58
59
|
"""Initialize this channel environment."""
|
|
59
60
|
|
|
@@ -105,6 +106,10 @@ class BaseChannelEnvironment(_NamespaceMixin, FinalizeMixin):
|
|
|
105
106
|
if values is not None:
|
|
106
107
|
self.apply(values)
|
|
107
108
|
|
|
109
|
+
if not views:
|
|
110
|
+
views = {}
|
|
111
|
+
self.views = views
|
|
112
|
+
|
|
108
113
|
def __setitem__(self, key: _RegistryKey, value: ChannelValue) -> None:
|
|
109
114
|
"""Mapping-set interface."""
|
|
110
115
|
return self.set(key, value)
|
|
@@ -20,6 +20,7 @@ from runtimepy.channel.environment.command.result import SUCCESS, CommandResult
|
|
|
20
20
|
from runtimepy.mixins.environment import ChannelEnvironmentMixin
|
|
21
21
|
from runtimepy.primitives.bool import Bool
|
|
22
22
|
from runtimepy.primitives.field import BitField
|
|
23
|
+
from runtimepy.ui.button import ActionButton
|
|
23
24
|
|
|
24
25
|
CommandHook = Callable[[Namespace, Optional[FieldOrChannel]], None]
|
|
25
26
|
|
|
@@ -33,7 +34,11 @@ class ChannelCommandProcessor(ChannelEnvironmentMixin):
|
|
|
33
34
|
"""A command processing interface for channel environments."""
|
|
34
35
|
|
|
35
36
|
def __init__(
|
|
36
|
-
self,
|
|
37
|
+
self,
|
|
38
|
+
env: ChannelEnvironment,
|
|
39
|
+
logger: LoggerType,
|
|
40
|
+
buttons: list[ActionButton] = None,
|
|
41
|
+
**kwargs,
|
|
37
42
|
) -> None:
|
|
38
43
|
"""Initialize this instance."""
|
|
39
44
|
|
|
@@ -49,6 +54,11 @@ class ChannelCommandProcessor(ChannelEnvironmentMixin):
|
|
|
49
54
|
|
|
50
55
|
self.parser.initialize()
|
|
51
56
|
|
|
57
|
+
# Action buttons.
|
|
58
|
+
if buttons is None:
|
|
59
|
+
buttons = []
|
|
60
|
+
self.buttons: list[ActionButton] = buttons
|
|
61
|
+
|
|
52
62
|
def register_custom_commands(
|
|
53
63
|
self, *custom_commands: CustomCommand
|
|
54
64
|
) -> None:
|
|
@@ -42,6 +42,8 @@ ENUMS_FILE = f"{ENUMS_KEY}.json"
|
|
|
42
42
|
VALUES_FILE = f"{VALUES_KEY}.json"
|
|
43
43
|
FIELDS_FILE = f"{FIELDS_KEY}.json"
|
|
44
44
|
NAMES_FILE = f"{NAMES_KEY}.json"
|
|
45
|
+
VIEWS_KEY = "views"
|
|
46
|
+
# VIEWS_FILE = f"{VIEWS_KEY}.json"
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class FileChannelEnvironment(_BaseChannelEnvironment):
|
|
@@ -64,6 +66,7 @@ class FileChannelEnvironment(_BaseChannelEnvironment):
|
|
|
64
66
|
VALUES_KEY: _cast(
|
|
65
67
|
_JsonObject, self.values(resolve_enum=resolve_enum)
|
|
66
68
|
),
|
|
69
|
+
VIEWS_KEY: _cast(_JsonObject, self.views),
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
def export(
|
|
@@ -143,6 +146,7 @@ class FileChannelEnvironment(_BaseChannelEnvironment):
|
|
|
143
146
|
enums=enum_reg,
|
|
144
147
|
values=_cast(_Optional[_ValueMap], data.get(VALUES_KEY)),
|
|
145
148
|
fields=_fields_from_dict(data[FIELDS_KEY]),
|
|
149
|
+
views=_cast(dict[str, str], data.get(VIEWS_KEY, {})),
|
|
146
150
|
)
|
|
147
151
|
|
|
148
152
|
# Typically, externally loaded environments should be final at load
|
|
@@ -110,6 +110,10 @@ select:hover {
|
|
|
110
110
|
background-image: linear-gradient(to bottom, var(--bs-tertiary-bg), rgba(var(--bs-tertiary-bg-rgb), 0), var(--bs-tertiary-bg));
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
.bg-gradient-tertiary-left-right {
|
|
114
|
+
background-image: linear-gradient(to right, var(--bs-tertiary-bg), rgba(var(--bs-tertiary-bg-rgb), 0), var(--bs-tertiary-bg));
|
|
115
|
+
}
|
|
116
|
+
|
|
113
117
|
.border-start-info-subtle {
|
|
114
118
|
border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-info-border-subtle);
|
|
115
119
|
}
|
runtimepy/data/css/main.css
CHANGED
runtimepy/data/dummy_load.yaml
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
---
|
|
2
|
+
# UDP JSON clients.
|
|
3
|
+
clients:
|
|
4
|
+
- factory: udp_json
|
|
5
|
+
name: udp_json_client
|
|
6
|
+
defer: true
|
|
7
|
+
kwargs:
|
|
8
|
+
remote_addr: [localhost, "$udp_json"]
|
|
9
|
+
views:
|
|
10
|
+
- [metrics, metrics]
|
|
11
|
+
- [transmit, tx]
|
|
12
|
+
- [receive, rx]
|
|
13
|
+
buttons: &buttons
|
|
14
|
+
- icon: tux
|
|
15
|
+
key: test
|
|
16
|
+
payload: {}
|
|
17
|
+
- icon: tux
|
|
18
|
+
key: test
|
|
19
|
+
text: asdf
|
|
20
|
+
payload: {}
|
|
21
|
+
- icon: tux
|
|
22
|
+
key: test
|
|
23
|
+
payload: {}
|
|
24
|
+
outline: false
|
|
25
|
+
- icon: tux
|
|
26
|
+
key: test
|
|
27
|
+
text: asdf
|
|
28
|
+
payload: {}
|
|
29
|
+
outline: false
|
|
30
|
+
markdown: |
|
|
31
|
+
# `udp_json_client`
|
|
32
|
+
|
|
33
|
+
Connects to `udp_json_server`.
|
|
34
|
+
|
|
35
|
+
- factory: udp_json
|
|
36
|
+
name: udp_json_server
|
|
37
|
+
kwargs:
|
|
38
|
+
local_addr: [localhost, "$udp_json"]
|
|
39
|
+
|
|
2
40
|
# Add some sample tasks.
|
|
3
41
|
tasks:
|
|
4
42
|
# Chaos.
|
|
@@ -9,6 +47,10 @@ tasks:
|
|
|
9
47
|
a: 1
|
|
10
48
|
b: 2
|
|
11
49
|
c: 3
|
|
50
|
+
buttons: *buttons
|
|
51
|
+
views:
|
|
52
|
+
- [zero, "\\.0\\."]
|
|
53
|
+
- [one, "\\.1\\."]
|
|
12
54
|
markdown: |
|
|
13
55
|
# This is a Test
|
|
14
56
|
|
|
@@ -29,6 +71,9 @@ tasks:
|
|
|
29
71
|
- name: wave3
|
|
30
72
|
factory: sinusoid
|
|
31
73
|
period_s: 0.03
|
|
74
|
+
config:
|
|
75
|
+
views:
|
|
76
|
+
- [trig, sin cos]
|
|
32
77
|
markdown: |
|
|
33
78
|
# Markdown for wave3
|
|
34
79
|
|
|
@@ -43,23 +88,6 @@ tasks:
|
|
|
43
88
|
# Drive interactions with runtime entities that won't otherwise be polled.
|
|
44
89
|
- {name: app, factory: SampleApp, period_s: 0.25}
|
|
45
90
|
|
|
46
|
-
# UDP JSON clients.
|
|
47
|
-
clients:
|
|
48
|
-
- factory: udp_json
|
|
49
|
-
name: udp_json_client
|
|
50
|
-
defer: true
|
|
51
|
-
kwargs:
|
|
52
|
-
remote_addr: [localhost, "$udp_json"]
|
|
53
|
-
markdown: |
|
|
54
|
-
# `udp_json_client`
|
|
55
|
-
|
|
56
|
-
Connects to `udp_json_server`.
|
|
57
|
-
|
|
58
|
-
- factory: udp_json
|
|
59
|
-
name: udp_json_server
|
|
60
|
-
kwargs:
|
|
61
|
-
local_addr: [localhost, "$udp_json"]
|
|
62
|
-
|
|
63
91
|
# Add some sample structs.
|
|
64
92
|
structs:
|
|
65
93
|
- name: example.struct1
|
|
@@ -70,6 +98,8 @@ structs:
|
|
|
70
98
|
b: 2
|
|
71
99
|
c: 3
|
|
72
100
|
|
|
101
|
+
buttons: *buttons
|
|
102
|
+
|
|
73
103
|
markdown: |
|
|
74
104
|
# Docs for `example.struct1`
|
|
75
105
|
|
|
@@ -56,6 +56,10 @@ class ChannelTable {
|
|
|
56
56
|
|
|
57
57
|
if (Number.isInteger(val)) {
|
|
58
58
|
/* Handle integer formatting. */
|
|
59
|
+
} else if (typeof val == "boolean") {
|
|
60
|
+
/* Use glyphs for booleans. */
|
|
61
|
+
val = val ? '<i class="bi bi-circle-fill"></i>'
|
|
62
|
+
: '<i class="bi bi-circle"></i>';
|
|
59
63
|
} else {
|
|
60
64
|
/* Handle floating-point numbers. */
|
|
61
65
|
let checkFloat = Number.parseFloat(val);
|
|
@@ -263,6 +263,19 @@ class TabInterface {
|
|
|
263
263
|
this.updateChannelStyles(this.channelFilter.value);
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
+
|
|
267
|
+
/* Initialize channel view dropdown. */
|
|
268
|
+
let channelView = this.query("#filter-view");
|
|
269
|
+
if (channelView) {
|
|
270
|
+
channelView.onchange = () => {
|
|
271
|
+
if (!channelView.value || channelView.value == '-') {
|
|
272
|
+
this.channelFilter.value = "";
|
|
273
|
+
} else {
|
|
274
|
+
this.channelFilter.value = channelView.value;
|
|
275
|
+
}
|
|
276
|
+
this.updateChannelStyles(this.channelFilter.value);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
266
279
|
}
|
|
267
280
|
|
|
268
281
|
setHandler(elem) {
|
|
@@ -10,7 +10,7 @@ class WorkerInterface {
|
|
|
10
10
|
|
|
11
11
|
command(data) { this.send({kind : "command", value : data}); }
|
|
12
12
|
|
|
13
|
-
bus(data) { this.worker.postMessage({bus : data}); }
|
|
13
|
+
bus(key, data) { this.worker.postMessage({key : key, bus : data}); }
|
|
14
14
|
|
|
15
15
|
toWorker(data, param) { return this.send({"worker" : data}, param); }
|
|
16
16
|
}
|
runtimepy/data/js/init.js
CHANGED
|
@@ -53,8 +53,10 @@ function isModifierKeyEvent(event) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function ignoreFilterKeyEvent(event) {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
return isModifierKeyEvent(event) || event.key == "Tab" ||
|
|
57
|
+
(event.key.startsWith("F") && event.key.length > 1) ||
|
|
58
|
+
event.key.startsWith("Del") || event.key == "Home" ||
|
|
59
|
+
event.key == "End" || event.key == "PageUp" || event.key == "PageDown";
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function globalKeyEvent(event) {
|
runtimepy/data/js/sample.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
console.log(`sample.js included (${tab.name})`);
|
|
2
|
-
// tab.worker.bus({a: 1, b: 2, c: 3});
|
|
2
|
+
// tab.worker.bus("test", {a: 1, b: 2, c: 3});
|
|
@@ -27,14 +27,7 @@ properties:
|
|
|
27
27
|
$ref: package://runtimepy/schemas/StructConfig.yaml
|
|
28
28
|
|
|
29
29
|
commands:
|
|
30
|
-
|
|
31
|
-
items:
|
|
32
|
-
type: array
|
|
33
|
-
minItems: 2
|
|
34
|
-
maxItems: 2
|
|
35
|
-
items:
|
|
36
|
-
- type: string
|
|
37
|
-
- type: string
|
|
30
|
+
$ref: package://runtimepy/schemas/TwoTupleArray.yaml
|
|
38
31
|
|
|
39
32
|
tasks:
|
|
40
33
|
type: array
|
runtimepy/net/arbiter/base.py
CHANGED
|
@@ -47,6 +47,7 @@ from runtimepy.net.manager import ConnectionManager as _ConnectionManager
|
|
|
47
47
|
from runtimepy.net.server import RuntimepyServerConnection
|
|
48
48
|
from runtimepy.subprocess.peer import RuntimepyPeer as _RuntimepyPeer
|
|
49
49
|
from runtimepy.tui.mixin import CursesWindow, TuiMixin
|
|
50
|
+
from runtimepy.ui.button import ActionButton
|
|
50
51
|
|
|
51
52
|
ServerTask = _Awaitable[None]
|
|
52
53
|
RuntimeProcessTask = tuple[
|
|
@@ -147,6 +148,8 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
|
|
|
147
148
|
self._ports: dict[str, int] = {}
|
|
148
149
|
|
|
149
150
|
self._commands: list[tuple[str, str]] = []
|
|
151
|
+
self._views: dict[str, dict[str, str]] = {}
|
|
152
|
+
self._buttons: dict[str, list[ActionButton]] = {}
|
|
150
153
|
|
|
151
154
|
self._init()
|
|
152
155
|
|
|
@@ -156,6 +159,16 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
|
|
|
156
159
|
def _register_connection(self, connection: _Connection, name: str) -> None:
|
|
157
160
|
"""Perform connection registration."""
|
|
158
161
|
|
|
162
|
+
# Handle views.
|
|
163
|
+
if name in self._views:
|
|
164
|
+
connection.env.views.update(self._views[name])
|
|
165
|
+
del self._views[name]
|
|
166
|
+
|
|
167
|
+
# Handle buttons.
|
|
168
|
+
if name in self._buttons:
|
|
169
|
+
connection.command.buttons.extend(self._buttons[name])
|
|
170
|
+
del self._buttons[name]
|
|
171
|
+
|
|
159
172
|
self._connections[name] = connection
|
|
160
173
|
self.manager.queue.put_nowait(connection)
|
|
161
174
|
connection.logger.info("Registered as '%s'.", name)
|
|
@@ -165,6 +178,8 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
|
|
|
165
178
|
connection: _Union[_Connection, _Awaitable[_Connection]],
|
|
166
179
|
*names: str,
|
|
167
180
|
delim: str = None,
|
|
181
|
+
views: dict[str, str] = None,
|
|
182
|
+
buttons: list[ActionButton] = None,
|
|
168
183
|
) -> bool:
|
|
169
184
|
"""Attempt to register a connection object."""
|
|
170
185
|
|
|
@@ -173,6 +188,11 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
|
|
|
173
188
|
with self.names_pushed(*names):
|
|
174
189
|
name = self.namespace(delim=delim)
|
|
175
190
|
|
|
191
|
+
if views:
|
|
192
|
+
self._views[name] = views
|
|
193
|
+
if buttons:
|
|
194
|
+
self._buttons[name] = buttons
|
|
195
|
+
|
|
176
196
|
if (
|
|
177
197
|
name not in self._connections
|
|
178
198
|
and name not in self._deferred_connections
|
|
@@ -170,7 +170,6 @@ class ConfigConnectionArbiter(_ImportConnectionArbiter):
|
|
|
170
170
|
kwargs = dict_resolve_env_vars(
|
|
171
171
|
client.get("kwargs", {}), env=self._ports # type: ignore
|
|
172
172
|
)
|
|
173
|
-
kwargs.setdefault("markdown", client.get("markdown"))
|
|
174
173
|
|
|
175
174
|
assert await self.factory_client(
|
|
176
175
|
factory,
|
|
@@ -179,6 +178,9 @@ class ConfigConnectionArbiter(_ImportConnectionArbiter):
|
|
|
179
178
|
defer=client["defer"],
|
|
180
179
|
# Perform some known fixes for common keyword arguments.
|
|
181
180
|
**fix_kwargs(kwargs),
|
|
181
|
+
views=client.get("views"),
|
|
182
|
+
markdown=client.get("markdown"),
|
|
183
|
+
buttons=client.get("buttons", []),
|
|
182
184
|
), f"Couldn't register client '{name}' ({factory})!"
|
|
183
185
|
|
|
184
186
|
# Register servers.
|
|
@@ -16,6 +16,7 @@ from runtimepy.net.arbiter.base import (
|
|
|
16
16
|
from runtimepy.net.arbiter.base import ServerTask as _ServerTask
|
|
17
17
|
from runtimepy.net.connection import Connection as _Connection
|
|
18
18
|
from runtimepy.net.manager import ConnectionManager as _ConnectionManager
|
|
19
|
+
from runtimepy.ui.button import ActionButton
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ConnectionFactory:
|
|
@@ -89,12 +90,19 @@ class FactoryConnectionArbiter(_BaseConnectionArbiter):
|
|
|
89
90
|
if factory in self._conn_factories:
|
|
90
91
|
factory_inst = self._conn_factories[factory]
|
|
91
92
|
|
|
93
|
+
views = kwargs.pop("views", {})
|
|
94
|
+
buttons = ActionButton.from_top_level(kwargs.pop("buttons", []))
|
|
95
|
+
|
|
92
96
|
conn = factory_inst.client(name, *args, **kwargs)
|
|
93
97
|
if not defer:
|
|
94
98
|
conn = await conn # type: ignore
|
|
95
99
|
|
|
96
100
|
result = self.register_connection(
|
|
97
|
-
conn,
|
|
101
|
+
conn,
|
|
102
|
+
*self._conn_names[factory_inst],
|
|
103
|
+
name,
|
|
104
|
+
views=views,
|
|
105
|
+
buttons=buttons,
|
|
98
106
|
)
|
|
99
107
|
|
|
100
108
|
return result
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
A module implementing a channel-environment tab HTML interface.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
# built-in
|
|
6
|
+
from io import StringIO
|
|
7
|
+
|
|
5
8
|
# third-party
|
|
6
9
|
from vcorelib.io.markdown import MarkdownMixin
|
|
7
10
|
from vcorelib.logging import LoggerMixin
|
|
@@ -39,3 +42,11 @@ class ChannelEnvironmentTabBase(Tab, LoggerMixin, MarkdownMixin):
|
|
|
39
42
|
# Logging.
|
|
40
43
|
LoggerMixin.__init__(self, logger=self.command.logger)
|
|
41
44
|
self.log_limiter = RateLimiter.from_s(1.0)
|
|
45
|
+
|
|
46
|
+
def _action_markdown(self) -> str:
|
|
47
|
+
"""Get action-button markdown."""
|
|
48
|
+
|
|
49
|
+
with StringIO() as stream:
|
|
50
|
+
for button in self.command.buttons:
|
|
51
|
+
button.element().encode(stream)
|
|
52
|
+
return stream.getvalue()
|
|
@@ -289,6 +289,16 @@ class ChannelEnvironmentTabHtml(ChannelEnvironmentTabControls):
|
|
|
289
289
|
)
|
|
290
290
|
logs.booleans.add("readonly")
|
|
291
291
|
|
|
292
|
+
if self.command.buttons:
|
|
293
|
+
centered_markdown(
|
|
294
|
+
vert_container,
|
|
295
|
+
self._action_markdown(),
|
|
296
|
+
"border-start",
|
|
297
|
+
"border-bottom",
|
|
298
|
+
"border-end",
|
|
299
|
+
"bg-gradient-tertiary-left-right",
|
|
300
|
+
)
|
|
301
|
+
|
|
292
302
|
self.channel_table(vert_container)
|
|
293
303
|
|
|
294
304
|
centered_markdown(
|
|
@@ -80,6 +80,22 @@ def enum_dropdown(
|
|
|
80
80
|
TABLE_BUTTON_CLASSES = ("border-top-0", "border-bottom-0")
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
def views_dropdown(parent: Element, command: ChannelCommandProcessor) -> None:
|
|
84
|
+
"""Dropdown menu for channel environment views."""
|
|
85
|
+
|
|
86
|
+
select = select_element(
|
|
87
|
+
parent=div(
|
|
88
|
+
tag="th", parent=parent, class_str="p-0 channel-views-min-width"
|
|
89
|
+
),
|
|
90
|
+
id="filter-view",
|
|
91
|
+
title="Canonical channel filters.",
|
|
92
|
+
).add_class("border-end-0", "w-100")
|
|
93
|
+
div(tag="option", value="", text="-", parent=select)
|
|
94
|
+
div(tag="option", value="^$", text="hide", parent=select)
|
|
95
|
+
for text, value in command.env.views.items():
|
|
96
|
+
div(tag="option", value=value, text=text, parent=select)
|
|
97
|
+
|
|
98
|
+
|
|
83
99
|
def channel_table_header(
|
|
84
100
|
parent: Element, command: ChannelCommandProcessor
|
|
85
101
|
) -> None:
|
|
@@ -105,7 +121,11 @@ def channel_table_header(
|
|
|
105
121
|
).add_class(*TABLE_BUTTON_CLASSES)
|
|
106
122
|
|
|
107
123
|
_, label, box = input_box(
|
|
108
|
-
div(
|
|
124
|
+
div(
|
|
125
|
+
tag="th",
|
|
126
|
+
parent=ctl_row,
|
|
127
|
+
class_str="p-0 border-end-0 channel-filter-min-width",
|
|
128
|
+
),
|
|
109
129
|
description="Channel name filter.",
|
|
110
130
|
pattern=".* ! @ $",
|
|
111
131
|
label="filter",
|
|
@@ -114,7 +134,9 @@ def channel_table_header(
|
|
|
114
134
|
spellcheck="false",
|
|
115
135
|
)
|
|
116
136
|
label.add_class("border-top-0", "border-bottom-0")
|
|
117
|
-
box.add_class("border-top-0", "border-bottom-0")
|
|
137
|
+
box.add_class("border-top-0", "border-bottom-0", "border-end-0")
|
|
138
|
+
|
|
139
|
+
views_dropdown(ctl_row, command)
|
|
118
140
|
|
|
119
141
|
cell = flex(
|
|
120
142
|
parent=div(tag="th", parent=ctl_row, colspan="2", class_str="p-0")
|
|
@@ -124,7 +146,6 @@ def channel_table_header(
|
|
|
124
146
|
select = select_element(
|
|
125
147
|
parent=cell, id="custom-commands", title="Custom command selector."
|
|
126
148
|
)
|
|
127
|
-
select.add_class("border-start-0")
|
|
128
149
|
|
|
129
150
|
if command.custom_commands:
|
|
130
151
|
for key in command.custom_commands:
|
|
@@ -141,12 +162,7 @@ def channel_table_header(
|
|
|
141
162
|
tooltip="Send selected command (left dropdown).",
|
|
142
163
|
).add_class(*TABLE_BUTTON_CLASSES)
|
|
143
164
|
else:
|
|
144
|
-
div(
|
|
145
|
-
tag="option",
|
|
146
|
-
parent=select,
|
|
147
|
-
value="noop",
|
|
148
|
-
text="no custom commands",
|
|
149
|
-
)
|
|
165
|
+
div(tag="option", parent=select, value="noop", text="-")
|
|
150
166
|
select.booleans.add("disabled")
|
|
151
167
|
|
|
152
168
|
# Button for 'reset all defaults' if this tab has more than one channel
|
runtimepy/requirements.txt
CHANGED
runtimepy/struct/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ from runtimepy.channel.environment.command.processor import (
|
|
|
21
21
|
from runtimepy.mixins.async_command import AsyncCommandProcessingMixin
|
|
22
22
|
from runtimepy.mixins.environment import ChannelEnvironmentMixin
|
|
23
23
|
from runtimepy.mixins.logging import LoggerMixinLevelControl
|
|
24
|
+
from runtimepy.ui.button import ActionButton
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class RuntimeStructBase(
|
|
@@ -45,10 +46,12 @@ class RuntimeStructBase(
|
|
|
45
46
|
self.set_markdown(config=config, markdown=markdown, package=PKG_NAME)
|
|
46
47
|
LoggerMixinLevelControl.__init__(self, logger=_getLogger(self.name))
|
|
47
48
|
ChannelEnvironmentMixin.__init__(self)
|
|
49
|
+
self.env.views.update(config.get("views", {})) # type: ignore
|
|
48
50
|
if self.log_level_channel:
|
|
49
51
|
self.setup_level_channel(self.env)
|
|
50
52
|
self.command = ChannelCommandProcessor(self.env, self.logger)
|
|
51
53
|
self.config = config
|
|
54
|
+
self.command.buttons.extend(ActionButton.from_top_level(self.config))
|
|
52
55
|
|
|
53
56
|
async def poll(args: Namespace, __: Optional[FieldOrChannel]) -> None:
|
|
54
57
|
"""Handle a test command."""
|
runtimepy/task/basic/periodic.py
CHANGED
|
@@ -33,6 +33,7 @@ from runtimepy.primitives import Bool as _Bool
|
|
|
33
33
|
from runtimepy.primitives import Double as _Double
|
|
34
34
|
from runtimepy.primitives import Float as _Float
|
|
35
35
|
from runtimepy.primitives.evaluation import EvalResult as _EvalResult
|
|
36
|
+
from runtimepy.ui.button import ActionButton
|
|
36
37
|
from runtimepy.ui.controls import Controlslike
|
|
37
38
|
|
|
38
39
|
|
|
@@ -72,8 +73,10 @@ class PeriodicTask(
|
|
|
72
73
|
self.metrics = metrics
|
|
73
74
|
|
|
74
75
|
ChannelEnvironmentMixin.__init__(self, env=env)
|
|
76
|
+
self.env.views.update(self.config.get("views", {})) # type: ignore
|
|
75
77
|
self.setup_level_channel(self.env)
|
|
76
78
|
self.command = ChannelCommandProcessor(self.env, self.logger)
|
|
79
|
+
self.command.buttons.extend(ActionButton.from_top_level(self.config))
|
|
77
80
|
self.register_task_metrics(self.metrics)
|
|
78
81
|
|
|
79
82
|
# State.
|
runtimepy/ui/button.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A module implementing an action-button interface.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# built-in
|
|
6
|
+
from json import dumps
|
|
7
|
+
from typing import NamedTuple
|
|
8
|
+
|
|
9
|
+
# third-party
|
|
10
|
+
from svgen.element import Element
|
|
11
|
+
from svgen.element.html import div
|
|
12
|
+
from vcorelib.dict import GenericStrDict
|
|
13
|
+
|
|
14
|
+
# internal
|
|
15
|
+
from runtimepy.net.html.bootstrap import icon_str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ActionButton(NamedTuple):
|
|
19
|
+
"""A class implementing an interface for action buttons."""
|
|
20
|
+
|
|
21
|
+
key: str
|
|
22
|
+
payload: GenericStrDict
|
|
23
|
+
text: str
|
|
24
|
+
icon: str
|
|
25
|
+
variant: str
|
|
26
|
+
outline: bool
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def from_dict(data: GenericStrDict) -> "ActionButton":
|
|
30
|
+
"""Create an action button from dictionary data."""
|
|
31
|
+
|
|
32
|
+
return ActionButton(
|
|
33
|
+
data["key"],
|
|
34
|
+
data["payload"],
|
|
35
|
+
data.get("text", ""),
|
|
36
|
+
data.get("icon", ""),
|
|
37
|
+
data.get("variant", "primary"),
|
|
38
|
+
data.get("outline", True),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def from_top_level(
|
|
43
|
+
data: GenericStrDict | list[GenericStrDict],
|
|
44
|
+
) -> list["ActionButton"]:
|
|
45
|
+
"""Create an action button from dictionary data."""
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
[ActionButton.from_dict(x) for x in data.get("buttons", [])]
|
|
49
|
+
if isinstance(data, dict)
|
|
50
|
+
else [ActionButton.from_dict(x) for x in data]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def element(self) -> Element:
|
|
54
|
+
"""Create an action button element."""
|
|
55
|
+
|
|
56
|
+
payload = dumps(self.payload).replace('"', """)
|
|
57
|
+
|
|
58
|
+
text_parts = []
|
|
59
|
+
if self.icon:
|
|
60
|
+
text_parts.append(
|
|
61
|
+
icon_str(
|
|
62
|
+
self.icon,
|
|
63
|
+
[f"text-{self.variant}-emphasis"] if self.text else [],
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
if self.text:
|
|
67
|
+
text_parts.append(self.text)
|
|
68
|
+
|
|
69
|
+
return div(
|
|
70
|
+
tag="button",
|
|
71
|
+
type="button",
|
|
72
|
+
onclick=f"tabs[shown_tab].worker.bus('{self.key}', {payload})",
|
|
73
|
+
class_str=f"btn btn{'-outline' if self.outline else ''}"
|
|
74
|
+
f"-{self.variant} m-2 ms-1 me-0",
|
|
75
|
+
text=" ".join(text_parts),
|
|
76
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtimepy
|
|
3
|
-
Version: 5.15.
|
|
3
|
+
Version: 5.15.5
|
|
4
4
|
Summary: A framework for implementing Python services.
|
|
5
5
|
Home-page: https://github.com/libre-embedded/runtimepy
|
|
6
6
|
Author: Libre Embedded
|
|
@@ -17,11 +17,11 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
17
17
|
Requires-Python: >=3.12
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: psutil
|
|
21
21
|
Requires-Dist: svgen>=0.8.0
|
|
22
|
+
Requires-Dist: aiofiles
|
|
23
|
+
Requires-Dist: vcorelib>=3.6.4
|
|
22
24
|
Requires-Dist: websockets
|
|
23
|
-
Requires-Dist: psutil
|
|
24
|
-
Requires-Dist: vcorelib>=3.6.3
|
|
25
25
|
Provides-Extra: test
|
|
26
26
|
Requires-Dist: pylint; extra == "test"
|
|
27
27
|
Requires-Dist: flake8; extra == "test"
|
|
@@ -51,11 +51,11 @@ Dynamic: requires-python
|
|
|
51
51
|
=====================================
|
|
52
52
|
generator=datazen
|
|
53
53
|
version=3.2.3
|
|
54
|
-
hash=
|
|
54
|
+
hash=fd0e57419cf9437a64c4f82cae3625f0
|
|
55
55
|
=====================================
|
|
56
56
|
-->
|
|
57
57
|
|
|
58
|
-
# runtimepy ([5.15.
|
|
58
|
+
# runtimepy ([5.15.5](https://pypi.org/project/runtimepy/))
|
|
59
59
|
|
|
60
60
|
[](https://pypi.org/project/runtimepy/)
|
|
61
61
|

|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
runtimepy/__init__.py,sha256=
|
|
1
|
+
runtimepy/__init__.py,sha256=wVKBUb0d3kdEu2RAyDqlguPD6weDDr_KQAXrx0YQlCY,391
|
|
2
2
|
runtimepy/__main__.py,sha256=IKioH2xOtsXwrwb9zABDQEJvuAX--Lnh84TeSz0XSs0,332
|
|
3
3
|
runtimepy/app.py,sha256=Er1ZKKrG9U0FV0gQg_GYF9xDb89HgYnVzS5SjxGa2Tg,970
|
|
4
4
|
runtimepy/dev_requirements.txt,sha256=VZhW6bJ5YbwaoN4d_XxZFuN5BbDLaG7ngKrGnugVPRw,245
|
|
5
5
|
runtimepy/entry.py,sha256=fy9irGEgyFAsu868bOymHX8I3CkkrMNThUWAm-4chiM,1930
|
|
6
6
|
runtimepy/mapping.py,sha256=VQK1vzmQVvYYKI85_II37-hIEbvgL3PzNy-WI6TTo80,5091
|
|
7
7
|
runtimepy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
runtimepy/requirements.txt,sha256=
|
|
8
|
+
runtimepy/requirements.txt,sha256=74DDO5RyBpZ38ziwZQinykqNsj1x9AHz4iZd0z12VgA,124
|
|
9
9
|
runtimepy/schemas.py,sha256=zTgxPm9DHZ0R_bmmOjNQMTXdtM_Hb1bE-Fog40jDCgg,839
|
|
10
10
|
runtimepy/util.py,sha256=ZHSucNi-gbrcajoCv2dNjQs48dJPC3mTM_wZHx7AW1U,1719
|
|
11
11
|
runtimepy/channel/__init__.py,sha256=bI1mfHmjUGrYcwePip2ddbMaGSkyQXQVfESL5dqXRdU,4716
|
|
12
12
|
runtimepy/channel/registry.py,sha256=AiRhSSnNbWCxwC-m8vP2fRaN1cfm5JjvlphQzkETf9w,4742
|
|
13
13
|
runtimepy/channel/environment/__init__.py,sha256=Mmq4S4NCX9pKSH7XEmD04zm0YcRhrTc8mGb25dTULgw,5333
|
|
14
14
|
runtimepy/channel/environment/array.py,sha256=f9cWaYsRXUw8qE629h6jQxbYKDpOwC2GLBo4QaMa1JM,3748
|
|
15
|
-
runtimepy/channel/environment/base.py,sha256=
|
|
15
|
+
runtimepy/channel/environment/base.py,sha256=vZAPydAZ58qGf3HvUR6KjB5RzARHfigW1s01VZNcCAs,14467
|
|
16
16
|
runtimepy/channel/environment/create.py,sha256=UVO_R5rM6xLjpAh3j3wwix-0V3uJMAno2tbBIV4boo8,5868
|
|
17
|
-
runtimepy/channel/environment/file.py,sha256=
|
|
17
|
+
runtimepy/channel/environment/file.py,sha256=keAlt9CrGXHVbHimZV0gv0WZkKYBQ-c9YHgQtojceAk,7125
|
|
18
18
|
runtimepy/channel/environment/sample.py,sha256=6926v0ZssvfeWX_C8MSbUfYjE4sr3jt0CLVSJ_2Cl3U,5316
|
|
19
19
|
runtimepy/channel/environment/telemetry.py,sha256=3A7Xcp-4eHJWz_oR1SnI6rsl4o8wiSUaiMHrnK1IaQ8,5338
|
|
20
20
|
runtimepy/channel/environment/command/__init__.py,sha256=PRuIp29QYkTDHF95HL55G6ogQDkVbq0M3SnNre4JXpI,9150
|
|
21
21
|
runtimepy/channel/environment/command/parser.py,sha256=cMOsEsXnfFlATiWTNSxlgvc_XoICsJlcZigFJlQ47tk,1804
|
|
22
|
-
runtimepy/channel/environment/command/processor.py,sha256=
|
|
22
|
+
runtimepy/channel/environment/command/processor.py,sha256=5Vzqgd8GP-3qUGT7m-dY8i2laKRouDrPPD3sUS49XMg,7367
|
|
23
23
|
runtimepy/channel/environment/command/result.py,sha256=Ko5lK4d04Z266WuCi2sHQItbUHJFYv7qUdrDi-OVKOU,624
|
|
24
24
|
runtimepy/channel/event/__init__.py,sha256=9LCSNa1Iiwr6Q6JkwQGELDQ7rWfU_xjAMh6qM1I-ueM,2793
|
|
25
25
|
runtimepy/channel/event/header.py,sha256=eDRZgzzM5HZQ8QtV4DjyAsrAhEZyM7IfwqK6WB5ACEw,868
|
|
@@ -45,7 +45,7 @@ runtimepy/control/env/__init__.py,sha256=RHJqysY7Pv4VDs2SGk0X-qc5xp_SrQ_oxb5Deug
|
|
|
45
45
|
runtimepy/data/404.md,sha256=_yzjvp2GeMc5OBvZX2zDbX-Ns6T7HCO9esRbS5PmkXU,397
|
|
46
46
|
runtimepy/data/base.yaml,sha256=PEsvcMV1qFb1Gr6Cx2SgmR6ScfPyuu3yTdnvSBTVSdA,453
|
|
47
47
|
runtimepy/data/browser.yaml,sha256=oc5KEV1C1uAJ4MkhNo4hyVVfJtZvHelRNqzNvD313Ow,79
|
|
48
|
-
runtimepy/data/dummy_load.yaml,sha256=
|
|
48
|
+
runtimepy/data/dummy_load.yaml,sha256=f6UYq6YVuv-lYZDN8h5OUwIWkpzSb7cQwqvL4BXX7ZI,2752
|
|
49
49
|
runtimepy/data/factories.yaml,sha256=CdpnjK2glj8HewI2tFSNvCSUdcNPeVD_tFbGy2QBSQg,1952
|
|
50
50
|
runtimepy/data/favicon.ico,sha256=boxAGaHbUjMFrOO2TZpsO0nIRC-LUgwHVQYOiG1YQnM,362870
|
|
51
51
|
runtimepy/data/sample_telemetry.yaml,sha256=OpdFurkvtWJGaNl9LMlU2rKo15AaVVr-U_hoZfsbp-Y,695
|
|
@@ -53,21 +53,21 @@ runtimepy/data/server.yaml,sha256=wS_Ceiu2TpkfPurpqoYoPlgzc9DAWtUd24MW7t-S5rU,97
|
|
|
53
53
|
runtimepy/data/server_base.yaml,sha256=Fiqz9C_ikxtYYFO8q17XzgaY5b3H9RuyqJr-QoTk-qg,524
|
|
54
54
|
runtimepy/data/server_dev.yaml,sha256=nQsPh7LuQig3pzHfdg_aD3yOUiCj1sKKfI-WwW3hXmQ,523
|
|
55
55
|
runtimepy/data/tftp_server.yaml,sha256=-bFOWJSagI-fEQQcT8k7eDMJVfSPm2XAxLVG3dqUTa4,204
|
|
56
|
-
runtimepy/data/css/bootstrap_extra.css,sha256=
|
|
56
|
+
runtimepy/data/css/bootstrap_extra.css,sha256=0NiTnZWnIYf04n9NHVe-nVSOLwePJ6X-3uCgUgAEKPI,3101
|
|
57
57
|
runtimepy/data/css/font.css,sha256=Pe82E66rNi-cwlQ-_1GHAuhPGu5L4x5KqgV0dbDe51w,977
|
|
58
|
-
runtimepy/data/css/main.css,sha256=
|
|
58
|
+
runtimepy/data/css/main.css,sha256=B3cBrwz6Leox1tYHTzJ7dW7Le0gf5_qZDCNlRsu-0aY,1316
|
|
59
59
|
runtimepy/data/js/DataConnection.js,sha256=DnX8FMehjJXqmI62UMYXSvl_XdfQMzq3XUDFbLu2GgI,98
|
|
60
60
|
runtimepy/data/js/JsonConnection.js,sha256=rclZrbmWc_zSs6I_JhOgxnVPFIyPMo5WdjAe8alyZ3o,2729
|
|
61
61
|
runtimepy/data/js/audio.js,sha256=bLkBqbeHMiGGidfL3iXjmVoF9seK-ZeZ3kwgOrcpgk4,1092
|
|
62
62
|
runtimepy/data/js/events.js,sha256=rgz3Q_8J6sfU_7Sa7fG1mZD0pQ4S3vwN2mqcvQfePkM,554
|
|
63
|
-
runtimepy/data/js/init.js,sha256=
|
|
63
|
+
runtimepy/data/js/init.js,sha256=LSZQKlgcIWmq4ue4kD1okRnN1qaVv0RalxFnqRiKWzI,2160
|
|
64
64
|
runtimepy/data/js/main.js,sha256=nYIQ6O76EWqlzwX7oEwPXqC-LCUFCZYDADK9QbYRDKk,404
|
|
65
65
|
runtimepy/data/js/markdown_page.js,sha256=R7Z2CwCk0dXED_4lwvDMXRNNB9ki9RvfwEKLPSwNWec,982
|
|
66
|
-
runtimepy/data/js/sample.js,sha256
|
|
66
|
+
runtimepy/data/js/sample.js,sha256=1aLHC-dsQZIWYH58P_AWGTzGS9YHwzT1aqH3M6Y_JaU,96
|
|
67
67
|
runtimepy/data/js/util.js,sha256=Xc8pHUiFDBDvIqTamWrCYUOpF7iR9VNvPDCSCQAfLDA,1424
|
|
68
68
|
runtimepy/data/js/worker.js,sha256=V9deGAynjvUr1D-WGi3wUW8rxoaNLvBvayMoLFZk3w0,2444
|
|
69
69
|
runtimepy/data/js/classes/App.js,sha256=t3XVC7zSi8L_sMJ4KP-lyWyuD4cysCKkptN-lgjewN8,5159
|
|
70
|
-
runtimepy/data/js/classes/ChannelTable.js,sha256=
|
|
70
|
+
runtimepy/data/js/classes/ChannelTable.js,sha256=djnUVmZyG6NDMLBKol8lN1tjfLd7pnhMVR1MyYXSnL4,2792
|
|
71
71
|
runtimepy/data/js/classes/DataConnection.js,sha256=DnX8FMehjJXqmI62UMYXSvl_XdfQMzq3XUDFbLu2GgI,98
|
|
72
72
|
runtimepy/data/js/classes/JsonConnection.js,sha256=vgQ3bGvVU5dNXn_uwvH7HjOQm0PwUSx0239Vfi7h1vE,2858
|
|
73
73
|
runtimepy/data/js/classes/OverlayManager.js,sha256=-eH8NtJi8doBUB2Czqand4cWuQlddo1p1wVUfjVx9w0,3362
|
|
@@ -78,10 +78,10 @@ runtimepy/data/js/classes/PlotModalManager.js,sha256=lEbACLC5VzV-aSAb7G-WacmLLf_
|
|
|
78
78
|
runtimepy/data/js/classes/PointBuffer.js,sha256=iVtq_q5gBaV9IVX55pHVjQdMJ4phJ6QTdAiM7EZrLRA,5061
|
|
79
79
|
runtimepy/data/js/classes/PointManager.js,sha256=0lr2AReLdDNrY47UuOjjCRDPMiSRsOVggFHDPf8V-6Y,460
|
|
80
80
|
runtimepy/data/js/classes/TabFilter.js,sha256=xajU1ZzVI0jFGhaHkKFX4-d2n2KlxiTC4it3N53-KxM,2115
|
|
81
|
-
runtimepy/data/js/classes/TabInterface.js,sha256=
|
|
81
|
+
runtimepy/data/js/classes/TabInterface.js,sha256=SXUSbhJyyLRWAPzbfKvn4IBMdk6fOgQcz4-DXh0JmR4,15280
|
|
82
82
|
runtimepy/data/js/classes/UnitSystem.js,sha256=ys4OMabq47k_VvJpRItm82U0IequDvx3ysRJOQzDf94,906
|
|
83
83
|
runtimepy/data/js/classes/WindowHashManager.js,sha256=aR8zs1gUq9vkStLjyQj4rssx4dujLdDcoWNs91ViV44,7461
|
|
84
|
-
runtimepy/data/js/classes/WorkerInterface.js,sha256=
|
|
84
|
+
runtimepy/data/js/classes/WorkerInterface.js,sha256=JtRWnLDPL09aUfhniWG5tqgkkeOnRkvIPJPhwI7S76U,418
|
|
85
85
|
runtimepy/data/js/tab/env.js,sha256=MB79l3XyXKELWRqHcTnwWHiwdiceLHl1N_s-mS33pyU,22
|
|
86
86
|
runtimepy/data/js/tab/sound.js,sha256=RSKp0AXM_zGOCsUvIT-BUjIzOE7Dp5NHiQG4fy7gBgY,1388
|
|
87
87
|
runtimepy/data/js/third-party/webgl-debug.js,sha256=AtuSr5qje8a37S2ZE-ztA01QuGlGj9bGl-ntjlaqJIo,36878
|
|
@@ -92,24 +92,28 @@ runtimepy/data/md/RuntimeStruct.md,sha256=Mu6fYUk2GYfGYQPFUZkXvTYkj2WSR0T_gKJXh0
|
|
|
92
92
|
runtimepy/data/md/RuntimepyPeer.md,sha256=ayjL1V5KXRBEixG0vfxIajY2Dt_H5SYaZGSscXHSL4E,34
|
|
93
93
|
runtimepy/data/md/SinusoidTask.md,sha256=nTGmZXFv-r9d-ZgmmIHr6SKSp-K-nzJW-B6NKhg7FQ8,739
|
|
94
94
|
runtimepy/data/schemas/BitFields.yaml,sha256=iE_3y30CgcpxvzwoC96BDhwbu2PZC-Lqu5C7XUeZ1go,1086
|
|
95
|
+
runtimepy/data/schemas/Button.yaml,sha256=avEdgdMQzVnzfVAZU2hTrERCcty47y9MYpRKkEkHY1Q,241
|
|
95
96
|
runtimepy/data/schemas/Channel.yaml,sha256=9OQ3mOtPOlcWugvObpWKoAdFW0WvR9mSEzfdDYNf3fc,471
|
|
96
97
|
runtimepy/data/schemas/ChannelCommand.yaml,sha256=h7-n5WjNwWgteZ8U5YpaBRncR_Uiqaa_wVfuTWQFrTY,257
|
|
97
98
|
runtimepy/data/schemas/ChannelRegistry.yaml,sha256=f51YngVC8zDM3HwRMicKBOyoqjJ9EWsx3GWD0EkHCZk,136
|
|
98
|
-
runtimepy/data/schemas/ClientConnectionConfig.yaml,sha256=
|
|
99
|
-
runtimepy/data/schemas/ConnectionArbiterConfig.yaml,sha256=
|
|
99
|
+
runtimepy/data/schemas/ClientConnectionConfig.yaml,sha256=cfiQe3K7h3dAHsEFqBqN8qc-nWX7AntOKgrHkWIAvWU,227
|
|
100
|
+
runtimepy/data/schemas/ConnectionArbiterConfig.yaml,sha256=iZI3psx0_v7TBiC_qae0mf1FNs615ZMXBTcp7SQrF-c,2203
|
|
100
101
|
runtimepy/data/schemas/EnumRegistry.yaml,sha256=BfLzEEUsHj6Fg_0JyGmgbNJ7F-auuLs5tAhJjSc86uU,145
|
|
101
102
|
runtimepy/data/schemas/FindFile.yaml,sha256=dSPCDQy4FQZLMgOjtbR0-o2sZECvN_tvGa3LXNd1-wc,934
|
|
102
|
-
runtimepy/data/schemas/PeerProcessConfig.yaml,sha256=
|
|
103
|
+
runtimepy/data/schemas/PeerProcessConfig.yaml,sha256=SA2DnZImkM8hfxLmg6N7r4sUHEdrL0FtYaDdsl1cIAQ,209
|
|
103
104
|
runtimepy/data/schemas/RuntimeEnum.yaml,sha256=P3SqlxJ7d_tbZlHZBZy2cCcnOASP8APwYKzAwITd7Kg,518
|
|
104
105
|
runtimepy/data/schemas/ServerConnectionConfig.yaml,sha256=IqQUQB09xte4zGmM6Qi0-LTm5McDkoi2tehYspoNUXM,100
|
|
105
106
|
runtimepy/data/schemas/StructConfig.yaml,sha256=KXcHMYsM5JgsIlHhfXW8tLx5GuekS6m5jsgPCm-_tZ4,175
|
|
106
107
|
runtimepy/data/schemas/TaskConfig.yaml,sha256=upEfToPllsqEcDoJf9D3rbvXk08nc0slmlUSMyCOYcQ,247
|
|
108
|
+
runtimepy/data/schemas/TwoTupleArray.yaml,sha256=HmZhgszwzSjj6j3GVAn6QNjPFaHGut9SZfskGWNoeXI,112
|
|
107
109
|
runtimepy/data/schemas/channel_controls.yaml,sha256=pctqV_fVjRpiKdaPqET7yyoGVYQuUtOYc1FLQUdfVJc,546
|
|
110
|
+
runtimepy/data/schemas/has_buttons.yaml,sha256=DQt4wQKQewIOq4du-KDUfd-Tr6YrE3x7OBF9aj6rPYg,106
|
|
108
111
|
runtimepy/data/schemas/has_config.yaml,sha256=SMuJ9tNWw06tsgDasz4myO9SSmMZGep9xBfJ89GJnqA,80
|
|
109
112
|
runtimepy/data/schemas/has_factory.yaml,sha256=Rsqrxv3HEBUGxPcW6CN7QczA96iBSgtm0gQNsu2P8LM,106
|
|
110
113
|
runtimepy/data/schemas/has_markdown.yaml,sha256=LbnEQRYdk5KR_GgORmIOiKjRg-HF8wjhA6Dm6pSm66I,45
|
|
111
114
|
runtimepy/data/schemas/has_name.yaml,sha256=I5YkXxQRYz-1-49CLyr_NA2zVqyRVQCAsb-icTaxF4s,59
|
|
112
115
|
runtimepy/data/schemas/has_request_flag.yaml,sha256=S8ly8g-FkrCVNEizbXPqicg_hpGvtH7WRsHZAA4uhjc,66
|
|
116
|
+
runtimepy/data/schemas/has_views.yaml,sha256=X-V30he_Y_l_T8teMCROp4byaJNK9I3tihE5YhP8Le8,82
|
|
113
117
|
runtimepy/data/static/css/bootstrap-icons.min.css,sha256=YWcTOU0uDzjc0ziOtRfrECKA-JAeU4SYUgIHmPwkqSk,87029
|
|
114
118
|
runtimepy/data/static/css/bootstrap.min.css,sha256=x7AwauKmt5DrGTlkPvW6439HiL-5Pjd1doeExMdCx3M,231973
|
|
115
119
|
runtimepy/data/static/css/fonts/bootstrap-icons.woff,sha256=9VUTt7WRy4SjuH_w406iTUgx1v7cIuVLkRymS1tUShU,180288
|
|
@@ -167,17 +171,17 @@ runtimepy/net/ssl.py,sha256=dj9uECPKDT5k-5vlR5I3Z7Go3WWZhbaJ9nb0rC3kJvg,854
|
|
|
167
171
|
runtimepy/net/util.py,sha256=XTQQ-Ql_ImhVd1_O8nSeDX9MY8xJwRBggvliSLCrsc8,5913
|
|
168
172
|
runtimepy/net/apps/__init__.py,sha256=vjo7e19QXtJwe6V6B-QGvYiJveYobnYIfpkKZrnS17w,710
|
|
169
173
|
runtimepy/net/arbiter/__init__.py,sha256=ptKF995rYKvkm4Mya92vA5QEDqcFq5NRD0IYGqZ6_do,740
|
|
170
|
-
runtimepy/net/arbiter/base.py,sha256=
|
|
174
|
+
runtimepy/net/arbiter/base.py,sha256=TDWIphAiBR_b7MuieVZM2_PgV-EEFmmmVq-Yjobm13w,15865
|
|
171
175
|
runtimepy/net/arbiter/info.py,sha256=QcBB1LQELXPs-Ud-GVq3aDBL4mpSMDcjjLtuKgojiU8,10962
|
|
172
176
|
runtimepy/net/arbiter/result.py,sha256=PHZo5qj4SI08ZeWPFk_8OZ1umI6L0dp5nJpjjS8QUpM,2871
|
|
173
177
|
runtimepy/net/arbiter/task.py,sha256=APcc5QioAG8uueIzxJU-vktIn8Ys3yJd_CFlRWb6Ieo,795
|
|
174
178
|
runtimepy/net/arbiter/udp.py,sha256=-ecHJs-utcsiTfr5wSb73hUUuYFBeRVT_D1znYp5q6A,893
|
|
175
179
|
runtimepy/net/arbiter/websocket.py,sha256=vYTDA7sXMUTTqW3i1zTWUdaxGKxfrQcQoVSYZtmCvfQ,1524
|
|
176
|
-
runtimepy/net/arbiter/config/__init__.py,sha256=
|
|
180
|
+
runtimepy/net/arbiter/config/__init__.py,sha256=Z-E7hJpwReVUannu_ksUHts1nNAuOHtQLxMdbB7VZw4,8826
|
|
177
181
|
runtimepy/net/arbiter/config/codec.py,sha256=JVutIe1AFVr3NN7GS4rZayxZR05LWzQZRqzEuJpEJd8,2635
|
|
178
182
|
runtimepy/net/arbiter/config/util.py,sha256=MezLSil5mEwioI5kxDOZa9y118st1_t0El8DYH6hFU8,1254
|
|
179
183
|
runtimepy/net/arbiter/factory/__init__.py,sha256=jC1szUYucH4Us3jbb0mhFgnklHlorya5DYGGw2pqfe4,334
|
|
180
|
-
runtimepy/net/arbiter/factory/connection.py,sha256=
|
|
184
|
+
runtimepy/net/arbiter/factory/connection.py,sha256=TxcwjK-Q2TEY20eBW-R5meko3RrIPcnvbrzHZlqV9jo,3952
|
|
181
185
|
runtimepy/net/arbiter/factory/task.py,sha256=YLRv55dnDBqHRJIMYgO9_uHi0AW-8hwa0n3wHAKyncE,1959
|
|
182
186
|
runtimepy/net/arbiter/housekeeping/__init__.py,sha256=-6P2x_qSWp1juml0wKZGJ343c-atrbp8YkDRWn162UA,3622
|
|
183
187
|
runtimepy/net/arbiter/imports/__init__.py,sha256=W7-kkM4B_zDU42Fyh0PmZL56JslS_0_qzvpzUhN6aDU,5041
|
|
@@ -216,14 +220,14 @@ runtimepy/net/server/app/tab.py,sha256=N4NYvxeo0fllb404aCHn7YKhlY6ZnG6G4HBiMS17q
|
|
|
216
220
|
runtimepy/net/server/app/env/__init__.py,sha256=vgiPIYkitx0aqtmgG_nkOehDM7vrQJHYkhRsGCP8pU8,4877
|
|
217
221
|
runtimepy/net/server/app/env/modal.py,sha256=HTipCYgQaAUtsmlBjXKfhAM5JyhLqoIIwEwsPnKhrG8,1740
|
|
218
222
|
runtimepy/net/server/app/env/settings.py,sha256=m87hHAapuNq0xKzDNSNTwG8D2Cb3QFSVrziszf8iVHs,1631
|
|
219
|
-
runtimepy/net/server/app/env/widgets.py,sha256=
|
|
223
|
+
runtimepy/net/server/app/env/widgets.py,sha256=LjEEXaqkKTelQ53YDpHEojbMH9PLfVpy9okdUv4yZaw,7734
|
|
220
224
|
runtimepy/net/server/app/env/tab/__init__.py,sha256=stTVKyHljLQWnnhxkWPwa7bLdZtjhiMFbiVFgbiYaFI,647
|
|
221
|
-
runtimepy/net/server/app/env/tab/base.py,sha256=
|
|
225
|
+
runtimepy/net/server/app/env/tab/base.py,sha256=LxheBq4P7jsDrDp5fiXsOL0cFeZgdZVw3ijI4gbz9Wc,1486
|
|
222
226
|
runtimepy/net/server/app/env/tab/controls.py,sha256=11TidvphKyvLCgdW8Sjiyd6BpX4-ybGZPEhrn-hzkuU,8263
|
|
223
|
-
runtimepy/net/server/app/env/tab/html.py,sha256=
|
|
227
|
+
runtimepy/net/server/app/env/tab/html.py,sha256=nf4UNYEJn69MvUUISGEN4VSncX_MTOmp1SkxGMVNAns,8508
|
|
224
228
|
runtimepy/net/server/app/env/tab/message.py,sha256=-J8wBo1KH0XoBi9VcUvl9LXZCaoFAmvFyMf_YFJVF6Q,3945
|
|
225
229
|
runtimepy/net/server/struct/__init__.py,sha256=Zy37r6RLFu-XFr9vsanSq80BJdS6Dxr7zmPzQbb7xdw,1799
|
|
226
|
-
runtimepy/net/server/websocket/__init__.py,sha256=
|
|
230
|
+
runtimepy/net/server/websocket/__init__.py,sha256=2S2Qt_xf0fL4mfzvgVn70mcMRNAeUAWyNIAyEv3cdTg,5463
|
|
227
231
|
runtimepy/net/server/websocket/state.py,sha256=tClzw_3pOV9fzsiqnmss0FUsZZPhInL2CnDi_jyrayI,2291
|
|
228
232
|
runtimepy/net/stream/__init__.py,sha256=B4NipXGwA2AQ5UJ8YVJqqOg_X_Cm7p2nfIbIXJMiGBI,2359
|
|
229
233
|
runtimepy/net/stream/base.py,sha256=lL2-t-T2ldfUJHuFIf8X4_WdXlsVZOue7w_FOLHtmQY,1987
|
|
@@ -283,7 +287,7 @@ runtimepy/registry/name.py,sha256=TkNm7kff_Id6p_b9jr8YTi1UN1xCNzwk38IEomE7o5Q,19
|
|
|
283
287
|
runtimepy/sample/__init__.py,sha256=N7P6hnCLF9NwnkZA1h3LzS2C334SAO48Fu8adHxWGLU,56
|
|
284
288
|
runtimepy/sample/peer.py,sha256=uNlIPAv1-wjGUWdR0AM3L9NUMjnIMJBqQ9ZsHdTLx3Q,1688
|
|
285
289
|
runtimepy/sample/program.py,sha256=AFqinsnsjzatgJXePAO4yXYerxmzN7ajtDmJPwWigYc,2428
|
|
286
|
-
runtimepy/struct/__init__.py,sha256=
|
|
290
|
+
runtimepy/struct/__init__.py,sha256=Rs3-aU3ffSNH6bVvFQWMHlSlhTJuBvYs7z_E-6GH0DA,2423
|
|
287
291
|
runtimepy/subprocess/__init__.py,sha256=VAiFrYFCU5ETDVoWLOVlrrSsx2d1_atYXUfXYc_OQ9g,4059
|
|
288
292
|
runtimepy/subprocess/interface.py,sha256=4nGVUfDThaZrfqgn2nZHMt1hfUwHezSvFjykBXGT36g,8041
|
|
289
293
|
runtimepy/subprocess/peer.py,sha256=oYw9a0yKAPR18Z6Qt24wYWrcX6EizeQE64htL11WVjM,7593
|
|
@@ -294,7 +298,7 @@ runtimepy/task/asynchronous.py,sha256=w4oEUCyj-X-zDVFmlsAtRL1gv66ahQ78QKE0GmLGT1
|
|
|
294
298
|
runtimepy/task/sample.py,sha256=_nbLj5Julwa1NJC_-WQsotI0890G-TlOWftVHEKiclY,2735
|
|
295
299
|
runtimepy/task/basic/__init__.py,sha256=NryfG-JmSyqYh-TNG4NLO6-9RJFVq5vn1Z-oV3qTVvg,243
|
|
296
300
|
runtimepy/task/basic/manager.py,sha256=4dd2k-azYfyKS6jiCK9MCL-yHomuq32maicpjZvrOjA,2357
|
|
297
|
-
runtimepy/task/basic/periodic.py,sha256=
|
|
301
|
+
runtimepy/task/basic/periodic.py,sha256=F4xNR5L1QeE5n2q1uF0D5YZa3Rkc4R7IjEMHIYdQwO4,7794
|
|
298
302
|
runtimepy/task/trig/__init__.py,sha256=GuGNb9eLuFEnl3nAI7Mi_eS5mqJ4MOhEy2Wfv48RFic,785
|
|
299
303
|
runtimepy/telemetry/__init__.py,sha256=G_JLZsp0EZMGaxSQ12fYgbG2kN4QkvVG4lExV_TzEhM,268
|
|
300
304
|
runtimepy/telemetry/sample.py,sha256=1weoGSH-yGg-fOaLysZAsO3dSTM6yAXX3XdulAiqi3s,3216
|
|
@@ -305,10 +309,11 @@ runtimepy/tui/mock.py,sha256=Vn0XK2OQ_ukSSHeS8xkzFfJPsmAplRiu7vIwb2SnUHI,1874
|
|
|
305
309
|
runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
|
|
306
310
|
runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
|
|
307
311
|
runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
312
|
+
runtimepy/ui/button.py,sha256=8MXnhqmVvANwbbTA8gu3b8N4IIvRYicnwxFQD6SZy3U,2045
|
|
308
313
|
runtimepy/ui/controls.py,sha256=L55Af-4vGq6ZHewdoA7C_mAYq35WXl8NzOdcsmQIo7M,1868
|
|
309
|
-
runtimepy-5.15.
|
|
310
|
-
runtimepy-5.15.
|
|
311
|
-
runtimepy-5.15.
|
|
312
|
-
runtimepy-5.15.
|
|
313
|
-
runtimepy-5.15.
|
|
314
|
-
runtimepy-5.15.
|
|
314
|
+
runtimepy-5.15.5.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
|
|
315
|
+
runtimepy-5.15.5.dist-info/METADATA,sha256=zAY7eYIZrG229MNSE6k3wZUyz7nVDQ78DJPS4nlPFGA,9268
|
|
316
|
+
runtimepy-5.15.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
317
|
+
runtimepy-5.15.5.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
|
|
318
|
+
runtimepy-5.15.5.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
|
|
319
|
+
runtimepy-5.15.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|