runtimepy 5.15.2__py3-none-any.whl → 5.15.3__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.2.3
4
- # hash=839ec3c3fa52de16f775789c0d38bf3e
4
+ # hash=f5155e85b2694b012648fdbe9c12b1cd
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.15.2"
13
+ VERSION = "5.15.3"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -12,7 +12,9 @@ from typing import Any, Iterator, Optional, cast
12
12
 
13
13
  # third-party
14
14
  from vcorelib import DEFAULT_ENCODING
15
+ from vcorelib.dict import GenericStrDict
15
16
  from vcorelib.io import ARBITER, JsonObject
17
+ from vcorelib.io.bus import BUS
16
18
  from vcorelib.logging import DEFAULT_TIME_FORMAT, LoggerMixin
17
19
  from vcorelib.math import default_time_ns, nano_str
18
20
  from vcorelib.names import name_search
@@ -25,6 +27,7 @@ from runtimepy.channel.environment.command.processor import (
25
27
  CommandHook,
26
28
  EnvironmentMap,
27
29
  )
30
+ from runtimepy.channel.environment.command.result import CommandResult
28
31
  from runtimepy.channel.registry import ParsedEvent
29
32
  from runtimepy.mapping import DEFAULT_PATTERN
30
33
 
@@ -243,6 +246,38 @@ GLOBAL = GlobalEnvironment()
243
246
  ENVIRONMENTS = GLOBAL
244
247
 
245
248
 
249
+ def global_command(env: str, value: str) -> Optional[CommandResult]:
250
+ """Handle a global command."""
251
+
252
+ result = None
253
+ if env in GLOBAL:
254
+ result = GLOBAL[env].command(value)
255
+ else:
256
+ GLOBAL.logger.error(
257
+ "Couldn't run command env='%s' value='%s'.", env, value
258
+ )
259
+ return result
260
+
261
+
262
+ def global_commands(*cmds: tuple[str, str]) -> None:
263
+ """Handle a global command."""
264
+ for env, value in cmds:
265
+ global_command(env, value)
266
+
267
+
268
+ async def global_command_bus(payload: GenericStrDict) -> None:
269
+ """Handle a bus message."""
270
+
271
+ if "env" in payload and "value" in payload:
272
+ global_command(payload["env"], payload["value"])
273
+ elif "cmds" in payload:
274
+ global_commands(*payload["cmds"])
275
+
276
+
277
+ BUS.register_ro("command", global_command_bus)
278
+ BUS.register_ro("cmd", global_command_bus)
279
+
280
+
246
281
  def clear_env() -> None:
247
282
  """Reset the global environment mapping."""
248
283
  GLOBAL.clear()
@@ -107,3 +107,7 @@ processes:
107
107
  app: runtimepy.sample.program.run
108
108
 
109
109
  program: runtimepy.sample.program.SampleProgram
110
+
111
+ commands:
112
+ - [udp_json_client, "set log_level warning"]
113
+ - [proc1.peer, "set log_level warning"]
@@ -26,6 +26,16 @@ properties:
26
26
  items:
27
27
  $ref: package://runtimepy/schemas/StructConfig.yaml
28
28
 
29
+ commands:
30
+ type: array
31
+ items:
32
+ type: array
33
+ minItems: 2
34
+ maxItems: 2
35
+ items:
36
+ - type: string
37
+ - type: string
38
+
29
39
  tasks:
30
40
  type: array
31
41
  items:
@@ -26,6 +26,7 @@ from vcorelib.namespace import NamespaceMixin as _NamespaceMixin
26
26
  from runtimepy.channel.environment.command import (
27
27
  clear_env,
28
28
  env_json_data,
29
+ global_commands,
29
30
  register_env,
30
31
  )
31
32
  from runtimepy.net.arbiter.housekeeping import housekeeping
@@ -145,6 +146,8 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
145
146
  # A copy of named port mappings (loaded via external config).
146
147
  self._ports: dict[str, int] = {}
147
148
 
149
+ self._commands: list[tuple[str, str]] = []
150
+
148
151
  self._init()
149
152
 
150
153
  def _init(self) -> None:
