runtimepy 5.7.9__py3-none-any.whl → 5.7.10__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=5d30f53776c61076df3ffeca2fdb30a1
4
+ # hash=01934a9c90fba4488654f035acc8b84e
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.9"
13
+ VERSION = "5.7.10"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -21,6 +21,8 @@ TASK_NAME = "housekeeping"
21
21
  class ConnectionMetricsPoller(_ArbiterTask):
22
22
  """A class that periodically polls connection metrics."""
23
23
 
24
+ processors: list[Awaitable[None]]
25
+
24
26
  def __init__(
25
27
  self,
26
28
  name: str,
@@ -31,6 +33,7 @@ class ConnectionMetricsPoller(_ArbiterTask):
31
33
 
32
34
  super().__init__(name, **kwargs)
33
35
  self.manager = manager
36
+ self.processors = []
34
37
 
35
38
  def _init_state(self) -> None:
36
39
  """Add channels to this instance's channel environment."""
@@ -51,7 +54,6 @@ class ConnectionMetricsPoller(_ArbiterTask):
51
54
  self.manager.poll_metrics()
52
55
 
53
56
  # Handle any incoming commands.
54
- processors: list[Awaitable[None]] = []
55
57
  for mapping in (
56
58
  self.app.connections.values(),
57
59
  self.app.tasks.values(),
@@ -59,14 +61,15 @@ class ConnectionMetricsPoller(_ArbiterTask):
59
61
  ):
60
62
  for item in mapping:
61
63
  if isinstance(item, AsyncCommandProcessingMixin):
62
- processors.append(item.process_command_queue())
64
+ self.processors.append(item.process_command_queue())
63
65
 
64
66
  # Service connection tasks. The connection manager should probably do
65
67
  # this on its own at some point.
66
- processors += list(self.app.conn_manager.connection_tasks)
68
+ self.processors += list(self.app.conn_manager.connection_tasks)
67
69
 
68
- if processors:
69
- await asyncio.gather(*processors)
70
+ if self.processors:
71
+ await asyncio.gather(*self.processors)
72
+ self.processors.clear()
70
73
 
71
74
  return True
72
75
 
@@ -11,7 +11,6 @@ from pathlib import Path
11
11
  from typing import Any, Optional, TextIO, Union
12
12
 
13
13
  # third-party
14
- import aiofiles
15
14
  from vcorelib import DEFAULT_ENCODING
16
15
  from vcorelib.io import IndentedFileWriter, JsonObject
17
16
  from vcorelib.paths import Pathlike, find_file, normalize
@@ -26,7 +25,7 @@ from runtimepy.net.http.response import ResponseHeader
26
25
  from runtimepy.net.server.html import HtmlApp, HtmlApps, get_html, html_handler
27
26
  from runtimepy.net.server.json import encode_json, json_handler
28
27
  from runtimepy.net.tcp.http import HttpConnection
29
- from runtimepy.util import normalize_root, path_has_part
28
+ from runtimepy.util import normalize_root, path_has_part, read_binary
30
29
 
31
30
  MIMETYPES_INIT = False
32
31
 
@@ -158,10 +157,9 @@ class RuntimepyServerConnection(HttpConnection):
158
157
  ) -> bytes:
159
158
  """Render a markdown file as HTML and return the result."""
160
159
 
161
- async with aiofiles.open(path, mode="r") as path_fd:
162
- return self.render_markdown(
163
- await path_fd.read(), response, **kwargs
164
- )
160
+ return self.render_markdown(
161
+ (await read_binary(path)).decode(), response, **kwargs
162
+ )
165
163
 
