pybare 1.2.1__tar.gz → 1.2.3__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 (27) hide show
  1. {pybare-1.2.1 → pybare-1.2.3}/PKG-INFO +19 -11
  2. {pybare-1.2.1 → pybare-1.2.3}/README.md +16 -9
  3. {pybare-1.2.1 → pybare-1.2.3}/bare/__init__.py +2 -1
  4. {pybare-1.2.1 → pybare-1.2.3}/bare/barearray.py +10 -5
  5. {pybare-1.2.1 → pybare-1.2.3}/bare/barestruct.py +25 -2
  6. {pybare-1.2.1 → pybare-1.2.3}/bare/baretype.py +3 -2
  7. {pybare-1.2.1 → pybare-1.2.3}/bare/data.py +3 -2
  8. {pybare-1.2.1 → pybare-1.2.3}/bare/map.py +4 -2
  9. {pybare-1.2.1 → pybare-1.2.3}/bare/misc.py +8 -6
  10. {pybare-1.2.1 → pybare-1.2.3}/bare/number.py +16 -14
  11. {pybare-1.2.1 → pybare-1.2.3}/bare/test_barestruct.py +8 -1
  12. {pybare-1.2.1 → pybare-1.2.3}/bare/union.py +2 -2
  13. {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/PKG-INFO +19 -11
  14. {pybare-1.2.1 → pybare-1.2.3}/setup.py +3 -2
  15. {pybare-1.2.1 → pybare-1.2.3}/LICENSE +0 -0
  16. {pybare-1.2.1 → pybare-1.2.3}/bare/test_barearray.py +0 -0
  17. {pybare-1.2.1 → pybare-1.2.3}/bare/test_data.py +0 -0
  18. {pybare-1.2.1 → pybare-1.2.3}/bare/test_encoder.py +0 -0
  19. {pybare-1.2.1 → pybare-1.2.3}/bare/test_map.py +0 -0
  20. {pybare-1.2.1 → pybare-1.2.3}/bare/test_misc.py +0 -0
  21. {pybare-1.2.1 → pybare-1.2.3}/bare/test_number.py +0 -0
  22. {pybare-1.2.1 → pybare-1.2.3}/bare/test_union.py +0 -0
  23. {pybare-1.2.1 → pybare-1.2.3}/bare/util.py +0 -0
  24. {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/SOURCES.txt +0 -0
  25. {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/dependency_links.txt +0 -0
  26. {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/top_level.txt +0 -0
  27. {pybare-1.2.1 → pybare-1.2.3}/setup.cfg +0 -0
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pybare
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: A declarative implementation of BARE for Python
5
5
  Home-page: https://sr.ht/~chiefnoah/pybare/
6
6
  Author: Noah Pederson
7
7
  Author-email: noah@packetlost.dev
8
+ Classifier: Programming Language :: Python :: 3.10
8
9
  Classifier: Programming Language :: Python :: 3.11
9
10
  Classifier: Programming Language :: Python :: 3.12
10
11
  Classifier: License :: OSI Approved :: MIT License
11
12
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.11
13
+ Requires-Python: >=3.10
13
14
  Description-Content-Type: text/markdown
14
15
  License-File: LICENSE
15
16
 
@@ -17,7 +18,7 @@ License-File: LICENSE
17
18
  [![builds.sr.ht status](https://builds.sr.ht/~chiefnoah/pybare.svg)](https://builds.sr.ht/~chiefnoah/pybare?)
18
19
 
19
20
  A declarative implementation of the [BARE](https://baremessages.org/) message
20
- format for Python 3.11+
21
+ format for Python 3.10+
21
22
 
22
23
  ---
23
24
 
@@ -39,36 +40,43 @@ pip install pybare
39
40
  pybare fully implements all BARE types for both encoding and decoding. This
40
41
  includes reading multiple messages from the same `BinaryIO` stream.
41
42
 
42
- ### TODO
43
+ ## Gotchas
43
44
 
44
- - [ ] Codegen based on `.schema` files
45
- - [ ] Better documentation
46
- - [ ] More tests
47
- - [ ] Fast C implementation for encoding
45
+ We use "Array" / "array" instead of "List" / "list" to avoid conflicts with Python's
46
+ common builtin type of the same name.
48
47
 
49
48
  ## Examples
50
49
 
50
+ An example that defines the types used as an example in the RFC may be found in
51
+ [`example.py`](./example.py)
52
+
51
53
  pybare currently requires you define your structures by hand. Examples can be
52
54
  found in the
53
55
  [tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
54
56
 
55
57
  ### Quickstart
56
58
 
59
+ The general convention is type identifiers start with a *capital* letter and anonymous
60
+ generating functions begin with a lowercase. This follows general "pythonic" style for
61
+ classes vs functions.
62
+
57
63
  ```python
58
- from bare import Struct, map, Str, UInt, optional, data, array, Void
64
+ from bare import Struct, map, Str, UInt, optional, data, array, Void, struct, U8
59
65
 
60
66
  # Alternatively, class Data(size=64): ...
61
67
  PubKey = data(64) # 512 bits
62
68
 
63
69
  class User(Struct):
64
- username = Filed(Str)
70
+ username = Field(Str)
65
71
  userid = Field(Int)
66
72
  email = Field(optional(Str))
67
73
  keys = Field(map(Str, PubKey))
68
74
  repos = Field(array(Str)) # variable length array
75
+ # anonymous array and struct
76
+ friends = Field(array(struct(name=Str, age=U8)))
69
77
 
70
78
 
71
- noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
79
+ noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[], friends=[])
72
80
  noah.username == 'chiefnoah'
73
81
  noah.username = 'someoneelse'
74
82
  noah.username == 'someoneelse'
@@ -2,7 +2,7 @@
2
2
  [![builds.sr.ht status](https://builds.sr.ht/~chiefnoah/pybare.svg)](https://builds.sr.ht/~chiefnoah/pybare?)
3
3
 
4
4
  A declarative implementation of the [BARE](https://baremessages.org/) message
5
- format for Python 3.11+
5
+ format for Python 3.10+
6
6
 
7
7
  ---
8
8
 
@@ -24,36 +24,43 @@ pip install pybare
24
24
  pybare fully implements all BARE types for both encoding and decoding. This
25
25
  includes reading multiple messages from the same `BinaryIO` stream.
26
26
 
27
- ### TODO
27
+ ## Gotchas
28
28
 
29
- - [ ] Codegen based on `.schema` files
30
- - [ ] Better documentation
31
- - [ ] More tests
32
- - [ ] Fast C implementation for encoding
29
+ We use "Array" / "array" instead of "List" / "list" to avoid conflicts with Python's
30
+ common builtin type of the same name.
33
31
 
34
32
  ## Examples
35
33
 
34
+ An example that defines the types used as an example in the RFC may be found in
35
+ [`example.py`](./example.py)
36
+
36
37
  pybare currently requires you define your structures by hand. Examples can be
37
38
  found in the
38
39
  [tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
39
40
 
40
41
  ### Quickstart
41
42
 
43
+ The general convention is type identifiers start with a *capital* letter and anonymous
44
+ generating functions begin with a lowercase. This follows general "pythonic" style for
45
+ classes vs functions.
46
+
42
47
  ```python
43
- from bare import Struct, map, Str, UInt, optional, data, array, Void
48
+ from bare import Struct, map, Str, UInt, optional, data, array, Void, struct, U8
44
49
 
45
50
  # Alternatively, class Data(size=64): ...
46
51
  PubKey = data(64) # 512 bits
47
52
 
48
53
  class User(Struct):
49
- username = Filed(Str)
54
+ username = Field(Str)
50
55
  userid = Field(Int)
51
56
  email = Field(optional(Str))
52
57
  keys = Field(map(Str, PubKey))
53
58
  repos = Field(array(Str)) # variable length array
59
+ # anonymous array and struct
60
+ friends = Field(array(struct(name=Str, age=U8)))
54
61
 
55
62
 
56
- noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
63
+ noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[], friends=[])
57
64
  noah.username == 'chiefnoah'
58
65
  noah.username = 'someoneelse'
59
66
  noah.username == 'someoneelse'
@@ -7,7 +7,7 @@ BARE RFC.
7
7
  """
8
8
 
9
9
  from .barearray import Array, array
10
- from .barestruct import Field, Struct
10
+ from .barestruct import Field, Struct, struct
11
11
  from .baretype import BAREType
12
12
  from .data import Data, data
13
13
  from .map import Map, map
@@ -43,6 +43,7 @@ __all__ = [
43
43
  "array",
44
44
  "Field",
45
45
  "Struct",
46
+ "struct",
46
47
  "Map",
47
48
  "map",
48
49
  ]
@@ -1,7 +1,8 @@
1
+ from __future__ import annotations
1
2
  import io
2
- from typing import (Any, BinaryIO, ClassVar, Generic, Iterable, Optional,
3
- Self, Type, TypeVar, _ProtocolMeta)
4
3
  from collections import UserList
4
+ from typing import (Any, BinaryIO, ClassVar, Generic, Iterable, Optional, Type,
5
+ TypeVar, _ProtocolMeta)
5
6
 
6
7
  from .baretype import BAREType
7
8
  from .number import UInt
@@ -60,7 +61,9 @@ class ArrayValidator(Generic[T, A]):
60
61
  else:
61
62
  _value.append(v) # type: ignore
62
63
  if self.size and len(_value) != self.size:
63
- raise ValueError(f"Array size mismatch. Expected {self.size}, got {len(_value)}")
64
+ raise ValueError(
65
+ f"Array size mismatch. Expected {self.size}, got {len(_value)}"
66
+ )
64
67
  inst.__dict__["_inner"] = _value
65
68
 
66
69
 
@@ -144,7 +147,7 @@ class Array(Generic[T, A], BAREType[T], metaclass=ArrayMeta):
144
147
  return fp.getbuffer()
145
148
 
146
149
  @classmethod
147
- def unpack(cls, fp: BinaryIO) -> Self:
150
+ def unpack(cls, fp: BinaryIO) -> Array:
148
151
  size = cls._size or UInt.unpack(fp).value
149
152
  out = []
150
153
  for _ in range(size):
@@ -166,7 +169,9 @@ class Array(Generic[T, A], BAREType[T], metaclass=ArrayMeta):
166
169
  return f"{self.__class__.__name__}({repr(self.value)})"
167
170
 
168
171
 
169
- def array(inner: Type[BAREType[Any]], size: Optional[int] = None) -> Type[Array[Any, A]]:
172
+ def array(
173
+ inner: Type[BAREType[Any]], size: Optional[int] = None
174
+ ) -> Type[Array[Any, A]]:
170
175
  """
171
176
  A function that defines and returns anonymous BARE `Array` subclass with
172
177
  the provided `inner` type and (optional) `size` arguments.
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, BinaryIO, _ProtocolMeta
3
+ from typing import Any, BinaryIO, _ProtocolMeta, Type, Dict, TypeVar
4
4
 
5
5
  from .util import Field
6
6
  from .baretype import BAREType
7
7
 
8
- __all__ = ["Struct"]
8
+ __all__ = ["Struct", "struct"]
9
9
 
10
10
 
11
+ T = TypeVar("T")
12
+
11
13
  class StructMeta(_ProtocolMeta, type):
12
14
  def __new__(cls, clsname, bases, clsdict):
13
15
  fields = {}
@@ -118,3 +120,24 @@ class Struct(BAREType, metaclass=StructMeta):
118
120
  return False
119
121
  return True
120
122
  return NotImplemented
123
+
124
+ def struct(**kwargs: Dict[str, BAREType[T]]) -> Type[Struct]:
125
+ """
126
+ A function that defines and returnes an anonymous BARE `Struct` subclass with the
127
+ provided `kwargs` as fields. The name of each kwarg becomes the field name, with the
128
+ value being the field type. The field type is implicitly wrapped in a `Field`.Field
129
+
130
+ Proper usage of this function is as follows:
131
+
132
+ ```
133
+ MyStruct = struct(a=UInt, b=Str)
134
+ ```
135
+
136
+ Note that the name of the class is unspecified, use at your own risk.
137
+ """
138
+ name = "Struct_{}_anonymous".format("_".join(kwargs.keys()))
139
+ namespace = StructMeta.__prepare__(name, (Struct,))
140
+ namespace.update({field: Field(ty) for field, ty in kwargs.items()}) # type: ignore
141
+ AnonymousStruct = StructMeta.__new__(StructMeta, name, (Struct,), namespace)
142
+ StructMeta.__init__(AnonymousStruct, name, (Struct,), namespace)
143
+ return AnonymousStruct
@@ -1,5 +1,6 @@
1
+ from __future__ import annotations
1
2
  from abc import abstractmethod
2
- from typing import Any, BinaryIO, Generic, Protocol, TypeVar, Self
3
+ from typing import Any, BinaryIO, Generic, Protocol, TypeVar
3
4
 
4
5
  T = TypeVar("T", covariant=True)
5
6
 
@@ -15,7 +16,7 @@ class BAREType(Protocol, Generic[T]):
15
16
 
16
17
  @classmethod
17
18
  @abstractmethod
18
- def unpack(cls, fp: BinaryIO) -> Self:
19
+ def unpack(cls, fp: BinaryIO) -> BAREType:
19
20
  ...
20
21
 
21
22
  @classmethod
@@ -1,5 +1,6 @@
1
+ from __future__ import annotations
1
2
  import io
2
- from typing import Any, BinaryIO, Optional, Self, Type, TypeVar, _ProtocolMeta
3
+ from typing import Any, BinaryIO, Optional, Type, TypeVar, _ProtocolMeta
3
4
 
4
5
  from .baretype import BAREType
5
6
  from .number import UInt
@@ -64,7 +65,7 @@ class Data(BAREType[bytes], metaclass=DataMeta):
64
65
  return self.value == other.value
65
66
 
66
67
  @classmethod
67
- def unpack(cls, fp: BinaryIO) -> Self:
68
+ def unpack(cls, fp: BinaryIO) -> Data:
68
69
  size = cls._size or UInt.unpack(fp).value
69
70
  buf = fp.read(size)
70
71
  return cls(buf)
@@ -1,5 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import io
2
- from typing import Any, BinaryIO, Mapping, Self, Type, TypeVar, _ProtocolMeta
4
+ from typing import Any, BinaryIO, Mapping, Type, TypeVar, _ProtocolMeta
3
5
 
4
6
  from .baretype import BAREType
5
7
  from .number import UInt
@@ -99,7 +101,7 @@ class Map(BAREType[dict[K, V]], metaclass=MapMeta):
99
101
  return fp.getbuffer()
100
102
 
101
103
  @classmethod
102
- def unpack(cls, fp: BinaryIO) -> Self:
104
+ def unpack(cls, fp: BinaryIO) -> Map:
103
105
  size = UInt.unpack(fp).value
104
106
  out = {}
105
107
  for _ in range(size):
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import io
2
- from enum import EnumType, IntEnum
3
- from typing import Any, BinaryIO, Self, _ProtocolMeta
4
+ from enum import EnumMeta, IntEnum
5
+ from typing import Any, BinaryIO
4
6
 
5
7
  from .baretype import BAREType
6
8
  from .number import UInt
@@ -8,7 +10,7 @@ from .number import UInt
8
10
  __all__ = ["Enum", "Void", "Str"]
9
11
 
10
12
 
11
- class Enum(IntEnum, metaclass=EnumType):
13
+ class Enum(IntEnum, metaclass=EnumMeta):
12
14
  """
13
15
  A BARE enum type. It is a subclass of `IntEnum` and is used to represent
14
16
  a BARE enum type.
@@ -34,7 +36,7 @@ class Enum(IntEnum, metaclass=EnumType):
34
36
  return UInt(self.value).pack()
35
37
 
36
38
  @classmethod
37
- def unpack(cls, fp: BinaryIO) -> Self:
39
+ def unpack(cls, fp: BinaryIO) -> Enum:
38
40
  x = UInt.unpack(fp).value
39
41
  return cls(x)
40
42
 
@@ -70,7 +72,7 @@ class Void:
70
72
  return bytes()
71
73
 
72
74
  @classmethod
73
- def unpack(cls, _: BinaryIO) -> Self:
75
+ def unpack(cls, _: BinaryIO) -> Void:
74
76
  return cls()
75
77
 
76
78
  def __eq__(self, other: Any) -> bool:
@@ -122,7 +124,7 @@ class Str(BAREType[str]):
122
124
  return isinstance(value, (str, cls))
123
125
 
124
126
  @classmethod
125
- def unpack(cls, fp: BinaryIO) -> Self:
127
+ def unpack(cls, fp: BinaryIO) -> Str:
126
128
  size = UInt.unpack(fp).value
127
129
  buf = fp.read(size)
128
130
  if len(buf) != size:
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import io
2
4
  import struct
3
- from typing import Any, BinaryIO, Generic, Self, TypeVar
5
+ from typing import Any, BinaryIO, Generic, TypeVar
4
6
 
5
7
  from .baretype import BAREType
6
8
 
@@ -64,7 +66,7 @@ class UInt(NumberMixin[int], BAREType[int]):
64
66
  return buf.getvalue()
65
67
 
66
68
  @classmethod
67
- def unpack(cls, fp: BinaryIO) -> Self:
69
+ def unpack(cls, fp: BinaryIO) -> UInt:
68
70
  return cls(_read_varint(fp, False))
69
71
 
70
72
  @classmethod
@@ -102,7 +104,7 @@ class Int(NumberMixin[int]):
102
104
  return buf.getvalue()
103
105
 
104
106
  @classmethod
105
- def unpack(cls, fp: BinaryIO) -> Self:
107
+ def unpack(cls, fp: BinaryIO) -> Int:
106
108
  return cls(_read_varint(fp, True))
107
109
 
108
110
  @classmethod
@@ -136,7 +138,7 @@ class F64(NumberMixin[float]):
136
138
  return struct.pack("<d", self.value)
137
139
 
138
140
  @classmethod
139
- def unpack(cls, fp: BinaryIO) -> Self:
141
+ def unpack(cls, fp: BinaryIO) -> F64:
140
142
  buf = fp.read(struct.calcsize("<d"))
141
143
  return cls(struct.unpack("<d", buf)[0])
142
144
 
@@ -169,7 +171,7 @@ class F32(NumberMixin[float]):
169
171
  return struct.pack("<f", self.value)
170
172
 
171
173
  @classmethod
172
- def unpack(cls, fp: BinaryIO) -> Self:
174
+ def unpack(cls, fp: BinaryIO) -> F32:
173
175
  buf = fp.read(struct.calcsize("<f"))
174
176
  return cls(struct.unpack("<f", buf)[0]) # type: ignore
175
177
 
@@ -193,7 +195,7 @@ class U8(NumberMixin[int]):
193
195
  return struct.pack("<B", self.value)
194
196
 
195
197
  @classmethod
196
- def unpack(cls, fp: BinaryIO) -> Self:
198
+ def unpack(cls, fp: BinaryIO) -> U8:
197
199
  buf = fp.read(struct.calcsize("<B"))
198
200
  return cls(struct.unpack("<B", buf)[0]) # type: ignore
199
201
 
@@ -217,7 +219,7 @@ class Bool(NumberMixin[bool]):
217
219
  return struct.pack("<?", self.value)
218
220
 
219
221
  @classmethod
220
- def unpack(cls, fp: BinaryIO) -> Self:
222
+ def unpack(cls, fp: BinaryIO) -> Bool:
221
223
  buf = fp.read(struct.calcsize("<?"))
222
224
  return cls(struct.unpack("<?", buf)[0]) # type: ignore
223
225
 
@@ -235,7 +237,7 @@ class I8(NumberMixin[int]):
235
237
  return struct.pack("<b", self.value)
236
238
 
237
239
  @classmethod
238
- def unpack(cls, fp: BinaryIO) -> Self:
240
+ def unpack(cls, fp: BinaryIO) -> I8:
239
241
  buf = fp.read(struct.calcsize("<b"))
240
242
  return cls(struct.unpack("<b", buf)[0]) # type: ignore
241
243
 
@@ -259,7 +261,7 @@ class U16(NumberMixin[int]):
259
261
  return struct.pack("<H", self.value)
260
262
 
261
263
  @classmethod
262
- def unpack(cls, fp: BinaryIO) -> Self:
264
+ def unpack(cls, fp: BinaryIO) -> U16:
263
265
  buf = fp.read(struct.calcsize("<H"))
264
266
  return cls(struct.unpack("<H", buf)[0]) # type: ignore
265
267
 
@@ -283,7 +285,7 @@ class I16(NumberMixin[int]):
283
285
  return struct.pack("<h", self.value)
284
286
 
285
287
  @classmethod
286
- def unpack(cls, fp: BinaryIO) -> Self:
288
+ def unpack(cls, fp: BinaryIO) -> I16:
287
289
  buf = fp.read(struct.calcsize("<h"))
288
290
  return cls(struct.unpack("<h", buf)[0]) # type: ignore
289
291
 
@@ -307,7 +309,7 @@ class U32(NumberMixin[int]):
307
309
  return struct.pack("<L", self.value)
308
310
 
309
311
  @classmethod
310
- def unpack(cls, fp: BinaryIO) -> Self:
312
+ def unpack(cls, fp: BinaryIO) -> U32:
311
313
  buf = fp.read(struct.calcsize("<L"))
312
314
  return cls(struct.unpack("<L", buf)[0]) # type: ignore
313
315
 
@@ -331,7 +333,7 @@ class I32(NumberMixin[int]):
331
333
  return struct.pack("<l", self.value)
332
334
 
333
335
  @classmethod
334
- def unpack(cls, fp: BinaryIO) -> Self:
336
+ def unpack(cls, fp: BinaryIO) -> I32:
335
337
  buf = fp.read(struct.calcsize("<l"))
336
338
  return cls(struct.unpack("<l", buf)[0]) # type: ignore
337
339
 
@@ -355,7 +357,7 @@ class U64(NumberMixin[int]):
355
357
  return struct.pack("<Q", self.value)
356
358
 
357
359
  @classmethod
358
- def unpack(cls, fp: BinaryIO) -> Self:
360
+ def unpack(cls, fp: BinaryIO) -> U64:
359
361
  buf = fp.read(struct.calcsize("<Q"))
360
362
  return cls(struct.unpack("<Q", buf)[0]) # type: ignore
361
363
 
@@ -382,7 +384,7 @@ class I64(NumberMixin[int]):
382
384
  return struct.pack("<q", self.value)
383
385
 
384
386
  @classmethod
385
- def unpack(cls, fp: BinaryIO) -> Self:
387
+ def unpack(cls, fp: BinaryIO) -> I64:
386
388
  buf = fp.read(struct.calcsize("<q"))
387
389
  return cls(struct.unpack("<q", buf)[0]) # type: ignore
388
390
 
@@ -1,5 +1,5 @@
1
1
  import io
2
- from bare import Struct, UInt, Str, Field
2
+ from bare import Struct, UInt, Str, Field, struct
3
3
 
4
4
  def test_struct_basic():
5
5
  class MyStruct(Struct):
@@ -22,3 +22,10 @@ def test_nested_struct():
22
22
  b = x.pack()
23
23
  x2 = MyStruct.unpack(io.BytesIO(b))
24
24
  assert x == x2
25
+
26
+ def test_anonymous_struct():
27
+ MyStruct = struct(name=Str, age=UInt)
28
+ x = MyStruct(name="Noah", age=27)
29
+ b = x.pack()
30
+ x2 = MyStruct.unpack(io.BytesIO(b))
31
+ assert x == x2
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
- from typing import Any, BinaryIO, Generic, Iterable, Self, Type, TypeVar
4
+ from typing import Any, BinaryIO, Generic, Iterable, Type, TypeVar
5
5
 
6
6
  from .misc import Void
7
7
  from .number import UInt
@@ -104,7 +104,7 @@ class Union(metaclass=UnionMeta, variants=()):
104
104
  return buf.getbuffer()
105
105
 
106
106
  @classmethod
107
- def unpack(cls, fp: BinaryIO) -> Self:
107
+ def unpack(cls, fp: BinaryIO) -> Union:
108
108
  discriminant = UInt.unpack(fp).value
109
109
  if discriminant not in cls._variants:
110
110
  raise TypeError(
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pybare
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: A declarative implementation of BARE for Python
5
5
  Home-page: https://sr.ht/~chiefnoah/pybare/
6
6
  Author: Noah Pederson
7
7
  Author-email: noah@packetlost.dev
8
+ Classifier: Programming Language :: Python :: 3.10
8
9
  Classifier: Programming Language :: Python :: 3.11
9
10
  Classifier: Programming Language :: Python :: 3.12
10
11
  Classifier: License :: OSI Approved :: MIT License
11
12
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.11
13
+ Requires-Python: >=3.10
13
14
  Description-Content-Type: text/markdown
14
15
  License-File: LICENSE
15
16
 
@@ -17,7 +18,7 @@ License-File: LICENSE
17
18
  [![builds.sr.ht status](https://builds.sr.ht/~chiefnoah/pybare.svg)](https://builds.sr.ht/~chiefnoah/pybare?)
18
19
 
19
20
  A declarative implementation of the [BARE](https://baremessages.org/) message
20
- format for Python 3.11+
21
+ format for Python 3.10+
21
22
 
22
23
  ---
23
24
 
@@ -39,36 +40,43 @@ pip install pybare
39
40
  pybare fully implements all BARE types for both encoding and decoding. This
40
41
  includes reading multiple messages from the same `BinaryIO` stream.
41
42
 
42
- ### TODO
43
+ ## Gotchas
43
44
 
44
- - [ ] Codegen based on `.schema` files
45
- - [ ] Better documentation
46
- - [ ] More tests
47
- - [ ] Fast C implementation for encoding
45
+ We use "Array" / "array" instead of "List" / "list" to avoid conflicts with Python's
46
+ common builtin type of the same name.
48
47
 
49
48
  ## Examples
50
49
 
50
+ An example that defines the types used as an example in the RFC may be found in
51
+ [`example.py`](./example.py)
52
+
51
53
  pybare currently requires you define your structures by hand. Examples can be
52
54
  found in the
53
55
  [tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
54
56
 
55
57
  ### Quickstart
56
58
 
59
+ The general convention is type identifiers start with a *capital* letter and anonymous
60
+ generating functions begin with a lowercase. This follows general "pythonic" style for
61
+ classes vs functions.
62
+
57
63
  ```python
58
- from bare import Struct, map, Str, UInt, optional, data, array, Void
64
+ from bare import Struct, map, Str, UInt, optional, data, array, Void, struct, U8
59
65
 
60
66
  # Alternatively, class Data(size=64): ...
61
67
  PubKey = data(64) # 512 bits
62
68
 
63
69
  class User(Struct):
64
- username = Filed(Str)
70
+ username = Field(Str)
65
71
  userid = Field(Int)
66
72
  email = Field(optional(Str))
67
73
  keys = Field(map(Str, PubKey))
68
74
  repos = Field(array(Str)) # variable length array
75
+ # anonymous array and struct
76
+ friends = Field(array(struct(name=Str, age=U8)))
69
77
 
70
78
 
71
- noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
79
+ noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[], friends=[])
72
80
  noah.username == 'chiefnoah'
73
81
  noah.username = 'someoneelse'
74
82
  noah.username == 'someoneelse'
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="pybare", # Replace with your own username
8
- version="1.2.1",
8
+ version="1.2.3",
9
9
  author="Noah Pederson",
10
10
  author_email="noah@packetlost.dev",
11
11
  description="A declarative implementation of BARE for Python",
@@ -14,10 +14,11 @@ setuptools.setup(
14
14
  url="https://sr.ht/~chiefnoah/pybare/",
15
15
  packages=setuptools.find_packages(),
16
16
  classifiers=[
17
+ "Programming Language :: Python :: 3.10",
17
18
  "Programming Language :: Python :: 3.11",
18
19
  "Programming Language :: Python :: 3.12",
19
20
  "License :: OSI Approved :: MIT License",
20
21
  "Operating System :: OS Independent",
21
22
  ],
22
- python_requires=">=3.11",
23
+ python_requires=">=3.10",
23
24
  )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes