runtimepy 4.2.0__tar.gz → 4.2.2__tar.gz

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.
Files changed (242) hide show
  1. {runtimepy-4.2.0/runtimepy.egg-info → runtimepy-4.2.2}/PKG-INFO +5 -5
  2. {runtimepy-4.2.0 → runtimepy-4.2.2}/README.md +2 -2
  3. {runtimepy-4.2.0 → runtimepy-4.2.2}/pyproject.toml +1 -1
  4. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/__init__.py +2 -2
  5. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/__init__.py +4 -8
  6. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/base.py +19 -0
  7. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/command/__init__.py +3 -2
  8. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/create.py +0 -14
  9. runtimepy-4.2.2/runtimepy/channel/environment/telemetry.py +173 -0
  10. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/event/__init__.py +4 -2
  11. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/registry.py +1 -45
  12. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/dummy_load.yaml +15 -0
  13. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/factories.yaml +1 -0
  14. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/TabFilter.js +2 -0
  15. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/TabInterface.js +3 -0
  16. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/WindowHashManager.js +82 -12
  17. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/util.js +3 -1
  18. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/BitFields.yaml +3 -0
  19. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mapping.py +2 -14
  20. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/message/interface.py +17 -15
  21. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/base.py +10 -0
  22. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/field/__init__.py +2 -0
  23. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/field/fields.py +16 -3
  24. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/field/manager/base.py +19 -1
  25. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/subprocess/interface.py +5 -3
  26. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/subprocess/peer.py +0 -1
  27. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/subprocess/program.py +7 -1
  28. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/sample.py +29 -0
  29. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/util.py +29 -1
  30. {runtimepy-4.2.0 → runtimepy-4.2.2/runtimepy.egg-info}/PKG-INFO +5 -5
  31. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy.egg-info/SOURCES.txt +1 -0
  32. {runtimepy-4.2.0 → runtimepy-4.2.2}/LICENSE +0 -0
  33. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/__main__.py +0 -0
  34. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/app.py +0 -0
  35. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/__init__.py +0 -0
  36. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/array.py +0 -0
  37. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/command/parser.py +0 -0
  38. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/command/processor.py +0 -0
  39. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/command/result.py +0 -0
  40. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/file.py +0 -0
  41. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/environment/sample.py +0 -0
  42. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/channel/event/header.py +0 -0
  43. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/codec/__init__.py +0 -0
  44. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/codec/protocol/__init__.py +0 -0
  45. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/codec/protocol/base.py +0 -0
  46. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/codec/protocol/json.py +0 -0
  47. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/codec/system/__init__.py +0 -0
  48. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/__init__.py +0 -0
  49. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/all.py +0 -0
  50. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/arbiter.py +0 -0
  51. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/common.py +0 -0
  52. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/server.py +0 -0
  53. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/task.py +0 -0
  54. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/commands/tui.py +0 -0
  55. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/css/bootstrap_extra.css +0 -0
  56. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/css/main.css +0 -0
  57. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/favicon.ico +0 -0
  58. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/DataConnection.js +0 -0
  59. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/JsonConnection.js +0 -0
  60. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/audio.js +0 -0
  61. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/App.js +0 -0
  62. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/ChannelTable.js +0 -0
  63. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/DataConnection.js +0 -0
  64. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/JsonConnection.js +0 -0
  65. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/Plot.js +0 -0
  66. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/PlotManager.js +0 -0
  67. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/PlotModalManager.js +0 -0
  68. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/classes/WorkerInterface.js +0 -0
  69. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/init.js +0 -0
  70. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/main.js +0 -0
  71. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/tab/env.js +0 -0
  72. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/tab/sound.js +0 -0
  73. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/unused/pyodide.js +0 -0
  74. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/js/worker.js +0 -0
  75. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/Channel.yaml +0 -0
  76. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/ChannelCommand.yaml +0 -0
  77. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/ChannelRegistry.yaml +0 -0
  78. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/ClientConnectionConfig.yaml +0 -0
  79. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/ConnectionArbiterConfig.yaml +0 -0
  80. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/EnumRegistry.yaml +0 -0
  81. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/FindFile.yaml +0 -0
  82. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/PeerProcessConfig.yaml +0 -0
  83. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/RuntimeEnum.yaml +0 -0
  84. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/ServerConnectionConfig.yaml +0 -0
  85. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/StructConfig.yaml +0 -0
  86. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/TaskConfig.yaml +0 -0
  87. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/has_config.yaml +0 -0
  88. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/has_factory.yaml +0 -0
  89. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/has_name.yaml +0 -0
  90. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/schemas/has_request_flag.yaml +0 -0
  91. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/server.yaml +0 -0
  92. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/server_base.yaml +0 -0
  93. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/data/server_dev.yaml +0 -0
  94. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/dev_requirements.txt +0 -0
  95. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/entry.py +0 -0
  96. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/enum/__init__.py +0 -0
  97. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/enum/registry.py +0 -0
  98. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/enum/types.py +0 -0
  99. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/message/__init__.py +0 -0
  100. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/message/handlers.py +0 -0
  101. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/message/types.py +0 -0
  102. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/metrics/__init__.py +0 -0
  103. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/metrics/channel.py +0 -0
  104. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/metrics/connection.py +0 -0
  105. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/metrics/task.py +0 -0
  106. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/__init__.py +0 -0
  107. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/async_command.py +0 -0
  108. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/enum.py +0 -0
  109. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/environment.py +0 -0
  110. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/finalize.py +0 -0
  111. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/logging.py +0 -0
  112. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/psutil.py +0 -0
  113. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/mixins/regex.py +0 -0
  114. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/__init__.py +0 -0
  115. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/apps/__init__.py +0 -0
  116. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/__init__.py +0 -0
  117. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/base.py +0 -0
  118. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/config/__init__.py +0 -0
  119. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/config/codec.py +0 -0
  120. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/config/util.py +0 -0
  121. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/factory/__init__.py +0 -0
  122. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/factory/connection.py +0 -0
  123. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/factory/task.py +0 -0
  124. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/housekeeping/__init__.py +0 -0
  125. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/imports/__init__.py +0 -0
  126. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/imports/util.py +0 -0
  127. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/info.py +0 -0
  128. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/result.py +0 -0
  129. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/task.py +0 -0
  130. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/tcp/__init__.py +0 -0
  131. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/tcp/json.py +0 -0
  132. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/udp.py +0 -0
  133. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/arbiter/websocket.py +0 -0
  134. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/backoff.py +0 -0
  135. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/connection.py +0 -0
  136. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/factories/__init__.py +0 -0
  137. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/__init__.py +0 -0
  138. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/common.py +0 -0
  139. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/header.py +0 -0
  140. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/request_target.py +0 -0
  141. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/response.py +0 -0
  142. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/state.py +0 -0
  143. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/http/version.py +0 -0
  144. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/manager.py +0 -0
  145. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/mixin.py +0 -0
  146. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/__init__.py +0 -0
  147. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/__init__.py +0 -0
  148. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/base.py +0 -0
  149. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/bootstrap/__init__.py +0 -0
  150. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/bootstrap/elements.py +0 -0
  151. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/bootstrap/tabs.py +0 -0
  152. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/create.py +0 -0
  153. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/elements.py +0 -0
  154. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/__init__.py +0 -0
  155. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/modal.py +0 -0
  156. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/tab/__init__.py +0 -0
  157. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/tab/base.py +0 -0
  158. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/tab/html.py +0 -0
  159. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/tab/message.py +0 -0
  160. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/env/widgets.py +0 -0
  161. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/files.py +0 -0
  162. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/placeholder.py +0 -0
  163. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/pyodide.py +0 -0
  164. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/sound.py +0 -0
  165. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/app/tab.py +0 -0
  166. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/html.py +0 -0
  167. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/json.py +0 -0
  168. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/struct/__init__.py +0 -0
  169. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/websocket/__init__.py +0 -0
  170. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/server/websocket/state.py +0 -0
  171. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/stream/__init__.py +0 -0
  172. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/stream/base.py +0 -0
  173. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/stream/json/__init__.py +0 -0
  174. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/stream/string.py +0 -0
  175. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/__init__.py +0 -0
  176. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/connection.py +0 -0
  177. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/create.py +0 -0
  178. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/http/__init__.py +0 -0
  179. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/protocol.py +0 -0
  180. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/telnet/__init__.py +0 -0
  181. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/tcp/telnet/codes.py +0 -0
  182. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/udp/__init__.py +0 -0
  183. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/udp/connection.py +0 -0
  184. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/udp/create.py +0 -0
  185. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/udp/protocol.py +0 -0
  186. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/util.py +0 -0
  187. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/websocket/__init__.py +0 -0
  188. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/net/websocket/connection.py +0 -0
  189. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/__init__.py +0 -0
  190. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/array/__init__.py +0 -0
  191. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/bool.py +0 -0
  192. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/byte_order.py +0 -0
  193. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/field/manager/__init__.py +0 -0
  194. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/float.py +0 -0
  195. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/int.py +0 -0
  196. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/scaling.py +0 -0
  197. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/serializable/__init__.py +0 -0
  198. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/serializable/base.py +0 -0
  199. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/serializable/fixed.py +0 -0
  200. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/serializable/prefixed.py +0 -0
  201. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/string.py +0 -0
  202. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/__init__.py +0 -0
  203. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/base.py +0 -0
  204. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/bool.py +0 -0
  205. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/bounds.py +0 -0
  206. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/float.py +0 -0
  207. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/primitives/types/int.py +0 -0
  208. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/py.typed +0 -0
  209. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/registry/__init__.py +0 -0
  210. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/registry/bool.py +0 -0
  211. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/registry/item.py +0 -0
  212. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/registry/name.py +0 -0
  213. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/requirements.txt +0 -0
  214. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/sample/__init__.py +0 -0
  215. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/sample/peer.py +0 -0
  216. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/sample/program.py +0 -0
  217. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/schemas.py +0 -0
  218. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/struct/__init__.py +0 -0
  219. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/subprocess/__init__.py +0 -0
  220. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/subprocess/protocol.py +0 -0
  221. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/__init__.py +0 -0
  222. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/asynchronous.py +0 -0
  223. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/basic/__init__.py +0 -0
  224. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/basic/manager.py +0 -0
  225. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/basic/periodic.py +0 -0
  226. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/task/trig/__init__.py +0 -0
  227. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/telemetry/__init__.py +0 -0
  228. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/__init__.py +0 -0
  229. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/channels/__init__.py +0 -0
  230. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/cursor.py +0 -0
  231. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/mixin.py +0 -0
  232. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/mock.py +0 -0
  233. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy/tui/task.py +0 -0
  234. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy.egg-info/dependency_links.txt +0 -0
  235. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy.egg-info/entry_points.txt +0 -0
  236. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy.egg-info/requires.txt +2 -2
  237. {runtimepy-4.2.0 → runtimepy-4.2.2}/runtimepy.egg-info/top_level.txt +0 -0
  238. {runtimepy-4.2.0 → runtimepy-4.2.2}/setup.cfg +0 -0
  239. {runtimepy-4.2.0 → runtimepy-4.2.2}/setup.py +0 -0
  240. {runtimepy-4.2.0 → runtimepy-4.2.2}/tests/test_entry.py +0 -0
  241. {runtimepy-4.2.0 → runtimepy-4.2.2}/tests/test_mapping.py +0 -0
  242. {runtimepy-4.2.0 → runtimepy-4.2.2}/tests/test_resources.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runtimepy