166
164
  async def try_file(
167
165
  self, path: PathMaybeQuery, response: ResponseHeader
@@ -195,8 +193,7 @@ class RuntimepyServerConnection(HttpConnection):
195
193
  self.logger.info("Serving '%s' (MIME: %s)", candidate, mime)
196
194
 
197
195
  # Return the file data.
198
- async with aiofiles.open(candidate, mode="rb") as path_fd:
199
- result = await path_fd.read()
196
+ result = await read_binary(candidate)
200
197
 
201
198
  break
202
199
 
@@ -69,9 +69,7 @@ class ChannelEnvironmentTabMessaging(ChannelEnvironmentTabBase):
69
69
  for name in env.names:
70
70
  self._setup_callback(name, state)
71
71
  else:
72
- # Remove callbacks for primitives.
73
- for name, val in state.callbacks.items():
74
- state.primitives[name].remove_callback(val)
72
+ state.clear_telemetry()
75
73
 
76
74
  # Save current UI state.
77
75
  state.latest_ui_values.update(latest)
@@ -53,19 +53,36 @@ class TabState:
53
53
 
54
54
  return result
55
55
 
56
+ def clear_telemetry(self) -> None:
57
+ """Clear all telemetry interactions."""
58
+
59
+ # Remove callbacks for primitives.
60
+ for name, val in self.callbacks.items():
61
+ self.primitives[name].remove_callback(val)
62
+ self.callbacks.clear()
63
+ self.primitives.clear()
64
+
65
+ # Clear points.
66
+ self.points.clear()
67
+
56
68
  def clear_loggers(self) -> None:
57
69
  """Clear all logging handlers."""
58
70
 
71
+ # Remove handlers.
59
72
  for logger in self._loggers:
60
73
  logger.removeHandler(self.tab_logger)
74
+ self._loggers.clear()
75
+
61
76
  self.tab_logger.drain_str()
62
- self._loggers = []
77
+
78
+ self.clear_telemetry()
63
79
 
64
80
  def add_logger(self, logger: logging.Logger) -> None:
65
81
  """Add a logger."""
66
82
 
67
- logger.addHandler(self.tab_logger)
68
- self._loggers.append(logger)
83
+ if logger not in self._loggers:
84
+ logger.addHandler(self.tab_logger)
85
+ self._loggers.append(logger)
69
86
 
70
87
  @staticmethod
71
88
  def create() -> "TabState":
runtimepy/util.py CHANGED
@@ -8,11 +8,20 @@ from pathlib import Path
8
8
  from typing import Iterator, Union
9
9
 
10
10
  # third-party
11
+ import aiofiles
11
12
  from vcorelib.paths import normalize
12
13
 
13
14
  ROOT_PATH = Path(sep)
14
15
 
15
16
 
17
+ async def read_binary(path: Path) -> bytes:
18
+ """An async wrapper for reading file contents."""
19
+
20
+ async with aiofiles.open(path, mode="rb") as path_fd:
21
+ result = await path_fd.read()
22
+ return result # type: ignore
23
+
24
+
16
25
  def normalize_root(*src_parts: Union[str, Path]) -> Path:
17
26
  """Make paths absolute that aren't. Useful for HTTP-request pathing."""
18
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runtimepy
3
- Version: 5.7.9
3
+ Version: 5.7.10
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/vkottler/runtimepy
6
6
  Author: Vaughn Kottler
@@ -17,11 +17,11 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Requires-Python: >=3.12
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: aiofiles
21
- Requires-Dist: svgen >=0.6.8
22
- Requires-Dist: websockets
23
20
  Requires-Dist: vcorelib >=3.4.5
21
+ Requires-Dist: websockets
24
22
  Requires-Dist: psutil
23
+ Requires-Dist: aiofiles
24
+ Requires-Dist: svgen >=0.6.8
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=9fbafffa1da655b349a05dfa48addf0b
48
+ hash=1e8298e9f6423ee6f9836f5ec04cc3ab
49
49
  =====================================
50
50
  -->
51
51
 
52
- # runtimepy ([5.7.9](https://pypi.org/project/runtimepy/))
52
+ # runtimepy ([5.7.10](https://pypi.org/project/runtimepy/))
53
53
 
54
54
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
55
55
  ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,4 +1,4 @@
1
- runtimepy/__init__.py,sha256=GL1as0g7a1sU6w4kqUfDAJXvMjjsu7qwNr2fYa4Jpes,390
1
+ runtimepy/__init__.py,sha256=xlFe4KOw9V-9A2MlIEX9gtA5eOEck1Z8uclAZojzciU,391
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
@@ -7,7 +7,7 @@ runtimepy/mapping.py,sha256=VQK1vzmQVvYYKI85_II37-hIEbvgL3PzNy-WI6TTo80,5091
7
7
  runtimepy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  runtimepy/requirements.txt,sha256=ntjU97UnQ5N7tqfwM7ljg77V5MBFFNBTfVuKS_KGwP0,124
9
9
  runtimepy/schemas.py,sha256=zTgxPm9DHZ0R_bmmOjNQMTXdtM_Hb1bE-Fog40jDCgg,839
10
- runtimepy/util.py,sha256=1279Ea7CNHa1U_JvUydGAb2oEI2TxilXkq5V1RseCwU,1437
10
+ runtimepy/util.py,sha256=GuyIHVFGMS02OR6-O3LnlV3DqG5hj4-IUud0QM6WicA,1684
11
11
  runtimepy/channel/__init__.py,sha256=pf0RJ5g37_FVV8xoUNgzFGuIfbZEYSBA_cQlJSDTPDo,4774
12
12
  runtimepy/channel/registry.py,sha256=nk36qM_Bf6qK6AFR0plaZHR1PU7b4LZqbQ0feJqk4lc,4784
13
13
  runtimepy/channel/environment/__init__.py,sha256=0Jj8g7Y4bdDvmWtzpegB9D4milGPhsZokoYxmps5zgU,1612
@@ -159,7 +159,7 @@ runtimepy/net/arbiter/config/util.py,sha256=MezLSil5mEwioI5kxDOZa9y118st1_t0El8D
159
159
  runtimepy/net/arbiter/factory/__init__.py,sha256=jC1szUYucH4Us3jbb0mhFgnklHlorya5DYGGw2pqfe4,334
160
160
  runtimepy/net/arbiter/factory/connection.py,sha256=uSwnvIWGJcDd2ReK-s9lBx-NcRQ1JH_7VbwkWjE_8zA,3690
161
161
  runtimepy/net/arbiter/factory/task.py,sha256=YLRv55dnDBqHRJIMYgO9_uHi0AW-8hwa0n3wHAKyncE,1959
162
- runtimepy/net/arbiter/housekeeping/__init__.py,sha256=mgrYnDdL1DSP6fXdtk2VU9uqTvtgX2VX5q2Q9EfcJus,3545
162
+ runtimepy/net/arbiter/housekeeping/__init__.py,sha256=-6P2x_qSWp1juml0wKZGJ343c-atrbp8YkDRWn162UA,3622
163
163
  runtimepy/net/arbiter/imports/__init__.py,sha256=W7-kkM4B_zDU42Fyh0PmZL56JslS_0_qzvpzUhN6aDU,5041
164
164
  runtimepy/net/arbiter/imports/util.py,sha256=d4_HnWphrkiHQkyYr8YJKvSSTXoMf3Bct_7jg_CS9eA,1086
165
165
  runtimepy/net/arbiter/struct/__init__.py,sha256=Vr38dp2X0PZOrAbjKsZ9xZdQ1j3z92s4QuvRtYYVuNI,5990
@@ -178,7 +178,7 @@ runtimepy/net/http/request_target.py,sha256=EE1aI5VSARw1h93jyZvP56ir5O5fjd6orYK-
178
178
  runtimepy/net/http/response.py,sha256=Sup8W_A0ADNzR5olKrQsVNhsQXUwPOD-eJLlLOgYlAY,2316
179
179
  runtimepy/net/http/state.py,sha256=qCMN8aWfCRfU9XP-cIhSOo2RqfljTjbQRCflfcy2bfY,1626
180
180
  runtimepy/net/http/version.py,sha256=mp6rgIM7-VUVKLCA0Uw96CmBkL0ET860lDVVEewpZ7w,1098
181
- runtimepy/net/server/__init__.py,sha256=ZsJGvak5OPHyuGPDThwj9roOj8ZcwqrOrzTD27hJhCo,9621
181
+ runtimepy/net/server/__init__.py,sha256=sYvvt7p3yTFCR2YXf4Qx3bKo_AafW0OjQ_30KKbg2DM,9488
182
182
  runtimepy/net/server/html.py,sha256=ufg0PQF_iUE7PT0n3Pn3jTcun7mspZUI6_ooblcNnvI,1217
183
183
  runtimepy/net/server/json.py,sha256=a7vM5yfq2er4DexzFqEMnxoMGDeuywKkVH4-uNJBAik,2522
184
184
  runtimepy/net/server/app/__init__.py,sha256=beU67t7zoKGlO7aldjQMUwYLm9mSlc78eMQazri-otw,3012
@@ -199,10 +199,10 @@ runtimepy/net/server/app/env/tab/__init__.py,sha256=stTVKyHljLQWnnhxkWPwa7bLdZtj
199
199
  runtimepy/net/server/app/env/tab/base.py,sha256=v5-Zbo_NqRqoLuceYclz348oFHcL68T2mbEh4EDWtTg,1162
200
200
  runtimepy/net/server/app/env/tab/controls.py,sha256=bifFMvshDNsar3UiqeXOCYi71JGFRlMnyixKXrdLAEI,4728
201
201
  runtimepy/net/server/app/env/tab/html.py,sha256=IfKCQ_2qupIRtWIuodIBM8XAal1esSXovht1SeGyvb4,7173
202
- runtimepy/net/server/app/env/tab/message.py,sha256=OWqBIPRt6UdBHAXk5qvFMbnWuvIRUyp34lzb3GYdthk,4070
202
+ runtimepy/net/server/app/env/tab/message.py,sha256=-J8wBo1KH0XoBi9VcUvl9LXZCaoFAmvFyMf_YFJVF6Q,3945
203
203
  runtimepy/net/server/struct/__init__.py,sha256=Zy37r6RLFu-XFr9vsanSq80BJdS6Dxr7zmPzQbb7xdw,1799
204
204
  runtimepy/net/server/websocket/__init__.py,sha256=KISuFUUQwNn6BXo8BOMuMOXyoVqE7Jw94ZQiSCQuRQE,5279
205
- runtimepy/net/server/websocket/state.py,sha256=a5xNeq13VPaz48HCqcIQdf1r3WV1a3iVernXM6w7P9g,1828
205
+ runtimepy/net/server/websocket/state.py,sha256=tClzw_3pOV9fzsiqnmss0FUsZZPhInL2CnDi_jyrayI,2291
206
206
  runtimepy/net/stream/__init__.py,sha256=1PNossiE4S9wtPYoiHVsTeAMJFGtiak5vbsoIkVGfZs,2374
207
207
  runtimepy/net/stream/base.py,sha256=Dg4vcR0n9y2122AyJ-9W-jkEhNla_EHO-DqJJPfGD4k,1977
208
208
  runtimepy/net/stream/string.py,sha256=61mgserU3p6j5gAcK0oe0aKqL6vDh7NtgJvbPoiAUPM,784
@@ -284,9 +284,9 @@ runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
284
284
  runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
285
285
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
286
  runtimepy/ui/controls.py,sha256=yvT7h3thbYaitsakcIAJ90EwKzJ4b-jnc6p3UuVf_XE,1241
287
- runtimepy-5.7.9.dist-info/LICENSE,sha256=s2ILEylm2dAJJXL25nM92IWLRKmJW92zQRQe_cfdsHo,1071
288
- runtimepy-5.7.9.dist-info/METADATA,sha256=n3bYpGmFzA7nS0zhHK2DNNzcTizdlZCfzCC2kRuKQoc,9308
289
- runtimepy-5.7.9.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
290
- runtimepy-5.7.9.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
291
- runtimepy-5.7.9.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
292
- runtimepy-5.7.9.dist-info/RECORD,,
287
+ runtimepy-5.7.10.dist-info/LICENSE,sha256=s2ILEylm2dAJJXL25nM92IWLRKmJW92zQRQe_cfdsHo,1071
288
+ runtimepy-5.7.10.dist-info/METADATA,sha256=iZiw-hLuQS1VPCwPvhZBTYUy-vmUeqO9Oqak235w63s,9310
289
+ runtimepy-5.7.10.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
290
+ runtimepy-5.7.10.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
291
+ runtimepy-5.7.10.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
292
+ runtimepy-5.7.10.dist-info/RECORD,,