runtimepy 5.14.2__py3-none-any.whl → 5.15.1__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.
Files changed (99) hide show
  1. runtimepy/__init__.py +2 -2
  2. runtimepy/channel/__init__.py +1 -4
  3. runtimepy/channel/environment/__init__.py +93 -2
  4. runtimepy/channel/environment/create.py +16 -1
  5. runtimepy/channel/environment/sample.py +10 -2
  6. runtimepy/channel/registry.py +2 -3
  7. runtimepy/codec/protocol/base.py +34 -14
  8. runtimepy/codec/protocol/json.py +5 -3
  9. runtimepy/codec/system/__init__.py +6 -2
  10. runtimepy/control/source.py +1 -1
  11. runtimepy/data/404.md +16 -0
  12. runtimepy/data/base.yaml +3 -0
  13. runtimepy/data/css/bootstrap_extra.css +59 -44
  14. runtimepy/data/css/main.css +23 -4
  15. runtimepy/data/dummy_load.yaml +5 -2
  16. runtimepy/data/factories.yaml +1 -0
  17. runtimepy/data/js/classes/App.js +54 -2
  18. runtimepy/data/js/classes/ChannelTable.js +6 -8
  19. runtimepy/data/js/classes/Plot.js +9 -4
  20. runtimepy/data/js/classes/TabFilter.js +47 -9
  21. runtimepy/data/js/classes/TabInterface.js +106 -11
  22. runtimepy/data/js/classes/WindowHashManager.js +30 -15
  23. runtimepy/data/js/init.js +18 -1
  24. runtimepy/data/js/markdown_page.js +10 -0
  25. runtimepy/data/js/sample.js +1 -0
  26. runtimepy/data/schemas/BitFields.yaml +9 -0
  27. runtimepy/data/schemas/RuntimeEnum.yaml +6 -0
  28. runtimepy/data/schemas/StructConfig.yaml +9 -1
  29. runtimepy/data/static/css/bootstrap-icons.min.css +4 -3
  30. runtimepy/data/static/css/bootstrap.min.css +3 -4
  31. runtimepy/data/static/css/fonts/bootstrap-icons.woff +0 -0
  32. runtimepy/data/static/css/fonts/bootstrap-icons.woff2 +0 -0
  33. runtimepy/data/static/js/bootstrap.bundle.min.js +5 -4
  34. runtimepy/data/static/js/webglplot.umd.min.js +2 -1
  35. runtimepy/data/static/svg/outline-dark.svg +22 -0
  36. runtimepy/data/static/svg/outline-light.svg +22 -0
  37. runtimepy/enum/__init__.py +13 -1
  38. runtimepy/enum/registry.py +13 -1
  39. runtimepy/message/__init__.py +3 -3
  40. runtimepy/mixins/logging.py +6 -1
  41. runtimepy/net/__init__.py +0 -2
  42. runtimepy/net/arbiter/info.py +36 -4
  43. runtimepy/net/arbiter/struct/__init__.py +3 -2
  44. runtimepy/net/connection.py +6 -7
  45. runtimepy/net/html/__init__.py +29 -11
  46. runtimepy/net/html/bootstrap/__init__.py +2 -2
  47. runtimepy/net/html/bootstrap/elements.py +44 -24
  48. runtimepy/net/html/bootstrap/tabs.py +18 -11
  49. runtimepy/net/http/__init__.py +3 -3
  50. runtimepy/net/http/request_target.py +3 -3
  51. runtimepy/net/mixin.py +4 -2
  52. runtimepy/net/server/__init__.py +16 -9
  53. runtimepy/net/server/app/__init__.py +1 -0
  54. runtimepy/net/server/app/create.py +3 -3
  55. runtimepy/net/server/app/env/__init__.py +30 -4
  56. runtimepy/net/server/app/env/settings.py +4 -7
  57. runtimepy/net/server/app/env/tab/base.py +2 -1
  58. runtimepy/net/server/app/env/tab/controls.py +141 -27
  59. runtimepy/net/server/app/env/tab/html.py +68 -26
  60. runtimepy/net/server/app/env/widgets.py +115 -61
  61. runtimepy/net/server/app/landing_page.py +1 -1
  62. runtimepy/net/server/app/tab.py +12 -3
  63. runtimepy/net/server/html.py +2 -2
  64. runtimepy/net/server/json.py +1 -1
  65. runtimepy/net/server/markdown.py +29 -12
  66. runtimepy/net/server/mux.py +29 -0
  67. runtimepy/net/stream/__init__.py +6 -5
  68. runtimepy/net/stream/base.py +4 -2
  69. runtimepy/net/tcp/connection.py +5 -3
  70. runtimepy/net/tcp/http/__init__.py +10 -9
  71. runtimepy/net/tcp/protocol.py +2 -2
  72. runtimepy/net/tcp/scpi/__init__.py +5 -2
  73. runtimepy/net/tcp/telnet/__init__.py +2 -1
  74. runtimepy/net/udp/connection.py +10 -6
  75. runtimepy/net/udp/protocol.py +5 -6
  76. runtimepy/net/udp/queue.py +5 -2
  77. runtimepy/net/udp/tftp/base.py +2 -1
  78. runtimepy/net/websocket/connection.py +58 -9
  79. runtimepy/primitives/array/__init__.py +7 -5
  80. runtimepy/primitives/base.py +3 -2
  81. runtimepy/primitives/field/__init__.py +35 -2
  82. runtimepy/primitives/field/fields.py +11 -2
  83. runtimepy/primitives/field/manager/base.py +19 -2
  84. runtimepy/primitives/serializable/base.py +5 -2
  85. runtimepy/primitives/serializable/fixed.py +5 -2
  86. runtimepy/primitives/serializable/prefixed.py +4 -1
  87. runtimepy/primitives/types/base.py +4 -1
  88. runtimepy/primitives/types/bounds.py +10 -4
  89. runtimepy/registry/__init__.py +20 -0
  90. runtimepy/registry/name.py +6 -0
  91. runtimepy/requirements.txt +2 -2
  92. runtimepy/ui/controls.py +20 -1
  93. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/METADATA +6 -6
  94. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/RECORD +98 -94
  95. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/WHEEL +1 -1
  96. runtimepy/data/404.html +0 -7
  97. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/entry_points.txt +0 -0
  98. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/licenses/LICENSE +0 -0
  99. {runtimepy-5.14.2.dist-info → runtimepy-5.15.1.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,7 @@ from typing import cast as _cast
14
14
  from vcorelib.io import ARBITER as _ARBITER
15
15
  from vcorelib.io.types import EncodeResult as _EncodeResult
16
16
  from vcorelib.io.types import JsonObject as _JsonObject
17
+ from vcorelib.namespace import Namespace
17
18
  from vcorelib.paths import Pathlike as _Pathlike
18
19
 
19
20
  # internal
@@ -86,7 +87,12 @@ class BitFieldsManagerBase:
86
87
  """Encode this bit-fields manager to a file."""
87
88
  return fields_to_file(path, self.fields, **kwargs)
88
89
 
89
- def add(self, fields: _BitFields) -> int:
90
+ def add(
91
+ self,
92
+ fields: _BitFields,
93
+ namespace: Namespace = None,
94
+ track: bool = False,
95
+ ) -> int:
90
96
  """Add new bit-fields to manage."""
91
97
 
92
98
  # Ensure that new fields can't be added after the current fields
@@ -97,7 +103,12 @@ class BitFieldsManagerBase:
97
103
  self.fields.append(fields)
98
104
 
99
105
  # Register fields into the lookup structure.
106
+ to_add = {}
100
107
  for name, field in fields.fields.items():
108
+ if namespace is not None:
109
+ name = namespace.namespace(name=name, track=track)
110
+ to_add[name] = field
111
+
101
112
  ident = self.registry.register_name(name)
102
113
  assert ident is not None, "Couldn't register bit-field '{name}'!"
103
114
  assert name not in self.lookup, name
@@ -105,7 +116,13 @@ class BitFieldsManagerBase:
105
116
 
106
117
  # Also store the enum mapping.
107
118
  if field.is_enum:
108
- self.enum_lookup[name] = self.enums[field.enum]
119
+ runtime = self.enums[field.enum]
120
+ self.enum_lookup[name] = runtime
121
+ if runtime.default:
122
+ self.set(name, runtime.default)
123
+
124
+ # Add possible namespaced-name mappings.
125
+ fields.fields.update(to_add)
109
126
 
110
127
  return index
111
128
 
@@ -11,6 +11,7 @@ from typing import TypeVar
11
11
 
12
12
  # third-party
13
13
  from vcorelib import DEFAULT_ENCODING
14
+ from vcorelib.io import BinaryMessage
14
15
 
15
16
  # internal
16
17
  from runtimepy.primitives.byte_order import (
@@ -133,7 +134,7 @@ class Serializable(ABC):
133
134
  )
134
135
 
135
136
  @abstractmethod
136
- def update(self, data: bytes, timestamp_ns: int = None) -> int:
137
+ def update(self, data: BinaryMessage, timestamp_ns: int = None) -> int:
137
138
  """Update this serializable from a bytes instance."""
138
139
 
139
140
  def update_str(self, data: str, timestamp_ns: int = None) -> int:
@@ -158,7 +159,9 @@ class Serializable(ABC):
158
159
 
159
160
  return result
160
161
 
161
- def update_chain(self, data: bytes, timestamp_ns: int = None) -> int:
162
+ def update_chain(
163
+ self, data: BinaryMessage, timestamp_ns: int = None
164
+ ) -> int:
162
165
  """Update this serializable from a bytes instance."""
163
166
 
164
167
  with _BytesIO(data) as stream:
@@ -5,6 +5,9 @@ A module implementing a fixed-size bytes serializable.
5
5
  # built-in
6
6
  from copy import copy as _copy
7
7
 
8
+ # third-party
9
+ from vcorelib.io import BinaryMessage
10
+
8
11
  # internal
9
12
  from runtimepy.primitives.serializable.base import Serializable
10
13
 
@@ -31,11 +34,11 @@ class FixedChunk(Serializable):
31
34
  """Get this serializable as a bytes instance."""
32
35
  return self.data
33
36
 
34
- def update(self, data: bytes, timestamp_ns: int = None) -> int:
37
+ def update(self, data: BinaryMessage, timestamp_ns: int = None) -> int:
35
38
  """Update this serializable from a bytes instance."""
36
39
 
37
40
  del timestamp_ns
38
41
 
39
- self.data = data
42
+ self.data = bytes(data)
40
43
  self.size = len(self.data)
41
44
  return self.size
@@ -7,6 +7,9 @@ primitive prefix to determine the size of the chunk portion.
7
7
  from typing import BinaryIO as _BinaryIO
8
8
  from typing import TypeVar
9
9
 
10
+ # third-party
11
+ from vcorelib.io import BinaryMessage
12
+
10
13
  # internal
11
14
  from runtimepy.primitives import Primitivelike, UnsignedInt, create
12
15
  from runtimepy.primitives.byte_order import (
@@ -44,7 +47,7 @@ class PrefixedChunk(Serializable):
44
47
  """Get this chunk as a string."""
45
48
  return str(self.chunk)
46
49
 
47
- def update(self, data: bytes, timestamp_ns: int = None) -> int:
50
+ def update(self, data: BinaryMessage, timestamp_ns: int = None) -> int:
48
51
  """Update this serializable from a bytes instance."""
49
52
 
50
53
  size = self.chunk.update(data, timestamp_ns=timestamp_ns)
@@ -14,6 +14,9 @@ from typing import TypeVar as _TypeVar
14
14
  from typing import Union as _Union
15
15
  from typing import cast as _cast
16
16
 
17
+ # third-party
18
+ from vcorelib.io import BinaryMessage
19
+
17
20
  # internal
18
21
  from runtimepy.primitives.byte_order import (
19
22
  DEFAULT_BYTE_ORDER as _DEFAULT_BYTE_ORDER,
@@ -134,7 +137,7 @@ class PrimitiveType(_Generic[T]):
134
137
  return _pack(byte_order.fmt + self.format, value)
135
138
 
136
139
  def decode(
137
- self, data: bytes, byte_order: _ByteOrder = _DEFAULT_BYTE_ORDER
140
+ self, data: BinaryMessage, byte_order: _ByteOrder = _DEFAULT_BYTE_ORDER
138
141
  ) -> PythonPrimitive:
139
142
  """Decode primitive based on this type."""
140
143
  return _unpack(byte_order.fmt + self.format, data)[0] # type: ignore
@@ -31,9 +31,15 @@ class IntegerBounds(NamedTuple):
31
31
  return max(self.min, min(val, self.max))
32
32
 
33
33
  @staticmethod
34
- def create(byte_count: int, signed: bool) -> "IntegerBounds":
34
+ def create_bit(bit_count: int, signed: bool) -> "IntegerBounds":
35
35
  """Compute maximum and minimum values given size and signedness."""
36
36
 
37
- min_val = 0 if not signed else -1 * (2 ** (byte_count * 8 - 1))
38
- width = 8 * byte_count if not signed else 8 * byte_count - 1
39
- return IntegerBounds(min_val, (2**width) - 1)
37
+ return IntegerBounds(
38
+ 0 if not signed else -1 * (2 ** (bit_count - 1)),
39
+ (2 ** (bit_count if not signed else bit_count - 1)) - 1,
40
+ )
41
+
42
+ @staticmethod
43
+ def create(byte_count: int, signed: bool) -> "IntegerBounds":
44
+ """Compute maximum and minimum values given size and signedness."""
45
+ return IntegerBounds.create_bit(8 * byte_count, signed)
@@ -20,6 +20,7 @@ from vcorelib.io.types import JsonValue as _JsonValue
20
20
  from runtimepy.registry.item import RegistryItem as _RegistryItem
21
21
  from runtimepy.registry.name import NameRegistry as _NameRegistry
22
22
  from runtimepy.registry.name import RegistryKey as _RegistryKey
23
+ from runtimepy.registry.name import is_registry_key as _is_registry_key
23
24
  from runtimepy.schemas import RuntimepyDictCodec as _RuntimepyDictCodec
24
25
 
25
26
  T = _TypeVar("T", bound=_RegistryItem)
@@ -66,6 +67,15 @@ class Registry(_RuntimepyDictCodec, _Generic[T]):
66
67
  for name, item in self.items.items()
67
68
  }
68
69
 
70
+ def register_from_other(self, other: "Registry[T]") -> None:
71
+ """Register missing elements from another registry."""
72
+
73
+ for name, instance in other.items.items():
74
+ if name in self.items:
75
+ assert self.items[name].id == instance.id, (name, instance)
76
+ else:
77
+ assert self.register(name, instance), (name, instance)
78
+
69
79
  def register(self, name: str, item: T) -> bool:
70
80
  """Attempt to register a new item."""
71
81
 
@@ -100,6 +110,16 @@ class Registry(_RuntimepyDictCodec, _Generic[T]):
100
110
  result = self.items[name]
101
111
  return result
102
112
 
113
+ def registry_normalize(self, key: _RegistryKey | T) -> T:
114
+ """Attempt to get an item from a registry key."""
115
+
116
+ if _is_registry_key(key):
117
+ result: T = self[_cast(str, key)]
118
+ else:
119
+ result = _cast(T, key)
120
+
121
+ return result
122
+
103
123
  def __getitem__(self, key: _RegistryKey) -> T:
104
124
  """Get a registry item."""
105
125
 
@@ -3,6 +3,7 @@ A simple name-to-identifier registry interface.
3
3
  """
4
4
 
5
5
  # built-in
6
+ from typing import Any as _Any
6
7
  from typing import MutableMapping as _MutableMapping
7
8
  from typing import Optional as _Optional
8
9
  from typing import Union as _Union
@@ -15,6 +16,11 @@ KeyToName = _MutableMapping[int, str]
15
16
  NameToKey = _MutableMapping[str, int]
16
17
 
17
18
 
19
+ def is_registry_key(data: _Any) -> bool:
20
+ """Determine if this data is a registry key."""
21
+ return isinstance(data, (int, str))
22
+
23
+
18
24
  class NameRegistry(_TwoWayNameMapping[int]):
19
25
  """A simple class for keeping track of name-to-identifier mappings."""
20
26
 
@@ -1,6 +1,6 @@
1
1
  aiofiles
2
- vcorelib>=3.6.0
3
- svgen>=0.7.12
2
+ vcorelib>=3.6.2
3
+ svgen>=0.8.0
4
4
  websockets
5
5
  psutil
6
6
  windows-curses; sys_platform == 'win32' and python_version < '3.12'
runtimepy/ui/controls.py CHANGED
@@ -7,8 +7,11 @@ import math
7
7
  from typing import Optional
8
8
 
9
9
  # internal
10
- from runtimepy.channel import Controls
10
+ from runtimepy.primitives.types.bounds import IntegerBounds
11
11
 
12
+ Literal = int | float | bool
13
+ Default = Optional[Literal]
14
+ Controls = dict[str, Literal | dict[str, Literal]]
12
15
  DEFAULT_STEPS = 64.0
13
16
 
14
17
 
@@ -30,6 +33,22 @@ def make_slider(
30
33
  return result
31
34
 
32
35
 
36
+ def bit_slider(width: int, signed: bool) -> Controls:
37
+ """Make a slider for an unsigned integer width."""
38
+ bounds = IntegerBounds.create_bit(width, signed)
39
+ return make_slider(bounds.min, bounds.max, 2**width - 1)
40
+
41
+
42
+ def signed_slider(width: int) -> Controls:
43
+ """Make a slider for an unsigned integer width."""
44
+ return bit_slider(width, True)
45
+
46
+
47
+ def unsigned_slider(width: int) -> Controls:
48
+ """Make a slider for an unsigned integer width."""
49
+ return bit_slider(width, False)
50
+
51
+
33
52
  CANONICAL: dict[str, Controls] = {
34
53
  "phase": make_slider(-math.pi, math.pi, 90, default=0.0),
35
54
  "amplitude": make_slider(0.0, 2.0, 100.0, default=1.0),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtimepy
3
- Version: 5.14.2
3
+ Version: 5.15.1
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,10 +17,10 @@ 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: aiofiles
21
- Requires-Dist: svgen>=0.7.12
22
- Requires-Dist: vcorelib>=3.6.0
20
+ Requires-Dist: vcorelib>=3.6.2
23
21
  Requires-Dist: websockets
22
+ Requires-Dist: svgen>=0.8.0
23
+ Requires-Dist: aiofiles
24
24
  Requires-Dist: psutil
25
25
  Provides-Extra: test
26
26
  Requires-Dist: pylint; extra == "test"
@@ -51,11 +51,11 @@ Dynamic: requires-python
51
51
  =====================================
52
52
  generator=datazen
53
53
  version=3.2.3
54
- hash=d3dcde1ba35cb14decfc6881b6a2d4e6
54
+ hash=21dc494ffe783ac9edf7769c4d5aa21e
55
55
  =====================================
56
56
  -->
57
57
 
58
- # runtimepy ([5.14.2](https://pypi.org/project/runtimepy/))
58
+ # runtimepy ([5.15.1](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)