runtimepy 5.4.2__py3-none-any.whl → 5.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # =====================================
2
2
  # generator=datazen
3
3
  # version=3.1.4
4
- # hash=e6970089f5f2935c496cb3e9bb06b774
4
+ # hash=5a37b52aedb2af82fb174fbea8ce0c1f
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.4.2"
13
+ VERSION = "5.4.4"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -8,13 +8,12 @@ import asyncio
8
8
  from pathlib import Path
9
9
  from socket import getaddrinfo
10
10
 
11
- from vcorelib.args import CommandFunction
12
-
13
11
  # third-party
12
+ from vcorelib.args import CommandFunction
14
13
  from vcorelib.asyncio import run_handle_stop
15
14
 
16
15
  # internal
17
- from runtimepy.net.udp.tftp import tftp_read, tftp_write
16
+ from runtimepy.net.udp.tftp import TFTP_PORT, tftp_read, tftp_write
18
17
  from runtimepy.net.udp.tftp.base import DEFAULT_TIMEOUT_S, REEMIT_PERIOD_S
19
18
  from runtimepy.net.udp.tftp.enums import DEFAULT_MODE
20
19
 
@@ -53,7 +52,7 @@ def add_tftp_cmd(parser: argparse.ArgumentParser) -> CommandFunction:
53
52
  "-p",
54
53
  "--port",
55
54
  type=int,
56
- default=69,
55
+ default=TFTP_PORT,
57
56
  help="port to message (default: %(default)s)",
58
57
  )
59
58
 
@@ -40,6 +40,10 @@
40
40
  width: auto;
41
41
  }
42
42
 