@@ -318,6 +321,11 @@ class BaseConnectionArbiter(_NamespaceMixin, _LoggerMixin, TuiMixin):
318
321
 
319
322
  # Run initialization methods.
320
323
  result = await self._run_apps_list(self._inits, info)
324
+
325
+ # Run commands.
326
+ await _asyncio.sleep(0)
327
+ global_commands(*self._commands)
328
+
321
329
  if result == 0:
322
330
  # Get application methods.
323
331
  apps = self._apps
@@ -245,5 +245,8 @@ class ConfigConnectionArbiter(_ImportConnectionArbiter):
245
245
  assert "root" not in config.config, config.config
246
246
  config.config["root"] = root
247
247
 
248
+ # Register commands.
249
+ self._commands = config.commands
250
+
248
251
 
249
252
  ConfigConnectionArbiter.add_search_package(PKG_NAME)
@@ -71,6 +71,11 @@ class ConnectionArbiterConfig(_RuntimepyDictCodec):
71
71
 
72
72
  self.directory = _Path(str(data.get("directory", ".")))
73
73
 
74
+ self.commands: list[tuple[str, str]] = data.get( # type: ignore
75
+ "commands",
76
+ [],
77
+ )
78
+
74
79
  def asdict(self) -> _JsonObject:
75
80
  """Obtain a dictionary representing this instance."""
76
81
  return self.data
@@ -18,7 +18,7 @@ from vcorelib.paths import Pathlike, find_file, normalize
18
18
 
19
19
  # internal
20
20
  from runtimepy import DEFAULT_EXT, PKG_NAME
21
- from runtimepy.channel.environment.command import GLOBAL
21
+ from runtimepy.channel.environment.command import GLOBAL, global_command
22
22
  from runtimepy.net.html import full_markdown_page
23
23
  from runtimepy.net.http.header import RequestHeader
24
24
  from runtimepy.net.http.request_target import PathMaybeQuery
@@ -270,17 +270,22 @@ class RuntimepyServerConnection(HttpConnection):
270
270
  response_data["success"] = False
271
271
  response_data["message"] = "No command executed."
272
272
 
273
- if not args or args[0] not in GLOBAL:
273
+ def cmd_usage() -> None:
274
+ """Set usage information for the response."""
274
275
  response_data["usage"] = (
275
276
  "/<environment (arg0)>/<arg1>[/.../<argN>]"
276
277
  )
277
278
  response_data["environments"] = list(GLOBAL)
278
279
 
279
- # Run command.
280
+ if args:
281
+ result = global_command(args[0], " ".join(args[1:]))
282
+ if result is None:
283
+ cmd_usage()
284
+ else:
285
+ response_data["success"] = result.success
286
+ response_data["message"] = str(result)
280
287
  else:
281
- result = GLOBAL[args[0]].command(" ".join(args[1:]))
282
- response_data["success"] = result.success
283
- response_data["message"] = str(result)
288
+ cmd_usage()
284
289
 
285
290
  # Send response.
