runtimepy 5.6.2__py3-none-any.whl → 5.6.4__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/command/__init__.py +1 -1
- runtimepy/channel/environment/telemetry.py +3 -1
- runtimepy/data/css/bootstrap_extra.css +1 -0
- runtimepy/data/css/main.css +4 -0
- runtimepy/data/dummy_load.yaml +34 -1
- runtimepy/data/js/classes/OverlayManager.js +1 -1
- runtimepy/data/js/util.js +3 -2
- runtimepy/data/md/Connection.md +3 -0
- runtimepy/data/md/PeriodicTask.md +3 -0
- runtimepy/data/md/RuntimeStruct.md +3 -0
- runtimepy/data/md/RuntimepyPeer.md +3 -0
- runtimepy/data/md/SinusoidTask.md +20 -0
- runtimepy/data/schemas/ClientConnectionConfig.yaml +1 -0
- runtimepy/data/schemas/ConnectionArbiterConfig.yaml +3 -0
- runtimepy/data/schemas/PeerProcessConfig.yaml +1 -0
- runtimepy/data/schemas/TaskConfig.yaml +1 -0
- runtimepy/data/schemas/has_markdown.yaml +4 -0
- runtimepy/mapping.py +1 -1
- runtimepy/message/interface.py +2 -2
- runtimepy/mixins/environment.py +10 -4
- runtimepy/mixins/logging.py +54 -1
- runtimepy/net/arbiter/base.py +13 -3
- runtimepy/net/arbiter/config/__init__.py +6 -2
- runtimepy/net/arbiter/imports/__init__.py +5 -5
- runtimepy/net/arbiter/imports/util.py +3 -1
- runtimepy/net/connection.py +7 -1
- runtimepy/net/http/common.py +5 -0
- runtimepy/net/http/header.py +5 -1
- runtimepy/net/http/response.py +1 -1
- runtimepy/net/server/app/__init__.py +3 -1
- runtimepy/net/server/app/bootstrap/elements.py +38 -1
- runtimepy/net/server/app/env/__init__.py +69 -19
- runtimepy/net/server/app/env/tab/base.py +5 -1
- runtimepy/net/server/app/env/tab/html.py +6 -6
- runtimepy/net/server/app/files.py +10 -6
- runtimepy/net/server/struct/__init__.py +1 -2
- runtimepy/net/server/websocket/state.py +3 -1
- runtimepy/net/tcp/connection.py +14 -4
- runtimepy/net/tcp/http/__init__.py +9 -7
- runtimepy/net/udp/connection.py +11 -4
- runtimepy/net/udp/tftp/__init__.py +1 -2
- runtimepy/net/websocket/connection.py +10 -5
- runtimepy/requirements.txt +2 -1
- runtimepy/struct/__init__.py +12 -2
- runtimepy/subprocess/interface.py +17 -4
- runtimepy/subprocess/peer.py +20 -6
- runtimepy/task/basic/periodic.py +7 -1
- runtimepy/util.py +0 -81
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/METADATA +9 -8
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/RECORD +55 -49
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/LICENSE +0 -0
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/WHEEL +0 -0
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/entry_points.txt +0 -0
- {runtimepy-5.6.2.dist-info → runtimepy-5.6.4.dist-info}/top_level.txt +0 -0
|
@@ -8,53 +8,75 @@ from svgen.element.html import div
|
|
|
8
8
|
# internal
|
|
9
9
|
from runtimepy import PKG_NAME
|
|
10
10
|
from runtimepy.net.arbiter.info import AppInfo
|
|
11
|
-
from runtimepy.net.server.app.bootstrap.elements import
|
|
11
|
+
from runtimepy.net.server.app.bootstrap.elements import (
|
|
12
|
+
centered_markdown,
|
|
13
|
+
input_box,
|
|
14
|
+
)
|
|
12
15
|
from runtimepy.net.server.app.bootstrap.tabs import TabbedContent
|
|
13
16
|
from runtimepy.net.server.app.env.modal import Modal
|
|
14
17
|
from runtimepy.net.server.app.env.settings import plot_settings
|
|
15
18
|
from runtimepy.net.server.app.env.tab import ChannelEnvironmentTab
|
|
16
|
-
from runtimepy.net.server.app.placeholder import dummy_tabs
|
|
19
|
+
from runtimepy.net.server.app.placeholder import dummy_tabs
|
|
17
20
|
from runtimepy.net.server.app.sound import SoundTab
|
|
18
21
|
|
|
19
22
|
|
|
20
|
-
def
|
|
21
|
-
"""Populate
|
|
22
|
-
|
|
23
|
-
# Remove tab-content scrolling.
|
|
24
|
-
tabs.set_scroll(False)
|
|
25
|
-
|
|
26
|
-
# Tab name filter.
|
|
27
|
-
input_box(tabs.tabs, label="tab", description="Tab name filter.")
|
|
23
|
+
def populate_tabs(app: AppInfo, tabs: TabbedContent) -> None:
|
|
24
|
+
"""Populate tab contents."""
|
|
28
25
|
|
|
29
26
|
# Connection tabs.
|
|
30
27
|
for name, conn in app.connections.items():
|
|
31
28
|
ChannelEnvironmentTab(
|
|
32
|
-
name,
|
|
29
|
+
name,
|
|
30
|
+
conn.command,
|
|
31
|
+
app,
|
|
32
|
+
tabs,
|
|
33
|
+
icon="ethernet",
|
|
34
|
+
markdown=conn.markdown,
|
|
33
35
|
).entry()
|
|
34
36
|
|
|
35
37
|
# Task tabs.
|
|
36
38
|
for name, task in app.tasks.items():
|
|
37
39
|
ChannelEnvironmentTab(
|
|
38
|
-
name,
|
|
40
|
+
name,
|
|
41
|
+
task.command,
|
|
42
|
+
app,
|
|
43
|
+
tabs,
|
|
44
|
+
icon="arrow-repeat",
|
|
45
|
+
markdown=task.markdown,
|
|
39
46
|
).entry()
|
|
40
47
|
|
|
41
48
|
# Struct tabs.
|
|
42
49
|
for struct in app.structs.values():
|
|
43
50
|
ChannelEnvironmentTab(
|
|
44
|
-
struct.name,
|
|
51
|
+
struct.name,
|
|
52
|
+
struct.command,
|
|
53
|
+
app,
|
|
54
|
+
tabs,
|
|
55
|
+
icon="bucket",
|
|
56
|
+
markdown=struct.markdown,
|
|
45
57
|
).entry()
|
|
46
58
|
|
|
47
59
|
# Subprocess tabs.
|
|
48
60
|
for peer in app.peers.values():
|
|
49
61
|
# Host side.
|
|
50
62
|
ChannelEnvironmentTab(
|
|
51
|
-
peer.struct.name,
|
|
63
|
+
peer.struct.name,
|
|
64
|
+
peer.struct.command,
|
|
65
|
+
app,
|
|
66
|
+
tabs,
|
|
67
|
+
icon="cpu-fill",
|
|
68
|
+
markdown=peer.markdown,
|
|
52
69
|
).entry()
|
|
53
70
|
|
|
54
71
|
# Remote side.
|
|
55
72
|
assert peer.peer is not None
|
|
56
73
|
ChannelEnvironmentTab(
|
|
57
|
-
peer.peer_name,
|
|
74
|
+
peer.peer_name,
|
|
75
|
+
peer.peer,
|
|
76
|
+
app,
|
|
77
|
+
tabs,
|
|
78
|
+
icon="cpu",
|
|
79
|
+
markdown=peer.struct.markdown,
|
|
58
80
|
).entry()
|
|
59
81
|
|
|
60
82
|
# If we are a peer program, load environments.
|
|
@@ -69,14 +91,40 @@ def channel_environments(app: AppInfo, tabs: TabbedContent) -> None:
|
|
|
69
91
|
app,
|
|
70
92
|
tabs,
|
|
71
93
|
icon="cpu-fill",
|
|
94
|
+
markdown=PROGRAM.struct.markdown,
|
|
72
95
|
).entry()
|
|
73
96
|
|
|
74
97
|
# Remote side.
|
|
75
98
|
assert PROGRAM.peer is not None
|
|
76
99
|
ChannelEnvironmentTab(
|
|
77
|
-
PROGRAM.peer_name,
|
|
100
|
+
PROGRAM.peer_name,
|
|
101
|
+
PROGRAM.peer,
|
|
102
|
+
app,
|
|
103
|
+
tabs,
|
|
104
|
+
icon="cpu",
|
|
105
|
+
markdown=PROGRAM.markdown,
|
|
78
106
|
).entry()
|
|
79
107
|
|
|
108
|
+
|
|
109
|
+
def channel_environments(app: AppInfo, tabs: TabbedContent) -> None:
|
|
110
|
+
"""Populate application elements."""
|
|
111
|
+
|
|
112
|
+
# Remove tab-content scrolling.
|
|
113
|
+
tabs.set_scroll(False)
|
|
114
|
+
|
|
115
|
+
# Tab name filter.
|
|
116
|
+
input_box(tabs.tabs, label="tab", description="Tab name filter.")
|
|
117
|
+
|
|
118
|
+
centered_markdown(
|
|
119
|
+
tabs.tabs,
|
|
120
|
+
app.config_param("top_markdown", "configure `top_markdown`"),
|
|
121
|
+
"border-start",
|
|
122
|
+
"border-bottom",
|
|
123
|
+
"border-end",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
populate_tabs(app, tabs)
|
|
127
|
+
|
|
80
128
|
# Toggle channel-table button.
|
|
81
129
|
tabs.add_button(
|
|
82
130
|
"Toggle channel table",
|
|
@@ -99,9 +147,11 @@ def channel_environments(app: AppInfo, tabs: TabbedContent) -> None:
|
|
|
99
147
|
Modal(tabs)
|
|
100
148
|
Modal(tabs, name="diagnostics", icon="activity")
|
|
101
149
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
centered_markdown(
|
|
151
|
+
tabs.tabs,
|
|
152
|
+
app.config_param("bottom_markdown", "configure `bottom_markdown`"),
|
|
153
|
+
"border-start",
|
|
154
|
+
"border-end",
|
|
105
155
|
)
|
|
106
156
|
|
|
107
157
|
# Add splash screen element.
|
|
@@ -3,10 +3,12 @@ A module implementing a channel-environment tab HTML interface.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# third-party
|
|
6
|
+
from vcorelib.io import MarkdownMixin
|
|
6
7
|
from vcorelib.logging import LoggerMixin
|
|
7
8
|
from vcorelib.math import RateLimiter
|
|
8
9
|
|
|
9
10
|
# internal
|
|
11
|
+
from runtimepy import PKG_NAME
|
|
10
12
|
from runtimepy.channel.environment.command.processor import (
|
|
11
13
|
ChannelCommandProcessor,
|
|
12
14
|
)
|
|
@@ -15,7 +17,7 @@ from runtimepy.net.server.app.bootstrap.tabs import TabbedContent
|
|
|
15
17
|
from runtimepy.net.server.app.tab import Tab
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
class ChannelEnvironmentTabBase(Tab, LoggerMixin):
|
|
20
|
+
class ChannelEnvironmentTabBase(Tab, LoggerMixin, MarkdownMixin):
|
|
19
21
|
"""A channel-environment tab interface."""
|
|
20
22
|
|
|
21
23
|
def __init__(
|
|
@@ -25,10 +27,12 @@ class ChannelEnvironmentTabBase(Tab, LoggerMixin):
|
|
|
25
27
|
app: AppInfo,
|
|
26
28
|
tabs: TabbedContent,
|
|
27
29
|
icon: str = "alarm",
|
|
30
|
+
markdown: str = None,
|
|
28
31
|
) -> None:
|
|
29
32
|
"""Initialize this instance."""
|
|
30
33
|
|
|
31
34
|
self.command = command
|
|
35
|
+
self.set_markdown(markdown=markdown, package=PKG_NAME)
|
|
32
36
|
super().__init__(name, app, tabs, source="env", icon=icon)
|
|
33
37
|
|
|
34
38
|
# Logging.
|
|
@@ -14,6 +14,7 @@ from runtimepy.channel import AnyChannel
|
|
|
14
14
|
from runtimepy.enum import RuntimeEnum
|
|
15
15
|
from runtimepy.net.server.app.bootstrap.elements import (
|
|
16
16
|
TEXT,
|
|
17
|
+
centered_markdown,
|
|
17
18
|
flex,
|
|
18
19
|
input_box,
|
|
19
20
|
set_tooltip,
|
|
@@ -26,7 +27,6 @@ from runtimepy.net.server.app.env.widgets import (
|
|
|
26
27
|
channel_table_header,
|
|
27
28
|
plot_checkbox,
|
|
28
29
|
)
|
|
29
|
-
from runtimepy.net.server.app.placeholder import under_construction
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def channel_color_button(parent: Element, name: str) -> Element:
|
|
@@ -246,12 +246,12 @@ class ChannelEnvironmentTabHtml(ChannelEnvironmentTabControls):
|
|
|
246
246
|
|
|
247
247
|
self.channel_table(vert_container)
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
# table doesn't take up full vertical space, few channels).
|
|
251
|
-
under_construction(
|
|
249
|
+
centered_markdown(
|
|
252
250
|
vert_container,
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
self.markdown,
|
|
252
|
+
"border-start",
|
|
253
|
+
"border-top",
|
|
254
|
+
"border-end",
|
|
255
255
|
)
|
|
256
256
|
|
|
257
257
|
# Divider.
|
|
@@ -77,12 +77,16 @@ def set_text_to_kind(
|
|
|
77
77
|
WORKER_TYPE = "text/js-worker"
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def handle_worker(writer: IndentedFileWriter) ->
|
|
80
|
+
def handle_worker(writer: IndentedFileWriter) -> int:
|
|
81
81
|
"""Boilerplate contents for worker thread block."""
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
83
|
+
# Not currently used.
|
|
84
|
+
# return write_found_file(
|
|
85
|
+
# writer, kind_url("js", "webgl-debug", subdir="third-party")
|
|
86
|
+
# )
|
|
87
|
+
del writer
|
|
88
|
+
|
|
89
|
+
return 0
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
def append_kind(
|
|
@@ -107,8 +111,8 @@ def append_kind(
|
|
|
107
111
|
):
|
|
108
112
|
found_count += 1
|
|
109
113
|
|
|
110
|
-
if worker
|
|
111
|
-
found_count +=
|
|
114
|
+
if worker:
|
|
115
|
+
found_count += handle_worker(writer)
|
|
112
116
|
|
|
113
117
|
if found_count:
|
|
114
118
|
elem.text = stream.getvalue()
|
|
@@ -43,8 +43,7 @@ class UiState(RuntimeStruct, PsutilMixin):
|
|
|
43
43
|
|
|
44
44
|
# JSON-messaging interface metrics.
|
|
45
45
|
self.json_metrics = ConnectionMetrics()
|
|
46
|
-
|
|
47
|
-
self.register_connection_metrics(self.json_metrics)
|
|
46
|
+
self.register_connection_metrics(self.json_metrics, "json")
|
|
48
47
|
|
|
49
48
|
# System metrics.
|
|
50
49
|
self.use_psutil = self.config.get("psutil", True) # type: ignore
|
|
@@ -7,11 +7,13 @@ from collections import defaultdict
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
import logging
|
|
9
9
|
|
|
10
|
+
# third-party
|
|
11
|
+
from vcorelib.logging import ListLogger
|
|
12
|
+
|
|
10
13
|
# internal
|
|
11
14
|
from runtimepy.channel.environment.base import ValueMap
|
|
12
15
|
from runtimepy.message import JsonMessage
|
|
13
16
|
from runtimepy.primitives import AnyPrimitive
|
|
14
|
-
from runtimepy.util import ListLogger
|
|
15
17
|
|
|
16
18
|
# (value, nanosecond timestamp)
|
|
17
19
|
Point = tuple[str | int | float | bool, int]
|
runtimepy/net/tcp/connection.py
CHANGED
|
@@ -51,7 +51,12 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
51
51
|
log_alias = "TCP"
|
|
52
52
|
log_prefix = ""
|
|
53
53
|
|
|
54
|
-
def __init__(
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
transport: _Transport,
|
|
57
|
+
protocol: QueueProtocol,
|
|
58
|
+
**kwargs,
|
|
59
|
+
) -> None:
|
|
55
60
|
"""Initialize this TCP connection."""
|
|
56
61
|
|
|
57
62
|
_TransportMixin.__init__(self, transport)
|
|
@@ -60,7 +65,9 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
60
65
|
self._transport: _Transport = transport
|
|
61
66
|
self._set_protocol(protocol)
|
|
62
67
|
|
|
63
|
-
super().__init__(
|
|
68
|
+
super().__init__(
|
|
69
|
+
_getLogger(self.logger_name(f"{self.log_alias} ")), **kwargs
|
|
70
|
+
)
|
|
64
71
|
|
|
65
72
|
# Store connection-instantiation arguments.
|
|
66
73
|
self._conn_kwargs: dict[str, _Any] = {}
|
|
@@ -121,14 +128,17 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
121
128
|
|
|
122
129
|
@classmethod
|
|
123
130
|
async def create_connection(
|
|
124
|
-
cls: type[T],
|
|
131
|
+
cls: type[T],
|
|
132
|
+
backoff: ExponentialBackoff = None,
|
|
133
|
+
markdown: str = None,
|
|
134
|
+
**kwargs,
|
|
125
135
|
) -> T:
|
|
126
136
|
"""Create a TCP connection."""
|
|
127
137
|
|
|
128
138
|
transport, protocol = await tcp_transport_protocol_backoff(
|
|
129
139
|
backoff=backoff, **kwargs
|
|
130
140
|
)
|
|
131
|
-
inst = cls(transport, protocol)
|
|
141
|
+
inst = cls(transport, protocol, markdown=markdown)
|
|
132
142
|
|
|
133
143
|
# Is there a better way to do this? We can't restart a server's side
|
|
134
144
|
# of a connection (seems okay).
|
|
@@ -7,7 +7,7 @@ import asyncio
|
|
|
7
7
|
from copy import copy
|
|
8
8
|
import http
|
|
9
9
|
from json import loads
|
|
10
|
-
from typing import Any, Awaitable, Callable, Optional, Tuple, Union
|
|
10
|
+
from typing import Any, Awaitable, Callable, Optional, Tuple, Union, cast
|
|
11
11
|
|
|
12
12
|
# third-party
|
|
13
13
|
from vcorelib import DEFAULT_ENCODING
|
|
@@ -174,11 +174,9 @@ class HttpConnection(_TcpConnection):
|
|
|
174
174
|
async def process_binary(self, data: bytes) -> bool:
|
|
175
175
|
"""Process a binary frame."""
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
for header, payload in self.processor.ingest( # type: ignore
|
|
177
|
+
for header, payload in self.processor.ingest(
|
|
180
178
|
data,
|
|
181
|
-
|
|
179
|
+
RequestHeader if not self.expecting_response else ResponseHeader,
|
|
182
180
|
):
|
|
183
181
|
header.log(self.logger, False)
|
|
184
182
|
|
|
@@ -187,11 +185,15 @@ class HttpConnection(_TcpConnection):
|
|
|
187
185
|
response = ResponseHeader()
|
|
188
186
|
self._send(
|
|
189
187
|
response,
|
|
190
|
-
await self._process_request(
|
|
188
|
+
await self._process_request(
|
|
189
|
+
response, cast(RequestHeader, header), payload
|
|
190
|
+
),
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
# Process the response to a pending request.
|
|
194
194
|
else:
|
|
195
|
-
await self.responses.put(
|
|
195
|
+
await self.responses.put(
|
|
196
|
+
(cast(ResponseHeader, header), payload)
|
|
197
|
+
)
|
|
196
198
|
|
|
197
199
|
return True
|
runtimepy/net/udp/connection.py
CHANGED
|
@@ -44,7 +44,10 @@ class UdpConnection(_Connection, _TransportMixin):
|
|
|
44
44
|
log_alias = "UDP"
|
|
45
45
|
|
|
46
46
|
def __init__(
|
|
47
|
-
self,
|
|
47
|
+
self,
|
|
48
|
+
transport: _DatagramTransport,
|
|
49
|
+
protocol: UdpQueueProtocol,
|
|
50
|
+
**kwargs,
|
|
48
51
|
) -> None:
|
|
49
52
|
"""Initialize this UDP connection."""
|
|
50
53
|
|
|
@@ -55,7 +58,9 @@ class UdpConnection(_Connection, _TransportMixin):
|
|
|
55
58
|
# Re-assign with updated type information.
|
|
56
59
|
self._transport: _DatagramTransport = transport
|
|
57
60
|
|
|
58
|
-
super().__init__(
|
|
61
|
+
super().__init__(
|
|
62
|
+
_getLogger(self.logger_name(f"{self.log_alias} ")), **kwargs
|
|
63
|
+
)
|
|
59
64
|
self._set_protocol(protocol)
|
|
60
65
|
|
|
61
66
|
# Store connection-instantiation arguments.
|
|
@@ -127,7 +132,9 @@ class UdpConnection(_Connection, _TransportMixin):
|
|
|
127
132
|
should_connect: bool = True
|
|
128
133
|
|
|
129
134
|
@classmethod
|
|
130
|
-
async def create_connection(
|
|
135
|
+
async def create_connection(
|
|
136
|
+
cls: type[T], markdown: str = None, **kwargs
|
|
137
|
+
) -> T:
|
|
131
138
|
"""Create a UDP connection."""
|
|
132
139
|
|
|
133
140
|
LOG.debug("kwargs: %s", kwargs)
|
|
@@ -151,7 +158,7 @@ class UdpConnection(_Connection, _TransportMixin):
|
|
|
151
158
|
|
|
152
159
|
# Create the underlying connection.
|
|
153
160
|
transport, protocol = await udp_transport_protocol_backoff(**kwargs)
|
|
154
|
-
conn = cls(transport, protocol)
|
|
161
|
+
conn = cls(transport, protocol, markdown=markdown)
|
|
155
162
|
conn._conn_kwargs = {**kwargs}
|
|
156
163
|
|
|
157
164
|
# Set the remote address manually if necessary.
|
|
@@ -11,7 +11,7 @@ from typing import Any, AsyncIterator
|
|
|
11
11
|
|
|
12
12
|
# third-party
|
|
13
13
|
from vcorelib.asyncio.poll import repeat_until
|
|
14
|
-
from vcorelib.paths.context import tempfile
|
|
14
|
+
from vcorelib.paths.context import PossiblePath, as_path, tempfile
|
|
15
15
|
from vcorelib.paths.hashing import file_md5_hex
|
|
16
16
|
from vcorelib.paths.info import FileInfo
|
|
17
17
|
|
|
@@ -25,7 +25,6 @@ from runtimepy.net.udp.tftp.base import (
|
|
|
25
25
|
)
|
|
26
26
|
from runtimepy.net.udp.tftp.enums import DEFAULT_MODE
|
|
27
27
|
from runtimepy.net.util import IpHostTuplelike
|
|
28
|
-
from runtimepy.util import PossiblePath, as_path
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
class TftpConnection(BaseTftpConnection):
|
|
@@ -51,11 +51,12 @@ class WebsocketConnection(Connection):
|
|
|
51
51
|
def __init__(
|
|
52
52
|
self,
|
|
53
53
|
protocol: _Union[_WebSocketClientProtocol, _WebSocketServerProtocol],
|
|
54
|
+
**kwargs,
|
|
54
55
|
) -> None:
|
|
55
56
|
"""Initialize this connection."""
|
|
56
57
|
|
|
57
58
|
self.protocol = protocol
|
|
58
|
-
super().__init__(self.protocol.logger)
|
|
59
|
+
super().__init__(self.protocol.logger, **kwargs)
|
|
59
60
|
|
|
60
61
|
async def _handle_connection_closed(
|
|
61
62
|
self, task: _Awaitable[V]
|
|
@@ -92,7 +93,9 @@ class WebsocketConnection(Connection):
|
|
|
92
93
|
await self.protocol.close()
|
|
93
94
|
|
|
94
95
|
@classmethod
|
|
95
|
-
async def create_connection(
|
|
96
|
+
async def create_connection(
|
|
97
|
+
cls: type[T], uri: str, markdown: str = None, **kwargs
|
|
98
|
+
) -> T:
|
|
96
99
|
"""Connect a client to an endpoint."""
|
|
97
100
|
|
|
98
101
|
kwargs.setdefault("use_ssl", uri.startswith("wss"))
|
|
@@ -100,11 +103,13 @@ class WebsocketConnection(Connection):
|
|
|
100
103
|
protocol = await getattr(websockets, "connect")(
|
|
101
104
|
uri, **handle_possible_ssl(**kwargs)
|
|
102
105
|
)
|
|
103
|
-
return cls(protocol)
|
|
106
|
+
return cls(protocol, markdown=markdown)
|
|
104
107
|
|
|
105
108
|
@classmethod
|
|
106
109
|
@_asynccontextmanager
|
|
107
|
-
async def client(
|
|
110
|
+
async def client(
|
|
111
|
+
cls: type[T], uri: str, markdown: str = None, **kwargs
|
|
112
|
+
) -> _AsyncIterator[T]:
|
|
108
113
|
"""A wrapper for connecting a client."""
|
|
109
114
|
|
|
110
115
|
kwargs.setdefault("use_ssl", uri.startswith("wss"))
|
|
@@ -112,7 +117,7 @@ class WebsocketConnection(Connection):
|
|
|
112
117
|
async with getattr(websockets, "connect")(
|
|
113
118
|
uri, **handle_possible_ssl(**kwargs)
|
|
114
119
|
) as protocol:
|
|
115
|
-
yield cls(protocol)
|
|
120
|
+
yield cls(protocol, markdown=markdown)
|
|
116
121
|
|
|
117
122
|
@classmethod
|
|
118
123
|
def server_handler(
|
runtimepy/requirements.txt
CHANGED
runtimepy/struct/__init__.py
CHANGED
|
@@ -6,9 +6,11 @@ A module implementing a runtime structure base.
|
|
|
6
6
|
from logging import getLogger as _getLogger
|
|
7
7
|
|
|
8
8
|
# third-party
|
|
9
|
+
from vcorelib.io import MarkdownMixin
|
|
9
10
|
from vcorelib.io.types import JsonObject as _JsonObject
|
|
10
11
|
|
|
11
12
|
# internal
|
|
13
|
+
from runtimepy import PKG_NAME
|
|
12
14
|
from runtimepy.channel.environment.command.processor import (
|
|
13
15
|
ChannelCommandProcessor,
|
|
14
16
|
)
|
|
@@ -16,15 +18,23 @@ from runtimepy.mixins.environment import ChannelEnvironmentMixin
|
|
|
16
18
|
from runtimepy.mixins.logging import LoggerMixinLevelControl
|
|
17
19
|
|
|
18
20
|
|
|
19
|
-
class RuntimeStructBase(
|
|
21
|
+
class RuntimeStructBase(
|
|
22
|
+
LoggerMixinLevelControl, ChannelEnvironmentMixin, MarkdownMixin
|
|
23
|
+
):
|
|
20
24
|
"""A base runtime structure."""
|
|
21
25
|
|
|
22
26
|
log_level_channel: bool = True
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
# Unclear why this is/was necessary (mypy bug?)
|
|
29
|
+
markdown: str
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self, name: str, config: _JsonObject, markdown: str = None
|
|
33
|
+
) -> None:
|
|
25
34
|
"""Initialize this instance."""
|
|
26
35
|
|
|
27
36
|
self.name = name
|
|
37
|
+
self.set_markdown(config=config, markdown=markdown, package=PKG_NAME)
|
|
28
38
|
LoggerMixinLevelControl.__init__(self, logger=_getLogger(self.name))
|
|
29
39
|
ChannelEnvironmentMixin.__init__(self)
|
|
30
40
|
if self.log_level_channel:
|
|
@@ -12,11 +12,12 @@ from logging import INFO, getLogger
|
|
|
12
12
|
from typing import Optional
|
|
13
13
|
|
|
14
14
|
# third-party
|
|
15
|
+
from vcorelib.io import MarkdownMixin
|
|
15
16
|
from vcorelib.io.types import JsonObject
|
|
16
17
|
from vcorelib.math import RateLimiter
|
|
17
18
|
|
|
18
19
|
# internal
|
|
19
|
-
from runtimepy import METRICS_NAME
|
|
20
|
+
from runtimepy import METRICS_NAME, PKG_NAME
|
|
20
21
|
from runtimepy.channel.environment import ChannelEnvironment
|
|
21
22
|
from runtimepy.channel.environment.base import FieldOrChannel
|
|
22
23
|
from runtimepy.channel.environment.command import register_env
|
|
@@ -34,7 +35,7 @@ PEER_SUFFIX = ".peer"
|
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class RuntimepyPeerInterface(
|
|
37
|
-
JsonMessageInterface, AsyncCommandProcessingMixin
|
|
38
|
+
JsonMessageInterface, AsyncCommandProcessingMixin, MarkdownMixin
|
|
38
39
|
):
|
|
39
40
|
"""A class implementing an interface for messaging peer subprocesses."""
|
|
40
41
|
|
|
@@ -42,13 +43,25 @@ class RuntimepyPeerInterface(
|
|
|
42
43
|
|
|
43
44
|
struct_type: type[RuntimeStruct] = SampleStruct
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
# Unclear why this is/was necessary (mypy bug?)
|
|
47
|
+
markdown: str
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self, name: str, config: JsonObject, markdown: str = None
|
|
51
|
+
) -> None:
|
|
46
52
|
"""Initialize this instance."""
|
|
47
53
|
|
|
54
|
+
self.set_markdown(markdown=markdown, package=PKG_NAME)
|
|
55
|
+
|
|
48
56
|
self.processor = MessageProcessor()
|
|
49
57
|
|
|
50
58
|
self.basename = name
|
|
51
|
-
|
|
59
|
+
|
|
60
|
+
self.struct = self.struct_type(
|
|
61
|
+
self.basename + HOST_SUFFIX,
|
|
62
|
+
config,
|
|
63
|
+
markdown=config.get("config", {}).get("markdown"), # type: ignore
|
|
64
|
+
)
|
|
52
65
|
|
|
53
66
|
self.peer: Optional[RemoteCommandProcessor] = None
|
|
54
67
|
self.peer_config: Optional[JsonMessage] = None
|
runtimepy/subprocess/peer.py
CHANGED
|
@@ -19,13 +19,13 @@ from typing import AsyncIterator, Iterator, Type, TypeVar
|
|
|
19
19
|
from vcorelib.io import ARBITER, DEFAULT_INCLUDES_KEY
|
|
20
20
|
from vcorelib.io.file_writer import IndentedFileWriter
|
|
21
21
|
from vcorelib.io.types import JsonObject
|
|
22
|
+
from vcorelib.names import import_str_and_item
|
|
22
23
|
from vcorelib.paths.context import tempfile
|
|
23
24
|
|
|
24
25
|
# internal
|
|
25
26
|
from runtimepy.subprocess import spawn_exec, spawn_shell
|
|
26
27
|
from runtimepy.subprocess.interface import RuntimepyPeerInterface
|
|
27
28
|
from runtimepy.subprocess.protocol import RuntimepySubprocessProtocol
|
|
28
|
-
from runtimepy.util import import_str_and_item
|
|
29
29
|
|
|
30
30
|
T = TypeVar("T", bound="RuntimepyPeer")
|
|
31
31
|
|
|
@@ -38,10 +38,11 @@ class RuntimepyPeer(RuntimepyPeerInterface):
|
|
|
38
38
|
protocol: RuntimepySubprocessProtocol,
|
|
39
39
|
name: str,
|
|
40
40
|
config: JsonObject,
|
|
41
|
+
markdown: str = None,
|
|
41
42
|
) -> None:
|
|
42
43
|
"""Initialize this instance."""
|
|
43
44
|
|
|
44
|
-
super().__init__(name, config)
|
|
45
|
+
super().__init__(name, config, markdown=markdown)
|
|
45
46
|
self.protocol = protocol
|
|
46
47
|
|
|
47
48
|
# Offset message identifiers.
|
|
@@ -89,14 +90,20 @@ class RuntimepyPeer(RuntimepyPeerInterface):
|
|
|
89
90
|
@classmethod
|
|
90
91
|
@asynccontextmanager
|
|
91
92
|
async def shell(
|
|
92
|
-
cls: Type[T],
|
|
93
|
+
cls: Type[T],
|
|
94
|
+
name: str,
|
|
95
|
+
config: JsonObject,
|
|
96
|
+
cmd: str,
|
|
97
|
+
markdown: str = None,
|
|
93
98
|
) -> AsyncIterator[T]:
|
|
94
99
|
"""Create an instance from a shell command."""
|
|
95
100
|
|
|
96
101
|
async with spawn_shell(
|
|
97
102
|
cmd, stdout=asyncio.Queue(), stderr=asyncio.Queue()
|
|
98
103
|
) as proto:
|
|
99
|
-
async with cls(
|
|
104
|
+
async with cls(
|
|
105
|
+
proto, name, config, markdown=markdown
|
|
106
|
+
)._context() as inst:
|
|
100
107
|
yield inst
|
|
101
108
|
|
|
102
109
|
async def main(self) -> None:
|
|
@@ -105,14 +112,21 @@ class RuntimepyPeer(RuntimepyPeerInterface):
|
|
|
105
112
|
@classmethod
|
|
106
113
|
@asynccontextmanager
|
|
107
114
|
async def exec(
|
|
108
|
-
cls: Type[T],
|
|
115
|
+
cls: Type[T],
|
|
116
|
+
name: str,
|
|
117
|
+
config: JsonObject,
|
|
118
|
+
*args,
|
|
119
|
+
markdown: str = None,
|
|
120
|
+
**kwargs,
|
|
109
121
|
) -> AsyncIterator[T]:
|
|
110
122
|
"""Create an instance from comand-line arguments."""
|
|
111
123
|
|
|
112
124
|
async with spawn_exec(
|
|
113
125
|
*args, stdout=asyncio.Queue(), stderr=asyncio.Queue(), **kwargs
|
|
114
126
|
) as proto:
|
|
115
|
-
async with cls(
|
|
127
|
+
async with cls(
|
|
128
|
+
proto, name, config, markdown=markdown
|
|
129
|
+
)._context() as inst:
|
|
116
130
|
yield inst
|
|
117
131
|
|
|
118
132
|
@classmethod
|
runtimepy/task/basic/periodic.py
CHANGED
|
@@ -13,12 +13,14 @@ from logging import getLogger as _getLogger
|
|
|
13
13
|
from typing import Optional as _Optional
|
|
14
14
|
|
|
15
15
|
# third-party
|
|
16
|
+
from vcorelib.io import MarkdownMixin
|
|
16
17
|
from vcorelib.math import DEFAULT_DEPTH as _DEFAULT_DEPTH
|
|
17
18
|
from vcorelib.math import MovingAverage as _MovingAverage
|
|
18
19
|
from vcorelib.math import RateTracker as _RateTracker
|
|
19
20
|
from vcorelib.math import rate_str as _rate_str
|
|
20
21
|
|
|
21
22
|
# internal
|
|
23
|
+
from runtimepy import PKG_NAME
|
|
22
24
|
from runtimepy.channel.environment import ChannelEnvironment
|
|
23
25
|
from runtimepy.channel.environment.command.processor import (
|
|
24
26
|
ChannelCommandProcessor,
|
|
@@ -32,7 +34,9 @@ from runtimepy.primitives import Float as _Float
|
|
|
32
34
|
from runtimepy.ui.controls import Controlslike
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
class PeriodicTask(
|
|
37
|
+
class PeriodicTask(
|
|
38
|
+
LoggerMixinLevelControl, ChannelEnvironmentMixin, MarkdownMixin, _ABC
|
|
39
|
+
):
|
|
36
40
|
"""A class implementing a simple periodic-task interface."""
|
|
37
41
|
|
|
38
42
|
auto_finalize = True
|
|
@@ -45,10 +49,12 @@ class PeriodicTask(LoggerMixinLevelControl, ChannelEnvironmentMixin, _ABC):
|
|
|
45
49
|
period_s: float = 1.0,
|
|
46
50
|
env: ChannelEnvironment = None,
|
|
47
51
|
period_controls: Controlslike = "period",
|
|
52
|
+
markdown: str = None,
|
|
48
53
|
) -> None:
|
|
49
54
|
"""Initialize this task."""
|
|
50
55
|
|
|
51
56
|
self.name = name
|
|
57
|
+
self.set_markdown(markdown=markdown, package=PKG_NAME)
|
|
52
58
|
LoggerMixinLevelControl.__init__(self, logger=_getLogger(self.name))
|
|
53
59
|
self._task: _Optional[_asyncio.Task[None]] = None
|
|
54
60
|
|