43
+ .table-container {
44
+ overflow-x: scroll;
45
+ }
46
+
43
47
  .channel-column {
44
48
  overflow-y: scroll;
45
49
  overflow-x: hidden;
@@ -134,7 +134,9 @@ class ChannelEnvironmentTabHtml(ChannelEnvironmentTabControls):
134
134
  def channel_table(self, parent: Element) -> None:
135
135
  """Create the channel table."""
136
136
 
137
- table = div(tag="table", parent=parent)
137
+ table = div(
138
+ tag="table", parent=div(parent=parent, class_str="table-container")
139
+ )
138
140
  table.add_class("table", TEXT)
139
141
 
140
142
  header = div(tag="thead", parent=table)
@@ -248,7 +250,7 @@ class ChannelEnvironmentTabHtml(ChannelEnvironmentTabControls):
248
250
  # table doesn't take up full vertical space, few channels).
249
251
  under_construction(
250
252
  vert_container,
251
- class_str="border-start border-end",
253
+ class_str="border-start border-top border-end",
252
254
  note="unused space",
253
255
  )
254
256
 
@@ -21,6 +21,7 @@ from runtimepy.net.udp.tftp.base import (
21
21
  DEFAULT_TIMEOUT_S,
22
22
  REEMIT_PERIOD_S,
23
23
  BaseTftpConnection,
24
+ TftpErrorHandler,
24
25
  )
25
26
  from runtimepy.net.udp.tftp.enums import DEFAULT_MODE
26
27
  from runtimepy.util import PossiblePath, as_path
@@ -194,6 +195,9 @@ class TftpConnection(BaseTftpConnection):
194
195
  return result
195
196
 
196
197
 
198
+ TFTP_PORT = 69
199
+
200
+
197
201
  @asynccontextmanager
198
202
  async def tftp(
199
203
  addr: Union[IpHost, tuple[str, int]],
@@ -201,6 +205,7 @@ async def tftp(
201
205
  connection_kwargs: dict[str, Any] = None,
202
206
  timeout_s: float = DEFAULT_TIMEOUT_S,
203
207
  reemit_period_s: float = REEMIT_PERIOD_S,
208
+ error_handler: TftpErrorHandler = None,
204
209
  ) -> AsyncIterator[TftpConnection]:
205
210
  """Use a tftp connection as a managed context."""
206
211
 
@@ -215,6 +220,11 @@ async def tftp(
215
220
  conn = await TftpConnection.create_connection(
216
221
  remote_addr=(addr.name, addr.port), **connection_kwargs
217
222
  )
223
+
224
+ # Add error handlers.
225
+ if error_handler is not None:
226
+ conn.error_handlers.append(error_handler)
227
+
218
228
  async with conn.process_then_disable(**process_kwargs):
219
229
  # Set parameters.
220
230
  conn.endpoint_timeout.value = timeout_s
@@ -232,6 +242,7 @@ async def tftp_write(
232
242
  connection_kwargs: dict[str, Any] = None,
233
243
  timeout_s: float = DEFAULT_TIMEOUT_S,
234
244
  reemit_period_s: float = REEMIT_PERIOD_S,
245
+ error_handler: TftpErrorHandler = None,
235
246
  ) -> bool:
236
247
  """Attempt to perform a tftp write."""
237
248
 
@@ -241,6 +252,7 @@ async def tftp_write(
241
252
  connection_kwargs=connection_kwargs,
242
253
  timeout_s=timeout_s,
243
254
  reemit_period_s=reemit_period_s,
255
+ error_handler=error_handler,
244
256
  ) as conn:
245
257
  # Perform tftp interaction.
246
258
  result = await conn.request_write(
@@ -259,6 +271,7 @@ async def tftp_read(
259
271
  connection_kwargs: dict[str, Any] = None,
260
272
  timeout_s: float = DEFAULT_TIMEOUT_S,
261
273
  reemit_period_s: float = REEMIT_PERIOD_S,
274
+ error_handler: TftpErrorHandler = None,
262
275
  ) -> bool:
263
276
  """Attempt to perform a tftp read."""
264
277
 
@@ -268,6 +281,7 @@ async def tftp_read(
268
281
  connection_kwargs=connection_kwargs,
269
282
  timeout_s=timeout_s,
270
283
  reemit_period_s=reemit_period_s,
284
+ error_handler=error_handler,
271
285
  ) as conn:
272
286
  result = await conn.request_read(
273
287
  destination, filename, mode=mode, addr=addr
@@ -8,7 +8,7 @@ from contextlib import AsyncExitStack
8
8
  from io import BytesIO
9
9
  import logging
10
10
  from pathlib import Path
11
- from typing import BinaryIO, Union
11
+ from typing import BinaryIO, Callable, Union
12
12
 
13
13
  # third-party
14
14
  from vcorelib.math import metrics_time_ns
@@ -30,6 +30,8 @@ from runtimepy.primitives import Double, Uint16
30
30
  REEMIT_PERIOD_S = 0.20
31
31
  DEFAULT_TIMEOUT_S = 1.0
32
32
 
33
+ TftpErrorHandler = Callable[[TftpErrorCode, str, tuple[str, int]], None]
34
+
33
35
 
34
36
  class BaseTftpConnection(UdpConnection):
35
37
  """A class implementing a basic tftp interface."""
@@ -73,6 +75,7 @@ class BaseTftpConnection(UdpConnection):
73
75
 
74
76
  self.error_code = Uint16(time_source=metrics_time_ns)
75
77
  self.env.channel("error_code", self.error_code, enum="TftpErrorCode")
78
+ self.error_handlers: list[TftpErrorHandler] = []
76
79
 
77
80
  self.endpoint_period = Double(value=REEMIT_PERIOD_S)
78
81
  self.env.channel(
@@ -346,10 +349,14 @@ class BaseTftpConnection(UdpConnection):
346
349
  """Handle an error message."""
347
350
 
348
351
  # Update underlying primitive.
349
- error_code = self.error_code.from_stream(stream)
350
- self.endpoint(addr).handle_error(
351
- TftpErrorCode(error_code), stream.read().decode()
352
- )
352
+ error_code = TftpErrorCode(self.error_code.from_stream(stream))
353
+ message = stream.read().decode()
354
+
355
+ # Call extra error handlers.
356
+ for handler in self.error_handlers:
357
+ handler(error_code, message, addr)
358
+
359
+ self.endpoint(addr).handle_error(error_code, message)
353
360
 
354
361
  async def process_datagram(
355
362
  self, data: bytes, addr: tuple[str, int]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runtimepy
3
- Version: 5.4.2
3
+ Version: 5.4.4
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/vkottler/runtimepy
6
6
  Author: Vaughn Kottler
@@ -17,9 +17,9 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Requires-Python: >=3.11
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: svgen >=0.6.8
21
- Requires-Dist: vcorelib >=3.3.1
22
20
  Requires-Dist: websockets
21
+ Requires-Dist: vcorelib >=3.3.1
22
+ Requires-Dist: svgen >=0.6.8
23
23
  Requires-Dist: psutil
24
24
  Provides-Extra: test
25
25
  Requires-Dist: pylint ; extra == 'test'
@@ -44,11 +44,11 @@ Requires-Dist: uvloop ; (sys_platform != "win32" and sys_platform != "cygwin") a
44
44
  =====================================
45
45
  generator=datazen
46
46
  version=3.1.4
47
- hash=4f8a71a6066638ed1a90f375188f0578
47
+ hash=dbac0e58214ea7e4abbe7161a3c64749
48
48
  =====================================
49
49
  -->
50
50
 
51
- # runtimepy ([5.4.2](https://pypi.org/project/runtimepy/))
51
+ # runtimepy ([5.4.4](https://pypi.org/project/runtimepy/))
52
52
 
53
53
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
54
54
  ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,4 +1,4 @@
1
- runtimepy/__init__.py,sha256=ojyvRCtIcczCpJWeruS4_U9LFJ6NMGfeWnb-MKrQ3pU,390
1
+ runtimepy/__init__.py,sha256=baR2RhS2-y7ZXgCnsPoi2qZNQvYUTMTNIsSR2qKnRNg,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
@@ -34,7 +34,7 @@ runtimepy/commands/arbiter.py,sha256=CtTMRYpqCAN3vWHkkr9jqWpoF7JGNXafKIBFmkarAfc
34
34
  runtimepy/commands/common.py,sha256=NvZdeIFBHAF52c1n7vqD59DW6ywc-rG5iC5MpuhGf-c,2449
35
35
  runtimepy/commands/server.py,sha256=T5IwBeqwJPpg35Ms_Vmz6xS1T-8U3fcgiRU6mAFlkEU,3767
36
36
  runtimepy/commands/task.py,sha256=6xRVlRwpEZVhrcY18sQcfdWEOxeQZLeOF-6UrUURtO4,1435
37
- runtimepy/commands/tftp.py,sha256=5dsrYSWbAY-ZAdvfukLOynkrJBtR4J1zEOZey4FerF4,2308
37
+ runtimepy/commands/tftp.py,sha256=97Ev0bg0zI9WL_-BYWAyKWe3mC9xKsZ-gL1637ExdvE,2325
38
38
  runtimepy/commands/tui.py,sha256=9hWA3_YATibUUDTVQr7UnKzPTDVJ7WxWKTYYQpLoyrE,1869
39
39
  runtimepy/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  runtimepy/control/source.py,sha256=nW3Q2D-LcekII7K5XKbxXCcR-9jYQyvv0UypeNy1Dnw,1695
@@ -48,7 +48,7 @@ runtimepy/data/server.yaml,sha256=wS_Ceiu2TpkfPurpqoYoPlgzc9DAWtUd24MW7t-S5rU,97
48
48
  runtimepy/data/server_base.yaml,sha256=QkF1xcHyMEViUhOc5rKFbFXjbRDDDHbX5LXvSQJRS58,615
49
49
  runtimepy/data/server_dev.yaml,sha256=nQsPh7LuQig3pzHfdg_aD3yOUiCj1sKKfI-WwW3hXmQ,523
50
50
  runtimepy/data/tftp_server.yaml,sha256=-bFOWJSagI-fEQQcT8k7eDMJVfSPm2XAxLVG3dqUTa4,204
51
- runtimepy/data/css/bootstrap_extra.css,sha256=UyGXyv7nULrJ0UHsXTSM2IIrJ4X2XIEbPjmmU87yQno,1334
51
+ runtimepy/data/css/bootstrap_extra.css,sha256=w1Y527gWUnfC40zfodA8AM49BwGQVsOOYOFUX657IF4,1378
52
52
  runtimepy/data/css/main.css,sha256=h1iKkxg6-t7wTwNQvxDiD9mjpWrqq_FmgT6yXP7UANQ,688
53
53
  runtimepy/data/js/DataConnection.js,sha256=DnX8FMehjJXqmI62UMYXSvl_XdfQMzq3XUDFbLu2GgI,98
54
54
  runtimepy/data/js/JsonConnection.js,sha256=rclZrbmWc_zSs6I_JhOgxnVPFIyPMo5WdjAe8alyZ3o,2729
@@ -171,7 +171,7 @@ runtimepy/net/server/app/env/widgets.py,sha256=_kNvPl7MXnZOiwTjoZiU2hfuSjkLnRUrO
171
171
  runtimepy/net/server/app/env/tab/__init__.py,sha256=stTVKyHljLQWnnhxkWPwa7bLdZtjhiMFbiVFgbiYaFI,647
172
172
  runtimepy/net/server/app/env/tab/base.py,sha256=uBPpOeqI23341IezIQcGvJciPfIL2P5qQgbZ74aQRKA,991
173
173
  runtimepy/net/server/app/env/tab/controls.py,sha256=hsj0ErywfmOBtJLNZbMISrE2ELJEfyXvtCtpsDbXlYg,4734
174
- runtimepy/net/server/app/env/tab/html.py,sha256=pynwnRyD_jjME20psO7LK5mABqZudeWs3oDZCVHWRUs,7188
174
+ runtimepy/net/server/app/env/tab/html.py,sha256=ElAPyLczQVdja8OTWibKwgB0Sn2dklrc7DkqBFZxm_Y,7262
175
175
  runtimepy/net/server/app/env/tab/message.py,sha256=3LLaEIJgr2hhZH2-v-Sq-OKfKkfRgP-RDU8EpKLYb74,3709
176
176
  runtimepy/net/server/struct/__init__.py,sha256=z62I3DUoOnCcTv7C9X3sA6jOBKAJQ_uSOQA7uNwyrj4,1839
177
177
  runtimepy/net/server/websocket/__init__.py,sha256=KISuFUUQwNn6BXo8BOMuMOXyoVqE7Jw94ZQiSCQuRQE,5279
@@ -194,8 +194,8 @@ runtimepy/net/udp/connection.py,sha256=YtLty6iveVH6PQRODKriwR5Q6lFi0Ed4k0o5r0bgA
194
194
  runtimepy/net/udp/create.py,sha256=84YDfJbNBlN0ZwbNpvh6Dl7ZPecbZfmpjMNRRWcvJDk,2005
195
195
  runtimepy/net/udp/protocol.py,sha256=A4SRHf0CgcL2zDs1nAsGDqz0RxKBy1soS8wtNdS5S0I,1492
196
196
  runtimepy/net/udp/queue.py,sha256=DF-YscxQcGbGCYQLz_l_BMaSRfraZOhRwieTEdXLMds,637
197
- runtimepy/net/udp/tftp/__init__.py,sha256=SJGkizBqV73XfQ9HIRAcFE8Xy9JXJHptr_WtbFswZgc,8724
198
- runtimepy/net/udp/tftp/base.py,sha256=FCrtpLdUFkQU4nkn5vJc6Gn5EsZauCf8eLhxx1r_vG8,11276
197
+ runtimepy/net/udp/tftp/__init__.py,sha256=C4YI06y-xwHphYBc68jXL35CrLVNzds3-tIhxTp1PWk,9081
198
+ runtimepy/net/udp/tftp/base.py,sha256=7nEgxv3j6ehqP8q2p769NjnkkMBOw3smt1XDqFBmdN4,11551
199
199
  runtimepy/net/udp/tftp/endpoint.py,sha256=so60LdPTG66N5tdhHhiX7j_TBHvNOTi4JIgLcg2MAm0,10890
200
200
  runtimepy/net/udp/tftp/enums.py,sha256=06juMd__pJZsyL8zO8p3hRucnOratt1qtz9zcxzMg4s,1579
201
201
  runtimepy/net/udp/tftp/io.py,sha256=w6cnUt-T-Ma6Vg8BWoRbsNnIWUv0HTY4am6bcLWxNJs,803
@@ -255,9 +255,9 @@ runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
255
255
  runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
256
256
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
257
  runtimepy/ui/controls.py,sha256=yvT7h3thbYaitsakcIAJ90EwKzJ4b-jnc6p3UuVf_XE,1241
258
- runtimepy-5.4.2.dist-info/LICENSE,sha256=okYCYhGsx_BlzvFdoNVBVpw_Cfb4SOqHA_VAARml4Hc,1071
259
- runtimepy-5.4.2.dist-info/METADATA,sha256=HuPnRnjp8IDWbQUxSwohDtt4GvNFQJYvZ8rgY_NfyoQ,8152
260
- runtimepy-5.4.2.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
261
- runtimepy-5.4.2.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
262
- runtimepy-5.4.2.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
263
- runtimepy-5.4.2.dist-info/RECORD,,
258
+ runtimepy-5.4.4.dist-info/LICENSE,sha256=okYCYhGsx_BlzvFdoNVBVpw_Cfb4SOqHA_VAARml4Hc,1071
259
+ runtimepy-5.4.4.dist-info/METADATA,sha256=QwSQWMUYIK5S4grh2n898qjjXI_7gHHMFtU10gqAX7c,8152
260
+ runtimepy-5.4.4.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
261
+ runtimepy-5.4.4.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
262
+ runtimepy-5.4.4.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
263
+ runtimepy-5.4.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.1.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5