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.
- {pybare-1.2.1 → pybare-1.2.3}/PKG-INFO +19 -11
- {pybare-1.2.1 → pybare-1.2.3}/README.md +16 -9
- {pybare-1.2.1 → pybare-1.2.3}/bare/__init__.py +2 -1
- {pybare-1.2.1 → pybare-1.2.3}/bare/barearray.py +10 -5
- {pybare-1.2.1 → pybare-1.2.3}/bare/barestruct.py +25 -2
- {pybare-1.2.1 → pybare-1.2.3}/bare/baretype.py +3 -2
- {pybare-1.2.1 → pybare-1.2.3}/bare/data.py +3 -2
- {pybare-1.2.1 → pybare-1.2.3}/bare/map.py +4 -2
- {pybare-1.2.1 → pybare-1.2.3}/bare/misc.py +8 -6
- {pybare-1.2.1 → pybare-1.2.3}/bare/number.py +16 -14
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_barestruct.py +8 -1
- {pybare-1.2.1 → pybare-1.2.3}/bare/union.py +2 -2
- {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/PKG-INFO +19 -11
- {pybare-1.2.1 → pybare-1.2.3}/setup.py +3 -2
- {pybare-1.2.1 → pybare-1.2.3}/LICENSE +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_barearray.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_data.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_encoder.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_map.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_misc.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_number.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/test_union.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/bare/util.py +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/SOURCES.txt +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/dependency_links.txt +0 -0
- {pybare-1.2.1 → pybare-1.2.3}/pybare.egg-info/top_level.txt +0 -0
- {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.
|
|
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.
|
|
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
|
[](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.
|
|
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
|
-
|
|
43
|
+
## Gotchas
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
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 =
|
|
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
|
[](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.
|
|
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
|
-
|
|
27
|
+
## Gotchas
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
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 =
|
|
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(
|
|
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) ->
|
|
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(
|
|
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
|
|
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) ->
|
|
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,
|
|
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) ->
|
|
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,
|
|
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) ->
|
|
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
|
|
3
|
-
from typing import Any, BinaryIO
|
|
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=
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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,
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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,
|
|
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) ->
|
|
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.
|
|
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.
|
|
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
|
[](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.
|
|
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
|
-
|
|
43
|
+
## Gotchas
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|