runtimepy 5.7.5__py3-none-any.whl → 5.7.7__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/data/404.html +3 -0
- runtimepy/data/server_base.yaml +15 -3
- runtimepy/metrics/channel.py +1 -1
- runtimepy/net/arbiter/housekeeping/__init__.py +5 -1
- runtimepy/net/arbiter/info.py +20 -0
- runtimepy/net/html/arbiter.py +32 -0
- runtimepy/net/server/__init__.py +47 -23
- runtimepy/net/server/app/__init__.py +4 -3
- runtimepy/net/server/html.py +3 -1
- runtimepy/net/tcp/connection.py +11 -4
- runtimepy/struct/__init__.py +26 -1
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.dist-info}/METADATA +4 -4
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.dist-info}/RECORD +18 -16
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.dist-info}/LICENSE +0 -0
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.dist-info}/WHEEL +0 -0
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.dist-info}/entry_points.txt +0 -0
- {runtimepy-5.7.5.dist-info → runtimepy-5.7.7.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.1.4
|
|
4
|
-
# hash=
|
|
4
|
+
# hash=ed12f307211d1317e3ce6a7c4409b97f
|
|
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.7.
|
|
13
|
+
VERSION = "5.7.7"
|
|
14
14
|
|
|
15
15
|
# runtimepy-specific content.
|
|
16
16
|
METRICS_NAME = "metrics"
|
runtimepy/data/404.html
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>runtimepy/5.7.6</title><style>@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Regular.woff2)}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Bold.woff2);font-weight:700}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-BoldItalic.woff2);font-weight:700;font-style:italic}@font-face{font-family:CascadiaCode;src:url(/static/woff2/CascadiaCode-Italic.woff2);font-style:italic}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Regular.woff2)}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Bold.woff2);font-weight:700}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-BoldItalic.woff2);font-weight:700;font-style:italic}@font-face{font-family:CascadiaMono;src:url(/static/woff2/CascadiaMono-Italic.woff2);font-style:italic}</style><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css></link>
|
|
2
|
+
<link href=https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css rel=stylesheet crossorigin=anonymous></link><style>html{height:100%}body{height:100%;margin:0;overflow:hidden}body>:first-child{height:100%}#runtimepy{height:100%}#runtimepy-tabs{width:min-content}#runtimepy-splash{position:fixed;top:0;left:0;width:100vw;height:100vh;opacity:1}.click-plot{cursor:pointer}.stale{color:var(--bs-warning-text-emphasis)!important}.slider{min-width:8em}.toggle-value{}.window-button{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-link-hover-color)!important}canvas:focus{outline:none}.flex-column-scroll-bodge{height:100%;flex-wrap:nowrap;overflow-y:scroll;flex-shrink:0}.scroll{overflow:scroll}.tab-content-bodge{width:100%;height:100%}.button-bodge{text-align:left}.collapsing{transition:none!important}.tab-pane.fade{transition:none!important}.modal.fade{transition:none!important}.modal-dialog{width:80%;max-width:80%}.table{margin-bottom:0;width:auto}.table-container{overflow-x:scroll;min-height:fit-content}.channel-column{overflow-y:scroll;overflow-x:hidden;flex-grow:0;flex-shrink:0}.channel-value{min-width:10em}.table>tbody>tr>td{vertical-align:middle}.collapse:not(.show){display:none!important}select.form-select{width:min-content}select.form-select:hover{cursor:pointer}textarea.text-logs{min-height:10em!important;padding-top:0!important;padding-bottom:0!important;border-top:0}.vertical-divider{flex-basis:.75em;flex-grow:0;flex-shrink:0}.vertical-divider:hover{cursor:col-resize;background-color:var(--bs-highlight-bg)!important}button:hover{background-color:var(--bs-tertiary-bg)}.channel-value-input{width:6em}:root,[data-bs-theme=dark],[data-bs-theme=light]{--bs-font-sans-serif:CascadiaCode, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace:CascadiaMono, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}</style><div id=runtimepy class="align-items-start bg-body d-flex" data-bs-theme=dark><div class="bg-dark-subtle h-100 d-flex flex-column"><button type=button id=theme-button class="btn btn-secondary rounded-0 font-monospace button-bodge text-nowrap has-tooltip" data-bs-title=" Toggle light/dark." data-bs-placement=right>
|
|
3
|
+
<i class="bi bi-lightbulb"></i></button></div><div class="overflow-y-auto d-flex flex-grow-1 h-100 flex-column justify-content-between text-body"><div></div><div class="d-flex flex-row justify-content-between"><div></div><div class="text-body p-3 pb-0"><h1><a href=https://libre-embedded.com><img alt=logo src=https://libre-embedded.com/static/png/chip-circle-bootstrap/128x128.png></a> Resource Not Found (404) <a href=https://libre-embedded.com><img alt=logo src=https://libre-embedded.com/static/png/chip-circle-bootstrap/128x128.png></a></h1><p>(<a href=https://libre-embedded.com>home</a>)</div><div></div></div><div></div></div></div><script>let lightMode=!1;function lightDarkClick(){lightMode=!lightMode,document.getElementById("runtimepy").setAttribute("data-bs-theme",lightMode?"light":"dark"),window.location.hash=lightMode?"#light-mode":""}let lightDarkButton=document.getElementById("theme-button");if(lightDarkButton&&lightDarkButton.addEventListener("click",lightDarkClick),window.location.hash){let e=window.location.hash.slice(1).split(",");e.includes("light-mode")&&lightDarkButton.click()}</script><script src=https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js crossorigin=anonymous></script>
|
runtimepy/data/server_base.yaml
CHANGED
|
@@ -13,11 +13,23 @@ structs:
|
|
|
13
13
|
init:
|
|
14
14
|
- runtimepy.net.arbiter.housekeeping.init
|
|
15
15
|
|
|
16
|
+
config:
|
|
17
|
+
# Default redirects.
|
|
18
|
+
http_redirects:
|
|
19
|
+
"/": "/index.html"
|
|
20
|
+
"/index.html": "/app.html"
|
|
21
|
+
|
|
22
|
+
# Serve these applications by default at these paths.
|
|
23
|
+
http_app_paths: ["/app.html"]
|
|
24
|
+
|
|
25
|
+
# Handles config["http_app_prefixes"].
|
|
26
|
+
config_builders:
|
|
27
|
+
- runtimepy.net.html.arbiter.web_app_paths
|
|
28
|
+
|
|
16
29
|
servers:
|
|
17
30
|
- factory: runtimepy_http
|
|
18
31
|
kwargs: {port: "$runtimepy_http_server"}
|
|
19
|
-
|
|
20
32
|
- factory: runtimepy_websocket_json
|
|
21
|
-
kwargs: {
|
|
33
|
+
kwargs: {port: "$runtimepy_websocket_json_server"}
|
|
22
34
|
- factory: runtimepy_websocket_data
|
|
23
|
-
kwargs: {
|
|
35
|
+
kwargs: {port: "$runtimepy_websocket_data_server"}
|
runtimepy/metrics/channel.py
CHANGED
|
@@ -52,7 +52,11 @@ class ConnectionMetricsPoller(_ArbiterTask):
|
|
|
52
52
|
|
|
53
53
|
# Handle any incoming commands.
|
|
54
54
|
processors: list[Awaitable[None]] = []
|
|
55
|
-
for mapping in
|
|
55
|
+
for mapping in (
|
|
56
|
+
self.app.connections.values(),
|
|
57
|
+
self.app.tasks.values(),
|
|
58
|
+
self.app.structs.values(),
|
|
59
|
+
):
|
|
56
60
|
for item in mapping:
|
|
57
61
|
if isinstance(item, AsyncCommandProcessingMixin):
|
|
58
62
|
processors.append(item.process_command_queue())
|
runtimepy/net/arbiter/info.py
CHANGED
|
@@ -6,6 +6,7 @@ A module implementing an application information interface.
|
|
|
6
6
|
from abc import ABC as _ABC
|
|
7
7
|
import asyncio as _asyncio
|
|
8
8
|
from contextlib import AsyncExitStack as _AsyncExitStack
|
|
9
|
+
from contextlib import contextmanager
|
|
9
10
|
from dataclasses import dataclass
|
|
10
11
|
from logging import getLogger as _getLogger
|
|
11
12
|
from re import compile as _compile
|
|
@@ -57,13 +58,29 @@ class RuntimeStruct(RuntimeStructBase, _ABC):
|
|
|
57
58
|
|
|
58
59
|
byte_order: ByteOrder = DEFAULT_BYTE_ORDER
|
|
59
60
|
|
|
61
|
+
# Set this for structs to automatically be polled when the application is
|
|
62
|
+
# going down.
|
|
63
|
+
final_poll = False
|
|
64
|
+
|
|
60
65
|
def init_env(self) -> None:
|
|
61
66
|
"""Initialize this sample environment."""
|
|
62
67
|
|
|
68
|
+
@contextmanager
|
|
69
|
+
def _final_poll(self) -> _Iterator[None]:
|
|
70
|
+
"""Poll when the context ends."""
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
yield
|
|
74
|
+
finally:
|
|
75
|
+
self.poll()
|
|
76
|
+
|
|
63
77
|
async def build(self, app: "AppInfo", **kwargs) -> None:
|
|
64
78
|
"""Build a struct instance's channel environment."""
|
|
65
79
|
|
|
66
80
|
self.app = app
|
|
81
|
+
if self.final_poll:
|
|
82
|
+
self.app.stack.enter_context(self._final_poll())
|
|
83
|
+
|
|
67
84
|
self.init_env()
|
|
68
85
|
self.update_byte_order(self.byte_order, **kwargs)
|
|
69
86
|
|
|
@@ -102,6 +119,9 @@ class TrigStruct(RuntimeStruct, TrigMixin):
|
|
|
102
119
|
class SampleStruct(TrigStruct):
|
|
103
120
|
"""A sample runtime structure."""
|
|
104
121
|
|
|
122
|
+
# For fun.
|
|
123
|
+
final_poll = True
|
|
124
|
+
|
|
105
125
|
def init_env(self) -> None:
|
|
106
126
|
"""Initialize this sample environment."""
|
|
107
127
|
super().init_env()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A module implementing connection-arbiter related utilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# internal
|
|
6
|
+
from runtimepy.net.arbiter.config import ConfigObject
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def web_app_paths(config: ConfigObject) -> None:
|
|
10
|
+
"""
|
|
11
|
+
Register boilerplate path handling for additional application-serving URIs.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
config.setdefault("config", {})
|
|
15
|
+
redirects = config["config"].setdefault("http_redirects", {})
|
|
16
|
+
app_paths = config["config"].setdefault("http_app_paths", [])
|
|
17
|
+
|
|
18
|
+
for prefix in config["config"].get("http_app_prefixes", []):
|
|
19
|
+
if not prefix.startswith("/"):
|
|
20
|
+
prefix = "/" + prefix
|
|
21
|
+
|
|
22
|
+
assert not prefix.endswith("/"), prefix
|
|
23
|
+
|
|
24
|
+
# Add re-directs.
|
|
25
|
+
index_path = f"{prefix}/index.html"
|
|
26
|
+
app_path = f"{prefix}/app.html"
|
|
27
|
+
redirects.setdefault(prefix, index_path)
|
|
28
|
+
redirects.setdefault(index_path, app_path)
|
|
29
|
+
|
|
30
|
+
# Add app path.
|
|
31
|
+
if app_path not in app_paths:
|
|
32
|
+
app_paths.append(app_path)
|
runtimepy/net/server/__init__.py
CHANGED
|
@@ -109,6 +109,18 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
109
109
|
with favicon.open("rb") as favicon_fd:
|
|
110
110
|
type(self).favicon_data = favicon_fd.read()
|
|
111
111
|
|
|
112
|
+
def redirect_to(
|
|
113
|
+
self,
|
|
114
|
+
path: str,
|
|
115
|
+
response: ResponseHeader,
|
|
116
|
+
status: http.HTTPStatus = http.HTTPStatus.TEMPORARY_REDIRECT,
|
|
117
|
+
) -> bytes:
|
|
118
|
+
"""Handle responding with redirection status."""
|
|
119
|
+
|
|
120
|
+
response["Location"] = path
|
|
121
|
+
response.status = status
|
|
122
|
+
return bytes()
|
|
123
|
+
|
|
112
124
|
async def try_redirect(
|
|
113
125
|
self, path: PathMaybeQuery, response: ResponseHeader
|
|
114
126
|
) -> Optional[bytes]:
|
|
@@ -118,33 +130,39 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
118
130
|
|
|
119
131
|
curr = Path(path[0])
|
|
120
132
|
if curr in self.class_redirect_paths:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# No data payload, but signal to caller that a response is ready.
|
|
125
|
-
result = bytes()
|
|
133
|
+
result = self.redirect_to(
|
|
134
|
+
str(self.class_redirect_paths[curr]), response
|
|
135
|
+
)
|
|
126
136
|
|
|
127
137
|
return result
|
|
128
138
|
|
|
129
|
-
|
|
130
|
-
self,
|
|
139
|
+
def render_markdown(
|
|
140
|
+
self, content: str, response: ResponseHeader, **kwargs
|
|
131
141
|
) -> bytes:
|
|
132
|
-
"""
|
|
142
|
+
"""Return rendered markdown content."""
|
|
133
143
|
|
|
134
144
|
document = get_html()
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
writer.stream.getvalue(), # type: ignore
|
|
142
|
-
)
|
|
145
|
+
with IndentedFileWriter.string() as writer:
|
|
146
|
+
writer.write_markdown(content, **kwargs)
|
|
147
|
+
full_markdown_page(
|
|
148
|
+
document,
|
|
149
|
+
writer.stream.getvalue(), # type: ignore
|
|
150
|
+
)
|
|
143
151
|
|
|
144
152
|
response["Content-Type"] = f"text/html; charset={DEFAULT_ENCODING}"
|
|
145
153
|
|
|
146
154
|
return document.encode_str().encode()
|
|
147
155
|
|
|
156
|
+
async def render_markdown_file(
|
|
157
|
+
self, path: Path, response: ResponseHeader, **kwargs
|
|
158
|
+
) -> bytes:
|
|
159
|
+
"""Render a markdown file as HTML and return the result."""
|
|
160
|
+
|
|
161
|
+
async with aiofiles.open(path, mode="r") as path_fd:
|
|
162
|
+
return self.render_markdown(
|
|
163
|
+
await path_fd.read(), response, **kwargs
|
|
164
|
+
)
|
|
165
|
+
|
|
148
166
|
async def try_file(
|
|
149
167
|
self, path: PathMaybeQuery, response: ResponseHeader
|
|
150
168
|
) -> Optional[bytes]:
|
|
@@ -157,9 +175,12 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
157
175
|
candidate = search.joinpath(path[0][1:])
|
|
158
176
|
|
|
159
177
|
# Handle markdown sources.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
if candidate.name:
|
|
179
|
+
md_candidate = candidate.with_suffix(".md")
|
|
180
|
+
if md_candidate.is_file():
|
|
181
|
+
return await self.render_markdown_file(
|
|
182
|
+
md_candidate, response
|
|
183
|
+
)
|
|
163
184
|
|
|
164
185
|
if candidate.is_file():
|
|
165
186
|
mime, encoding = mimetypes.guess_type(candidate, strict=False)
|
|
@@ -239,6 +260,7 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
239
260
|
request.log(self.logger, False, level=logging.INFO)
|
|
240
261
|
|
|
241
262
|
result = None
|
|
263
|
+
populated = False
|
|
242
264
|
|
|
243
265
|
with StringIO() as stream:
|
|
244
266
|
if request.target.origin_form:
|
|
@@ -250,7 +272,7 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
250
272
|
return self.favicon_data
|
|
251
273
|
|
|
252
274
|
# Try serving a file and handling redirects.
|
|
253
|
-
for handler in [self.
|
|
275
|
+
for handler in [self.try_file, self.try_redirect]:
|
|
254
276
|
result = await handler(
|
|
255
277
|
request.target.origin_form, response
|
|
256
278
|
)
|
|
@@ -266,10 +288,11 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
266
288
|
request_data,
|
|
267
289
|
self.json_data,
|
|
268
290
|
)
|
|
291
|
+
populated = True
|
|
269
292
|
|
|
270
293
|
# Serve the application.
|
|
271
294
|
else:
|
|
272
|
-
await html_handler(
|
|
295
|
+
populated = await html_handler(
|
|
273
296
|
type(self).apps,
|
|
274
297
|
stream,
|
|
275
298
|
request,
|
|
@@ -278,6 +301,7 @@ class RuntimepyServerConnection(HttpConnection):
|
|
|
278
301
|
default_app=type(self).default_app,
|
|
279
302
|
)
|
|
280
303
|
|
|
281
|
-
|
|
304
|
+
if populated:
|
|
305
|
+
result = stream.getvalue().encode()
|
|
282
306
|
|
|
283
|
-
return result
|
|
307
|
+
return result or self.redirect_to("/404.html", response)
|
|
@@ -69,9 +69,10 @@ async def setup(app: AppInfo) -> int:
|
|
|
69
69
|
)
|
|
70
70
|
|
|
71
71
|
# Default application (environment tabs).
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
html_app = create_app(app, getattr(_import_module(module), method))
|
|
73
|
+
target: str
|
|
74
|
+
for target in app.config_param("http_app_paths", []):
|
|
75
|
+
RuntimepyServerConnection.apps[target] = html_app
|
|
75
76
|
|
|
76
77
|
# Register redirects.
|
|
77
78
|
redirects: dict[str, str] = app.config_param("http_redirects", {})
|
runtimepy/net/server/html.py
CHANGED
|
@@ -33,7 +33,7 @@ async def html_handler(
|
|
|
33
33
|
response: ResponseHeader,
|
|
34
34
|
request_data: Optional[bytes],
|
|
35
35
|
default_app: HtmlApp = None,
|
|
36
|
-
) ->
|
|
36
|
+
) -> bool:
|
|
37
37
|
"""Render an HTML document in response to an HTTP request."""
|
|
38
38
|
|
|
39
39
|
# Set response headers.
|
|
@@ -43,3 +43,5 @@ async def html_handler(
|
|
|
43
43
|
app = apps.get(request.target.path, default_app)
|
|
44
44
|
if app is not None:
|
|
45
45
|
(await app(get_html(), request, response, request_data)).render(stream)
|
|
46
|
+
|
|
47
|
+
return app is not None
|
runtimepy/net/tcp/connection.py
CHANGED
|
@@ -219,6 +219,7 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
219
219
|
peer: type[V] = None,
|
|
220
220
|
serve_kwargs: dict[str, _Any] = None,
|
|
221
221
|
connect_kwargs: dict[str, _Any] = None,
|
|
222
|
+
host: str = "127.0.0.1",
|
|
222
223
|
) -> _AsyncIterator[tuple[V, T]]:
|
|
223
224
|
"""Create a connection pair."""
|
|
224
225
|
|
|
@@ -241,16 +242,22 @@ class TcpConnection(_Connection, _TransportMixin):
|
|
|
241
242
|
serve_kwargs = {}
|
|
242
243
|
|
|
243
244
|
server = await stack.enter_async_context(
|
|
244
|
-
peer.serve(
|
|
245
|
+
peer.serve(
|
|
246
|
+
callback,
|
|
247
|
+
host=host,
|
|
248
|
+
port=0,
|
|
249
|
+
backlog=1,
|
|
250
|
+
**serve_kwargs,
|
|
251
|
+
)
|
|
245
252
|
)
|
|
246
253
|
|
|
247
|
-
host = server.sockets[0].getsockname()
|
|
248
|
-
|
|
249
254
|
if connect_kwargs is None:
|
|
250
255
|
connect_kwargs = {}
|
|
251
256
|
|
|
252
257
|
client = await cls.create_connection(
|
|
253
|
-
host=
|
|
258
|
+
host=host,
|
|
259
|
+
port=server.sockets[0].getsockname()[1],
|
|
260
|
+
**connect_kwargs,
|
|
254
261
|
)
|
|
255
262
|
await cond.acquire()
|
|
256
263
|
|
runtimepy/struct/__init__.py
CHANGED
|
@@ -3,7 +3,10 @@ A module implementing a runtime structure base.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# built-in
|
|
6
|
+
from argparse import Namespace
|
|
7
|
+
import asyncio
|
|
6
8
|
from logging import getLogger as _getLogger
|
|
9
|
+
from typing import Optional
|
|
7
10
|
|
|
8
11
|
# third-party
|
|
9
12
|
from vcorelib.io import MarkdownMixin
|
|
@@ -11,15 +14,20 @@ from vcorelib.io.types import JsonObject as _JsonObject
|
|
|
11
14
|
|
|
12
15
|
# internal
|
|
13
16
|
from runtimepy import PKG_NAME
|
|
17
|
+
from runtimepy.channel.environment.command import FieldOrChannel
|
|
14
18
|
from runtimepy.channel.environment.command.processor import (
|
|
15
19
|
ChannelCommandProcessor,
|
|
16
20
|
)
|
|
21
|
+
from runtimepy.mixins.async_command import AsyncCommandProcessingMixin
|
|
17
22
|
from runtimepy.mixins.environment import ChannelEnvironmentMixin
|
|
18
23
|
from runtimepy.mixins.logging import LoggerMixinLevelControl
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class RuntimeStructBase(
|
|
22
|
-
LoggerMixinLevelControl,
|
|
27
|
+
LoggerMixinLevelControl,
|
|
28
|
+
ChannelEnvironmentMixin,
|
|
29
|
+
AsyncCommandProcessingMixin,
|
|
30
|
+
MarkdownMixin,
|
|
23
31
|
):
|
|
24
32
|
"""A base runtime structure."""
|
|
25
33
|
|
|
@@ -42,6 +50,23 @@ class RuntimeStructBase(
|
|
|
42
50
|
self.command = ChannelCommandProcessor(self.env, self.logger)
|
|
43
51
|
self.config = config
|
|
44
52
|
|
|
53
|
+
async def poll(args: Namespace, __: Optional[FieldOrChannel]) -> None:
|
|
54
|
+
"""Handle a test command."""
|
|
55
|
+
|
|
56
|
+
count = 1
|
|
57
|
+
delay = 0.0
|
|
58
|
+
|
|
59
|
+
if args.extra:
|
|
60
|
+
count = int(args.extra[0])
|
|
61
|
+
if len(args.extra) > 1:
|
|
62
|
+
delay = float(args.extra[1])
|
|
63
|
+
|
|
64
|
+
for _ in range(count):
|
|
65
|
+
self.poll()
|
|
66
|
+
await asyncio.sleep(delay)
|
|
67
|
+
|
|
68
|
+
self._setup_async_commands(poll)
|
|
69
|
+
|
|
45
70
|
def poll(self) -> None:
|
|
46
71
|
"""
|
|
47
72
|
A method that other runtime entities can call to perform canonical
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: runtimepy
|
|
3
|
-
Version: 5.7.
|
|
3
|
+
Version: 5.7.7
|
|
4
4
|
Summary: A framework for implementing Python services.
|
|
5
5
|
Home-page: https://github.com/vkottler/runtimepy
|
|
6
6
|
Author: Vaughn Kottler
|
|
@@ -19,9 +19,9 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: svgen >=0.6.8
|
|
21
21
|
Requires-Dist: aiofiles
|
|
22
|
-
Requires-Dist: psutil
|
|
23
22
|
Requires-Dist: websockets
|
|
24
23
|
Requires-Dist: vcorelib >=3.4.2
|
|
24
|
+
Requires-Dist: psutil
|
|
25
25
|
Provides-Extra: test
|
|
26
26
|
Requires-Dist: pylint ; extra == 'test'
|
|
27
27
|
Requires-Dist: flake8 ; extra == 'test'
|
|
@@ -45,11 +45,11 @@ Requires-Dist: uvloop ; (sys_platform != "win32" and sys_platform != "cygwin") a
|
|
|
45
45
|
=====================================
|
|
46
46
|
generator=datazen
|
|
47
47
|
version=3.1.4
|
|
48
|
-
hash=
|
|
48
|
+
hash=0bf47d2ff3fb0ed78ed013f4fcbc59e2
|
|
49
49
|
=====================================
|
|
50
50
|
-->
|
|
51
51
|
|
|
52
|
-
# runtimepy ([5.7.
|
|
52
|
+
# runtimepy ([5.7.7](https://pypi.org/project/runtimepy/))
|
|
53
53
|
|
|
54
54
|
[](https://pypi.org/project/runtimepy/)
|
|
55
55
|

|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
runtimepy/__init__.py,sha256=
|
|
1
|
+
runtimepy/__init__.py,sha256=ifv28DYiMhfJHoYR8HQoEZaUsU7HvJuVEy6bMmCZqYc,390
|
|
2
2
|
runtimepy/__main__.py,sha256=OPAed6hggoQdw-6QAR62mqLC-rCkdDhOq0wyeS2vDRI,332
|
|
3
3
|
runtimepy/app.py,sha256=sTvatbsGZ2Hdel36Si_WUbNMtg9CzsJyExr5xjIcxDE,970
|
|
4
4
|
runtimepy/dev_requirements.txt,sha256=j0dh11ztJAzfaUL0iFheGjaZj9ppDzmTkclTT8YKO8c,230
|
|
@@ -41,13 +41,14 @@ runtimepy/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
41
41
|
runtimepy/control/source.py,sha256=nW3Q2D-LcekII7K5XKbxXCcR-9jYQyvv0UypeNy1Dnw,1695
|
|
42
42
|
runtimepy/control/step.py,sha256=2LdZTpMHLwHLdpPVinpC2qByTs5I5LTDt-xONn_6Fc8,5491
|
|
43
43
|
runtimepy/control/env/__init__.py,sha256=RHJqysY7Pv4VDs2SGk0X-qc5xp_SrQ_oxb5Deug8HEM,1339
|
|
44
|
+
runtimepy/data/404.html,sha256=sn0Mcntzb1-AekukYZAQqz43xfmZtTWa2n2HD2ItqDM,4697
|
|
44
45
|
runtimepy/data/browser.yaml,sha256=oc5KEV1C1uAJ4MkhNo4hyVVfJtZvHelRNqzNvD313Ow,79
|
|
45
46
|
runtimepy/data/dummy_load.yaml,sha256=89BPHLaHJOgiiZSVACBA0_xmlN_N6v2fpcCauB03ZkE,1830
|
|
46
47
|
runtimepy/data/factories.yaml,sha256=Jm0QPwW2BQgnj1fIFQwygsWXzT78QJuf7auVRtRzWKQ,1847
|
|
47
48
|
runtimepy/data/favicon.ico,sha256=iPOtqnqrFVr5tNBM6kTsw7nh0KullEz4TpS6CEQ2HVM,362870
|
|
48
49
|
runtimepy/data/sample_telemetry.yaml,sha256=OpdFurkvtWJGaNl9LMlU2rKo15AaVVr-U_hoZfsbp-Y,695
|
|
49
50
|
runtimepy/data/server.yaml,sha256=wS_Ceiu2TpkfPurpqoYoPlgzc9DAWtUd24MW7t-S5rU,97
|
|
50
|
-
runtimepy/data/server_base.yaml,sha256=
|
|
51
|
+
runtimepy/data/server_base.yaml,sha256=R_varVgGPGV4nxWYYwKUnHC9ufINi4V92YVhxCCC5wg,875
|
|
51
52
|
runtimepy/data/server_dev.yaml,sha256=nQsPh7LuQig3pzHfdg_aD3yOUiCj1sKKfI-WwW3hXmQ,523
|
|
52
53
|
runtimepy/data/tftp_server.yaml,sha256=-bFOWJSagI-fEQQcT8k7eDMJVfSPm2XAxLVG3dqUTa4,204
|
|
53
54
|
runtimepy/data/css/bootstrap_extra.css,sha256=-clAoiYcyOkS4xana4O-jvGVsphOaYB_VlmX9mUjI80,1796
|
|
@@ -124,7 +125,7 @@ runtimepy/message/handlers.py,sha256=He9NC7MCkaV2clKc8dTSZ_T8N2l3ATjaTFzBr9n1T34
|
|
|
124
125
|
runtimepy/message/interface.py,sha256=vg25Uhp7wrDFH6v2iRKUBwnCBpjc3JAFjwdu3KArrLo,11285
|
|
125
126
|
runtimepy/message/types.py,sha256=vajDWKW32LjzsfH-eVCCxZTWVctj4_-tjanhnmCqUis,821
|
|
126
127
|
runtimepy/metrics/__init__.py,sha256=NyCcB3uJi4_tGMO-ws3CPRA2fph2t15BcW_tfeV9fmc,357
|
|
127
|
-
runtimepy/metrics/channel.py,sha256=
|
|
128
|
+
runtimepy/metrics/channel.py,sha256=rCF_NZ4UaIYV4NExmw0Mskzyd2Npy-c_hFiRe36_bzg,2527
|
|
128
129
|
runtimepy/metrics/connection.py,sha256=FiN0BQ79eDBf5qCh1sx7m6CSDbMMW7WMi7li96mHkoo,808
|
|
129
130
|
runtimepy/metrics/task.py,sha256=g6CttQ3SS217DJRBFnj-rDUq6uauZdMOcTOjHgSEfWo,1911
|
|
130
131
|
runtimepy/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -147,7 +148,7 @@ runtimepy/net/util.py,sha256=P6WnH4n8JJkEfKwepk1eP4lGPxWjqcFv0yL3N0mvtrw,5897
|
|
|
147
148
|
runtimepy/net/apps/__init__.py,sha256=vjo7e19QXtJwe6V6B-QGvYiJveYobnYIfpkKZrnS17w,710
|
|
148
149
|
runtimepy/net/arbiter/__init__.py,sha256=ptKF995rYKvkm4Mya92vA5QEDqcFq5NRD0IYGqZ6_do,740
|
|
149
150
|
runtimepy/net/arbiter/base.py,sha256=w5IFDRU1Zfw7sfg0eS8hj7_Ip6bHHYQ4S212Ic-Kj-c,14817
|
|
150
|
-
runtimepy/net/arbiter/info.py,sha256=
|
|
151
|
+
runtimepy/net/arbiter/info.py,sha256=tlzfbn3soyejArxfZ4lrLbaBVOjiz4nX52wa6XMfXuo,9684
|
|
151
152
|
runtimepy/net/arbiter/result.py,sha256=PHZo5qj4SI08ZeWPFk_8OZ1umI6L0dp5nJpjjS8QUpM,2871
|
|
152
153
|
runtimepy/net/arbiter/task.py,sha256=APcc5QioAG8uueIzxJU-vktIn8Ys3yJd_CFlRWb6Ieo,795
|
|
153
154
|
runtimepy/net/arbiter/udp.py,sha256=-ecHJs-utcsiTfr5wSb73hUUuYFBeRVT_D1znYp5q6A,893
|
|
@@ -158,7 +159,7 @@ runtimepy/net/arbiter/config/util.py,sha256=MezLSil5mEwioI5kxDOZa9y118st1_t0El8D
|
|
|
158
159
|
runtimepy/net/arbiter/factory/__init__.py,sha256=jC1szUYucH4Us3jbb0mhFgnklHlorya5DYGGw2pqfe4,334
|
|
159
160
|
runtimepy/net/arbiter/factory/connection.py,sha256=uSwnvIWGJcDd2ReK-s9lBx-NcRQ1JH_7VbwkWjE_8zA,3690
|
|
160
161
|
runtimepy/net/arbiter/factory/task.py,sha256=YLRv55dnDBqHRJIMYgO9_uHi0AW-8hwa0n3wHAKyncE,1959
|
|
161
|
-
runtimepy/net/arbiter/housekeeping/__init__.py,sha256=
|
|
162
|
+
runtimepy/net/arbiter/housekeeping/__init__.py,sha256=mgrYnDdL1DSP6fXdtk2VU9uqTvtgX2VX5q2Q9EfcJus,3545
|
|
162
163
|
runtimepy/net/arbiter/imports/__init__.py,sha256=W7-kkM4B_zDU42Fyh0PmZL56JslS_0_qzvpzUhN6aDU,5041
|
|
163
164
|
runtimepy/net/arbiter/imports/util.py,sha256=d4_HnWphrkiHQkyYr8YJKvSSTXoMf3Bct_7jg_CS9eA,1086
|
|
164
165
|
runtimepy/net/arbiter/struct/__init__.py,sha256=Vr38dp2X0PZOrAbjKsZ9xZdQ1j3z92s4QuvRtYYVuNI,5990
|
|
@@ -166,6 +167,7 @@ runtimepy/net/arbiter/tcp/__init__.py,sha256=djNm8il_9aLNpGsYResJlFmyIqx9XNLqVay
|
|
|
166
167
|
runtimepy/net/arbiter/tcp/json.py,sha256=W9a_OwBPmIoB2XZf4iuAIWQhMg2qA9xejBhGBdNCPnI,742
|
|
167
168
|
runtimepy/net/factories/__init__.py,sha256=rPdBVpgzzQYF61w6efQrEre71yMPHd6kanBpMdOX-3c,4672
|
|
168
169
|
runtimepy/net/html/__init__.py,sha256=BQeGpZWfGSmMg1R0N9GaOZfyFn4UQGHKoFOhd-1G75Q,3983
|
|
170
|
+
runtimepy/net/html/arbiter.py,sha256=SkZZm-CmyCxbAcWZbvCLH-RwFUJPvrvR5yWysVVuvCM,951
|
|
169
171
|
runtimepy/net/html/bootstrap/__init__.py,sha256=ONhwx68piWjsrf88FMpda84TWSPqgi-RZCBuWCci_ak,1444
|
|
170
172
|
runtimepy/net/html/bootstrap/elements.py,sha256=8sq79EPR8TF9oYV_4I0Sba_PikHOpEaxUvQ7bqtm0ow,4800
|
|
171
173
|
runtimepy/net/html/bootstrap/tabs.py,sha256=7-0lJOD9SALmsViUYGJAXF0PTSqtffc4mpoFwmYcnXE,3842
|
|
@@ -176,10 +178,10 @@ runtimepy/net/http/request_target.py,sha256=EE1aI5VSARw1h93jyZvP56ir5O5fjd6orYK-
|
|
|
176
178
|
runtimepy/net/http/response.py,sha256=Sup8W_A0ADNzR5olKrQsVNhsQXUwPOD-eJLlLOgYlAY,2316
|
|
177
179
|
runtimepy/net/http/state.py,sha256=qCMN8aWfCRfU9XP-cIhSOo2RqfljTjbQRCflfcy2bfY,1626
|
|
178
180
|
runtimepy/net/http/version.py,sha256=mp6rgIM7-VUVKLCA0Uw96CmBkL0ET860lDVVEewpZ7w,1098
|
|
179
|
-
runtimepy/net/server/__init__.py,sha256=
|
|
180
|
-
runtimepy/net/server/html.py,sha256=
|
|
181
|
+
runtimepy/net/server/__init__.py,sha256=ZsJGvak5OPHyuGPDThwj9roOj8ZcwqrOrzTD27hJhCo,9621
|
|
182
|
+
runtimepy/net/server/html.py,sha256=ufg0PQF_iUE7PT0n3Pn3jTcun7mspZUI6_ooblcNnvI,1217
|
|
181
183
|
runtimepy/net/server/json.py,sha256=a7vM5yfq2er4DexzFqEMnxoMGDeuywKkVH4-uNJBAik,2522
|
|
182
|
-
runtimepy/net/server/app/__init__.py,sha256=
|
|
184
|
+
runtimepy/net/server/app/__init__.py,sha256=beU67t7zoKGlO7aldjQMUwYLm9mSlc78eMQazri-otw,3012
|
|
183
185
|
runtimepy/net/server/app/base.py,sha256=HF_Qa3ufrZNaYBVnBwGi-Siv3nneqEo01j5h5pK-ZTk,1871
|
|
184
186
|
runtimepy/net/server/app/create.py,sha256=eRT8qxubht5A7149Xol3Z8rkdYd_pjNLqrlyMnXk-Zg,2660
|
|
185
187
|
runtimepy/net/server/app/elements.py,sha256=KJt9vWqkfvniJMiLOJN467JjPPrEqJYZXmDuY1JoY1g,455
|
|
@@ -206,7 +208,7 @@ runtimepy/net/stream/base.py,sha256=Dg4vcR0n9y2122AyJ-9W-jkEhNla_EHO-DqJJPfGD4k,
|
|
|
206
208
|
runtimepy/net/stream/string.py,sha256=61mgserU3p6j5gAcK0oe0aKqL6vDh7NtgJvbPoiAUPM,784
|
|
207
209
|
runtimepy/net/stream/json/__init__.py,sha256=h--C_9moW92TC_e097FRRXcg8GJ6VVbMLXl1cICknys,2508
|
|
208
210
|
runtimepy/net/tcp/__init__.py,sha256=OOWohegpoioSTf8M7uDf-4EV1IDungz7-U19L_2yW4I,250
|
|
209
|
-
runtimepy/net/tcp/connection.py,sha256=
|
|
211
|
+
runtimepy/net/tcp/connection.py,sha256=sYWj2aDiAHQf70zfRJM24cHraFd_TuzTD4fRWeSQZXE,8811
|
|
210
212
|
runtimepy/net/tcp/create.py,sha256=zZsRs5KYpO3bNGh-DwEOEzjUDE4ixj-UBHYgZ0GvC7c,2013
|
|
211
213
|
runtimepy/net/tcp/protocol.py,sha256=vEnIX3gUX2nrw9ofT_e4KYU4VY2k4WP0WuOi4eE_OOQ,1444
|
|
212
214
|
runtimepy/net/tcp/http/__init__.py,sha256=BY9ra0s7kHkoUQKIRHsnk3_WDWP7Xt-FxQ90K14nwkc,5737
|
|
@@ -259,7 +261,7 @@ runtimepy/registry/name.py,sha256=uhxmijwUT7x59NUYV4hJSe7VcnAbLDUSAPaKhR6K-N0,17
|
|
|
259
261
|
runtimepy/sample/__init__.py,sha256=N7P6hnCLF9NwnkZA1h3LzS2C334SAO48Fu8adHxWGLU,56
|
|
260
262
|
runtimepy/sample/peer.py,sha256=eZJ3gmvGsXoRTIyJ_OS71T5C1kjysbvd-yF5xZxedLU,1621
|
|
261
263
|
runtimepy/sample/program.py,sha256=wL61OzdUYrHw3Lwbd_eeBDARhS7kEzdifGOFgXvvnAw,2332
|
|
262
|
-
runtimepy/struct/__init__.py,sha256=
|
|
264
|
+
runtimepy/struct/__init__.py,sha256=wFjLwX83ztCF1CuFxnldetzMOoBs_UMirLV778GBGZc,2220
|
|
263
265
|
runtimepy/subprocess/__init__.py,sha256=VAiFrYFCU5ETDVoWLOVlrrSsx2d1_atYXUfXYc_OQ9g,4059
|
|
264
266
|
runtimepy/subprocess/interface.py,sha256=rYvM8bgoT4DoXGhdidhfmivzrjmEoW6788DjGyAdJ7A,8032
|
|
265
267
|
runtimepy/subprocess/peer.py,sha256=oYw9a0yKAPR18Z6Qt24wYWrcX6EizeQE64htL11WVjM,7593
|
|
@@ -282,9 +284,9 @@ runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
|
|
|
282
284
|
runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
|
|
283
285
|
runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
284
286
|
runtimepy/ui/controls.py,sha256=yvT7h3thbYaitsakcIAJ90EwKzJ4b-jnc6p3UuVf_XE,1241
|
|
285
|
-
runtimepy-5.7.
|
|
286
|
-
runtimepy-5.7.
|
|
287
|
-
runtimepy-5.7.
|
|
288
|
-
runtimepy-5.7.
|
|
289
|
-
runtimepy-5.7.
|
|
290
|
-
runtimepy-5.7.
|
|
287
|
+
runtimepy-5.7.7.dist-info/LICENSE,sha256=okYCYhGsx_BlzvFdoNVBVpw_Cfb4SOqHA_VAARml4Hc,1071
|
|
288
|
+
runtimepy-5.7.7.dist-info/METADATA,sha256=RbUgD3uuOyvV__lOtj8TAIca5btsr-2wW4monhtDWZ8,9308
|
|
289
|
+
runtimepy-5.7.7.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
290
|
+
runtimepy-5.7.7.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
|
|
291
|
+
runtimepy-5.7.7.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
|
|
292
|
+
runtimepy-5.7.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|