3
- Version: 4.2.0
3
+ Version: 4.2.2
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/vkottler/runtimepy
6
6
  Author: Vaughn Kottler
@@ -17,10 +17,10 @@ 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: websockets
20
+ Requires-Dist: psutil
21
21
  Requires-Dist: vcorelib>=3.2.4
22
22
  Requires-Dist: svgen>=0.6.6
23
- Requires-Dist: psutil
23
+ Requires-Dist: websockets
24
24
  Provides-Extra: test
25
25
  Requires-Dist: pylint; extra == "test"
26
26
  Requires-Dist: flake8; extra == "test"
@@ -44,11 +44,11 @@ Requires-Dist: uvloop; (sys_platform != "win32" and sys_platform != "cygwin") an
44
44
  =====================================
45
45
  generator=datazen
46
46
  version=3.1.4
47
- hash=e23f54e60d0c62ebc1be3cae63bc7678
47
+ hash=5f56531d1be3002a8547ddb013d713f9
48
48
  =====================================
49
49
  -->
50
50
 
51
- # runtimepy ([4.2.0](https://pypi.org/project/runtimepy/))
51
+ # runtimepy ([4.2.2](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)
@@ -2,11 +2,11 @@
2
2
  =====================================
3
3
  generator=datazen
4
4
  version=3.1.4
5
- hash=e23f54e60d0c62ebc1be3cae63bc7678
5
+ hash=5f56531d1be3002a8547ddb013d713f9
6
6
  =====================================
7
7
  -->
8
8
 
9
- # runtimepy ([4.2.0](https://pypi.org/project/runtimepy/))
9
+ # runtimepy ([4.2.2](https://pypi.org/project/runtimepy/))
10
10
 
11
11
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
12
12
  ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"
4
4
 
5
5
  [project]
6
6
  name = "runtimepy"
7
- version = "4.2.0"
7
+ version = "4.2.2"
8
8
  description = "A framework for implementing Python services."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,7 +1,7 @@
1
1
  # =====================================
2
2
  # generator=datazen
3
3
  # version=3.1.4
4
- # hash=58424f506ef1e5d09db770f3e0dd8fce
4
+ # hash=9427b9a1aadbcbc5e128b88c10b37dbe
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 = "4.2.0"
13
+ VERSION = "4.2.2"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -15,23 +15,19 @@ from runtimepy.channel.environment.create import (
15
15
  from runtimepy.channel.environment.file import (
16
16
  FileChannelEnvironment as _FileChannelEnvironment,
17
17
  )
18
- from runtimepy.channel.registry import ParsedEvent
18
+ from runtimepy.channel.environment.telemetry import (
19
+ TelemetryChannelEnvironment as _TelemetryChannelEnvironment,
20
+ )
19
21
 
20
22
 
21
23
  class ChannelEnvironment(
24
+ _TelemetryChannelEnvironment,
22
25
  _ArrayChannelEnvironment,
23
26
  _FileChannelEnvironment,
24
27
  _CreateChannelEnvironment,
25
28
  ):
26
29
  """A class integrating channel and enumeration registries."""
27
30
 
28
- def ingest(self, point: ParsedEvent) -> None:
29
- """
30
- Update internal state based on an event. Note that the event timestamp
31
- is not respected.
32
- """
33
- self.set(point.name, point.value)
34
-
35
31
  @property
36
32
  def names(self) -> _Iterator[str]:
37
33
  """Iterate over registered names in the environment."""
@@ -73,6 +73,8 @@ class BaseChannelEnvironment(_NamespaceMixin, FinalizeMixin):
73
73
  for field in fields:
74
74
  self.fields.add(field)
75
75
 
76
+ self.to_add: list[_BitFields] = []
77
+
76
78
  # Keep a mapping of each channel's name and integer identifier to the
77
79
  # underlying enumeration.
78
80
  self.channel_enums: dict[_AnyChannel, _RuntimeEnum] = {
@@ -282,3 +284,20 @@ class BaseChannelEnvironment(_NamespaceMixin, FinalizeMixin):
282
284
  prim = self.fields[key].raw
283
285
 
284
286
  return prim.age_ns()
287
+
288
+ def add_field(self, field: _BitField, namespace: Namespace = None) -> str:
289
+ """Add a bit field to the environment."""
290
+
291
+ result = self.fields.add_field(field)
292
+ if result is not None:
293
+ self.to_add.append(result)
294
+
295
+ return self.namespace(name=field.name, namespace=namespace)
296
+
297
+ def finalize(self, strict: bool = True) -> None:
298
+ """Finalize this instance."""
299
+
300
+ for fields in self.to_add:
301
+ self.fields.add(fields)
302
+
303
+ super().finalize(strict=strict)
@@ -25,7 +25,8 @@ from runtimepy.channel.environment.command.processor import (
25
25
  EnvironmentMap,
26
26
  )
27
27
  from runtimepy.channel.registry import ParsedEvent
28
- from runtimepy.mapping import DEFAULT_PATTERN, name_search
28
+ from runtimepy.mapping import DEFAULT_PATTERN
29
+ from runtimepy.util import name_search
29
30
 
30
31
  # Declared so we re-export FieldOrChannel after moving where it's declared.
31
32
  __all__ = [
@@ -115,7 +116,7 @@ class GlobalEnvironment(UserDict[str, ChannelCommandProcessor], LoggerMixin):
115
116
  """Reade events from a specific environment."""
116
117
 
117
118
  with self.valid_root.joinpath(env, path).open("rb") as path_fd:
118
- yield from self[env].env.channels.parse_event_stream(path_fd)
119
+ yield from self[env].env.parse_event_stream(path_fd)
119
120
 
120
121
  def export(self, env: str) -> Path:
121
122
  """Export an environment to a sub-directory of the root directory."""
@@ -28,8 +28,6 @@ from runtimepy.enum.types import EnumTypelike as _EnumTypelike
28
28
  from runtimepy.mapping import EnumMappingData as _EnumMappingData
29
29
  from runtimepy.primitives import ChannelScaling, Primitive
30
30
  from runtimepy.primitives import Primitivelike as _Primitivelike
31
- from runtimepy.primitives.field import BitField
32
- from runtimepy.primitives.field.fields import BitFields
33
31
  from runtimepy.registry.name import RegistryKey as _RegistryKey
34
32
 
35
33
 
@@ -184,15 +182,3 @@ class CreateChannelEnvironment(_BaseChannelEnvironment):
184
182
  )
185
183
  assert result is not None, f"Can't create enum '{name}'!"
186
184
  return result
187
-
188
- def add_field(self, field: BitField, namespace: _Namespace = None) -> str:
189
- """Add a bit field to the environment."""
190
-
191
- fields = BitFields.new()
192
- fields.claim_field(field)
193
-
194
- name = self.namespace(name=field.name, namespace=namespace)
195
-
196
- self.fields.add(fields)
197
-
198
- return name
@@ -0,0 +1,173 @@
1
+ """
2
+ A module implementing channel-environment telemetry registration.
3
+ """
4
+
5
+ # built-in
6
+ from contextlib import ExitStack, contextmanager
7
+ from typing import BinaryIO, Iterator, Optional, cast
8
+
9
+ # internal
10
+ from runtimepy.channel.environment.base import (
11
+ BaseChannelEnvironment as _BaseChannelEnvironment,
12
+ )
13
+ from runtimepy.channel.event import PrimitiveEvent
14
+ from runtimepy.channel.registry import ParsedEvent
15
+ from runtimepy.mapping import DEFAULT_PATTERN
16
+ from runtimepy.metrics.channel import ChannelMetrics
17
+ from runtimepy.util import name_search
18
+
19
+
20
+ class TelemetryChannelEnvironment(_BaseChannelEnvironment):
21
+ """A class integrating telemetry streaming."""
22
+
23
+ events: list[str]
24
+
25
+ @contextmanager
26
+ def registered(
27
+ self,
28
+ stream: BinaryIO,
29
+ pattern: str = DEFAULT_PATTERN,
30
+ exact: bool = False,
31
+ flush: bool = False,
32
+ channel: ChannelMetrics = None,
33
+ ) -> Iterator[list[str]]:
34
+ """
35
+ Register a stream as a managed context. Returns a list of all channels
36
+ registered.
37
+ """
38
+
39
+ names: list[str] = []
40
+ events: list[PrimitiveEvent] = []
41
+
42
+ # Gather event telemetry emitters for bit-fields.
43
+ for fields in self.fields.fields:
44
+ for name in name_search(fields.fields, pattern, exact=exact):
45
+ names.append(name)
46
+ field = fields.fields[name]
47
+
48
+ ident = self.channels.names.identifier(name)
49
+ assert ident is not None, name
50
+
51
+ events.append(PrimitiveEvent(field.raw, ident))
52
+
53
+ with ExitStack() as stack:
54
+ # Register bit-field event telemetry.
55
+ for event in events:
56
+ stack.enter_context(
57
+ event.registered(
58
+ stream, flush=flush, channel=channel, force=True
59
+ )
60
+ )
61
+
62
+ # Register channel telemetry.
63
+ names += stack.enter_context(
64
+ self.channels.registered(
65
+ stream,
66
+ pattern=pattern,
67
+ exact=exact,
68
+ flush=flush,
69
+ channel=channel,
70
+ )
71
+ )
72
+
73
+ yield names
74
+
75
+ def ingest(self, point: ParsedEvent) -> None:
76
+ """
77
+ Update internal state based on an event. Note that the event timestamp
78
+ is not respected.
79
+ """
80
+
81
+ if self.fields.has_field(point.name):
82
+ self.fields[point.name].raw.value = point.value # type: ignore
83
+ else:
84
+ self.set(point.name, point.value)
85
+
86
+ def _parse_channel_event(self, name: str) -> Optional[ParsedEvent]:
87
+ """Attempt to parse a channel event from the fifo."""
88
+
89
+ result = None
90
+ channels = self.channels
91
+
92
+ kind = channels[name].type
93
+ data = channels.event_fifo.pop(kind.size)
94
+ if data is not None:
95
+ result = ParsedEvent(
96
+ name,
97
+ cast(int, channels.event_header["timestamp"]),
98
+ kind.decode(
99
+ data,
100
+ byte_order=channels.event_header.array.byte_order,
101
+ ),
102
+ )
103
+
104
+ return result
105
+
106
+ def _parse_field_event(self, name: str) -> Optional[ParsedEvent]:
107
+ """Attempt to parse a bit-field event from the fifo."""
108
+
109
+ result = None
110
+
111
+ field = self.fields[name]
112
+
113
+ kind = field.raw.kind
114
+ data = self.channels.event_fifo.pop(kind.size)
115
+ if data is not None:
116
+ result = ParsedEvent(
117
+ name,
118
+ cast(int, self.channels.event_header["timestamp"]),
119
+ kind.decode(
120
+ data,
121
+ byte_order=self.channels.event_header.array.byte_order,
122
+ ),
123
+ )
124
+
125
+ return result
126
+
127
+ def parse_event_stream(self, stream: BinaryIO) -> Iterator[ParsedEvent]:
128
+ """Parse individual events from a stream."""
129
+
130
+ channels = self.channels
131
+
132
+ # Ingest stream.
133
+ channels.event_fifo.ingest(stream.read())
134
+
135
+ ident = -1
136
+ name = ""
137
+
138
+ keep_going = True
139
+ while keep_going:
140
+ keep_going = False
141
+
142
+ # Read header.
143
+ if not channels.header_ready:
144
+ read_size = channels.event_header.size
145
+ data = channels.event_fifo.pop(read_size)
146
+ if data is not None:
147
+ channels.event_header.array.update(data)
148
+
149
+ # Update local variables.
150
+ ident = cast(int, channels.event_header["identifier"])
151
+ name = channels.names.name(ident) # type: ignore
152
+ assert name is not None, ident
153
+
154
+ # Update state.
155
+ channels.header_ready = True
156
+ keep_going = True
157
+ else:
158
+ event = None
159
+
160
+ # Handle bit-field.
161
+ if self.fields.has_field(name):
162
+ event = self._parse_field_event(name)
163
+
164
+ # Handle channel.
165
+ elif name in channels.items:
166
+ event = self._parse_channel_event(name)
167
+
168
+ if event is not None:
169
+ yield event
170
+
171
+ # Update state.
172
+ channels.header_ready = False
173
+ keep_going = True
@@ -44,6 +44,7 @@ class PrimitiveEvent:
44
44
  stream: BinaryIO,
45
45
  flush: bool = False,
46
46
  channel: ChannelMetrics = None,
47
+ force: bool = False,
47
48
  ) -> Iterator[None]:
48
49
  """Register a stream as a managed context."""
49
50
 
@@ -51,7 +52,7 @@ class PrimitiveEvent:
51
52
 
52
53
  def callback(_, __) -> None:
53
54
  """Emit a change event to the stream."""
54
- self._poll(stream, flush=flush, channel=channel)
55
+ self._poll(stream, flush=flush, channel=channel, force=force)
55
56
 
56
57
  # Poll immediately.
57
58
  self.prev_ns = 0
@@ -71,6 +72,7 @@ class PrimitiveEvent:
71
72
  stream: BinaryIO,
72
73
  flush: bool = False,
73
74
  channel: ChannelMetrics = None,
75
+ force: bool = False,
74
76
  ) -> int:
