moat-lib-codec 0.4.10__tar.gz → 0.4.14__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 (34) hide show
  1. {moat_lib_codec-0.4.10/src/moat_lib_codec.egg-info → moat_lib_codec-0.4.14}/PKG-INFO +5 -5
  2. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/README.md +12 -0
  3. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/debian/changelog +42 -0
  4. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/pyproject.toml +5 -5
  5. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/__init__.py +6 -2
  6. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/_base.py +33 -14
  7. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/_cbor.py +14 -9
  8. moat_lib_codec-0.4.14/src/moat/lib/codec/_moat_cbor.py +136 -0
  9. moat_lib_codec-0.4.14/src/moat/lib/codec/_moat_msgpack.py +132 -0
  10. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/cbor.py +6 -8
  11. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/errors.py +6 -5
  12. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/json.py +1 -1
  13. moat_lib_codec-0.4.14/src/moat/lib/codec/moat_cbor.py +257 -0
  14. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/moat_kv.py +1 -1
  15. moat_lib_codec-0.4.14/src/moat/lib/codec/moat_msgpack.py +29 -0
  16. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/msgpack.py +8 -8
  17. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/yaml.py +7 -2
  18. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14/src/moat_lib_codec.egg-info}/PKG-INFO +5 -5
  19. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat_lib_codec.egg-info/SOURCES.txt +4 -0
  20. moat_lib_codec-0.4.14/src/moat_lib_codec.egg-info/requires.txt +5 -0
  21. moat_lib_codec-0.4.10/src/moat_lib_codec.egg-info/requires.txt +0 -5
  22. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/LICENSE.txt +0 -0
  23. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/Makefile +0 -0
  24. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/debian/.gitignore +0 -0
  25. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/debian/control +0 -0
  26. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/debian/py3dist-overrides +0 -0
  27. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/debian/rules +0 -0
  28. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/setup.cfg +0 -0
  29. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/binary.py +0 -0
  30. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/bool.py +0 -0
  31. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/noop.py +0 -0
  32. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat/lib/codec/utf8.py +0 -0
  33. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat_lib_codec.egg-info/dependency_links.txt +0 -0
  34. {moat_lib_codec-0.4.10 → moat_lib_codec-0.4.14}/src/moat_lib_codec.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-lib-codec
3
- Version: 0.4.10
3
+ Version: 0.4.14
4
4
  Summary: A simple command/stream multiplexer
5
5
  Author-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
@@ -17,8 +17,8 @@ Requires-Python: >=3.8
17
17
  Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE.txt
19
19
  Requires-Dist: anyio~=4.2
20
- Requires-Dist: moat-lib-rpc~=0.4.6
21
- Requires-Dist: moat-lib-micro~=0.1.0
22
- Requires-Dist: moat-lib-proxy~=0.1.0
23
- Requires-Dist: moat-util~=0.61.1
20
+ Requires-Dist: moat-lib-rpc~=0.6.1
21
+ Requires-Dist: moat-lib-micro~=0.2.2
22
+ Requires-Dist: moat-lib-proxy~=0.1.1
23
+ Requires-Dist: moat-util~=0.62.6
24
24
  Dynamic: license-file
@@ -1,12 +1,24 @@
1
1
  # The MoaT-Codec library
2
2
 
3
3
  % start synopsis
4
+ % start main
4
5
 
5
6
  This library packages various codecs used for MoaT protocols
6
7
  in a way that's reason- and stream-able.
7
8
 
8
9
  % end synopsis
9
10
 
11
+ ## Supplied codecs
12
+
13
+ - CBOR
14
+ - msgpack
15
+ - no-op
16
+ - UTF-8
17
+ - JSON
18
+ - YAML
19
+
20
+ % end main
21
+
10
22
  ## Interface
11
23
 
12
24
  ### Codec
@@ -1,3 +1,45 @@
1
+ moat-lib-codec (0.4.14-1) unstable; urgency=medium
2
+
3
+ * New release for 26.0.1
4
+
5
+ -- Matthias Urlichs <matthias@urlichs.de> Wed, 18 Feb 2026 18:28:41 +0100
6
+
7
+ moat-lib-codec (0.4.14-1) unstable; urgency=medium
8
+
9
+ * New release for 26.0.1
10
+
11
+ -- Matthias Urlichs <matthias@urlichs.de> Wed, 18 Feb 2026 18:06:02 +0100
12
+
13
+ moat-lib-codec (0.4.11-2) unstable; urgency=medium
14
+
15
+ * New release for 26.0.0
16
+
17
+ -- Matthias Urlichs <matthias@urlichs.de> Sun, 08 Feb 2026 11:05:29 +0100
18
+
19
+ moat-lib-codec (0.4.11-2) unstable; urgency=medium
20
+
21
+ * New release for 26.0.0
22
+
23
+ -- Matthias Urlichs <matthias@urlichs.de> Sun, 08 Feb 2026 11:00:01 +0100
24
+
25
+ moat-lib-codec (0.4.11-2) unstable; urgency=medium
26
+
27
+ * New release for 26.0.0
28
+
29
+ -- Matthias Urlichs <matthias@urlichs.de> Sun, 08 Feb 2026 10:32:15 +0100
30
+
31
+ moat-lib-codec (0.4.10-7) unstable; urgency=medium
32
+
33
+ * New release for 25.8.0
34
+
35
+ -- Matthias Urlichs <matthias@urlichs.de> Mon, 05 Jan 2026 15:32:38 +0100
36
+
37
+ moat-lib-codec (0.4.10-6) unstable; urgency=medium
38
+
39
+ * New release for 25.7.7
40
+
41
+ -- Matthias Urlichs <matthias@urlichs.de> Mon, 05 Jan 2026 15:06:55 +0100
42
+
1
43
  moat-lib-codec (0.4.10-5) unstable; urgency=medium
