runtimepy 5.15.4__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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # =====================================
2
2
  # generator=datazen
3
3
  # version=3.2.3
4
- # hash=b0f0463a508c9a1312a071f5c0b06f3b
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.4"
13
+ VERSION = "5.15.5"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -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, env: ChannelEnvironment, logger: LoggerType, **kwargs
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:
@@ -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
  }
@@ -74,3 +74,11 @@ body > :first-child {
74
74
  .overscroll-behavior-none {
75
75
  overscroll-behavior: none;
76
76
  }
77
+
78
+ .channel-views-min-width {
79
+ min-width: 5.5em;
80
+ }
81
+
82
+ .channel-filter-min-width {
83
+ min-width: 5.5em;
84
+ }
@@ -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,7 @@ tasks:
9
47
  a: 1
10
48
  b: 2
11
49
  c: 3
50
+ buttons: *buttons
12
51
  views:
13
52
  - [zero, "\\.0\\."]
14
53
  - [one, "\\.1\\."]
@@ -49,27 +88,6 @@ tasks:
49
88
  # Drive interactions with runtime entities that won't otherwise be polled.
50
89
  - {name: app, factory: SampleApp, period_s: 0.25}
51
90
 
52
- # UDP JSON clients.
53
- clients:
54
- - factory: udp_json
55
- name: udp_json_client
56
- defer: true
57
- kwargs:
58
- remote_addr: [localhost, "$udp_json"]
59
- views:
60
- - [metrics, metrics]
61
- - [transmit, tx]
62
- - [receive, rx]
63
- markdown: |
64
- # `udp_json_client`
65
-
66
- Connects to `udp_json_server`.
67
-
68
- - factory: udp_json
69
- name: udp_json_server
70
- kwargs:
71
- local_addr: [localhost, "$udp_json"]
72
-
73
91
  # Add some sample structs.
74
92
  structs:
75
93
  - name: example.struct1
@@ -80,6 +98,8 @@ structs:
80
98
  b: 2
81
99
  c: 3
82
100
 
101
+ buttons: *buttons
102
+
83
103
  markdown: |
84
104
  # Docs for `example.struct1`
85
105
 
@@ -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
  }
@@ -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});
@@ -0,0 +1,17 @@
1
+ ---
2
+ type: object
3
+ additionalProperties: false
4
+ required: [key, payload]
5
+ properties:
6
+ key:
7
+ type: string
8
+ payload:
9
+ type: object
10
+ text:
11
+ type: string
12
+ icon:
13
+ type: string
14
+ variant:
15
+ type: string
16
+ outline:
17
+ type: boolean
@@ -4,6 +4,7 @@ includes:
4
4
  - has_name.yaml
5
5
  - has_markdown.yaml
6
6
  - has_views.yaml
7
+ - has_buttons.yaml
7
8
 
8
9
  properties:
9
10
  defer:
@@ -0,0 +1,6 @@
1
+ ---
2
+ properties:
3
+ buttons:
4
+ type: array
5
+ items:
6
+ $ref: package://runtimepy/schemas/Button.yaml
@@ -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[
@@ -148,6 +149,7 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
148
149
 
149
150
  self._commands: list[tuple[str, str]] = []
150
151
  self._views: dict[str, dict[str, str]] = {}
152
+ self._buttons: dict[str, list[ActionButton]] = {}
151
153
 
152
154
  self._init()
153
155
 
@@ -157,9 +159,16 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
157
159
  def _register_connection(self, connection: _Connection, name: str) -> None:
158
160
  """Perform connection registration."""
159
161
 
162
+ # Handle views.
160
163
  if name in self._views:
161
164
  connection.env.views.update(self._views[name])
162
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
+
163
172
  self._connections[name] = connection
164
173
  self.manager.queue.put_nowait(connection)
165
174
  connection.logger.info("Registered as '%s'.", name)
@@ -170,6 +179,7 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
170
179
  *names: str,
171
180
  delim: str = None,
172
181
  views: dict[str, str] = None,
182
+ buttons: list[ActionButton] = None,
173
183
  ) -> bool:
174
184
  """Attempt to register a connection object."""
175
185
 
@@ -180,6 +190,8 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
180
190
 
181
191
  if views:
182
192
  self._views[name] = views