75
77
  """
76
78
  Poll this event so that if the underlying channel has changed since the
@@ -83,7 +85,7 @@ class PrimitiveEvent:
83
85
  raw = self.primitive
84
86
  curr_ns = raw.last_updated_ns
85
87
 
86
- if curr_ns - self.prev_ns >= self.min_period_ns:
88
+ if force or curr_ns - self.prev_ns >= self.min_period_ns:
87
89
  self.prev_ns = curr_ns
88
90
  self.header["timestamp"] = curr_ns
89
91
 
@@ -7,7 +7,7 @@ from contextlib import ExitStack, contextmanager
7
7
  from typing import Any as _Any
8
8
  from typing import BinaryIO, Iterable, Iterator, NamedTuple
9
9
  from typing import Optional as _Optional
10
- from typing import Union, cast
10
+ from typing import Union
11
11
 
12
12
  # third-party
13
13
  from vcorelib.io import ByteFifo
@@ -150,47 +150,3 @@ class ChannelRegistry(_Registry[_Channel[_Any]]):
150
150
  names.append(name)
151
151
 
152
152
  yield names
153
-
154
- def parse_event_stream(self, stream: BinaryIO) -> Iterator[ParsedEvent]:
155
- """Parse individual events from a stream."""
156
-
157
- # Ingest stream.
158
- self.event_fifo.ingest(stream.read())
159
-
160
- ident = -1
161
- name = ""
162
-
163
- keep_going = True
164
- while keep_going:
165
- keep_going = False
166
-
167
- # Read header.
168
- if not self.header_ready:
169
- read_size = self.event_header.size
170
- data = self.event_fifo.pop(read_size)
171
- if data is not None:
172
- self.event_header.array.update(data)
173
-
174
- # Update local variables.
175
- ident = cast(int, self.event_header["identifier"])
176
- name = self.names.name(ident) # type: ignore
177
- assert name is not None, ident
178
-
179
- # Update state.
180
- self.header_ready = True
181
- keep_going = True
182
- else:
183
- kind = self[name].type
184
- data = self.event_fifo.pop(kind.size)
185
- if data is not None:
186
- yield ParsedEvent(
187
- name,
188
- cast(int, self.event_header["timestamp"]),
189
- kind.decode(
190
- data, byte_order=self.event_header.array.byte_order
191
- ),
192
- )
193
-
194
- # Update state.
195
- self.header_ready = False
196
- keep_going = True
@@ -11,6 +11,21 @@ tasks:
11
11
  - {name: wave2, factory: sinusoid, period_s: 0.02}
12
12
  - {name: wave3, factory: sinusoid, period_s: 0.03}
13
13
 
14
+ # Drive interactions with runtime entities that won't otherwise be polled.
15
+ - {name: app, factory: SampleApp, period_s: 0.25}
16
+
17
+ # UDP JSON clients.
18
+ clients:
19
+ - factory: udp_json
20
+ name: udp_json_client
21
+ defer: true
22
+ kwargs:
23
+ remote_addr: [localhost, "$udp_json"]
24
+ - factory: udp_json
25
+ name: udp_json_server
26
+ kwargs:
27
+ local_addr: [localhost, "$udp_json"]
28
+
14
29
  # Add some sample structs.
15
30
  structs:
16
31
  - name: example.struct1
@@ -25,6 +25,7 @@ factories:
25
25
  # Useful tasks.
26
26
  - {name: runtimepy.task.trig.Sinusoid}
27
27
  - {name: runtimepy.task.sample.Sample}
28
+ - {name: runtimepy.task.sample.SampleApp}
28
29
 
29
30
  # Useful structs.
30
31
  - {name: runtimepy.net.arbiter.info.SampleStruct}
@@ -15,6 +15,8 @@ class TabFilter {
15
15
  }
16
16
 
17
17
  updateStyles(pattern) {
18
+ hash.setTabFilter(pattern);
19
+
18
20
  if (!pattern) {
19
21
  pattern = ".*";
20
22
  }
@@ -71,6 +71,8 @@ class TabInterface {
71
71
  }
72
72
 
73
73
  updateChannelStyles(pattern) {
74
+ hash.handleChannelFilter(this.name, pattern);
75
+
74
76
  if (!pattern) {
75
77
  pattern = ".*";
76
78
  }
@@ -96,6 +98,7 @@ class TabInterface {
96
98
  } else {
97
99
  curr += event.key;
98
100
  }
101
+
99
102
  this.updateChannelStyles(curr);
100
103
  }
101
104
  }
@@ -4,9 +4,11 @@ class WindowHashManager {
4
4
  this.original = this.hash();
5
5
 
6
6
  this.tab = "";
7
+ this.tabFilter = "";
7
8
  this.tabsShown = true;
8
9
  this.channelsShown = true;
9
10
  this.plotChannels = {};
11
+ this.filters = {};
10
12
  }
11
13
 
12
14
  tabClick(event) {
@@ -29,6 +31,11 @@ class WindowHashManager {
29
31
  this.update();
30
32
  }
31
33
 
34
+ handleChannelFilter(tabName, value) {
35
+ this.filters[tabName] = value;
36
+ this.update();
37
+ }
38
+
32
39
  handlePlotChannelToggle(tabName, channel, state) {
33
40
  /* Service settings modal. */
34
41
  if (modalManager) {
@@ -51,6 +58,29 @@ class WindowHashManager {
51
58
  }
52
59
  }
53
60
 
61
+ setTabFilter(value) {
62
+ this.tabFilter = value;
63
+ this.update();
64
+ }
65
+
66
+ updateTabFilter(value) {
67
+ this.tabFilter = value;
68
+ if (tabFilter) {
69
+ tabFilter.input.value = value;
70
+ tabFilter.updateStyles(value);
71
+ }
72
+ }
73
+
74
+ setTabChannelFilter(tabName, value) {
75
+ this.filters[tabName] = value;
76
+
77
+ let elem = tabs[tabName].query("#channel-filter");
78
+ if (elem) {
79
+ elem.value = value;
80
+ tabs[tabName].updateChannelStyles(value);
81
+ }
82
+ }
83
+
54
84
  clearPlotChannels(tabName) {
55
85
  if (tabName in this.plotChannels) {
56
86
  let channels = this.plotChannels[tabName];
@@ -85,7 +115,16 @@ class WindowHashManager {
85
115
  for (let i = 1; i < boolsChannels.length; i++) {
86
116
  let nameChannels = boolsChannels[i].split(":");
87
117
  for (let chan of nameChannels[1].split(",")) {
88
- this.togglePlotChannel(nameChannels[0], chan);
118
+ if (!chan.includes("=")) {
119
+ /* Handle regular channel names. */
120
+ this.togglePlotChannel(nameChannels[0], chan);
121
+ } else {
122
+ /* Handle key-value pairs. */
123
+ let keyVal = chan.split("=");
124
+ if (keyVal.length == 2 && keyVal[0] == "filter" && keyVal[1]) {
125
+ this.setTabChannelFilter(nameChannels[0], keyVal[1]);
126
+ }
127
+ }
89
128
  }
90
129
  }
91
130
 
@@ -95,6 +134,16 @@ class WindowHashManager {
95
134
  if (split.includes("hide-channels")) {
96
135
  channelsButton.click();
97
136
  }
137
+
138
+ /* Check for tab filter. */
139
+ for (let item of split) {
140
+ if (item.includes("=")) {
141
+ let keyVal = item.split("=");
142
+ if (keyVal.length == 2 && keyVal[0] == "filter" && keyVal[1]) {
143
+ this.updateTabFilter(keyVal[1]);
144
+ }
145
+ }
146
+ }
98
147
  }
99
148
  }
100
149
 
@@ -109,6 +158,9 @@ class WindowHashManager {
109
158
  update() {
110
159
  let hash = this.tab;
111
160
 
161
+ if (this.tabFilter) {
162
+ hash += ",filter=" + this.tabFilter;
163
+ }
112
164
  if (!this.tabsShown) {
113
165
  hash += ",hide-tabs"
114
166
  }
@@ -116,22 +168,40 @@ class WindowHashManager {
116
168
  hash += ",hide-channels"
117
169
  }
118
170
 
119
- for (let tab in this.plotChannels) {
171
+ for (let tab in tabs) {
120
172
  let firstChan = true;
121
- let channels = this.plotChannels[tab];
122
- for (let name in channels) {
123
- if (channels[name]) {
124
- if (firstChan) {
125
- hash += "/" + tab + ":"
126
- firstChan = false;
127
- }
128
173
 
129
- if (hash.slice(-1) != ":") {
130
- hash += ",";
174
+ /* Write plot channels if present. */
175
+ if (tab in this.plotChannels) {
176
+ let channels = this.plotChannels[tab];
177
+
178
+ for (let name in channels) {
179
+ if (channels[name]) {
180
+ if (firstChan) {
181
+ hash += "/" + tab + ":"
182
+ firstChan = false;
183
+ }
184
+
185
+ if (hash.slice(-1) != ":") {
186
+ hash += ",";
187
+ }
188
+ hash += name;
131
189
  }
132
- hash += name;
133
190
  }
134
191
  }
192
+
193
+ /* Write tab filter if present. */
194
+ if (tab in this.filters && this.filters[tab]) {
195
+ if (firstChan) {
196
+ hash += "/" + tab + ":"
197
+ firstChan = false;
198
+ }
199
+
200
+ if (hash.slice(-1) != ":") {
201
+ hash += ",";
202
+ }
203
+ hash += "filter=" + this.filters[tab];
204
+ }
135
205
  }
136
206
 
137
207
  window.location.hash = hash;
@@ -22,6 +22,8 @@ function worker_config(config) {
22
22
  return worker_cfg;
23
23
  }
24
24
 
25
+ let tabFilter = undefined;
26
+
25
27
  function bootstrap_init() {
26
28
  /*
27
29
  * Enable tooltips.
@@ -34,6 +36,6 @@ function bootstrap_init() {
34
36
  /* Initialize tab filter. */
35
37
  let tabs = document.getElementById("runtimepy-tabs");
36
38
  if (tabs) {
37
- const tabFilter = new TabFilter(tabs);
39
+ tabFilter = new TabFilter(tabs);
38
40
  }
39
41
  }
@@ -27,6 +27,9 @@ properties:
27
27
  type: string
28
28
  pattern: "^[a-z0-9-_.]+$"
29
29
 
30
+ commandable:
31
+ type: boolean
32
+
30
33
  description:
31
34
  type: string
32
35