2
44
 
3
45
  * New release for 25.7.0
@@ -13,10 +13,10 @@ classifiers = [
13
13
  ]
14
14
  dependencies = [
15
15
  "anyio ~= 4.2",
16
- "moat-lib-rpc ~= 0.4.6",
17
- "moat-lib-micro ~= 0.1.0",
18
- "moat-lib-proxy ~= 0.1.0",
19
- "moat-util ~= 0.61.1",
16
+ "moat-lib-rpc ~= 0.6.1",
17
+ "moat-lib-micro ~= 0.2.2",
18
+ "moat-lib-proxy ~= 0.1.1",
19
+ "moat-util ~= 0.62.6",
20
20
  ]
21
21
  keywords = ["MoaT"]
22
22
  requires-python = ">=3.8"
@@ -25,7 +25,7 @@ maintainers = [{email = "matthias@urlichs.de",name = "Matthias Urlichs"}]
25
25
  description='A simple command/stream multiplexer'
26
26
  readme = "README.rst"
27
27
  authors = [{email = "matthias@urlichs.de",name = "Matthias Urlichs"}]
28
- version = "0.4.10"
28
+ version = "0.4.14"
29
29
 
30
30
  [project.urls]
31
31
  homepage = "https://m-o-a-t.org"
@@ -15,15 +15,19 @@ if TYPE_CHECKING:
15
15
  __all__ = ["Codec", "Extension", "NoCodecError", "get_codec"]
16
16
 
17
17
 
18
- def get_codec(codec: str, **kw) -> Codec:
18
+ def get_codec(codec: str | Codec, **kw) -> Codec:
19
19
  """
20
20
  Loads and initializes the named codec.
21
21
  """
22
22
  from moat.util import import_ # noqa: PLC0415
23
23
 
24
+ if isinstance(codec, Codec):
25
+ return codec
26
+ if codec is None:
27
+ codec = "noop"
24
28
  if "." not in codec:
25
29
  if codec.startswith("std-"):
26
- codec = "moat.util." + codec[4:]
30
+ codec = "moat.lib.codec.moat_" + codec[4:]
27
31
  else:
28
32
  codec = "moat.lib.codec." + codec
29
33
  return import_(codec).Codec(**kw)
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, overload
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from collections.abc import Callable
7
7
  from typing import Any, Self
8
8
 
9
- ByteType = bytes | bytearray | memoryview[bytes]
10
- VarByteType = bytearray | memoryview[bytes]
9
+ ByteType = bytes | bytearray | memoryview
10
+ VarByteType = bytearray | memoryview
11
11
 
12
12
 
13
13
  class NoCodecError(ValueError):
@@ -34,12 +34,12 @@ class Codec:
34
34
  detected.
35
35
 
36
36
  After the header is consumed, the application must repeat calling