286
291
  encode_json(stream, response, response_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtimepy
3
- Version: 5.15.2
3
+ Version: 5.15.3
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/libre-embedded/runtimepy
6
6
  Author: Libre Embedded
@@ -17,11 +17,11 @@ Classifier: Development Status :: 5 - Production/Stable
17
17
  Requires-Python: >=3.12
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: websockets
21
20
  Requires-Dist: aiofiles
22
- Requires-Dist: vcorelib>=3.6.3
23
21
  Requires-Dist: svgen>=0.8.0
22
+ Requires-Dist: websockets
24
23
  Requires-Dist: psutil
24
+ Requires-Dist: vcorelib>=3.6.3
25
25
  Provides-Extra: test
26
26
  Requires-Dist: pylint; extra == "test"
27
27
  Requires-Dist: flake8; extra == "test"
@@ -51,11 +51,11 @@ Dynamic: requires-python
51
51
  =====================================
52
52
  generator=datazen
53
53
  version=3.2.3
54
- hash=f1c75c9b812e8ff29fdda46f58009aef
54
+ hash=ed2f5b3731ba189c4a3ec2f2252b622e
55
55
  =====================================
56
56
  -->
57
57
 
58
- # runtimepy ([5.15.2](https://pypi.org/project/runtimepy/))
58
+ # runtimepy ([5.15.3](https://pypi.org/project/runtimepy/))
59
59
 
60
60
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
61
61
  ![Build Status](https://github.com/libre-embedded/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,4 +1,4 @@
1
- runtimepy/__init__.py,sha256=flvkr-56G2rUSF5MWiJFKowe_0LjNtJbpWzAdUU_B3M,391
1
+ runtimepy/__init__.py,sha256=p2KGfCnic5ku-9e4b4_crfVty8m2X40WLOHJiBsFOuo,391
2
2
  runtimepy/__main__.py,sha256=IKioH2xOtsXwrwb9zABDQEJvuAX--Lnh84TeSz0XSs0,332
3
3
  runtimepy/app.py,sha256=Er1ZKKrG9U0FV0gQg_GYF9xDb89HgYnVzS5SjxGa2Tg,970
4
4
  runtimepy/dev_requirements.txt,sha256=VZhW6bJ5YbwaoN4d_XxZFuN5BbDLaG7ngKrGnugVPRw,245
@@ -17,7 +17,7 @@ runtimepy/channel/environment/create.py,sha256=UVO_R5rM6xLjpAh3j3wwix-0V3uJMAno2
17
17
  runtimepy/channel/environment/file.py,sha256=PV05KZ3-CvftbKUM8acQmawOMeGGCcMrEESEBuymykg,6949
18
18
  runtimepy/channel/environment/sample.py,sha256=6926v0ZssvfeWX_C8MSbUfYjE4sr3jt0CLVSJ_2Cl3U,5316
19
19
  runtimepy/channel/environment/telemetry.py,sha256=3A7Xcp-4eHJWz_oR1SnI6rsl4o8wiSUaiMHrnK1IaQ8,5338
20
- runtimepy/channel/environment/command/__init__.py,sha256=mymqk5roA-7evUovXlD2dmWaprSzrPb_3ae6bA9oEZ0,8162
20
+ runtimepy/channel/environment/command/__init__.py,sha256=PRuIp29QYkTDHF95HL55G6ogQDkVbq0M3SnNre4JXpI,9150
21
21
  runtimepy/channel/environment/command/parser.py,sha256=cMOsEsXnfFlATiWTNSxlgvc_XoICsJlcZigFJlQ47tk,1804
22
22
  runtimepy/channel/environment/command/processor.py,sha256=NiiWRdwBHOFEisjqNOW5oawprxcpR25ONNABoZpELdg,7122
23
23
  runtimepy/channel/environment/command/result.py,sha256=Ko5lK4d04Z266WuCi2sHQItbUHJFYv7qUdrDi-OVKOU,624
@@ -45,7 +45,7 @@ runtimepy/control/env/__init__.py,sha256=RHJqysY7Pv4VDs2SGk0X-qc5xp_SrQ_oxb5Deug
45
45
  runtimepy/data/404.md,sha256=_yzjvp2GeMc5OBvZX2zDbX-Ns6T7HCO9esRbS5PmkXU,397
46
46
  runtimepy/data/base.yaml,sha256=PEsvcMV1qFb1Gr6Cx2SgmR6ScfPyuu3yTdnvSBTVSdA,453
47
47
  runtimepy/data/browser.yaml,sha256=oc5KEV1C1uAJ4MkhNo4hyVVfJtZvHelRNqzNvD313Ow,79
48
- runtimepy/data/dummy_load.yaml,sha256=oAuccgvM3lDoN5RZBo4UECiegxzOgpllBfXXwZ5odCw,2071
48
+ runtimepy/data/dummy_load.yaml,sha256=gx3B-rGXj5iEcs7iUssy7aZxRvUNz0Q73whSlNwY1tU,2171
49
49
  runtimepy/data/factories.yaml,sha256=CdpnjK2glj8HewI2tFSNvCSUdcNPeVD_tFbGy2QBSQg,1952
50
50
  runtimepy/data/favicon.ico,sha256=boxAGaHbUjMFrOO2TZpsO0nIRC-LUgwHVQYOiG1YQnM,362870
51
51
  runtimepy/data/sample_telemetry.yaml,sha256=OpdFurkvtWJGaNl9LMlU2rKo15AaVVr-U_hoZfsbp-Y,695
@@ -96,7 +96,7 @@ runtimepy/data/schemas/Channel.yaml,sha256=9OQ3mOtPOlcWugvObpWKoAdFW0WvR9mSEzfdD
96
96
  runtimepy/data/schemas/ChannelCommand.yaml,sha256=h7-n5WjNwWgteZ8U5YpaBRncR_Uiqaa_wVfuTWQFrTY,257
97
97
  runtimepy/data/schemas/ChannelRegistry.yaml,sha256=f51YngVC8zDM3HwRMicKBOyoqjJ9EWsx3GWD0EkHCZk,136
98
98
  runtimepy/data/schemas/ClientConnectionConfig.yaml,sha256=XUWkOiCOw175harOsDRV3TXJja-EAKnipYDQFf_XC4Q,187
99
- runtimepy/data/schemas/ConnectionArbiterConfig.yaml,sha256=WorY7ttxvQA78TYuFoNHw41hc9johxvgO72dusfLSqE,2133
99
+ runtimepy/data/schemas/ConnectionArbiterConfig.yaml,sha256=tjqtu270LVhlBzP1Y-EdJqTSlC7yYLylaLzrW3Q1l-w,2286
100
100
  runtimepy/data/schemas/EnumRegistry.yaml,sha256=BfLzEEUsHj6Fg_0JyGmgbNJ7F-auuLs5tAhJjSc86uU,145
101
101
  runtimepy/data/schemas/FindFile.yaml,sha256=dSPCDQy4FQZLMgOjtbR0-o2sZECvN_tvGa3LXNd1-wc,934
102
102
  runtimepy/data/schemas/PeerProcessConfig.yaml,sha256=RNVGG1NV9-Goa7aLE0ah7QNwKOTEoJj6EZDXTW-TALM,190
@@ -167,14 +167,14 @@ runtimepy/net/ssl.py,sha256=dj9uECPKDT5k-5vlR5I3Z7Go3WWZhbaJ9nb0rC3kJvg,854
167
167
  runtimepy/net/util.py,sha256=XTQQ-Ql_ImhVd1_O8nSeDX9MY8xJwRBggvliSLCrsc8,5913
168
168
  runtimepy/net/apps/__init__.py,sha256=vjo7e19QXtJwe6V6B-QGvYiJveYobnYIfpkKZrnS17w,710
169
169
  runtimepy/net/arbiter/__init__.py,sha256=ptKF995rYKvkm4Mya92vA5QEDqcFq5NRD0IYGqZ6_do,740
170
- runtimepy/net/arbiter/base.py,sha256=q9mQ3vjVjlzjGrDcTq8gengsEV2AnCUx8IK_40h6uxg,15011
170
+ runtimepy/net/arbiter/base.py,sha256=IBM6m7BxPLHmHqndU2tbEbgo3VxevWnqBdGfJQlMg4Q,15195
171
171
  runtimepy/net/arbiter/info.py,sha256=QcBB1LQELXPs-Ud-GVq3aDBL4mpSMDcjjLtuKgojiU8,10962
172
172
  runtimepy/net/arbiter/result.py,sha256=PHZo5qj4SI08ZeWPFk_8OZ1umI6L0dp5nJpjjS8QUpM,2871
173
173
  runtimepy/net/arbiter/task.py,sha256=APcc5QioAG8uueIzxJU-vktIn8Ys3yJd_CFlRWb6Ieo,795
174
174
  runtimepy/net/arbiter/udp.py,sha256=-ecHJs-utcsiTfr5wSb73hUUuYFBeRVT_D1znYp5q6A,893
175
175
  runtimepy/net/arbiter/websocket.py,sha256=vYTDA7sXMUTTqW3i1zTWUdaxGKxfrQcQoVSYZtmCvfQ,1524
176
- runtimepy/net/arbiter/config/__init__.py,sha256=NnsVUllwHGAOVhQYAhFIYq1Il5xQdP86bNPrqzwL0oQ,8678
177
- runtimepy/net/arbiter/config/codec.py,sha256=TQj8UXEb1EvhiCqRdyq5BkeubMytEZAs9B4E5ZYsEuY,2511
176
+ runtimepy/net/arbiter/config/__init__.py,sha256=D13gU5jLLP36fkPKKpXd2VLRvvA-Rgb4SolWB5UeiKQ,8749
177
+ runtimepy/net/arbiter/config/codec.py,sha256=JVutIe1AFVr3NN7GS4rZayxZR05LWzQZRqzEuJpEJd8,2635
178
178
  runtimepy/net/arbiter/config/util.py,sha256=MezLSil5mEwioI5kxDOZa9y118st1_t0El8DYH6hFU8,1254
179
179
  runtimepy/net/arbiter/factory/__init__.py,sha256=jC1szUYucH4Us3jbb0mhFgnklHlorya5DYGGw2pqfe4,334
180
180
  runtimepy/net/arbiter/factory/connection.py,sha256=uSwnvIWGJcDd2ReK-s9lBx-NcRQ1JH_7VbwkWjE_8zA,3690
@@ -198,7 +198,7 @@ runtimepy/net/http/request_target.py,sha256=EfcOozUeXqOuQaMXH9cErfJqUkG0A5v9HEK4
198
198
  runtimepy/net/http/response.py,sha256=fD0R_BUgmNwdwKQtXvYfTYM7DyJlwsGmlNVi5HkCOhU,3409
199
199
  runtimepy/net/http/state.py,sha256=qCMN8aWfCRfU9XP-cIhSOo2RqfljTjbQRCflfcy2bfY,1626
200
200
  runtimepy/net/http/version.py,sha256=mp6rgIM7-VUVKLCA0Uw96CmBkL0ET860lDVVEewpZ7w,1098
201
- runtimepy/net/server/__init__.py,sha256=5KalIcOWv8lYm8747pq2rzGwpLz713XdHuQIKbWJsXc,11649
201
+ runtimepy/net/server/__init__.py,sha256=ppfMgymYGRz13MvHvwXyy9WAos2wWgaZDf1TW8D1ILU,11813
202
202
  runtimepy/net/server/html.py,sha256=OB2K37kA7MHtcKqp0pZE1q_XgiyvfooigzS0-OQFCCM,1900
203
203
  runtimepy/net/server/json.py,sha256=AaMPw-G-7xX67mf1LvQAipNdwrnmbLDunVlkWf6iOz0,2526
204
204
  runtimepy/net/server/markdown.py,sha256=UIJ9R9WM5XodPs8WEevFNJ3LX9tMevhUe8QKKdeY4ak,2143
@@ -306,9 +306,9 @@ runtimepy/tui/task.py,sha256=nUZo9fuOC-k1Wpqdzkv9v1tQirCI28fZVgcC13Ijvus,1093
306
306
  runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9JuSET0o,4536
307
307
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
308
  runtimepy/ui/controls.py,sha256=L55Af-4vGq6ZHewdoA7C_mAYq35WXl8NzOdcsmQIo7M,1868
309
- runtimepy-5.15.2.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
310
- runtimepy-5.15.2.dist-info/METADATA,sha256=6UbqbnIRB9WTeD5cDcSUfCqcqlxEEC6o5CQgPSBAohM,9268
311
- runtimepy-5.15.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
312
- runtimepy-5.15.2.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
313
- runtimepy-5.15.2.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
314
- runtimepy-5.15.2.dist-info/RECORD,,
309
+ runtimepy-5.15.3.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
310
+ runtimepy-5.15.3.dist-info/METADATA,sha256=r94cxl_YOL9LR18H30DZwOmNbIQ3S-EcTzvbBhCssRo,9268
311
+ runtimepy-5.15.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
312
+ runtimepy-5.15.3.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
313
+ runtimepy-5.15.3.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
314
+ runtimepy-5.15.3.dist-info/RECORD,,