193
+ if buttons:
194
+ self._buttons[name] = buttons
183
195
 
184
196
  if (
185
197
  name not in self._connections
@@ -180,6 +180,7 @@ class ConfigConnectionArbiter(_ImportConnectionArbiter):
180
180
  **fix_kwargs(kwargs),
181
181
  views=client.get("views"),
182
182
  markdown=client.get("markdown"),
183
+ buttons=client.get("buttons", []),
183
184
  ), f"Couldn't register client '{name}' ({factory})!"
184
185
 
185
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:
@@ -90,6 +91,7 @@ class FactoryConnectionArbiter(_BaseConnectionArbiter):
90
91
  factory_inst = self._conn_factories[factory]
91
92
 
92
93
  views = kwargs.pop("views", {})
94
+ buttons = ActionButton.from_top_level(kwargs.pop("buttons", []))
93
95
 
94
96
  conn = factory_inst.client(name, *args, **kwargs)
95
97
  if not defer:
@@ -100,6 +102,7 @@ class FactoryConnectionArbiter(_BaseConnectionArbiter):
100
102
  *self._conn_names[factory_inst],
101
103
  name,
102
104
  views=views,
105
+ buttons=buttons,
103
106
  )
104
107
 
105
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(
@@ -84,15 +84,16 @@ def views_dropdown(parent: Element, command: ChannelCommandProcessor) -> None:
84
84
  """Dropdown menu for channel environment views."""
85
85
 
86
86
  select = select_element(
87
- parent=div(tag="th", parent=parent, class_str="p-0"),
87
+ parent=div(
88
+ tag="th", parent=parent, class_str="p-0 channel-views-min-width"
89
+ ),
88
90
  id="filter-view",
89
91
  title="Canonical channel filters.",
90
92
  ).add_class("border-end-0", "w-100")
91
93
  div(tag="option", value="", text="-", parent=select)
94
+ div(tag="option", value="^$", text="hide", parent=select)
92
95
  for text, value in command.env.views.items():
93
96
  div(tag="option", value=value, text=text, parent=select)
94
- if not command.env.views:
95
- select.booleans.add("disabled")
96
97
 
97
98
 
98
99
  def channel_table_header(
@@ -120,7 +121,11 @@ def channel_table_header(
120
121
  ).add_class(*TABLE_BUTTON_CLASSES)
121
122
 
122
123
  _, label, box = input_box(
123
- div(tag="th", parent=ctl_row, class_str="p-0 border-end-0"),
124
+ div(
125
+ tag="th",
126
+ parent=ctl_row,
127
+ class_str="p-0 border-end-0 channel-filter-min-width",
128
+ ),
124
129
  description="Channel name filter.",
125
130
  pattern=".* ! @ $",
126
131
  label="filter",
@@ -137,7 +142,7 @@ def channel_table_header(
137
142
  parent=div(tag="th", parent=ctl_row, colspan="2", class_str="p-0")
138
143
  )
139
144
 
140
- # Add a selection menu for custom commands. (need a data source for this)
145
+ # Add a selection menu for custom commands.
141
146
  select = select_element(
142
147
  parent=cell, id="custom-commands", title="Custom command selector."
143
148
  )
@@ -157,12 +162,7 @@ def channel_table_header(
157
162
  tooltip="Send selected command (left dropdown).",
158
163
  ).add_class(*TABLE_BUTTON_CLASSES)
159
164
  else:
160
- div(
161
- tag="option",
162
- parent=select,
163
- value="noop",
164
- text="no custom commands",
165
- )
165
+ div(tag="option", parent=select, value="noop", text="-")
166
166
  select.booleans.add("disabled")
167
167
 
168
168
  # Button for 'reset all defaults' if this tab has more than one channel
@@ -96,7 +96,7 @@ class RuntimepyWebsocketConnection(WebsocketJsonMessageConnection):
96
96
 
97
97
  # Handle bus messages.
98
98
  if "bus" in inbox:
99
- await BUS.send_ro("ui", inbox["bus"])
99
+ await BUS.send_ro(inbox["key"], inbox["bus"])
100
100
  return
101
101
 
102
102
  # Handle frame messages.
@@ -1,5 +1,5 @@
1
1
  aiofiles
2
- vcorelib>=3.6.3
2
+ vcorelib>=3.6.4
3
3
  svgen>=0.8.0
4
4
  websockets
5
5
  psutil
@@ -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(
@@ -50,6 +51,7 @@ class RuntimeStructBase(
50
51
  self.setup_level_channel(self.env)
51
52
  self.command = ChannelCommandProcessor(self.env, self.logger)
52
53
  self.config = config
54
+ self.command.buttons.extend(ActionButton.from_top_level(self.config))
53
55
 
54
56
  async def poll(args: Namespace, __: Optional[FieldOrChannel]) -> None:
55
57
  """Handle a test command."""
@@ -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
 
@@ -75,6 +76,7 @@ class PeriodicTask(
75
76
  self.env.views.update(self.config.get("views", {})) # type: ignore
76
77
  self.setup_level_channel(self.env)
77
78
  self.command = ChannelCommandProcessor(self.env, self.logger)
79
+ self.command.buttons.extend(ActionButton.from_top_level(self.config))
78
80
  self.register_task_metrics(self.metrics)
79
81
 
80
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.4
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
@@ -20,8 +20,8 @@ License-File: LICENSE
20
20
  Requires-Dist: psutil
21
21
  Requires-Dist: svgen>=0.8.0
22
22
  Requires-Dist: aiofiles
23
+ Requires-Dist: vcorelib>=3.6.4
23
24
  Requires-Dist: websockets
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=99eea1986d2105483ebb2fe566128f5f
54
+ hash=fd0e57419cf9437a64c4f82cae3625f0
55
55
  =====================================
56
56
  -->
57
57
 
58
- # runtimepy ([5.15.4](https://pypi.org/project/runtimepy/))
58
+ # runtimepy ([5.15.5](https://pypi.org/project/runtimepy/))
59
59
 
60
60
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
61
61
  ![Build Status](https://github.com/libre-embedded/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,11 +1,11 @@
1
- runtimepy/__init__.py,sha256=Aiyyy3r4Wwnu9sBdfqT5dwlbFGVLbVwehYURBXxjA2g,391
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=dBOVAEEsNN89zt8_WRCcQsge6pxvmJ5srJdFAC8uAL8,124
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
@@ -19,7 +19,7 @@ runtimepy/channel/environment/sample.py,sha256=6926v0ZssvfeWX_C8MSbUfYjE4sr3jt0C
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=NiiWRdwBHOFEisjqNOW5oawprxcpR25ONNABoZpELdg,7122
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=pVX29qClBJ4NzE2q7fVF3NCVpts6oaxvH2owmdtdohU,2373
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,9 +53,9 @@ 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=hQV_f0DXboYPlsuI7PahFT11g5xjVw6nqp1DACOKWns,2934
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=SuSNaXXd3iT53bbp7bsCxPJGZtkC0cNZeCeEpvjIEhU,1215
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
@@ -63,7 +63,7 @@ runtimepy/data/js/events.js,sha256=rgz3Q_8J6sfU_7Sa7fG1mZD0pQ4S3vwN2mqcvQfePkM,5
63
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=-ezzcH-U4hchWYgE2mMzO9lUdkmNDA_OojJRzA17AGU,88
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
@@ -81,7 +81,7 @@ runtimepy/data/js/classes/TabFilter.js,sha256=xajU1ZzVI0jFGhaHkKFX4-d2n2KlxiTC4i
81
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=5jn5JX3cx-1oBVBpZ6X6E5x6uk5vmKQ98EMBoRAaqxk,402
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,10 +92,11 @@ 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=biBuRiKUx6kSC5GdO1zdia878n8Cqz4xCm3mranukqo,206
99
+ runtimepy/data/schemas/ClientConnectionConfig.yaml,sha256=cfiQe3K7h3dAHsEFqBqN8qc-nWX7AntOKgrHkWIAvWU,227
99
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
@@ -106,6 +107,7 @@ runtimepy/data/schemas/StructConfig.yaml,sha256=KXcHMYsM5JgsIlHhfXW8tLx5GuekS6m5
106
107
  runtimepy/data/schemas/TaskConfig.yaml,sha256=upEfToPllsqEcDoJf9D3rbvXk08nc0slmlUSMyCOYcQ,247
107
108
  runtimepy/data/schemas/TwoTupleArray.yaml,sha256=HmZhgszwzSjj6j3GVAn6QNjPFaHGut9SZfskGWNoeXI,112
108
109
  runtimepy/data/schemas/channel_controls.yaml,sha256=pctqV_fVjRpiKdaPqET7yyoGVYQuUtOYc1FLQUdfVJc,546
110
+ runtimepy/data/schemas/has_buttons.yaml,sha256=DQt4wQKQewIOq4du-KDUfd-Tr6YrE3x7OBF9aj6rPYg,106
109
111
  runtimepy/data/schemas/has_config.yaml,sha256=SMuJ9tNWw06tsgDasz4myO9SSmMZGep9xBfJ89GJnqA,80
110
112
  runtimepy/data/schemas/has_factory.yaml,sha256=Rsqrxv3HEBUGxPcW6CN7QczA96iBSgtm0gQNsu2P8LM,106
111
113
  runtimepy/data/schemas/has_markdown.yaml,sha256=LbnEQRYdk5KR_GgORmIOiKjRg-HF8wjhA6Dm6pSm66I,45
@@ -169,17 +171,17 @@ runtimepy/net/ssl.py,sha256=dj9uECPKDT5k-5vlR5I3Z7Go3WWZhbaJ9nb0rC3kJvg,854
169
171
  runtimepy/net/util.py,sha256=XTQQ-Ql_ImhVd1_O8nSeDX9MY8xJwRBggvliSLCrsc8,5913
170
172
  runtimepy/net/apps/__init__.py,sha256=vjo7e19QXtJwe6V6B-QGvYiJveYobnYIfpkKZrnS17w,710
171
173
  runtimepy/net/arbiter/__init__.py,sha256=ptKF995rYKvkm4Mya92vA5QEDqcFq5NRD0IYGqZ6_do,740
172
- runtimepy/net/arbiter/base.py,sha256=s7fyyC7MSLu3qD472vkcmKNZfinMuoTWNUlv0kT8HZw,15467
174
+ runtimepy/net/arbiter/base.py,sha256=TDWIphAiBR_b7MuieVZM2_PgV-EEFmmmVq-Yjobm13w,15865
173
175
  runtimepy/net/arbiter/info.py,sha256=QcBB1LQELXPs-Ud-GVq3aDBL4mpSMDcjjLtuKgojiU8,10962
174
176
  runtimepy/net/arbiter/result.py,sha256=PHZo5qj4SI08ZeWPFk_8OZ1umI6L0dp5nJpjjS8QUpM,2871
175
177
  runtimepy/net/arbiter/task.py,sha256=APcc5QioAG8uueIzxJU-vktIn8Ys3yJd_CFlRWb6Ieo,795
176
178
  runtimepy/net/arbiter/udp.py,sha256=-ecHJs-utcsiTfr5wSb73hUUuYFBeRVT_D1znYp5q6A,893
177
179
  runtimepy/net/arbiter/websocket.py,sha256=vYTDA7sXMUTTqW3i1zTWUdaxGKxfrQcQoVSYZtmCvfQ,1524
178
- runtimepy/net/arbiter/config/__init__.py,sha256=sHiXni5s93VnmWu1SaoYuM5rK_CQejj0CDb_S6pQTHs,8775
180
+ runtimepy/net/arbiter/config/__init__.py,sha256=Z-E7hJpwReVUannu_ksUHts1nNAuOHtQLxMdbB7VZw4,8826
179
181
  runtimepy/net/arbiter/config/codec.py,sha256=JVutIe1AFVr3NN7GS4rZayxZR05LWzQZRqzEuJpEJd8,2635
180
182
  runtimepy/net/arbiter/config/util.py,sha256=MezLSil5mEwioI5kxDOZa9y118st1_t0El8DYH6hFU8,1254
181
183
  runtimepy/net/arbiter/factory/__init__.py,sha256=jC1szUYucH4Us3jbb0mhFgnklHlorya5DYGGw2pqfe4,334
182
- runtimepy/net/arbiter/factory/connection.py,sha256=MFdO82g2VrUaXwmqDGGWpt2AjhlYyCqH7gW-ELAMtNs,3797
184
+ runtimepy/net/arbiter/factory/connection.py,sha256=TxcwjK-Q2TEY20eBW-R5meko3RrIPcnvbrzHZlqV9jo,3952
183
185
  runtimepy/net/arbiter/factory/task.py,sha256=YLRv55dnDBqHRJIMYgO9_uHi0AW-8hwa0n3wHAKyncE,1959
184
186
  runtimepy/net/arbiter/housekeeping/__init__.py,sha256=-6P2x_qSWp1juml0wKZGJ343c-atrbp8YkDRWn162UA,3622
185
187
  runtimepy/net/arbiter/imports/__init__.py,sha256=W7-kkM4B_zDU42Fyh0PmZL56JslS_0_qzvpzUhN6aDU,5041
@@ -218,14 +220,14 @@ runtimepy/net/server/app/tab.py,sha256=N4NYvxeo0fllb404aCHn7YKhlY6ZnG6G4HBiMS17q
218
220
  runtimepy/net/server/app/env/__init__.py,sha256=vgiPIYkitx0aqtmgG_nkOehDM7vrQJHYkhRsGCP8pU8,4877
219
221
  runtimepy/net/server/app/env/modal.py,sha256=HTipCYgQaAUtsmlBjXKfhAM5JyhLqoIIwEwsPnKhrG8,1740
220
222
  runtimepy/net/server/app/env/settings.py,sha256=m87hHAapuNq0xKzDNSNTwG8D2Cb3QFSVrziszf8iVHs,1631
221
- runtimepy/net/server/app/env/widgets.py,sha256=jghzleoD3EsUO8GNeeHv51DstXfa-maviDK0hoahmvo,7730
223
+ runtimepy/net/server/app/env/widgets.py,sha256=LjEEXaqkKTelQ53YDpHEojbMH9PLfVpy9okdUv4yZaw,7734
222
224
  runtimepy/net/server/app/env/tab/__init__.py,sha256=stTVKyHljLQWnnhxkWPwa7bLdZtjhiMFbiVFgbiYaFI,647
223
- runtimepy/net/server/app/env/tab/base.py,sha256=mljE6n56HUwcWO1lrjj5PR7qh5dvY8CZYMwDcrV-yVk,1199
225
+ runtimepy/net/server/app/env/tab/base.py,sha256=LxheBq4P7jsDrDp5fiXsOL0cFeZgdZVw3ijI4gbz9Wc,1486
224
226
  runtimepy/net/server/app/env/tab/controls.py,sha256=11TidvphKyvLCgdW8Sjiyd6BpX4-ybGZPEhrn-hzkuU,8263
225
- runtimepy/net/server/app/env/tab/html.py,sha256=MbEQzxEPNcMCntGGm2qWli6ND_Ina46CEVzt7HF2yBg,8210
227
+ runtimepy/net/server/app/env/tab/html.py,sha256=nf4UNYEJn69MvUUISGEN4VSncX_MTOmp1SkxGMVNAns,8508
226
228
  runtimepy/net/server/app/env/tab/message.py,sha256=-J8wBo1KH0XoBi9VcUvl9LXZCaoFAmvFyMf_YFJVF6Q,3945
227
229
  runtimepy/net/server/struct/__init__.py,sha256=Zy37r6RLFu-XFr9vsanSq80BJdS6Dxr7zmPzQbb7xdw,1799
228
- runtimepy/net/server/websocket/__init__.py,sha256=A0RTRGVsC_Ug2AyhfMdUqHjIN7O2Dor_rnP260g6YSM,5455
230
+ runtimepy/net/server/websocket/__init__.py,sha256=2S2Qt_xf0fL4mfzvgVn70mcMRNAeUAWyNIAyEv3cdTg,5463
229
231
  runtimepy/net/server/websocket/state.py,sha256=tClzw_3pOV9fzsiqnmss0FUsZZPhInL2CnDi_jyrayI,2291
230
232
  runtimepy/net/stream/__init__.py,sha256=B4NipXGwA2AQ5UJ8YVJqqOg_X_Cm7p2nfIbIXJMiGBI,2359
231
233
  runtimepy/net/stream/base.py,sha256=lL2-t-T2ldfUJHuFIf8X4_WdXlsVZOue7w_FOLHtmQY,1987
@@ -285,7 +287,7 @@ runtimepy/registry/name.py,sha256=TkNm7kff_Id6p_b9jr8YTi1UN1xCNzwk38IEomE7o5Q,19
285
287
  runtimepy/sample/__init__.py,sha256=N7P6hnCLF9NwnkZA1h3LzS2C334SAO48Fu8adHxWGLU,56
286
288
  runtimepy/sample/peer.py,sha256=uNlIPAv1-wjGUWdR0AM3L9NUMjnIMJBqQ9ZsHdTLx3Q,1688
287
289
  runtimepy/sample/program.py,sha256=AFqinsnsjzatgJXePAO4yXYerxmzN7ajtDmJPwWigYc,2428
288
- runtimepy/struct/__init__.py,sha256=m-ZAP-_L_1ezAIbCPrhcBPN_R0vLv5Pc97eiFVDpn2w,2300
290
+ runtimepy/struct/__init__.py,sha256=Rs3-aU3ffSNH6bVvFQWMHlSlhTJuBvYs7z_E-6GH0DA,2423
289
291
  runtimepy/subprocess/__init__.py,sha256=VAiFrYFCU5ETDVoWLOVlrrSsx2d1_atYXUfXYc_OQ9g,4059
290
292
  runtimepy/subprocess/interface.py,sha256=4nGVUfDThaZrfqgn2nZHMt1hfUwHezSvFjykBXGT36g,8041
291
293
  runtimepy/subprocess/peer.py,sha256=oYw9a0yKAPR18Z6Qt24wYWrcX6EizeQE64htL11WVjM,7593
@@ -296,7 +298,7 @@ runtimepy/task/asynchronous.py,sha256=w4oEUCyj-X-zDVFmlsAtRL1gv66ahQ78QKE0GmLGT1
296
298
  runtimepy/task/sample.py,sha256=_nbLj5Julwa1NJC_-WQsotI0890G-TlOWftVHEKiclY,2735
297
299
  runtimepy/task/basic/__init__.py,sha256=NryfG-JmSyqYh-TNG4NLO6-9RJFVq5vn1Z-oV3qTVvg,243
298
300
  runtimepy/task/basic/manager.py,sha256=4dd2k-azYfyKS6jiCK9MCL-yHomuq32maicpjZvrOjA,2357
299
- runtimepy/task/basic/periodic.py,sha256=vADMqQ5o9ZpWJjf9_Kks-uCSvx_Pq7GUWgF5wdkAozs,7671
301
+ runtimepy/task/basic/periodic.py,sha256=F4xNR5L1QeE5n2q1uF0D5YZa3Rkc4R7IjEMHIYdQwO4,7794
300
302
  runtimepy/task/trig/__init__.py,sha256=GuGNb9eLuFEnl3nAI7Mi_eS5mqJ4MOhEy2Wfv48RFic,785
301
303
  runtimepy/telemetry/__init__.py,sha256=G_JLZsp0EZMGaxSQ12fYgbG2kN4QkvVG4lExV_TzEhM,268
302
304
  runtimepy/telemetry/sample.py,sha256=1weoGSH-yGg-fOaLysZAsO3dSTM6yAXX3XdulAiqi3s,3216
@@ -307,10 +309,11 @@ runtimepy/tui/mock.py,sha256=Vn0XK2OQ_ukSSHeS8xkzFfJPsmAplRiu7vIwb2SnUHI,1874
307
309
  runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
308
310
  runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
309
311
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
312
+ runtimepy/ui/button.py,sha256=8MXnhqmVvANwbbTA8gu3b8N4IIvRYicnwxFQD6SZy3U,2045
310
313
  runtimepy/ui/controls.py,sha256=L55Af-4vGq6ZHewdoA7C_mAYq35WXl8NzOdcsmQIo7M,1868
311
- runtimepy-5.15.4.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
312
- runtimepy-5.15.4.dist-info/METADATA,sha256=1G73SimzqDIEjYrlCRLPQ5haVEZX2KGsCKyP2Lpqm0Q,9268
313
- runtimepy-5.15.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
314
- runtimepy-5.15.4.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
315
- runtimepy-5.15.4.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
316
- runtimepy-5.15.4.dist-info/RECORD,,
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,,