37
- `__anext__` until an object is returned. (If the decoder
37
+ ``__anext__`` until an object is returned. (If the decoder
38
38
  raises `StopAsyncIteration`, feed more data to it and repeat.)
39
39
 
40
40
  The decoder must leave all incoming data extending past the object in
41
41
  its buffer. It must not attempt to decode them until the next call to
42
- `__anext__`.
42
+ ``__anext__``.
43
43
  """
44
44
 
45
45
  def __init__(self, ext=None):
@@ -99,6 +99,11 @@ class Codec:
99
99
  raise NotImplementedError
100
100
 
101
101
 
102
+ if TYPE_CHECKING:
103
+ EncoderFunc = Callable[[Codec, Any], ByteType | tuple[int, ByteType]]
104
+ DecoderFunc = Callable[[Codec, ByteType], Any]
105
+
106
+
102
107
  class Extension:
103
108
  def __init__(self):
104
109
  self.enc: dict[type, tuple[int | None, Callable]] = {}
@@ -110,25 +115,39 @@ class Extension:
110
115
  res.dec.update(self.dec)
111
116
  return res
112
117
 
113
- def encoder(self, key: int | None, cls: type, fn=None) -> None | Callable:
114
- def _enc(fn):
118
+ @overload
119
+ def encoder(self, key: int | None, cls: type) -> Callable[[EncoderFunc], EncoderFunc]: ...
120
+
121
+ @overload
122
+ def encoder(self, key: int | None, cls: type, fn: EncoderFunc) -> EncoderFunc: ...
123
+
124
+ def encoder(
125
+ self, key: int | None, cls: type, fn: EncoderFunc | None = None
126
+ ) -> EncoderFunc | Callable[[EncoderFunc], EncoderFunc]:
127
+ def _enc(fn: EncoderFunc) -> EncoderFunc:
115
128
  self.enc[cls] = (key, fn)
116
129
  return fn
117
130
 
118
131
  if fn is None:
119
132
  return _enc
120
- else:
121
- _enc(fn)
133
+ return _enc(fn)
134
+
135
+ @overload
136
+ def decoder(self, key: int | None) -> Callable[[DecoderFunc], DecoderFunc]: ...
137
+
138
+ @overload
139
+ def decoder(self, key: int | None, fn: DecoderFunc) -> DecoderFunc: ...
122
140
 
123
- def decoder(self, key: int, fn=None) -> None | Callable:
124
- def _dec(fn):
125
- self.dec[key] = fn
141
+ def decoder(
142
+ self, key: int | None, fn: DecoderFunc | None = None
143
+ ) -> DecoderFunc | Callable[[DecoderFunc], DecoderFunc]:
144
+ def _dec(fn: DecoderFunc) -> DecoderFunc:
145
+ self.dec[key] = fn # type: ignore[index] # key can be None for catch-all
126
146
  return fn
127
147
 
128
148
  if fn is None:
129
149
  return _dec
130
- else:
131
- _dec(fn)
150
+ return _dec(fn)
132
151
 
133
152
  def encode(self, codec: Codec, obj) -> tuple[int, ByteType]:
134
153
  try:
@@ -13,10 +13,10 @@ from ._base import Codec as _Codec
13
13
  from ._base import NoCodecError
14
14
 
15
15
  # Typing
16
- from typing import cast, TYPE_CHECKING # isort:skip
16
+ from typing import TYPE_CHECKING, cast # isort:skip
17
17
 
18
18
  try:
19
- from micropython import const # pyright:ignore
19
+ from micropython import const # type: ignore[import-not-found] # MicroPython only
20
20
  except ImportError:
21
21
 
22
22
  def const(x: int) -> int:
@@ -29,7 +29,11 @@ if TYPE_CHECKING:
29
29
  from typing import Any
30
30
 
31
31
 
32
- __all__ = ["Codec", "ExtraData", "Tag"]
32
+ __all__ = ["CBOR_TAG_CBOR_FILEHEADER", "CBOR_TAG_CBOR_LEADER", "Codec", "ExtraData", "Tag"]
33
+
34
+
35
+ CBOR_TAG_CBOR_FILEHEADER = const(55799) # single CBOR content
36
+ CBOR_TAG_CBOR_LEADER = const(55800) # header for multiple CBOR items
33
37
 
34
38
 
35
39
  class Tag:
@@ -142,7 +146,7 @@ class Codec(_Codec):
142
146
  if self._buffer:
143
147
  raise RuntimeError("Codec is busy")
144
148
 
145
- self._buffer = data # pyright:ignore # type mismatch
149
+ self._buffer = cast(bytes, data)
146
150
  try:
147
151
  res = self._dec_any()
148
152
  # chop off the part we've read
@@ -157,12 +161,12 @@ class Codec(_Codec):
157
161
  "Add additional input"
158
162
  if self._buffer and self._buf_pos < len(self._buffer):
159
163
  if self._buf_pos == 0:
160
- self._buffer += data
164
+ self._buffer = cast(bytes, self._buffer + data)
161
165
  return
162
- data = self._buffer[self._buf_pos :] + data
163
- elif isinstance(data, memoryview):
166
+ data = cast(bytes, self._buffer[self._buf_pos :] + data)
167
+ if isinstance(data, memoryview):
164
168
  data = bytearray(data)
165
- self._buffer = data
169
+ self._buffer = cast(bytes, data)
166
170
  self._buf_pos = 0
167
171
 
168
172
  def unfeed(self, buf: VarByteType) -> int:
@@ -276,7 +280,8 @@ class Codec(_Codec):
276
280
  self._enc_any(val)
277
281
 
278
282
  def _w(self, d):
279
- self._buf_out.extend(d) # pyright:ignore[reportOptionalMemberAccess]
283
+ assert self._buf_out is not None # set by encode() before calling _enc_any()
284
+ self._buf_out.extend(d)
280
285
 
281
286
  def _enc_any(self, ob):
282
287
  w = self._w
@@ -0,0 +1,136 @@
1
+ """
2
+ An overly-simple CBOR packer/unpacker.
3
+ """
4
+
5
+ # Original Copyright 2014-2015 Brian Olson
6
+ # Apache 2.0 license
7
+ # http://docs.ros.org/en/noetic/api/rosbridge_library/html/cbor_8py_source.html
8
+ # rather heavily modified
9
+
10
+ from __future__ import annotations
11
+
12
+ from moat.util import NotGiven
13
+
14
+ # Typing
15
+ from moat.lib.path import Path
16
+ from moat.lib.proxy import DProxy, Proxy, get_proxy, name2obj, obj2name, unwrap_obj, wrap_obj
17
+
18
+ from . import Extension
19
+ from . import cbor as _cbor
20
+ from .cbor import Tag
21
+
22
+ from typing import TYPE_CHECKING
23
+
24
+ if TYPE_CHECKING:
25
+ from moat.lib.codec import ByteType
26
+
27
+ __all__ = ["Codec", "std_ext"]
28
+
29
+ std_ext = Extension()
30
+
31
+
32
+ class Codec(_cbor.Codec):
33
+ """
34
+ CBOR codec with MoaT's standard extensions.
35
+ """
36
+
37
+ def __init__(self):
38
+ super().__init__(ext=std_ext)
39
+
40
+ def decode(self, data: ByteType): # noqa:D102,RUF100
41
+ if data == b"":
42
+ return NotGiven
43
+ return super().decode(data)
44
+
45
+
46
+ @std_ext.encoder(27, DProxy)
47
+ def _enc_dpr(codec, obj):
48
+ codec # noqa:B018
49
+ res = [obj.name] + obj.a
50
+ if obj.k or (res and isinstance(res[-1], dict)):
51
+ res.append(obj.k)
52
+ return res
53
+
54
+
55
+ @std_ext.encoder(32769, Proxy)
56
+ def _enc_pr(codec, obj):
57
+ codec # noqa:B018
58
+ return obj.name
59
+
60
+
61
+ @std_ext.decoder(32769)
62
+ def _dec_proxy(codec, val):
63
+ codec # noqa:B018
64
+ try:
65
+ if isinstance(val, (str, int)):
66
+ return name2obj(val)
67
+ return unwrap_obj(val)
68
+ except KeyError:
69
+ return Proxy(val)
70
+
71
+
72
+ @std_ext.decoder(27)
73
+ def _dec_obj(codec, val):
74
+ codec # noqa:B018
75
+ if isinstance(val[0], Tag):
76
+ if val[0].tag != 32769:
77
+ return Tag(27, val) # not decodable
78
+ val[0] = val[0].value
79
+ return unwrap_obj(val)
80
+
81
+
82
+ @std_ext.encoder(39, Path)
83
+ def _enc_path(codec, val):
84
+ codec # noqa:B018
85
+ return val.raw_rooted
86
+
87
+
88
+ @std_ext.decoder(39)
89
+ def _dec_path(codec, val):
90
+ codec # noqa:B018
91
+ if not isinstance(val, (list, tuple)):
92
+ return Tag(39, val) # not decodable
93
+ return Path.build(val, decoded=True)
94
+
95
+
96
+ @std_ext.encoder(258, set)
97
+ def _pack_set(codec, data):
98
+ codec # noqa:B018
99
+ return list(data)
100
+
101
+
102
+ @std_ext.decoder(258)
103
+ def _dec_set(codec, val):
104
+ codec # noqa:B018
105
+ return set(val)
106
+
107
+
108
+ @std_ext.encoder(None, object)
109
+ def enc_any(codec, obj):
110
+ codec # noqa:B018
111
+
112
+ try:
113
+ name = obj2name(obj)
114
+ except KeyError:
115
+ pass
116
+ else:
117
+ return 32769, name
118
+
119
+ try:
120
+ name = obj2name(type(obj))
121
+ except KeyError:
122
+ pass
123
+ else:
124
+ p = wrap_obj(obj, name=name)
125
+ return 27, p
126
+
127
+ if isinstance(obj, Exception):
128
+ # RemoteError, cf. moat.lib.codec.errors
129
+ res = ["_rErr", obj.__class__.__name__]
130
+ try:
131
+ res.extend(obj.args)
132
+ except AttributeError:
133
+ pass
134
+ return 27, res
135
+
136
+ return 32769, get_proxy(obj)
@@ -0,0 +1,132 @@
1
+ """
2
+ This module contains extension handlers for msgpack.
3
+
4
+ Extension types defined here:
5
+ 2: contains raw bytes, interpreted as unsigned bignum
6
+ 3: Path, as a msgpack object stream of its elements
7
+ 4: contains raw bytes, interpreted as UTF-8, returned as (named) Proxy object
8
+ 5: object constructor
9
+ 6: marked Path
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from moat.lib.path import Path
15
+ from moat.lib.proxy import DProxy, Proxy, _CProxy, get_proxy, obj2name, unwrap_obj, wrap_obj
16
+
17
+ from . import Extension
18
+ from . import msgpack as _msgpack
19
+
20
+ __all__ = ["Codec", "std_ext"]
21
+
22
+
23
+ std_ext = Extension()
24
+ ExtType = _msgpack.ExtType
25
+
26
+
27
+ class Codec(_msgpack.Codec):
28
+ "A MsgPack codec with our extensions"
29
+
30
+ def __init__(self):
31
+ super().__init__(ext=std_ext, use_attrdict=True)
32
+
33
+
34
+ @std_ext.encoder(5, DProxy)
35
+ def _enc_dproxy(codec, obj):
36
+ a = obj.a[:]
37
+ if obj.k or (a and isinstance(a[-1], dict)):
38
+ a.append(obj.k)
39
+ return codec.encode(obj.name) + b"".join(codec.encode(x) for x in a)
40
+
41
+
42
+ # not actually used
43
+ @std_ext.encoder(None, ExtType)
44
+ def _enc_exttype(codec, obj):
45
+ codec # noqa:B018
46
+ return obj.type, obj.data
47
+
48
+
49
+ @std_ext.encoder(None, Path)
50
+ def _enc_path(codec, obj):
51
+ if obj.mark:
52
+ return 6, codec.encode(obj.mark) + b"".join(codec.encode(x) for x in obj.raw)
53
+ return 3, b"".join(codec.encode(x) for x in obj.raw)
54
+
55
+
56
+ @std_ext.encoder(4, Proxy)
57
+ def _enc_proxy(codec, obj):
58
+ codec # noqa:B018
59
+ return obj.name.encode("utf-8")
60
+
61
+
62
+ @std_ext.encoder(None, object)
63
+ def _enc_any(codec, obj):
64
+ try:
65
+ name = obj2name(obj)
66
+ except KeyError:
67
+ pass
68
+ else:
69
+ return 4, name.encode("utf-8")
70
+
71
+ try:
72
+ name = obj2name(type(obj))
73
+ except KeyError:
74
+ pass
75
+ else:
76
+ p = wrap_obj(obj, name=name)
77
+ return 5, b"".join(codec.encode(x) for x in p)
78
+
79
+ if isinstance(obj, Exception):
80
+ # RemoteError, cf. moat.lib.codec.errors
81
+ return 5, codec.encode("_rErr") + codec.encode(obj.__class__.__name__) + b"".join(
82
+ codec.encode(x) for x in obj.args
83
+ )
84
+
85
+ return 4, get_proxy(obj).encode("utf-8")
86
+
87
+
88
+ @std_ext.decoder(3)
89
+ def _dec_path(codec, data):
90
+ codec # noqa:B018
91
+ s = Codec()
92
+ s.feed(data)
93
+ return Path.build(iter(s), decoded=True)
94
+
95
+
96
+ @std_ext.decoder(4)
97
+ def _dec_proxy(codec, data):
98
+ codec # noqa:B018
99
+ if isinstance(data, memoryview):
100
+ data = bytearray(data)
101
+ try:
102
+ n = data.decode("utf-8")
103
+ except UnicodeError:
104
+ n = str(data)
105
+ try:
106
+ return _CProxy[n]
107
+ except KeyError:
108
+ return Proxy(n)
109
+
110
+
111
+ @std_ext.decoder(5)
112
+ def _dec_proxy_obj(codec, data):
113
+ codec = codec.copy()
114
+ codec.feed(data)
115
+ s = list(iter(codec))
116
+ return unwrap_obj(s)
117
+
118
+
119
+ @std_ext.decoder(6)
120
+ def _dec_marked_path(codec, data):
121
+ # A marked path. Deprecated.
122
+ codec = codec.copy()
123
+ codec.feed(data)
124
+ s = iter(codec)
125
+ mark = next(s)
126
+ p = Path(*s, mark=mark)
127
+ return p
128
+
129
+
130
+ @std_ext.decoder(None)
131
+ def _dec_blank(codec, data):
132
+ return ExtType(codec, data)
@@ -6,16 +6,14 @@ from __future__ import annotations
6
6
 
7
7
  import struct
8
8
 
9
- import ruyaml as yaml
9
+ try:
10
+ from ruyaml import representer
11
+ except ImportError:
12
+ from ruamel.yaml import representer # fallback if ruyaml unavailable
10
13
 
11
- from moat.lib.micro import const
14
+ from ._cbor import CBOR_TAG_CBOR_FILEHEADER, CBOR_TAG_CBOR_LEADER, Codec, Tag # noqa:F401
12
15
 
13
- from ._cbor import Codec, Tag # noqa:F401
14
-
15
- SafeRepresenter = yaml.representer.SafeRepresenter # pyright:ignore
16
-
17
- CBOR_TAG_CBOR_FILEHEADER = const(55799) # single CBOR content
18
- CBOR_TAG_CBOR_LEADER = const(55800) # header for multiple CBOR items
16
+ SafeRepresenter = representer.SafeRepresenter
19
17
 
20
18
 
21
19
  def _tag_repr(dumper, data):
@@ -4,8 +4,8 @@ FUSE operations for MoaT-micro-FS
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ from moat.util import ExpAttrError, ExpKeyError
7
8
  from moat.lib.proxy import as_proxy
8
- from moat.util.exc import ExpAttrError, ExpKeyError
9
9
 
10
10
  try:
11
11
  from moat.lib.rpc import RemoteError
@@ -16,7 +16,7 @@ except ImportError:
16
16
 
17
17
 
18
18
  @as_proxy("_rErrS")
19
- class SilentRemoteError(RemoteError):
19
+ class SilentRemoteError(RemoteError): # type: ignore[misc] # RemoteError may be from two different sources
20
20
  """
21
21
  Forwarded error from a remote system.
22
22
 
@@ -29,7 +29,7 @@ class NoPathError(KeyError):
29
29
  """An error that marks a nonexisting path"""
30
30
 
31
31
  def __str__(self):
32
- from moat.util import Path # noqa: PLC0415
32
+ from moat.lib.path import Path # noqa: PLC0415
33
33
 
34
34
  return (
35
35
  f"‹NoPath {self.args[0]} {Path.build(self.args[1])}"
@@ -42,7 +42,7 @@ class NoPathError(KeyError):
42
42
 
43
43
 
44
44
  try:
45
- FileNotFoundError # noqa:B018 # pyright:ignore
45
+ FileNotFoundError = FileNotFoundError # noqa:A001,PLW0127
46
46
  except NameError:
47
47
 
48
48
  class FileNotFoundError(SilentRemoteError): # noqa:A001
@@ -55,7 +55,7 @@ except NameError:
55
55
  as_proxy("_FnErr", FileNotFoundError)
56
56
 
57
57
  try:
58
- FileExistsError # noqa:B018 # pyright:ignore
58
+ FileExistsError = FileExistsError # noqa:A001,PLW0127
59
59
  except NameError:
60
60
 
61
61
  class FileExistsError(SilentRemoteError): # noqa:A001
@@ -69,6 +69,7 @@ as_proxy("_FxErr", FileExistsError)
69
69
 
70
70
  as_proxy("_KyErr", KeyError)
71
71
  as_proxy("_EKyErr", ExpKeyError)
72
+ as_proxy("_IdxErr", IndexError)
72
73
  as_proxy("_AtErr", AttributeError)
73
74
  as_proxy("_EAtErr", ExpAttrError)
74
75
  as_proxy("_NiErr", NotImplementedError)
@@ -9,7 +9,7 @@ from ._base import Codec as _Codec
9
9
  try:
10
10
  from json import dumps, loads
11
11
  except ImportError:
12
- from simplejson import dumps, loads
12
+ from simplejson import dumps, loads # type: ignore[import-not-found] # optional fallback
13
13
 
14
14
 
15
15
  class Codec(_Codec):
@@ -0,0 +1,257 @@
1
+ """
2
+ An overly-simple CBOR packer/unpacker.
3
+ """
4
+
5
+ # Original Copyright 2014-2015 Brian Olson
6
+ # Apache 2.0 license
7
+ # http://docs.ros.org/en/noetic/api/rosbridge_library/html/cbor_8py_source.html
8
+ # rather heavily modified
9
+
10
+ from __future__ import annotations
11
+
12
+ import contextlib
13
+ import datetime as dt
14
+ import re
15
+ from ipaddress import (
16
+ IPv4Address,
17
+ IPv4Interface,
18
+ IPv4Network,
19
+ IPv6Address,
20
+ IPv6Interface,
21
+ IPv6Network,
22
+ )
23
+
24
+ from ._moat_cbor import Codec, std_ext
25
+
26
+ # Typing
27
+ from .cbor import CBOR_TAG_CBOR_FILEHEADER, CBOR_TAG_CBOR_LEADER, Tag
28
+
29
+ __all__ = [
30
+ "CBOR_TAG_MOAT_CHANGE",
31
+ "CBOR_TAG_MOAT_FILE_END",
32
+ "CBOR_TAG_MOAT_FILE_ID",
33
+ "Codec",
34
+ "gen_start",
35
+ "gen_stop",
36
+ "std_ext",
37
+ ]
38
+
39
+ CBOR_TAG_MOAT_FILE_ID = 1299145044 # 'MoaT'
40
+ CBOR_TAG_MOAT_FILE_END = 1298493254 # 'MeoF'
41
+ CBOR_TAG_MOAT_CHANGE = 1298360423 # 'Mchg'
42
+
43
+
44
+ def gen_start(text: str, /, **kw) -> Tag:
45
+ """
46
+ Generate a MoaT file start tag
47
+ """
48
+ if len(text) > 255:
49
+ raise ValueError("Description too long")
50
+ # add padding if too short
51
+ text += " " * (24 - len(text))
52
+
53
+ return Tag(CBOR_TAG_CBOR_LEADER, Tag(CBOR_TAG_MOAT_FILE_ID, (text, kw)))
54
+
55
+
56
+ def gen_stop(**kw) -> Tag:
57
+ """
58
+ Generate a MoaT file stop tag
59
+ """
60
+ return Tag(CBOR_TAG_MOAT_FILE_END, kw)
61
+
62
+
63
+ def gen_change(**kw) -> Tag:
64
+ """
65
+ Generate a MoaT file stop tag
66
+ """
67
+ return Tag(CBOR_TAG_MOAT_CHANGE, kw)
68
+
69
+
70
+ @std_ext.encoder(1, dt.datetime)
71
+ def _enc_datetime_ts(codec, value):
72
+ codec # noqa:B018
73
+ if not value.tzinfo:
74
+ raise ValueError(f"naive datetime {value!r}")
75
+
76
+ return value.timestamp()
77
+
78
+
79
+ # @std_ext.encoder(0, dt.datetime)
80
+ def _enc_datetime_str(codec, value):
81
+ codec # noqa:B018
82
+
83
+ if not value.tzinfo:
84
+ raise ValueError(f"naive datetime {value!r}")
85
+
86
+ return value.isoformat().replace("+00:00", "Z")
87
+
88
+
89
+ _timestamp_re = re.compile(
90
+ r"^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:\.(\d{1,6})\d*)?(?:Z|([+-])(\d\d):(\d\d))$",
91
+ )
92
+
93
+
94
+ @std_ext.decoder(0)
95
+ def _dec_datetime_string(codec, value) -> dt.datetime:
96
+ codec # noqa:B018
97
+
98
+ match = _timestamp_re.match(value)
99
+ if match:
100
+ (
101
+ year,
102
+ month,
103
+ day,
104
+ hour,
105
+ minute,
106
+ second,
107
+ secfrac,
108
+ offset_sign,
109
+ offset_h,
110
+ offset_m,
111
+ ) = match.groups()
112
+ microsecond = 0 if secfrac is None else int(f"{secfrac:<06}")
113
+
114
+ if offset_h:
115
+ sign = -1 if offset_sign == "-" else 1
116
+ hours = int(offset_h) * sign
117
+ minutes = int(offset_m) * sign
118
+ tz = dt.timezone(dt.timedelta(hours=hours, minutes=minutes))
119
+ else:
120
+ tz = dt.UTC
121
+
122
+ return dt.datetime(
123
+ int(year),
124
+ int(month),
125
+ int(day),
126
+ int(hour),
127
+ int(minute),
128
+ int(second),
129
+ microsecond,
130
+ tz,
131
+ )
132
+ else:
133
+ raise ValueError(f"invalid datetime string: {value!r}")
134
+
135
+
136
+ @std_ext.decoder(1)
137
+ def _dec_ts(codec, val):
138
+ codec # noqa:B018
139
+ return dt.datetime.fromtimestamp(val, dt.UTC)
140
+
141
+
142
+ @std_ext.decoder(2)
143
+ def _dec_bigp(codec, val):
144
+ codec # noqa:B018
145
+ return int.from_bytes(val, "big")
146
+
147
+
148
+ @std_ext.decoder(3)
149
+ def _dec_bign(codec, val):
150
+ codec # noqa:B018
151
+ return -1 - int.from_bytes(val, "big")
152
+
153
+
154
+ def _pad(buf, n):
155
+ buf = bytes(buf) # SIGH
156
+ if (lb := len(buf)) == n:
157
+ return buf
158
+ return buf + b"\x00" * (n - lb)
159
+
160
+
161
+ @std_ext.decoder(52)
162
+ def _dec_ip4address(codec, buf) -> IPv4Address | IPv4Network | IPv4Interface:
163
+ codec # noqa:B018
164
+ from ipaddress import IPv4Address, IPv4Interface, IPv4Network # noqa: PLC0415
165
+
166
+ if isinstance(buf, (bytes, bytearray, memoryview)):
167
+ return IPv4Address(_pad(buf, 4))
168
+ elif len(buf) != 2:
169
+ pass
170
+ elif isinstance(buf[1], int):
171
+ return IPv4Interface((_pad(buf[0], 4), buf[1]))
172
+ else:
173
+ return IPv4Network((_pad(buf[1], 4), buf[0]))
174
+
175
+ raise ValueError(f"invalid ipaddress value {buf!r}")
176
+
177
+
178
+ @std_ext.decoder(54)
179
+ def _dec_ip6address(codec, buf) -> IPv6Address | IPv6Network | IPv6Interface:
180
+ codec # noqa:B018
181
+ from ipaddress import IPv6Address, IPv6Interface, IPv6Network # noqa: PLC0415
182
+
183
+ if isinstance(buf, (bytes, bytearray, memoryview)):
184
+ return IPv6Address(_pad(buf, 16))
185
+ elif len(buf) != 2:
186
+ pass
187
+ elif isinstance(buf[1], int):
188
+ return IPv6Interface((_pad(buf[0], 16), buf[1]))
189
+ else:
190
+ return IPv6Network((_pad(buf[1], 16), buf[0]))
191
+
192
+ raise ValueError(f"invalid ipaddress value {buf!r}")
193
+
194
+
195
+ @std_ext.encoder(52, IPv4Address)
196
+ @std_ext.encoder(54, IPv6Address)
197
+ def _pack_ip(codec, adr):
198
+ codec # noqa:B018
199
+ return adr.packed.rstrip(b"\x00")
200
+
201
+
202
+ @std_ext.encoder(52, IPv4Network)
203
+ @std_ext.encoder(54, IPv6Network)
204
+ def _pack_ipnet(codec, adr):
205
+ codec # noqa:B018
206
+ return (adr.prefixlen, _pack_ip(codec, adr.network_address))
207
+
208
+
209
+ @std_ext.encoder(52, IPv4Interface)
210
+ @std_ext.encoder(54, IPv6Interface)
211
+ def _pack_ipintf(codec, adr):
212
+ codec # noqa:B018
213
+ return (_pack_ip(codec, adr), adr.network.prefixlen)
214
+
215
+
216
+ @std_ext.decoder(260)
217
+ def _dec_old_ipaddress(codec, buf) -> IPv4Address | IPv6Address | Tag:
218
+ codec # noqa:B018
219
+ if isinstance(buf, (bytes, bytearray, memoryview)) or len(buf) not in (4, 6, 16):
220
+ if len(buf) == 4:
221
+ return IPv4Address(bytes(buf))
222
+ elif len(buf) == 16:
223
+ return IPv6Address(bytes(buf))
224
+ elif len(buf) == 6:
225
+ # MAC address
226
+ return Tag(260, buf)
227
+
228
+ raise ValueError(f"invalid ipaddress value {buf!r}")
229
+
230
+
231
+ @std_ext.decoder(261)
232
+ def _dec_old_ipnetwork(codec, buf) -> IPv4Network | IPv6Network:
233
+ codec # noqa:B018
234
+ if isinstance(buf, dict) and len(buf) == 1:
235
+ mask, buf = next(iter(buf.items()))
236
+ if len(buf) == 4:
237
+ return IPv4Network((bytes(buf), mask))
238
+ elif len(buf) == 16:
239
+ return IPv6Network((bytes(buf), mask))
240
+
241
+ raise ValueError(f"invalid ipnetwork value {buf!r}")
242
+
243
+
244
+ @std_ext.decoder(CBOR_TAG_CBOR_FILEHEADER)
245
+ def _dec_file_cbor(codec, val):
246
+ codec # noqa:B018
247
+ with contextlib.suppress(AttributeError):
248
+ val._cbor_tag = CBOR_TAG_CBOR_FILEHEADER # noqa: SLF001
249
+ return val
250
+
251
+
252
+ @std_ext.decoder(CBOR_TAG_CBOR_LEADER)
253
+ def _dec_file_cbor(codec, val):
254
+ codec # noqa:B018
255
+ with contextlib.suppress(AttributeError):
256
+ val._cbor_tag = CBOR_TAG_CBOR_LEADER # noqa: SLF001
257
+ return val
@@ -4,4 +4,4 @@ Alias for moat-kv legacy
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from moat.util.msgpack import Codec # noqa:F401
7
+ from moat.lib.codec.moat_msgpack import Codec # noqa:F401
@@ -0,0 +1,29 @@
1
+ """
2
+ This module contains helper functions for packing+unpacking of single messages,
3
+ plus an unpacker factory for streams.
4
+
5
+ Extension types defined here:
6
+ 2: contains raw bytes, interpreted as unsigned bignum
7
+ 3: Path, as a msgpack object stream of its elements
8
+ 4: contains raw bytes, interpreted as UTF-8, returned as (named) Proxy object
9
+ 5: object constructor
10
+ 6: marked Path
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from ._moat_msgpack import Codec, std_ext
16
+
17
+ __all__ = ["Codec", "std_ext"]
18
+
19
+
20
+ @std_ext.encoder(2, int)
21
+ def _enc_int(codec, n):
22
+ codec # noqa:B018
23
+ return n.to_bytes((n.bit_length() + 7) // 8, "big")
24
+
25
+
26
+ @std_ext.decoder(2)
27
+ def _dec_bignum(codec, data):
28
+ codec # noqa:B018
29
+ return int.from_bytes(data, "big")
@@ -137,7 +137,7 @@ _TYPE_EXT = const(5)
137
137
  DEFAULT_RECURSE_LIMIT = 20
138
138
 
139
139
 
140
- _MSGPACK_HEADERS = {
140
+ _MSGPACK_HEADERS: dict[int, tuple[int, str] | tuple[int, str, int]] = {
141
141
  0xC4: (1, "", _TYPE_BIN),
142
142
  0xC5: (2, ">H", _TYPE_BIN),
143
143
  0xC6: (4, ">I", _TYPE_BIN),
@@ -273,7 +273,7 @@ class Unpacker:
273
273
  elif b == 0xC3:
274
274
  obj = True
275
275
  elif b <= 0xC6:
276
- size, fmt, typ = _MSGPACK_HEADERS[b]
276
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
277
277
  self._reserve(size)
278
278
  if len(fmt) > 0:
279
279
  n = struct.unpack_from(fmt, self._buffer, self._buf_pos)[0]
@@ -282,13 +282,13 @@ class Unpacker:
282
282
  self._buf_pos += size
283
283
  obj = self._read(n)
284
284
  elif b <= 0xC9:
285
- size, fmt, typ = _MSGPACK_HEADERS[b]
285
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
286
286
  self._reserve(size)
287
287
  L, n = struct.unpack_from(fmt, self._buffer, self._buf_pos)
288
288
  self._buf_pos += size
289
289
  obj = self._read(L)
290
290
  elif b <= 0xD3:
291
- size, fmt = _MSGPACK_HEADERS[b]
291
+ size, fmt = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 2-tuple for these keys
292
292
  self._reserve(size)
293
293
  if len(fmt) > 0:
294
294
  obj = struct.unpack_from(fmt, self._buffer, self._buf_pos)[0]
@@ -296,12 +296,12 @@ class Unpacker:
296
296
  obj = self._buffer[self._buf_pos]
297
297
  self._buf_pos += size
298
298
  elif b <= 0xD8:
299
- size, fmt, typ = _MSGPACK_HEADERS[b]
299
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
300
300
  self._reserve(size + 1)
301
301
  n, obj = struct.unpack_from(fmt, self._buffer, self._buf_pos)
302
302
  self._buf_pos += size + 1
303
303
  elif b <= 0xDB:
304
- size, fmt, typ = _MSGPACK_HEADERS[b]
304
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
305
305
  self._reserve(size)
306
306
  if len(fmt) > 0:
307
307
  (n,) = struct.unpack_from(fmt, self._buffer, self._buf_pos)
@@ -310,12 +310,12 @@ class Unpacker:
310
310
  self._buf_pos += size
311
311
  obj = self._read(n)
312
312
  elif b <= 0xDD:
313
- size, fmt, typ = _MSGPACK_HEADERS[b]
313
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
314
314
  self._reserve(size)
315
315
  (n,) = struct.unpack_from(fmt, self._buffer, self._buf_pos)
316
316
  self._buf_pos += size
317
317
  else: # if b <= 0xDF: # can't be anything else
318
- size, fmt, typ = _MSGPACK_HEADERS[b]
318
+ size, fmt, typ = _MSGPACK_HEADERS[b] # type: ignore[misc] # always 3-tuple for these keys
319
319
  self._reserve(size)
320
320
  (n,) = struct.unpack_from(fmt, self._buffer, self._buf_pos)
321
321
  self._buf_pos += size
@@ -6,6 +6,11 @@ from moat.util import yload, yprint
6
6
 
7
7
  from ._base import Codec as _Codec
8
8
 
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from ._base import ByteType
13
+
9
14
 
10
15
  class Codec(_Codec):
11
16
  "basic JSON codec"
@@ -24,8 +29,8 @@ class Codec(_Codec):
24
29
  "basic decoder"
25
30
  return yload(data.decode("utf-8"))
26
31
 
27
- def feed(self, buffer): # noqa: D102
28
- self._buf.extend(buffer)
32
+ def feed(self, data: ByteType) -> None: # noqa: D102
33
+ self._buf.extend(data)
29
34
 
30
35
  def __next__(self):
31
36
  i = self._buf.find(b"\n---\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-lib-codec
3
- Version: 0.4.10
3
+ Version: 0.4.14
4
4
  Summary: A simple command/stream multiplexer
5
5
  Author-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
@@ -17,8 +17,8 @@ Requires-Python: >=3.8
17
17
  Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE.txt
19
19
  Requires-Dist: anyio~=4.2
20
- Requires-Dist: moat-lib-rpc~=0.4.6
21
- Requires-Dist: moat-lib-micro~=0.1.0
22
- Requires-Dist: moat-lib-proxy~=0.1.0
23
- Requires-Dist: moat-util~=0.61.1
20
+ Requires-Dist: moat-lib-rpc~=0.6.1
21
+ Requires-Dist: moat-lib-micro~=0.2.2
22
+ Requires-Dist: moat-lib-proxy~=0.1.1
23
+ Requires-Dist: moat-util~=0.62.6
24
24
  Dynamic: license-file
@@ -10,12 +10,16 @@ debian/rules
10
10
  src/moat/lib/codec/__init__.py
11
11
  src/moat/lib/codec/_base.py
12
12
  src/moat/lib/codec/_cbor.py
13
+ src/moat/lib/codec/_moat_cbor.py
14
+ src/moat/lib/codec/_moat_msgpack.py
13
15
  src/moat/lib/codec/binary.py
14
16
  src/moat/lib/codec/bool.py
15
17
  src/moat/lib/codec/cbor.py
16
18
  src/moat/lib/codec/errors.py
17
19
  src/moat/lib/codec/json.py
20
+ src/moat/lib/codec/moat_cbor.py
18
21
  src/moat/lib/codec/moat_kv.py
22
+ src/moat/lib/codec/moat_msgpack.py
19
23
  src/moat/lib/codec/msgpack.py
20
24
  src/moat/lib/codec/noop.py
21
25
  src/moat/lib/codec/utf8.py
@@ -0,0 +1,5 @@
1
+ anyio~=4.2
2
+ moat-lib-micro~=0.2.2
3
+ moat-lib-proxy~=0.1.1
4
+ moat-lib-rpc~=0.6.1
5
+ moat-util~=0.62.6
@@ -1,5 +0,0 @@
1
- anyio~=4.2
2
- moat-lib-micro~=0.1.0
3
- moat-lib-proxy~=0.1.0
4
- moat-lib-rpc~=0.4.6
5
- moat-util~=0.61.1