runtimepy 5.14.2__py3-none-any.whl → 5.15.0__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/__init__.py +1 -4
- runtimepy/channel/environment/__init__.py +93 -2
- runtimepy/channel/environment/create.py +16 -1
- runtimepy/channel/environment/sample.py +10 -2
- runtimepy/channel/registry.py +2 -3
- runtimepy/codec/protocol/base.py +34 -14
- runtimepy/codec/protocol/json.py +5 -3
- runtimepy/codec/system/__init__.py +6 -2
- runtimepy/control/source.py +1 -1
- runtimepy/data/404.md +16 -0
- runtimepy/data/base.yaml +3 -0
- runtimepy/data/css/bootstrap_extra.css +59 -44
- runtimepy/data/css/main.css +23 -4
- runtimepy/data/dummy_load.yaml +2 -2
- runtimepy/data/factories.yaml +1 -0
- runtimepy/data/js/classes/App.js +54 -2
- runtimepy/data/js/classes/ChannelTable.js +6 -8
- runtimepy/data/js/classes/Plot.js +9 -4
- runtimepy/data/js/classes/TabFilter.js +47 -9
- runtimepy/data/js/classes/TabInterface.js +106 -11
- runtimepy/data/js/classes/WindowHashManager.js +30 -15
- runtimepy/data/js/init.js +18 -1
- runtimepy/data/js/markdown_page.js +10 -0
- runtimepy/data/schemas/BitFields.yaml +9 -0
- runtimepy/data/schemas/RuntimeEnum.yaml +6 -0
- runtimepy/data/schemas/StructConfig.yaml +9 -1
- runtimepy/data/static/css/bootstrap-icons.min.css +4 -3
- runtimepy/data/static/css/bootstrap.min.css +3 -4
- runtimepy/data/static/css/fonts/bootstrap-icons.woff +0 -0
- runtimepy/data/static/css/fonts/bootstrap-icons.woff2 +0 -0
- runtimepy/data/static/js/bootstrap.bundle.min.js +5 -4
- runtimepy/data/static/js/webglplot.umd.min.js +2 -1
- runtimepy/data/static/svg/outline-dark.svg +22 -0
- runtimepy/data/static/svg/outline-light.svg +22 -0
- runtimepy/enum/__init__.py +13 -1
- runtimepy/enum/registry.py +13 -1
- runtimepy/message/__init__.py +3 -3
- runtimepy/mixins/logging.py +6 -1
- runtimepy/net/__init__.py +0 -2
- runtimepy/net/arbiter/info.py +36 -4
- runtimepy/net/arbiter/struct/__init__.py +3 -2
- runtimepy/net/connection.py +4 -5
- runtimepy/net/html/__init__.py +29 -11
- runtimepy/net/html/bootstrap/__init__.py +2 -2
- runtimepy/net/html/bootstrap/elements.py +44 -24
- runtimepy/net/html/bootstrap/tabs.py +18 -11
- runtimepy/net/http/__init__.py +3 -3
- runtimepy/net/http/request_target.py +3 -3
- runtimepy/net/mixin.py +4 -2
- runtimepy/net/server/__init__.py +16 -9
- runtimepy/net/server/app/__init__.py +1 -0
- runtimepy/net/server/app/create.py +3 -3
- runtimepy/net/server/app/env/__init__.py +28 -4
- runtimepy/net/server/app/env/settings.py +4 -7
- runtimepy/net/server/app/env/tab/controls.py +141 -27
- runtimepy/net/server/app/env/tab/html.py +68 -26
- runtimepy/net/server/app/env/widgets.py +115 -61
- runtimepy/net/server/app/landing_page.py +1 -1
- runtimepy/net/server/html.py +2 -2
- runtimepy/net/server/json.py +1 -1
- runtimepy/net/server/markdown.py +18 -12
- runtimepy/net/server/mux.py +29 -0
- runtimepy/net/stream/__init__.py +6 -5
- runtimepy/net/stream/base.py +4 -2
- runtimepy/net/tcp/connection.py +5 -3
- runtimepy/net/tcp/http/__init__.py +10 -9
- runtimepy/net/tcp/protocol.py +2 -2
- runtimepy/net/tcp/scpi/__init__.py +5 -2
- runtimepy/net/tcp/telnet/__init__.py +2 -1
- runtimepy/net/udp/connection.py +10 -6
- runtimepy/net/udp/protocol.py +5 -6
- runtimepy/net/udp/queue.py +5 -2
- runtimepy/net/udp/tftp/base.py +2 -1
- runtimepy/net/websocket/connection.py +50 -8
- runtimepy/primitives/array/__init__.py +7 -5
- runtimepy/primitives/base.py +3 -2
- runtimepy/primitives/field/__init__.py +35 -2
- runtimepy/primitives/field/fields.py +11 -2
- runtimepy/primitives/field/manager/base.py +19 -2
- runtimepy/primitives/serializable/base.py +5 -2
- runtimepy/primitives/serializable/fixed.py +5 -2
- runtimepy/primitives/serializable/prefixed.py +4 -1
- runtimepy/primitives/types/base.py +4 -1
- runtimepy/primitives/types/bounds.py +10 -4
- runtimepy/registry/__init__.py +20 -0
- runtimepy/registry/name.py +6 -0
- runtimepy/requirements.txt +2 -2
- runtimepy/ui/controls.py +20 -1
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/METADATA +6 -6
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/RECORD +95 -92
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/WHEEL +1 -1
- runtimepy/data/404.html +0 -7
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/entry_points.txt +0 -0
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/licenses/LICENSE +0 -0
- {runtimepy-5.14.2.dist-info → runtimepy-5.15.0.dist-info}/top_level.txt +0 -0
|
@@ -14,6 +14,7 @@ from runtimepy.channel.environment.command.processor import (
|
|
|
14
14
|
ChannelCommandProcessor,
|
|
15
15
|
)
|
|
16
16
|
from runtimepy.enum import RuntimeEnum
|
|
17
|
+
from runtimepy.net.html.bootstrap import icon_str
|
|
17
18
|
from runtimepy.net.html.bootstrap.elements import (
|
|
18
19
|
flex,
|
|
19
20
|
input_box,
|
|
@@ -25,7 +26,9 @@ from runtimepy.net.html.bootstrap.elements import (
|
|
|
25
26
|
def plot_checkbox(parent: Element, name: str) -> None:
|
|
26
27
|
"""Add a checkbox for individual channel plot status."""
|
|
27
28
|
|
|
28
|
-
container = div(tag="td", parent=parent, class_str="text-center p-0")
|
|
29
|
+
container = div(tag="td", parent=parent, class_str="text-center p-0 fs-5")
|
|
30
|
+
|
|
31
|
+
title = f"Enable plotting channel '{name}'."
|
|
29
32
|
|
|
30
33
|
set_tooltip(
|
|
31
34
|
div(
|
|
@@ -34,10 +37,11 @@ def plot_checkbox(parent: Element, name: str) -> None:
|
|
|
34
37
|
value="",
|
|
35
38
|
id=f"plot-{name}",
|
|
36
39
|
allow_no_end_tag=True,
|
|
37
|
-
parent=container,
|
|
38
|
-
class_str="form-check-input",
|
|
40
|
+
parent=div(tag="label", parent=container),
|
|
41
|
+
class_str="form-check-input rounded-0",
|
|
42
|
+
title=title,
|
|
39
43
|
),
|
|
40
|
-
|
|
44
|
+
title,
|
|
41
45
|
placement="left",
|
|
42
46
|
)
|
|
43
47
|
|
|
@@ -45,7 +49,10 @@ def plot_checkbox(parent: Element, name: str) -> None:
|
|
|
45
49
|
def select_element(**kwargs) -> Element:
|
|
46
50
|
"""Create a select element."""
|
|
47
51
|
|
|
48
|
-
select = div(tag="select",
|
|
52
|
+
select = div(tag="select", **kwargs)
|
|
53
|
+
select.add_class(
|
|
54
|
+
"form-select", "rounded-0", "border-top-0", "border-bottom-0"
|
|
55
|
+
)
|
|
49
56
|
if "title" in kwargs:
|
|
50
57
|
select["aria-label"] = kwargs["title"]
|
|
51
58
|
return select
|
|
@@ -53,7 +60,7 @@ def select_element(**kwargs) -> Element:
|
|
|
53
60
|
|
|
54
61
|
def enum_dropdown(
|
|
55
62
|
parent: Element, name: str, enum: RuntimeEnum, current: int | bool
|
|
56
|
-
) ->
|
|
63
|
+
) -> Element:
|
|
57
64
|
"""Implement a drop down for enumeration options."""
|
|
58
65
|
|
|
59
66
|
select = select_element(
|
|
@@ -67,8 +74,10 @@ def enum_dropdown(
|
|
|
67
74
|
if current == val:
|
|
68
75
|
opt.booleans.add("selected")
|
|
69
76
|
|
|
77
|
+
return select
|
|
78
|
+
|
|
70
79
|
|
|
71
|
-
TABLE_BUTTON_CLASSES = ("
|
|
80
|
+
TABLE_BUTTON_CLASSES = ("border-top-0", "border-bottom-0")
|
|
72
81
|
|
|
73
82
|
|
|
74
83
|
def channel_table_header(
|
|
@@ -78,73 +87,45 @@ def channel_table_header(
|
|
|
78
87
|
|
|
79
88
|
env = command.env
|
|
80
89
|
|
|
81
|
-
# Add header.
|
|
82
|
-
header_row = div(
|
|
83
|
-
tag="tr", parent=parent, class_str="border-start border-end"
|
|
84
|
-
)
|
|
85
|
-
for heading, desc in [
|
|
86
|
-
("plot", "Toggle plotting for channels."),
|
|
87
|
-
("name", "Channel names."),
|
|
88
|
-
("value", "Channel values."),
|
|
89
|
-
("ctl", "Type-specific channel controls."),
|
|
90
|
-
("type", "Channel types."),
|
|
91
|
-
]:
|
|
92
|
-
set_tooltip(
|
|
93
|
-
div(
|
|
94
|
-
tag="th",
|
|
95
|
-
scope="col",
|
|
96
|
-
parent=header_row,
|
|
97
|
-
text=heading,
|
|
98
|
-
class_str="text-secondary p-1",
|
|
99
|
-
),
|
|
100
|
-
desc,
|
|
101
|
-
placement="left",
|
|
102
|
-
)
|
|
103
|
-
|
|
104
90
|
# Add some controls.
|
|
105
|
-
ctl_row = div(
|
|
91
|
+
ctl_row = div(
|
|
92
|
+
tag="tr",
|
|
93
|
+
parent=parent,
|
|
94
|
+
class_str="bg-body-tertiary border-start border-end",
|
|
95
|
+
)
|
|
106
96
|
|
|
107
97
|
# Button for clearing plotted channels.
|
|
108
98
|
toggle_button(
|
|
109
99
|
div(tag="th", parent=ctl_row, class_str="text-center p-0"),
|
|
110
100
|
tooltip="Clear plotted channels.",
|
|
111
101
|
icon="x-lg",
|
|
102
|
+
placement="left",
|
|
112
103
|
id="clear-plotted-channels",
|
|
113
104
|
title="button for clearing plotted channels",
|
|
114
|
-
).add_class(
|
|
105
|
+
).add_class(*TABLE_BUTTON_CLASSES)
|
|
115
106
|
|
|
116
|
-
input_box(
|
|
117
|
-
div(tag="th", parent=ctl_row, class_str="p-0
|
|
107
|
+
_, label, box = input_box(
|
|
108
|
+
div(tag="th", parent=ctl_row, colspan="2", class_str="p-0"),
|
|
118
109
|
description="Channel name filter.",
|
|
110
|
+
pattern=".* ! @ $",
|
|
111
|
+
label="filter",
|
|
119
112
|
id="channel-filter",
|
|
113
|
+
icon="funnel",
|
|
114
|
+
spellcheck="false",
|
|
120
115
|
)
|
|
116
|
+
label.add_class("border-top-0", "border-bottom-0")
|
|
117
|
+
box.add_class("border-top-0", "border-bottom-0")
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
icon="trash",
|
|
126
|
-
tooltip="Clear all plot points.",
|
|
127
|
-
id="clear-plotted-points",
|
|
128
|
-
title="button for clearing plot point data",
|
|
129
|
-
).add_class("pb-2")
|
|
130
|
-
|
|
131
|
-
cell = flex(tag="th", parent=ctl_row)
|
|
132
|
-
cell.add_class("p-0")
|
|
133
|
-
|
|
134
|
-
# Button for 'reset all defaults' if this tab has more than one channel
|
|
135
|
-
# with a default value.
|
|
136
|
-
if env.num_defaults > 1:
|
|
137
|
-
toggle_button(
|
|
138
|
-
cell,
|
|
139
|
-
id="set-defaults",
|
|
140
|
-
icon="arrow-counterclockwise",
|
|
141
|
-
tooltip="Reset all channels to their default values.",
|
|
142
|
-
).add_class(*TABLE_BUTTON_CLASSES)
|
|
119
|
+
cell = flex(
|
|
120
|
+
parent=div(tag="th", parent=ctl_row, colspan="2", class_str="p-0")
|
|
121
|
+
)
|
|
143
122
|
|
|
144
123
|
# Add a selection menu for custom commands.
|
|
145
124
|
select = select_element(
|
|
146
125
|
parent=cell, id="custom-commands", title="Custom command selector."
|
|
147
126
|
)
|
|
127
|
+
select.add_class("border-start-0")
|
|
128
|
+
|
|
148
129
|
if command.custom_commands:
|
|
149
130
|
for key in command.custom_commands:
|
|
150
131
|
opt = div(tag="option", value=key, text=key, parent=select)
|
|
@@ -156,7 +137,8 @@ def channel_table_header(
|
|
|
156
137
|
cell,
|
|
157
138
|
icon="send",
|
|
158
139
|
id="send-custom-commands",
|
|
159
|
-
title="
|
|
140
|
+
title="send selected command button",
|
|
141
|
+
tooltip="Send selected command (left dropdown).",
|
|
160
142
|
).add_class(*TABLE_BUTTON_CLASSES)
|
|
161
143
|
else:
|
|
162
144
|
div(
|
|
@@ -167,8 +149,79 @@ def channel_table_header(
|
|
|
167
149
|
)
|
|
168
150
|
select.booleans.add("disabled")
|
|
169
151
|
|
|
170
|
-
#
|
|
171
|
-
|
|
152
|
+
# Button for 'reset all defaults' if this tab has more than one channel
|
|
153
|
+
# with a default value.
|
|
154
|
+
if env.num_defaults > 1:
|
|
155
|
+
toggle_button(
|
|
156
|
+
cell,
|
|
157
|
+
id="set-defaults",
|
|
158
|
+
icon="arrow-counterclockwise",
|
|
159
|
+
tooltip="Reset all channels to their default values.",
|
|
160
|
+
).add_class(*TABLE_BUTTON_CLASSES)
|
|
161
|
+
|
|
162
|
+
toggle_button(
|
|
163
|
+
cell,
|
|
164
|
+
icon="eye-slash",
|
|
165
|
+
tooltip="Toggle command channels.",
|
|
166
|
+
id="toggle-command-channels",
|
|
167
|
+
title="button for toggling command-channel visibility",
|
|
168
|
+
icon_classes=["text-info-emphasis"],
|
|
169
|
+
).add_class(*TABLE_BUTTON_CLASSES)
|
|
170
|
+
toggle_button(
|
|
171
|
+
cell,
|
|
172
|
+
icon="eye-slash-fill",
|
|
173
|
+
tooltip="Toggle regular channels.",
|
|
174
|
+
id="toggle-regular-channels",
|
|
175
|
+
title="button for toggling regular-channel visibility",
|
|
176
|
+
).add_class(*TABLE_BUTTON_CLASSES)
|
|
177
|
+
toggle_button(
|
|
178
|
+
cell,
|
|
179
|
+
icon="trash",
|
|
180
|
+
tooltip="Clear all plot points.",
|
|
181
|
+
id="clear-plotted-points",
|
|
182
|
+
title="button for clearing plot point data",
|
|
183
|
+
).add_class("me-auto", *TABLE_BUTTON_CLASSES)
|
|
184
|
+
|
|
185
|
+
# Add header.
|
|
186
|
+
header_row = div(
|
|
187
|
+
tag="tr",
|
|
188
|
+
parent=parent,
|
|
189
|
+
class_str="border-end text-center bg-body-tertiary",
|
|
190
|
+
)
|
|
191
|
+
icon_classes = ["text-primary-emphasis"]
|
|
192
|
+
for heading, desc in [
|
|
193
|
+
(
|
|
194
|
+
icon_str("activity", classes=icon_classes) + "?",
|
|
195
|
+
"Toggle plotting for channels.",
|
|
196
|
+
),
|
|
197
|
+
(
|
|
198
|
+
icon_str("pen", classes=icon_classes) + " name",
|
|
199
|
+
"Channel names.",
|
|
200
|
+
),
|
|
201
|
+
(
|
|
202
|
+
icon_str("database", classes=icon_classes) + " value",
|
|
203
|
+
"Channel values.",
|
|
204
|
+
),
|
|
205
|
+
(
|
|
206
|
+
icon_str("controller", classes=icon_classes) + " controls",
|
|
207
|
+
"Type-specific channel controls.",
|
|
208
|
+
),
|
|
209
|
+
(
|
|
210
|
+
icon_str("braces", classes=icon_classes) + " type",
|
|
211
|
+
"Channel types.",
|
|
212
|
+
),
|
|
213
|
+
]:
|
|
214
|
+
set_tooltip(
|
|
215
|
+
div(
|
|
216
|
+
tag="th",
|
|
217
|
+
scope="col",
|
|
218
|
+
parent=header_row,
|
|
219
|
+
text=heading,
|
|
220
|
+
class_str="text-secondary p-1 border-start text-nowrap",
|
|
221
|
+
),
|
|
222
|
+
"(column) " + desc,
|
|
223
|
+
placement="left",
|
|
224
|
+
)
|
|
172
225
|
|
|
173
226
|
|
|
174
227
|
def value_input_box(name: str, parent: Element) -> Element:
|
|
@@ -186,15 +239,16 @@ def value_input_box(name: str, parent: Element) -> Element:
|
|
|
186
239
|
"rounded-0",
|
|
187
240
|
"font-monospace",
|
|
188
241
|
"form-control",
|
|
189
|
-
"m-1",
|
|
190
242
|
"p-0",
|
|
191
|
-
"ps-
|
|
243
|
+
"ps-2",
|
|
244
|
+
"border-0",
|
|
245
|
+
"text-secondary-emphasis",
|
|
192
246
|
)
|
|
193
247
|
toggle_button(
|
|
194
248
|
input_container,
|
|
195
249
|
icon="send",
|
|
196
250
|
id=name,
|
|
197
251
|
title=f"Send command value for '{name}'.",
|
|
198
|
-
).add_class(*TABLE_BUTTON_CLASSES)
|
|
252
|
+
).add_class("pt-0", "pb-0", *TABLE_BUTTON_CLASSES)
|
|
199
253
|
|
|
200
254
|
return input_container
|
runtimepy/net/server/html.py
CHANGED
|
@@ -16,7 +16,7 @@ from runtimepy.net.http.response import ResponseHeader
|
|
|
16
16
|
from runtimepy.net.tcp.http import HttpConnection
|
|
17
17
|
|
|
18
18
|
HtmlApp = Callable[
|
|
19
|
-
[Html, RequestHeader, ResponseHeader, Optional[
|
|
19
|
+
[Html, RequestHeader, ResponseHeader, Optional[bytearray]], Awaitable[Html]
|
|
20
20
|
]
|
|
21
21
|
HtmlApps = dict[str, HtmlApp]
|
|
22
22
|
|
|
@@ -58,7 +58,7 @@ async def html_handler(
|
|
|
58
58
|
stream: TextIO,
|
|
59
59
|
request: RequestHeader,
|
|
60
60
|
response: ResponseHeader,
|
|
61
|
-
request_data: Optional[
|
|
61
|
+
request_data: Optional[bytearray],
|
|
62
62
|
default_app: HtmlApp = None,
|
|
63
63
|
**kwargs,
|
|
64
64
|
) -> bool:
|
runtimepy/net/server/json.py
CHANGED
|
@@ -87,7 +87,7 @@ def json_handler(
|
|
|
87
87
|
stream: TextIO,
|
|
88
88
|
request: RequestHeader,
|
|
89
89
|
response: ResponseHeader,
|
|
90
|
-
request_data: Optional[
|
|
90
|
+
request_data: Optional[bytearray],
|
|
91
91
|
data: JsonObject,
|
|
92
92
|
) -> None:
|
|
93
93
|
"""Create an HTTP response from some JSON object data."""
|
runtimepy/net/server/markdown.py
CHANGED
|
@@ -12,10 +12,12 @@ from vcorelib.io.file_writer import IndentedFileWriter
|
|
|
12
12
|
from vcorelib.paths import rel
|
|
13
13
|
|
|
14
14
|
LOGO_MARKDOWN = "[](/)"
|
|
15
|
+
DIR_FILE = "dir.html"
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def markdown_for_dir(
|
|
18
|
-
|
|
19
|
+
paths_bases: Iterable[tuple[Path, Path]],
|
|
20
|
+
extra_links: dict[str, Iterable[str]] = None,
|
|
19
21
|
) -> str:
|
|
20
22
|
"""Get markdown data for a directory."""
|
|
21
23
|
|
|
@@ -32,22 +34,26 @@ def markdown_for_dir(
|
|
|
32
34
|
for app in apps:
|
|
33
35
|
writer.write(f"* [{app}]({app})")
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
writer.write(f"## `{curr_dir}`")
|
|
37
|
+
writer.write("## directories")
|
|
37
38
|
writer.empty()
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
writer.write(f"
|
|
40
|
+
for path, base in paths_bases:
|
|
41
|
+
curr_dir = rel(path, base=base)
|
|
42
|
+
writer.write(f"### `{base.name}/{curr_dir}`")
|
|
43
|
+
writer.empty()
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
# Link to go up a directory.
|
|
46
|
+
if curr_dir != Path():
|
|
47
|
+
writer.write(f"* [..](/{curr_dir.parent}/{DIR_FILE})")
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
name = f"**{name}**"
|
|
49
|
+
for item in sorted(path.iterdir()):
|
|
50
|
+
curr = rel(item, base=base)
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
name = f"`{curr.name}`"
|
|
53
|
+
if item.is_dir():
|
|
54
|
+
name = f"**{name}**"
|
|
55
|
+
|
|
56
|
+
writer.write(f"* [{name}](/{curr})")
|
|
51
57
|
|
|
52
58
|
result: str = cast(StringIO, writer.stream).getvalue()
|
|
53
59
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A module implementing an interface for serving a web application that allows
|
|
3
|
+
iframe-based grid multiplexing.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# built-in
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
# third-party
|
|
10
|
+
from svgen.element.html import Html
|
|
11
|
+
|
|
12
|
+
# internal
|
|
13
|
+
from runtimepy.net.http.header import RequestHeader
|
|
14
|
+
from runtimepy.net.http.response import ResponseHeader
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def mux_app(
|
|
18
|
+
document: Html,
|
|
19
|
+
request: RequestHeader,
|
|
20
|
+
response: ResponseHeader,
|
|
21
|
+
request_data: Optional[bytearray],
|
|
22
|
+
) -> Html:
|
|
23
|
+
"""An iframe multiplexing application."""
|
|
24
|
+
|
|
25
|
+
del request
|
|
26
|
+
del response
|
|
27
|
+
del request_data
|
|
28
|
+
|
|
29
|
+
return document
|
runtimepy/net/stream/__init__.py
CHANGED
|
@@ -4,10 +4,11 @@ A module aggregating stream-oriented connection interfaces.
|
|
|
4
4
|
|
|
5
5
|
# built-in
|
|
6
6
|
from typing import BinaryIO as _BinaryIO
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
# third-party
|
|
9
|
+
from vcorelib.io import BinaryMessage
|
|
8
10
|
|
|
9
11
|
# internal
|
|
10
|
-
from runtimepy.net.connection import BinaryMessage
|
|
11
12
|
from runtimepy.net.stream.base import PrefixedMessageConnection
|
|
12
13
|
from runtimepy.net.stream.string import StringMessageConnection
|
|
13
14
|
from runtimepy.net.tcp.connection import TcpConnection
|
|
@@ -34,14 +35,14 @@ class UdpPrefixedMessageConnection(PrefixedMessageConnection, UdpConnection):
|
|
|
34
35
|
"""A UDP implementation for size-prefixed messages."""
|
|
35
36
|
|
|
36
37
|
async def process_datagram(
|
|
37
|
-
self, data:
|
|
38
|
+
self, data: BinaryMessage, addr: tuple[str, int]
|
|
38
39
|
) -> bool:
|
|
39
40
|
"""Process a datagram."""
|
|
40
41
|
|
|
41
42
|
return await self.process_binary(data, addr=addr)
|
|
42
43
|
|
|
43
44
|
def _send_message(
|
|
44
|
-
self, data: BinaryMessage, addr:
|
|
45
|
+
self, data: BinaryMessage, addr: tuple[str, int] = None
|
|
45
46
|
) -> None:
|
|
46
47
|
"""Underlying data send."""
|
|
47
48
|
|
|
@@ -52,7 +53,7 @@ class EchoMessageConnection(PrefixedMessageConnection):
|
|
|
52
53
|
"""A connection that just echoes what it was sent."""
|
|
53
54
|
|
|
54
55
|
async def process_single(
|
|
55
|
-
self, stream: _BinaryIO, addr:
|
|
56
|
+
self, stream: _BinaryIO, addr: tuple[str, int] = None
|
|
56
57
|
) -> bool:
|
|
57
58
|
"""Process a single message."""
|
|
58
59
|
|
runtimepy/net/stream/base.py
CHANGED
|
@@ -6,9 +6,11 @@ A module implementing a base, stream-oriented connection interface.
|
|
|
6
6
|
from io import BytesIO as _BytesIO
|
|
7
7
|
from typing import BinaryIO as _BinaryIO
|
|
8
8
|
|
|
9
|
+
# third-party
|
|
10
|
+
from vcorelib.io import BinaryMessage
|
|
11
|
+
|
|
9
12
|
# internal
|
|
10
13
|
from runtimepy.message import MessageProcessor
|
|
11
|
-
from runtimepy.net.connection import BinaryMessage
|
|
12
14
|
from runtimepy.net.connection import Connection as _Connection
|
|
13
15
|
|
|
14
16
|
|
|
@@ -58,7 +60,7 @@ class PrefixedMessageConnection(_Connection):
|
|
|
58
60
|
return True
|
|
59
61
|
|
|
60
62
|
async def process_binary(
|
|
61
|
-
self, data:
|
|
63
|
+
self, data: BinaryMessage, addr: tuple[str, int] = None
|
|
62
64
|
) -> bool:
|
|
63
65
|
"""Process an incoming message."""
|
|
64
66
|
|
runtimepy/net/tcp/connection.py
CHANGED
|
@@ -17,10 +17,12 @@ from typing import Optional as _Optional
|
|
|
17
17
|
from typing import TypeVar as _TypeVar
|
|
18
18
|
from typing import Union as _Union
|
|
19
19
|
|
|
20
|
+
# third-party
|
|
21
|
+
from vcorelib.io import BinaryMessage
|
|
22
|
+
|
|
20
23
|
# internal
|
|
21
24
|
from runtimepy.net import sockname as _sockname
|
|
22
25
|
from runtimepy.net.backoff import ExponentialBackoff
|
|
23
|
-
from runtimepy.net.connection import BinaryMessage as _BinaryMessage
|
|
24
26
|
from runtimepy.net.connection import Connection as _Connection
|
|
25
27
|
from runtimepy.net.connection import EchoConnection as _EchoConnection
|
|
26
28
|
from runtimepy.net.connection import NullConnection as _NullConnection
|
|
@@ -91,7 +93,7 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
91
93
|
"""Determine if this connection uses SSL."""
|
|
92
94
|
return self._transport.get_extra_info("sslcontext") is not None
|
|
93
95
|
|
|
94
|
-
async def _await_message(self) -> _Optional[_Union[
|
|
96
|
+
async def _await_message(self) -> _Optional[_Union[BinaryMessage, str]]:
|
|
95
97
|
"""Await the next message. Return None on error or failure."""
|
|
96
98
|
|
|
97
99
|
data = await self._protocol.queue.get()
|
|
@@ -103,7 +105,7 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
103
105
|
"""Enqueue a text message to send."""
|
|
104
106
|
self.send_binary(data.encode())
|
|
105
107
|
|
|
106
|
-
def send_binary(self, data:
|
|
108
|
+
def send_binary(self, data: BinaryMessage) -> None:
|
|
107
109
|
"""Enqueue a binary message tos end."""
|
|
108
110
|
self._transport.write(data)
|
|
109
111
|
self.metrics.tx.increment(len(data))
|
|
@@ -11,6 +11,7 @@ from typing import Any, Awaitable, Callable, Optional, Tuple, Union, cast
|
|
|
11
11
|
|
|
12
12
|
# third-party
|
|
13
13
|
from vcorelib import DEFAULT_ENCODING
|
|
14
|
+
from vcorelib.io import BinaryMessage
|
|
14
15
|
|
|
15
16
|
# internal
|
|
16
17
|
from runtimepy import PKG_NAME, VERSION
|
|
@@ -19,18 +20,18 @@ from runtimepy.net.http.header import RequestHeader
|
|
|
19
20
|
from runtimepy.net.http.response import AsyncResponse, ResponseHeader
|
|
20
21
|
from runtimepy.net.tcp.connection import TcpConnection as _TcpConnection
|
|
21
22
|
|
|
22
|
-
HttpResult = Optional[
|
|
23
|
+
HttpResult = Optional[BinaryMessage | AsyncResponse]
|
|
23
24
|
|
|
24
25
|
#
|
|
25
26
|
# async def handler(
|
|
26
27
|
# response: ResponseHeader,
|
|
27
28
|
# request: RequestHeader,
|
|
28
|
-
# request_data: Optional[
|
|
29
|
+
# request_data: Optional[bytearray],
|
|
29
30
|
# ) -> HttpResult:
|
|
30
31
|
# """Sample handler."""
|
|
31
32
|
#
|
|
32
33
|
HttpRequestHandler = Callable[
|
|
33
|
-
[ResponseHeader, RequestHeader, Optional[
|
|
34
|
+
[ResponseHeader, RequestHeader, Optional[bytearray]],
|
|
34
35
|
Awaitable[HttpResult],
|
|
35
36
|
]
|
|
36
37
|
HttpResponse = Tuple[ResponseHeader, HttpResult]
|
|
@@ -99,7 +100,7 @@ class HttpConnection(_TcpConnection):
|
|
|
99
100
|
self,
|
|
100
101
|
response: ResponseHeader,
|
|
101
102
|
request: RequestHeader,
|
|
102
|
-
request_data: Optional[
|
|
103
|
+
request_data: Optional[bytearray],
|
|
103
104
|
) -> HttpResult:
|
|
104
105
|
"""Sample handler."""
|
|
105
106
|
|
|
@@ -107,7 +108,7 @@ class HttpConnection(_TcpConnection):
|
|
|
107
108
|
self,
|
|
108
109
|
response: ResponseHeader,
|
|
109
110
|
request: RequestHeader,
|
|
110
|
-
request_data: Optional[
|
|
111
|
+
request_data: Optional[bytearray],
|
|
111
112
|
) -> HttpResult:
|
|
112
113
|
"""Sample handler."""
|
|
113
114
|
|
|
@@ -115,7 +116,7 @@ class HttpConnection(_TcpConnection):
|
|
|
115
116
|
self,
|
|
116
117
|
response: ResponseHeader,
|
|
117
118
|
request_header: RequestHeader,
|
|
118
|
-
request_data: Optional[
|
|
119
|
+
request_data: Optional[bytearray] = None,
|
|
119
120
|
) -> HttpResult:
|
|
120
121
|
"""Process an individual request."""
|
|
121
122
|
|
|
@@ -141,7 +142,7 @@ class HttpConnection(_TcpConnection):
|
|
|
141
142
|
return result
|
|
142
143
|
|
|
143
144
|
async def request(
|
|
144
|
-
self, request: RequestHeader, data: Optional[
|
|
145
|
+
self, request: RequestHeader, data: Optional[BinaryMessage] = None
|
|
145
146
|
) -> HttpResponse:
|
|
146
147
|
"""Make an HTTP request."""
|
|
147
148
|
|
|
@@ -157,7 +158,7 @@ class HttpConnection(_TcpConnection):
|
|
|
157
158
|
return result
|
|
158
159
|
|
|
159
160
|
async def request_json(
|
|
160
|
-
self, request: RequestHeader, data: Optional[
|
|
161
|
+
self, request: RequestHeader, data: Optional[BinaryMessage] = None
|
|
161
162
|
) -> Any:
|
|
162
163
|
"""
|
|
163
164
|
Perform a request and convert the response to a data structure by
|
|
@@ -191,7 +192,7 @@ class HttpConnection(_TcpConnection):
|
|
|
191
192
|
|
|
192
193
|
header.log(self.logger, True)
|
|
193
194
|
|
|
194
|
-
async def process_binary(self, data:
|
|
195
|
+
async def process_binary(self, data: BinaryMessage) -> bool:
|
|
195
196
|
"""Process a binary frame."""
|
|
196
197
|
|
|
197
198
|
for header, payload in self.processor.ingest(
|
runtimepy/net/tcp/protocol.py
CHANGED
|
@@ -9,10 +9,10 @@ from logging import getLogger as _getLogger
|
|
|
9
9
|
from typing import Optional as _Optional
|
|
10
10
|
|
|
11
11
|
# third-party
|
|
12
|
+
from vcorelib.io import BinaryMessage
|
|
12
13
|
from vcorelib.logging import LoggerType as _LoggerType
|
|
13
14
|
|
|
14
15
|
# internal
|
|
15
|
-
from runtimepy.net.connection import BinaryMessage as _BinaryMessage
|
|
16
16
|
from runtimepy.net.connection import Connection as _Connection
|
|
17
17
|
from runtimepy.net.mixin import (
|
|
18
18
|
BinaryMessageQueueMixin as _BinaryMessageQueueMixin,
|
|
@@ -26,7 +26,7 @@ class QueueProtocol(_BinaryMessageQueueMixin, _Protocol):
|
|
|
26
26
|
logger: _LoggerType
|
|
27
27
|
conn: _Connection
|
|
28
28
|
|
|
29
|
-
def data_received(self, data:
|
|
29
|
+
def data_received(self, data: BinaryMessage) -> None:
|
|
30
30
|
"""Handle incoming data."""
|
|
31
31
|
self.queue.put_nowait(data)
|
|
32
32
|
|
|
@@ -5,6 +5,9 @@ A module implementing an SCPI interface.
|
|
|
5
5
|
# built-in
|
|
6
6
|
import asyncio
|
|
7
7
|
|
|
8
|
+
# third-party
|
|
9
|
+
from vcorelib.io import BinaryMessage
|
|
10
|
+
|
|
8
11
|
# internal
|
|
9
12
|
from runtimepy.net.arbiter.tcp import TcpConnectionFactory
|
|
10
13
|
from runtimepy.net.tcp import TcpConnection
|
|
@@ -34,9 +37,9 @@ class ScpiConnection(TcpConnection):
|
|
|
34
37
|
|
|
35
38
|
return True
|
|
36
39
|
|
|
37
|
-
async def process_binary(self, data:
|
|
40
|
+
async def process_binary(self, data: BinaryMessage) -> bool:
|
|
38
41
|
"""Process a binary frame."""
|
|
39
|
-
return await self.process_text(data.decode())
|
|
42
|
+
return await self.process_text(bytes(data).decode())
|
|
40
43
|
|
|
41
44
|
async def send_command(
|
|
42
45
|
self,
|
|
@@ -10,6 +10,7 @@ from typing import BinaryIO as _BinaryIO
|
|
|
10
10
|
|
|
11
11
|
# third-party
|
|
12
12
|
from vcorelib import DEFAULT_ENCODING
|
|
13
|
+
from vcorelib.io import BinaryMessage
|
|
13
14
|
|
|
14
15
|
# internal
|
|
15
16
|
from runtimepy.net.tcp.connection import TcpConnection as _TcpConnection
|
|
@@ -65,7 +66,7 @@ class Telnet(_TcpConnection):
|
|
|
65
66
|
) -> None:
|
|
66
67
|
"""Process a telnet option."""
|
|
67
68
|
|
|
68
|
-
async def process_binary(self, data:
|
|
69
|
+
async def process_binary(self, data: BinaryMessage) -> bool:
|
|
69
70
|
"""Process a binary frame."""
|
|
70
71
|
|
|
71
72
|
result = True
|