pybare 1.2.2__tar.gz → 1.2.4__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.4/PKG-INFO +123 -0
- {pybare-1.2.2 → pybare-1.2.4}/README.md +18 -9
- {pybare-1.2.2 → pybare-1.2.4}/bare/__init__.py +2 -1
- pybare-1.2.4/bare/_examples/customer.bin +0 -0
- pybare-1.2.4/bare/_examples/employee.bin +0 -0
- pybare-1.2.4/bare/_examples/people.bin +0 -0
- pybare-1.2.4/bare/_examples/terminated.bin +1 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/barearray.py +11 -2
- {pybare-1.2.2 → pybare-1.2.4}/bare/barestruct.py +29 -5
- {pybare-1.2.2 → pybare-1.2.4}/bare/baretype.py +3 -6
- {pybare-1.2.2 → pybare-1.2.4}/bare/map.py +4 -1
- {pybare-1.2.2 → pybare-1.2.4}/bare/misc.py +5 -2
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_barearray.py +30 -24
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_barestruct.py +12 -1
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_data.py +7 -7
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_map.py +21 -17
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_misc.py +9 -4
- pybare-1.2.4/bare/test_number.py +49 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_union.py +65 -40
- {pybare-1.2.2 → pybare-1.2.4}/bare/util.py +3 -3
- pybare-1.2.4/pyproject.toml +36 -0
- pybare-1.2.2/PKG-INFO +0 -92
- pybare-1.2.2/bare/test_number.py +0 -43
- pybare-1.2.2/pybare.egg-info/PKG-INFO +0 -92
- pybare-1.2.2/pybare.egg-info/SOURCES.txt +0 -25
- pybare-1.2.2/pybare.egg-info/dependency_links.txt +0 -1
- pybare-1.2.2/pybare.egg-info/top_level.txt +0 -1
- pybare-1.2.2/setup.cfg +0 -4
- pybare-1.2.2/setup.py +0 -24
- {pybare-1.2.2 → pybare-1.2.4}/LICENSE +0 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/data.py +0 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/number.py +0 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/test_encoder.py +0 -0
- {pybare-1.2.2 → pybare-1.2.4}/bare/union.py +0 -0
pybare-1.2.4/PKG-INFO
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pybare
|
|
3
|
+
Version: 1.2.4
|
|
4
|
+
Summary: A BARE Encoding Library and Data Validation Library for Python.
|
|
5
|
+
Author-Email: Noah Pederson <noah@packetlost.dev>
|
|
6
|
+
License: Copyright 2020 Noah Pederson
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
9
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
10
|
+
the Software without restriction, including without limitation the rights to
|
|
11
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
12
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
13
|
+
so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
|
25
|
+
Classifier: Development Status :: 4 - Beta
|
|
26
|
+
Classifier: Intended Audience :: Developers
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Project-URL: Homepage, https://sr.ht/~chiefnoah/pybare/
|
|
33
|
+
Project-URL: Repository, https://git.sr.ht/~chiefnoah/pybare
|
|
34
|
+
Project-URL: Issues, https://todo.sr.ht/~chiefnoah/pybare
|
|
35
|
+
Project-URL: Changelog, https://git.sr.ht/~chiefnoah/pybare/tree/master/item/CHANGELOG.md
|
|
36
|
+
Requires-Python: >=3.10
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# PyBARE
|
|
40
|
+
[](https://builds.sr.ht/~chiefnoah/pybare?)
|
|
41
|
+
|
|
42
|
+
A declarative implementation of the [BARE](https://baremessages.org/) message
|
|
43
|
+
format for Python 3.10+
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
pybare is a general purpose library for strongly typed primitives in Python that
|
|
48
|
+
supports serializing to and from BARE messages.
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
pip install pybare
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Goals
|
|
55
|
+
|
|
56
|
+
* Provide a declarative structure for defining types
|
|
57
|
+
* Validation on value updates
|
|
58
|
+
* Support streaming messages
|
|
59
|
+
|
|
60
|
+
## Status
|
|
61
|
+
|
|
62
|
+
pybare fully implements all BARE types for both encoding and decoding. This
|
|
63
|
+
includes reading multiple messages from the same `BinaryIO` stream.
|
|
64
|
+
|
|
65
|
+
It is well tested and in use on production systems.
|
|
66
|
+
|
|
67
|
+
## Gotchas
|
|
68
|
+
|
|
69
|
+
We use "Array" / "array" instead of "List" / "list" to avoid conflicts with Python's
|
|
70
|
+
common builtin type of the same name.
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
An example that defines the types used as an example in the RFC may be found in
|
|
75
|
+
[`example.py`](./example.py)
|
|
76
|
+
|
|
77
|
+
pybare currently requires you define your structures by hand. Examples can be
|
|
78
|
+
found in the
|
|
79
|
+
[tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
|
|
80
|
+
|
|
81
|
+
### Quickstart
|
|
82
|
+
|
|
83
|
+
The general convention is type identifiers start with a *capital* letter and anonymous
|
|
84
|
+
generating functions begin with a lowercase. This follows general "pythonic" style for
|
|
85
|
+
classes vs functions.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from bare import Struct, map, Str, UInt, optional, data, array, Void, struct, U8
|
|
89
|
+
|
|
90
|
+
# Alternatively, class Data(size=64): ...
|
|
91
|
+
PubKey = data(64) # 512 bits
|
|
92
|
+
|
|
93
|
+
class User(Struct):
|
|
94
|
+
username = Field(Str)
|
|
95
|
+
userid = Field(Int)
|
|
96
|
+
email = Field(optional(Str))
|
|
97
|
+
keys = Field(map(Str, PubKey))
|
|
98
|
+
repos = Field(array(Str)) # variable length array
|
|
99
|
+
# anonymous array and struct
|
|
100
|
+
friends = Field(array(struct(name=Str, age=U8)))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[], friends=[])
|
|
104
|
+
noah.username == 'chiefnoah'
|
|
105
|
+
noah.username = 'someoneelse'
|
|
106
|
+
noah.username == 'someoneelse'
|
|
107
|
+
noah.userid == 1 # True
|
|
108
|
+
noah.username = 1 # raise: bare.ValidationError
|
|
109
|
+
noah.keys # {} (empty dict)
|
|
110
|
+
noah.keys['my key'] = bytes(64) #\x00\x00...
|
|
111
|
+
noah.keys['oops'] = bytes(1) # raise: bare.ValidationError
|
|
112
|
+
noah.email == Void() # True
|
|
113
|
+
noah.email = 12345 # raise: bare.ValidationError
|
|
114
|
+
noah.pack() # \x00\x01 ... (binary data)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Note, you **must** wrap the desired type in a `Field` to get its 'magic' behavior.
|
|
118
|
+
Class or instance fields that are not wrapped in a `Field` will be ignored by the `pack`
|
|
119
|
+
and `unpack` methods.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
To contribute, send patches to [~chiefnoah/inbox@lists.sr.ht](mailto:~chiefnoah/inbox@lists.sr.ht)
|
|
@@ -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,45 @@ 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
|
+
It is well tested and in use on production systems.
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
## Gotchas
|
|
30
|
+
|
|
31
|
+
We use "Array" / "array" instead of "List" / "list" to avoid conflicts with Python's
|
|
32
|
+
common builtin type of the same name.
|
|
33
33
|
|
|
34
34
|
## Examples
|
|
35
35
|
|
|
36
|
+
An example that defines the types used as an example in the RFC may be found in
|
|
37
|
+
[`example.py`](./example.py)
|
|
38
|
+
|
|
36
39
|
pybare currently requires you define your structures by hand. Examples can be
|
|
37
40
|
found in the
|
|
38
41
|
[tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
|
|
39
42
|
|
|
40
43
|
### Quickstart
|
|
41
44
|
|
|
45
|
+
The general convention is type identifiers start with a *capital* letter and anonymous
|
|
46
|
+
generating functions begin with a lowercase. This follows general "pythonic" style for
|
|
47
|
+
classes vs functions.
|
|
48
|
+
|
|
42
49
|
```python
|
|
43
|
-
from bare import Struct, map, Str, UInt, optional, data, array, Void
|
|
50
|
+
from bare import Struct, map, Str, UInt, optional, data, array, Void, struct, U8
|
|
44
51
|
|
|
45
52
|
# Alternatively, class Data(size=64): ...
|
|
46
53
|
PubKey = data(64) # 512 bits
|
|
47
54
|
|
|
48
55
|
class User(Struct):
|
|
49
|
-
username =
|
|
56
|
+
username = Field(Str)
|
|
50
57
|
userid = Field(Int)
|
|
51
58
|
email = Field(optional(Str))
|
|
52
59
|
keys = Field(map(Str, PubKey))
|
|
53
60
|
repos = Field(array(Str)) # variable length array
|
|
61
|
+
# anonymous array and struct
|
|
62
|
+
friends = Field(array(struct(name=Str, age=U8)))
|
|
54
63
|
|
|
55
64
|
|
|
56
|
-
noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
|
|
65
|
+
noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[], friends=[])
|
|
57
66
|
noah.username == 'chiefnoah'
|
|
58
67
|
noah.username = 'someoneelse'
|
|
59
68
|
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
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import io
|
|
3
3
|
from collections import UserList
|
|
4
|
-
from typing import (
|
|
5
|
-
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
BinaryIO,
|
|
7
|
+
ClassVar,
|
|
8
|
+
Generic,
|
|
9
|
+
Iterable,
|
|
10
|
+
Optional,
|
|
11
|
+
Type,
|
|
12
|
+
TypeVar,
|
|
13
|
+
_ProtocolMeta,
|
|
14
|
+
)
|
|
6
15
|
|
|
7
16
|
from .baretype import BAREType
|
|
8
17
|
from .number import UInt
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import io
|
|
4
|
+
from typing import Any, BinaryIO, Dict, Type, TypeVar, _ProtocolMeta
|
|
4
5
|
|
|
5
|
-
from .util import Field
|
|
6
6
|
from .baretype import BAREType
|
|
7
|
+
from .util import Field
|
|
8
|
+
|
|
9
|
+
__all__ = ["Struct", "struct"]
|
|
10
|
+
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
T = TypeVar("T")
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class StructMeta(_ProtocolMeta, type):
|
|
@@ -77,8 +81,6 @@ class Struct(BAREType, metaclass=StructMeta):
|
|
|
77
81
|
)
|
|
78
82
|
|
|
79
83
|
def pack(self: Struct) -> bytes:
|
|
80
|
-
import io
|
|
81
|
-
|
|
82
84
|
buf = io.BytesIO()
|
|
83
85
|
for name, ty in self._fields.items():
|
|
84
86
|
val = getattr(self, name)
|
|
@@ -118,3 +120,25 @@ class Struct(BAREType, metaclass=StructMeta):
|
|
|
118
120
|
return False
|
|
119
121
|
return True
|
|
120
122
|
return NotImplemented
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def struct(**kwargs: Dict[str, Type[BAREType]]) -> Type[Struct]:
|
|
126
|
+
"""
|
|
127
|
+
A function that defines and returnes an anonymous BARE `Struct` subclass with the
|
|
128
|
+
provided `kwargs` as fields. The name of each kwarg becomes the field name, with the
|
|
129
|
+
value being the field type. The field type is implicitly wrapped in a `Field`.Field
|
|
130
|
+
|
|
131
|
+
Proper usage of this function is as follows:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
MyStruct = struct(a=UInt, b=Str)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Note that the name of the class is unspecified, use at your own risk.
|
|
138
|
+
"""
|
|
139
|
+
name = "Struct_{}_anonymous".format("_".join(kwargs.keys()))
|
|
140
|
+
namespace = StructMeta.__prepare__(name, (Struct,))
|
|
141
|
+
namespace.update({field: Field(ty) for field, ty in kwargs.items()}) # type: ignore
|
|
142
|
+
AnonymousStruct = StructMeta.__new__(StructMeta, name, (Struct,), namespace)
|
|
143
|
+
StructMeta.__init__(AnonymousStruct, name, (Struct,), namespace)
|
|
144
|
+
return AnonymousStruct
|
|
@@ -7,17 +7,14 @@ T = TypeVar("T", covariant=True)
|
|
|
7
7
|
|
|
8
8
|
class BAREType(Protocol, Generic[T]):
|
|
9
9
|
@abstractmethod
|
|
10
|
-
def __init__(self, *arg, **kwargs):
|
|
11
|
-
...
|
|
10
|
+
def __init__(self, *arg, **kwargs): ...
|
|
12
11
|
|
|
13
12
|
@abstractmethod
|
|
14
|
-
def pack(self) -> bytes:
|
|
15
|
-
...
|
|
13
|
+
def pack(self) -> bytes: ...
|
|
16
14
|
|
|
17
15
|
@classmethod
|
|
18
16
|
@abstractmethod
|
|
19
|
-
def unpack(cls, fp: BinaryIO) -> BAREType:
|
|
20
|
-
...
|
|
17
|
+
def unpack(cls, fp: BinaryIO) -> BAREType: ...
|
|
21
18
|
|
|
22
19
|
@classmethod
|
|
23
20
|
@abstractmethod
|
|
@@ -115,7 +115,10 @@ class Map(BAREType[dict[K, V]], metaclass=MapMeta):
|
|
|
115
115
|
return other._values == self._values
|
|
116
116
|
try:
|
|
117
117
|
for other_key, other_value in other.values():
|
|
118
|
-
if
|
|
118
|
+
if (
|
|
119
|
+
other_key not in self._values
|
|
120
|
+
and self._key_type(other_key) not in self._values
|
|
121
|
+
):
|
|
119
122
|
return False
|
|
120
123
|
if other_value != self._values[other_key]:
|
|
121
124
|
return False
|
|
@@ -60,7 +60,7 @@ class Enum(IntEnum, metaclass=EnumMeta):
|
|
|
60
60
|
return False
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
class Void:
|
|
63
|
+
class Void(BAREType[None]):
|
|
64
64
|
"""
|
|
65
65
|
A Void type. It is similar to the `None` type, but is used to represent
|
|
66
66
|
a BARE void type.
|
|
@@ -68,11 +68,14 @@ class Void:
|
|
|
68
68
|
If should *generally* be used directly, but is also used implicitly in `option`.
|
|
69
69
|
"""
|
|
70
70
|
|
|
71
|
+
def __init__(self):
|
|
72
|
+
...
|
|
73
|
+
|
|
71
74
|
def pack(self) -> bytes:
|
|
72
75
|
return bytes()
|
|
73
76
|
|
|
74
77
|
@classmethod
|
|
75
|
-
def unpack(cls,
|
|
78
|
+
def unpack(cls, fp: BinaryIO) -> Void:
|
|
76
79
|
return cls()
|
|
77
80
|
|
|
78
81
|
def __eq__(self, other: Any) -> bool:
|
|
@@ -5,11 +5,11 @@ import bare
|
|
|
5
5
|
import pytest
|
|
6
6
|
import io
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def test_2d_array_unsized():
|
|
9
|
-
class InnerArray(Array, inner=UInt):
|
|
10
|
-
|
|
11
|
-
class My2DArray(Array, inner=InnerArray):
|
|
12
|
-
...
|
|
10
|
+
class InnerArray(Array, inner=UInt): ...
|
|
11
|
+
|
|
12
|
+
class My2DArray(Array, inner=InnerArray): ...
|
|
13
13
|
|
|
14
14
|
input = [[1, 2], [3, 4]]
|
|
15
15
|
x = My2DArray(input)
|
|
@@ -18,6 +18,7 @@ def test_2d_array_unsized():
|
|
|
18
18
|
assert x2 == x
|
|
19
19
|
assert x2 == input
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
def test_anonymous_array_unsized():
|
|
22
23
|
AnonymousArray = array(UInt)
|
|
23
24
|
input = [1, 2, 3]
|
|
@@ -27,6 +28,7 @@ def test_anonymous_array_unsized():
|
|
|
27
28
|
assert x2 == x
|
|
28
29
|
assert x2 == input
|
|
29
30
|
|
|
31
|
+
|
|
30
32
|
def test_anonymous_array_sized():
|
|
31
33
|
AnonymousArray = array(UInt, 3)
|
|
32
34
|
input = [1, 2, 3]
|
|
@@ -41,9 +43,9 @@ def test_anonymous_array_sized():
|
|
|
41
43
|
with pytest.raises(ValueError):
|
|
42
44
|
AnonymousArray([1, 2, 3, 4])
|
|
43
45
|
|
|
46
|
+
|
|
44
47
|
def test_array_sized():
|
|
45
|
-
class MyArry(Array, inner=UInt, size=3):
|
|
46
|
-
...
|
|
48
|
+
class MyArry(Array, inner=UInt, size=3): ...
|
|
47
49
|
|
|
48
50
|
input = [1, 2, 3]
|
|
49
51
|
x = MyArry(input)
|
|
@@ -56,24 +58,28 @@ def test_array_sized():
|
|
|
56
58
|
with pytest.raises(ValueError):
|
|
57
59
|
MyArry([1, 2, 3, 4])
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
])
|
|
61
|
+
|
|
62
|
+
@pytest.mark.parametrize(
|
|
63
|
+
"inner,data",
|
|
64
|
+
[
|
|
65
|
+
(bare.I8, [1, -1]),
|
|
66
|
+
(bare.U8, [1, 5]),
|
|
67
|
+
(bare.I16, [-1, 10000]),
|
|
68
|
+
(bare.U16, [1, 100]),
|
|
69
|
+
(bare.I32, [1, 100]),
|
|
70
|
+
(bare.U32, [1, 100]),
|
|
71
|
+
(bare.I64, [1, 100]),
|
|
72
|
+
(bare.U64, [1, 100]),
|
|
73
|
+
(bare.F32, [1.0, 2.0]),
|
|
74
|
+
(bare.F64, [1.1, 2.2]),
|
|
75
|
+
(bare.UInt, [1, 2, 3]),
|
|
76
|
+
(bare.Int, [-1, 2, -3]),
|
|
77
|
+
(bare.Str, ["Hello", "world"]),
|
|
78
|
+
(bare.Bool, [False, True]),
|
|
79
|
+
(bare.map(bare.Str, bare.Str), [{"Hello": "world"}, {"abc": "123"}]),
|
|
80
|
+
(bare.union(bare.Str, bare.UInt), [bare.Str("hello"), bare.UInt(123)]),
|
|
81
|
+
],
|
|
82
|
+
)
|
|
77
83
|
def test_all_types(inner: Type[BAREType], data: list):
|
|
78
84
|
anon_array = array(inner)
|
|
79
85
|
x = anon_array(data)
|
|
@@ -1,16 +1,19 @@
|
|
|
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
5
|
def test_struct_basic():
|
|
5
6
|
class MyStruct(Struct):
|
|
6
7
|
a = Field(UInt)
|
|
7
8
|
b = Field(Str)
|
|
8
9
|
c: int
|
|
10
|
+
|
|
9
11
|
x = MyStruct(a=123, b="Hello")
|
|
10
12
|
b = x.pack()
|
|
11
13
|
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
12
14
|
assert x == x2
|
|
13
15
|
|
|
16
|
+
|
|
14
17
|
def test_nested_struct():
|
|
15
18
|
class InnerStruct(Struct):
|
|
16
19
|
i = Field(UInt)
|
|
@@ -22,3 +25,11 @@ def test_nested_struct():
|
|
|
22
25
|
b = x.pack()
|
|
23
26
|
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
24
27
|
assert x == x2
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_anonymous_struct():
|
|
31
|
+
MyStruct = struct(name=Str, age=UInt)
|
|
32
|
+
x = MyStruct(name="Noah", age=27)
|
|
33
|
+
b = x.pack()
|
|
34
|
+
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
35
|
+
assert x == x2
|
|
@@ -3,27 +3,27 @@ import io
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def test_data_sized():
|
|
6
|
-
class SizedData(Data, size=5):
|
|
7
|
-
...
|
|
6
|
+
class SizedData(Data, size=5): ...
|
|
8
7
|
|
|
9
|
-
x = SizedData(b
|
|
8
|
+
x = SizedData(b"12345")
|
|
10
9
|
b = x.pack()
|
|
11
10
|
assert len(b) == 5
|
|
12
11
|
x2 = SizedData.unpack(io.BytesIO(b))
|
|
13
12
|
assert x == x2
|
|
14
13
|
|
|
14
|
+
|
|
15
15
|
def test_data_unsized():
|
|
16
|
-
class SizedData(Data):
|
|
17
|
-
...
|
|
16
|
+
class SizedData(Data): ...
|
|
18
17
|
|
|
19
|
-
x = SizedData(b
|
|
18
|
+
x = SizedData(b"12345")
|
|
20
19
|
b = x.pack()
|
|
21
20
|
assert len(b) == 6
|
|
22
21
|
x2 = SizedData.unpack(io.BytesIO(b))
|
|
23
22
|
assert x == x2
|
|
24
23
|
|
|
24
|
+
|
|
25
25
|
def test_data_raw():
|
|
26
|
-
x = Data(b
|
|
26
|
+
x = Data(b"12345")
|
|
27
27
|
b = x.pack()
|
|
28
28
|
assert len(b) == 6
|
|
29
29
|
x2 = Data.unpack(io.BytesIO(b))
|
|
@@ -5,8 +5,8 @@ import io
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class MyMap(Map, key_type=Str, value_type=Int):
|
|
9
|
-
|
|
8
|
+
class MyMap(Map, key_type=Str, value_type=Int): ...
|
|
9
|
+
|
|
10
10
|
|
|
11
11
|
def test_basic_map():
|
|
12
12
|
x = MyMap({"Hello": 123, "abc": 456})
|
|
@@ -16,6 +16,7 @@ def test_basic_map():
|
|
|
16
16
|
assert x["Hello"] == 123
|
|
17
17
|
assert x["abc"] == 456
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
def test_anonymous_map():
|
|
20
21
|
MyMap = map(Str, Int)
|
|
21
22
|
x = MyMap({"Hello": 123, "abc": 456})
|
|
@@ -25,37 +26,41 @@ def test_anonymous_map():
|
|
|
25
26
|
assert x["Hello"] == 123
|
|
26
27
|
assert x["abc"] == 456
|
|
27
28
|
|
|
29
|
+
|
|
28
30
|
def test_nested_map():
|
|
29
31
|
InnerMap = map(Str, Str)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
|
|
33
|
+
class NestedMap(Map, key_type=Str, value_type=InnerMap): ...
|
|
34
|
+
|
|
32
35
|
x = NestedMap({"outer": {"hello": "world"}})
|
|
33
36
|
b = x.pack()
|
|
34
37
|
x2 = NestedMap.unpack(io.BytesIO(b))
|
|
35
38
|
assert x == x2
|
|
36
39
|
|
|
40
|
+
|
|
37
41
|
class MyEnum(bare.Enum):
|
|
38
42
|
A = 1
|
|
39
43
|
B = 2
|
|
40
44
|
|
|
45
|
+
|
|
41
46
|
@pytest.mark.parametrize(
|
|
42
|
-
|
|
47
|
+
"key_type,value_type,data",
|
|
43
48
|
[
|
|
44
49
|
(Str, Str, {"hello": "world"}),
|
|
45
50
|
(bare.UInt, bare.Int, {123: -123}),
|
|
46
|
-
(bare.U8, bare.U8, {1:2}),
|
|
47
|
-
(bare.I8, bare.I8, {1
|
|
48
|
-
(bare.U16, bare.U16, {1:2}),
|
|
49
|
-
(bare.I16, bare.I16, {1
|
|
50
|
-
(bare.U32, bare.U32, {1:2}),
|
|
51
|
-
(bare.I32, bare.I32, {1
|
|
52
|
-
(bare.U64, bare.U64, {1:2}),
|
|
53
|
-
(bare.I64, bare.I64, {1
|
|
54
|
-
(bare.UInt, bare.UInt, {1:2}),
|
|
55
|
-
(bare.Int, bare.Int, {1
|
|
51
|
+
(bare.U8, bare.U8, {1: 2}),
|
|
52
|
+
(bare.I8, bare.I8, {1: -2}),
|
|
53
|
+
(bare.U16, bare.U16, {1: 2}),
|
|
54
|
+
(bare.I16, bare.I16, {1: -2}),
|
|
55
|
+
(bare.U32, bare.U32, {1: 2}),
|
|
56
|
+
(bare.I32, bare.I32, {1: -2}),
|
|
57
|
+
(bare.U64, bare.U64, {1: 2}),
|
|
58
|
+
(bare.I64, bare.I64, {1: -2}),
|
|
59
|
+
(bare.UInt, bare.UInt, {1: 2}),
|
|
60
|
+
(bare.Int, bare.Int, {1: -2}),
|
|
56
61
|
(bare.Bool, bare.Bool, {True: False}),
|
|
57
62
|
(bare.Bool, bare.Bool, {True: False}),
|
|
58
|
-
]
|
|
63
|
+
],
|
|
59
64
|
)
|
|
60
65
|
def test_all_types(key_type, value_type, data):
|
|
61
66
|
anon_map = map(key_type, value_type)
|
|
@@ -63,4 +68,3 @@ def test_all_types(key_type, value_type, data):
|
|
|
63
68
|
b = x.pack()
|
|
64
69
|
x2 = anon_map.unpack(io.BytesIO(b))
|
|
65
70
|
assert x == x2
|
|
66
|
-
|
|
@@ -3,10 +3,12 @@ from bare import Enum, UInt, Void, Str
|
|
|
3
3
|
import io
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
class MyEnum(Enum):
|
|
7
8
|
A = 1
|
|
8
9
|
B = 2
|
|
9
10
|
|
|
11
|
+
|
|
10
12
|
def test_enum_basic():
|
|
11
13
|
b = MyEnum.A.pack()
|
|
12
14
|
a = MyEnum.unpack(io.BytesIO(b))
|
|
@@ -16,15 +18,17 @@ def test_enum_basic():
|
|
|
16
18
|
b = UInt(3).pack()
|
|
17
19
|
MyEnum.unpack(io.BytesIO(b))
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
|
|
22
|
+
class MyVoid(Void): ...
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
def test_custom_void():
|
|
23
26
|
x = MyVoid()
|
|
24
27
|
b = x.pack()
|
|
25
28
|
x2 = MyVoid.unpack(io.BytesIO(b))
|
|
26
29
|
assert x == x2
|
|
27
|
-
assert b == b
|
|
30
|
+
assert b == b""
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
def test_void():
|
|
30
34
|
x = Void()
|
|
@@ -33,9 +37,10 @@ def test_void():
|
|
|
33
37
|
assert x == x2
|
|
34
38
|
assert len(b) == 0
|
|
35
39
|
|
|
40
|
+
|
|
36
41
|
def test_str():
|
|
37
42
|
x = Str("Hello, world")
|
|
38
43
|
b = x.pack()
|
|
39
44
|
x2 = Str.unpack(io.BytesIO(b))
|
|
40
45
|
assert x == x2
|
|
41
|
-
assert b == b
|
|
46
|
+
assert b == b"\x0cHello, world"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import bare
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.parametrize(
|
|
7
|
+
"type_, good_values, bad_values",
|
|
8
|
+
[
|
|
9
|
+
(bare.UInt, [0, 1, 2**64 - 1], [-1, 2**64, 2**64 + 1]),
|
|
10
|
+
(
|
|
11
|
+
bare.Int,
|
|
12
|
+
[-(2**63), -(2**63) + 1, 0, 2**63, 2**63 - 1],
|
|
13
|
+
[-(2**63) - 1, 2**63 + 1],
|
|
14
|
+
),
|
|
15
|
+
(bare.U8, [0, 1, 2**8 - 1], [-1, 2**8]),
|
|
16
|
+
(bare.I8, [-(2**7), 0, 2**7 - 1], [-(2**7) - 1, 2**7]),
|
|
17
|
+
(bare.U16, [0, 1, 2**16 - 1], [-1, 2**16]),
|
|
18
|
+
(bare.I16, [-(2**15), 0, 2**15 - 1], [-(2**15) - 1, 2**15]),
|
|
19
|
+
(bare.U32, [0, 1, 2**32 - 1], [-1, 2**32]),
|
|
20
|
+
(bare.I32, [-(2**31), 0, 2**31 - 1], [-(2**31) - 1, 2**31]),
|
|
21
|
+
(bare.U64, [0, 1, 2**64 - 1], [-1, 2**64]),
|
|
22
|
+
(bare.I64, [-(2**63), 0, 2**63 - 1], [-(2**63) - 1, 2**63]),
|
|
23
|
+
],
|
|
24
|
+
)
|
|
25
|
+
def test_bounds(type_, good_values, bad_values):
|
|
26
|
+
for value in good_values:
|
|
27
|
+
x = type_(value)
|
|
28
|
+
b = x.pack()
|
|
29
|
+
x2 = type_.unpack(io.BytesIO(b))
|
|
30
|
+
assert x == x2
|
|
31
|
+
for value in bad_values:
|
|
32
|
+
with pytest.raises(TypeError):
|
|
33
|
+
type_(value)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_f32():
|
|
37
|
+
x = bare.F32(123.5)
|
|
38
|
+
b = x.pack()
|
|
39
|
+
assert len(b) == 4
|
|
40
|
+
x2 = bare.F32.unpack(io.BytesIO(b))
|
|
41
|
+
assert x == x2
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_f64():
|
|
45
|
+
x = bare.F64(123.5)
|
|
46
|
+
b = x.pack()
|
|
47
|
+
assert len(b) == 8
|
|
48
|
+
x2 = bare.F64.unpack(io.BytesIO(b))
|
|
49
|
+
assert x == x2
|
|
@@ -3,98 +3,123 @@ import bare
|
|
|
3
3
|
import io
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
class MyUnion(Union, variants=(Str, Int)): ...
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
def test_union_basic():
|
|
10
11
|
x = MyUnion(Str("Hello"))
|
|
11
12
|
b = x.pack()
|
|
12
|
-
assert b == b
|
|
13
|
+
assert b == b"\x00\x05Hello"
|
|
13
14
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
14
15
|
assert x == x2
|
|
15
16
|
with pytest.raises(TypeError):
|
|
16
17
|
x = MyUnion(True)
|
|
17
18
|
with pytest.raises(TypeError):
|
|
18
|
-
MyUnion.unpack(io.BytesIO(b
|
|
19
|
+
MyUnion.unpack(io.BytesIO(b"\x02\x05Hello"))
|
|
19
20
|
|
|
20
21
|
x = MyUnion(Int(16))
|
|
21
22
|
b = x.pack()
|
|
22
|
-
assert b == b
|
|
23
|
+
assert b == b"\x01\x20"
|
|
23
24
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
24
25
|
assert x == x2
|
|
25
26
|
|
|
27
|
+
|
|
26
28
|
def test_anonymous_union():
|
|
27
29
|
MyUnion = union(Str, Int)
|
|
28
30
|
x = MyUnion(Str("Hello"))
|
|
29
31
|
b = x.pack()
|
|
30
|
-
assert b == b
|
|
32
|
+
assert b == b"\x00\x05Hello"
|
|
31
33
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
32
34
|
assert x == x2
|
|
33
35
|
with pytest.raises(TypeError):
|
|
34
36
|
x = MyUnion(True)
|
|
35
37
|
|
|
38
|
+
|
|
36
39
|
def test_explicit_discriminant_implicit_following():
|
|
37
|
-
class MyUnion(Union, variants=(UnionVariant(Str, 4), Int)):
|
|
38
|
-
|
|
40
|
+
class MyUnion(Union, variants=(UnionVariant(Str, 4), Int)): ...
|
|
41
|
+
|
|
39
42
|
x = MyUnion(Str("Hello"))
|
|
40
43
|
b = x.pack()
|
|
41
|
-
assert b == b
|
|
44
|
+
assert b == b"\x04\x05Hello"
|
|
42
45
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
43
46
|
assert x == x2
|
|
44
47
|
with pytest.raises(TypeError):
|
|
45
48
|
x = MyUnion(True)
|
|
46
49
|
with pytest.raises(TypeError):
|
|
47
|
-
MyUnion.unpack(io.BytesIO(b
|
|
50
|
+
MyUnion.unpack(io.BytesIO(b"\x02\x05Hello"))
|
|
48
51
|
|
|
49
52
|
x = MyUnion(Int(16))
|
|
50
53
|
b = x.pack()
|
|
51
|
-
assert b == b
|
|
54
|
+
assert b == b"\x05\x20"
|
|
52
55
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
53
56
|
assert x == x2
|
|
54
57
|
|
|
58
|
+
|
|
55
59
|
def test_explicit_discriminant_all_cases():
|
|
56
|
-
class MyUnion(Union, variants=(UnionVariant(Str, 4), UnionVariant(Int, 10))):
|
|
57
|
-
|
|
60
|
+
class MyUnion(Union, variants=(UnionVariant(Str, 4), UnionVariant(Int, 10))): ...
|
|
61
|
+
|
|
58
62
|
x = MyUnion(Str("Hello"))
|
|
59
63
|
b = x.pack()
|
|
60
|
-
assert b == b
|
|
64
|
+
assert b == b"\x04\x05Hello"
|
|
61
65
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
62
66
|
assert x == x2
|
|
63
67
|
with pytest.raises(TypeError):
|
|
64
68
|
x = MyUnion(True)
|
|
65
69
|
with pytest.raises(TypeError):
|
|
66
|
-
MyUnion.unpack(io.BytesIO(b
|
|
70
|
+
MyUnion.unpack(io.BytesIO(b"\x02\x05Hello"))
|
|
67
71
|
|
|
68
72
|
x = MyUnion(Int(16))
|
|
69
73
|
b = x.pack()
|
|
70
|
-
assert b == b
|
|
74
|
+
assert b == b"\x0a\x20"
|
|
71
75
|
x2 = MyUnion.unpack(io.BytesIO(b))
|
|
72
76
|
assert x == x2
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
78
|
+
|
|
79
|
+
class AllPrimativeTypes(
|
|
80
|
+
Union,
|
|
81
|
+
variants=(
|
|
82
|
+
Str,
|
|
83
|
+
Int,
|
|
84
|
+
bare.UInt,
|
|
85
|
+
bare.Bool,
|
|
86
|
+
bare.F32,
|
|
87
|
+
bare.F64,
|
|
88
|
+
bare.U8,
|
|
89
|
+
bare.I8,
|
|
90
|
+
bare.U16,
|
|
91
|
+
bare.I16,
|
|
92
|
+
bare.U32,
|
|
93
|
+
bare.I32,
|
|
94
|
+
bare.U64,
|
|
95
|
+
bare.I64,
|
|
96
|
+
bare.Data,
|
|
97
|
+
bare.Void,
|
|
98
|
+
),
|
|
99
|
+
): ...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize(
|
|
103
|
+
"value,discriminant",
|
|
104
|
+
[
|
|
105
|
+
(Str("Hello"), 0),
|
|
106
|
+
(Int(123), 1),
|
|
107
|
+
(bare.UInt(123), 2),
|
|
108
|
+
(bare.Bool(True), 3),
|
|
109
|
+
(bare.F32(123.5), 4),
|
|
110
|
+
(bare.F64(123.5), 5),
|
|
111
|
+
(bare.U8(123), 6),
|
|
112
|
+
(bare.I8(123), 7),
|
|
113
|
+
(bare.U16(123), 8),
|
|
114
|
+
(bare.I16(123), 9),
|
|
115
|
+
(bare.U32(123), 10),
|
|
116
|
+
(bare.I32(123), 11),
|
|
117
|
+
(bare.U64(123), 12),
|
|
118
|
+
(bare.I64(123), 13),
|
|
119
|
+
(bare.Data(b"123"), 14),
|
|
120
|
+
(bare.Void(), 15),
|
|
121
|
+
],
|
|
122
|
+
)
|
|
98
123
|
def test_union_all_primative_types(value, discriminant):
|
|
99
124
|
x = AllPrimativeTypes(value)
|
|
100
125
|
b = x.pack()
|
|
@@ -5,7 +5,7 @@ from .baretype import BAREType
|
|
|
5
5
|
T = TypeVar("T", bound=BAREType)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class Field
|
|
8
|
+
class Field:
|
|
9
9
|
"""
|
|
10
10
|
A descriptor that assists with assigning/retrieving values from a BARE Struct.
|
|
11
11
|
It is reponsible for performing type validation on assignment of struct fields.
|
|
@@ -21,9 +21,9 @@ class Field(Generic[T]):
|
|
|
21
21
|
|
|
22
22
|
attr: str
|
|
23
23
|
name: str
|
|
24
|
-
ty: Type[BAREType
|
|
24
|
+
ty: Type[BAREType]
|
|
25
25
|
|
|
26
|
-
def __init__(self, ty: Type[BAREType
|
|
26
|
+
def __init__(self, ty: Type[BAREType], attr: str | None = None):
|
|
27
27
|
# ignore the typing here because these will always be set when assigned to an
|
|
28
28
|
# object
|
|
29
29
|
self.attr = attr # type: ignore
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"pdm-backend",
|
|
4
|
+
]
|
|
5
|
+
build-backend = "pdm.backend"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "pybare"
|
|
9
|
+
version = "1.2.4"
|
|
10
|
+
description = "A BARE Encoding Library and Data Validation Library for Python."
|
|
11
|
+
dependencies = []
|
|
12
|
+
requires-python = ">= 3.10"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Noah Pederson", email = "noah@packetlost.dev" },
|
|
15
|
+
]
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.license]
|
|
28
|
+
file = "LICENSE"
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://sr.ht/~chiefnoah/pybare/"
|
|
32
|
+
Repository = "https://git.sr.ht/~chiefnoah/pybare"
|
|
33
|
+
Issues = "https://todo.sr.ht/~chiefnoah/pybare"
|
|
34
|
+
Changelog = "https://git.sr.ht/~chiefnoah/pybare/tree/master/item/CHANGELOG.md"
|
|
35
|
+
|
|
36
|
+
[tool]
|
pybare-1.2.2/PKG-INFO
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pybare
|
|
3
|
-
Version: 1.2.2
|
|
4
|
-
Summary: A declarative implementation of BARE for Python
|
|
5
|
-
Home-page: https://sr.ht/~chiefnoah/pybare/
|
|
6
|
-
Author: Noah Pederson
|
|
7
|
-
Author-email: noah@packetlost.dev
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.10
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
|
|
17
|
-
# PyBARE
|
|
18
|
-
[](https://builds.sr.ht/~chiefnoah/pybare?)
|
|
19
|
-
|
|
20
|
-
A declarative implementation of the [BARE](https://baremessages.org/) message
|
|
21
|
-
format for Python 3.11+
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
pybare is a general purpose library for strongly typed primitives in Python that
|
|
26
|
-
supports serializing to and from BARE messages.
|
|
27
|
-
|
|
28
|
-
```shell
|
|
29
|
-
pip install pybare
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Goals
|
|
33
|
-
|
|
34
|
-
* Provide a declarative structure for defining types
|
|
35
|
-
* Validation on value updates
|
|
36
|
-
* Support streaming messages
|
|
37
|
-
|
|
38
|
-
## Status
|
|
39
|
-
|
|
40
|
-
pybare fully implements all BARE types for both encoding and decoding. This
|
|
41
|
-
includes reading multiple messages from the same `BinaryIO` stream.
|
|
42
|
-
|
|
43
|
-
### TODO
|
|
44
|
-
|
|
45
|
-
- [ ] Codegen based on `.schema` files
|
|
46
|
-
- [ ] Better documentation
|
|
47
|
-
- [ ] More tests
|
|
48
|
-
- [ ] Fast C implementation for encoding
|
|
49
|
-
|
|
50
|
-
## Examples
|
|
51
|
-
|
|
52
|
-
pybare currently requires you define your structures by hand. Examples can be
|
|
53
|
-
found in the
|
|
54
|
-
[tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
|
|
55
|
-
|
|
56
|
-
### Quickstart
|
|
57
|
-
|
|
58
|
-
```python
|
|
59
|
-
from bare import Struct, map, Str, UInt, optional, data, array, Void
|
|
60
|
-
|
|
61
|
-
# Alternatively, class Data(size=64): ...
|
|
62
|
-
PubKey = data(64) # 512 bits
|
|
63
|
-
|
|
64
|
-
class User(Struct):
|
|
65
|
-
username = Filed(Str)
|
|
66
|
-
userid = Field(Int)
|
|
67
|
-
email = Field(optional(Str))
|
|
68
|
-
keys = Field(map(Str, PubKey))
|
|
69
|
-
repos = Field(array(Str)) # variable length array
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
|
|
73
|
-
noah.username == 'chiefnoah'
|
|
74
|
-
noah.username = 'someoneelse'
|
|
75
|
-
noah.username == 'someoneelse'
|
|
76
|
-
noah.userid == 1 # True
|
|
77
|
-
noah.username = 1 # raise: bare.ValidationError
|
|
78
|
-
noah.keys # {} (empty dict)
|
|
79
|
-
noah.keys['my key'] = bytes(64) #\x00\x00...
|
|
80
|
-
noah.keys['oops'] = bytes(1) # raise: bare.ValidationError
|
|
81
|
-
noah.email == Void() # True
|
|
82
|
-
noah.email = 12345 # raise: bare.ValidationError
|
|
83
|
-
noah.pack() # \x00\x01 ... (binary data)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Note, you **must** wrap the desired type in a `Field` to get its 'magic' behavior.
|
|
87
|
-
Class or instance fields that are not wrapped in a `Field` will be ignored by the `pack`
|
|
88
|
-
and `unpack` methods.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
To contribute, send patches to [~chiefnoah/inbox@lists.sr.ht](mailto:~chiefnoah/inbox@lists.sr.ht)
|
pybare-1.2.2/bare/test_number.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import io
|
|
2
|
-
import bare
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@pytest.mark.parametrize(
|
|
7
|
-
"type_, good_values, bad_values",
|
|
8
|
-
[
|
|
9
|
-
(bare.UInt, [0, 1, 2**64-1], [-1, 2**64, 2**64+1]),
|
|
10
|
-
(bare.Int, [-2**63, -(2**63)+1, 0, 2**63, 2**63-1], [-2**63-1, 2**63+1]),
|
|
11
|
-
(bare.U8, [0, 1, 2**8-1], [-1, 2**8]),
|
|
12
|
-
(bare.I8, [-2**7, 0, 2**7-1], [-2**7-1, 2**7]),
|
|
13
|
-
(bare.U16, [0, 1, 2**16-1], [-1, 2**16]),
|
|
14
|
-
(bare.I16, [-2**15, 0, 2**15-1], [-2**15-1, 2**15]),
|
|
15
|
-
(bare.U32, [0, 1, 2**32-1], [-1, 2**32]),
|
|
16
|
-
(bare.I32, [-2**31, 0, 2**31-1], [-2**31-1, 2**31]),
|
|
17
|
-
(bare.U64, [0, 1, 2**64-1], [-1, 2**64]),
|
|
18
|
-
(bare.I64, [-2**63, 0, 2**63-1], [-2**63-1, 2**63]),
|
|
19
|
-
]
|
|
20
|
-
)
|
|
21
|
-
def test_bounds(type_, good_values, bad_values):
|
|
22
|
-
for value in good_values:
|
|
23
|
-
x = type_(value)
|
|
24
|
-
b = x.pack()
|
|
25
|
-
x2 = type_.unpack(io.BytesIO(b))
|
|
26
|
-
assert x == x2
|
|
27
|
-
for value in bad_values:
|
|
28
|
-
with pytest.raises(TypeError):
|
|
29
|
-
type_(value)
|
|
30
|
-
|
|
31
|
-
def test_f32():
|
|
32
|
-
x = bare.F32(123.5)
|
|
33
|
-
b = x.pack()
|
|
34
|
-
assert len(b) == 4
|
|
35
|
-
x2 = bare.F32.unpack(io.BytesIO(b))
|
|
36
|
-
assert x == x2
|
|
37
|
-
|
|
38
|
-
def test_f64():
|
|
39
|
-
x = bare.F64(123.5)
|
|
40
|
-
b = x.pack()
|
|
41
|
-
assert len(b) == 8
|
|
42
|
-
x2 = bare.F64.unpack(io.BytesIO(b))
|
|
43
|
-
assert x == x2
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pybare
|
|
3
|
-
Version: 1.2.2
|
|
4
|
-
Summary: A declarative implementation of BARE for Python
|
|
5
|
-
Home-page: https://sr.ht/~chiefnoah/pybare/
|
|
6
|
-
Author: Noah Pederson
|
|
7
|
-
Author-email: noah@packetlost.dev
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.10
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
|
|
17
|
-
# PyBARE
|
|
18
|
-
[](https://builds.sr.ht/~chiefnoah/pybare?)
|
|
19
|
-
|
|
20
|
-
A declarative implementation of the [BARE](https://baremessages.org/) message
|
|
21
|
-
format for Python 3.11+
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
pybare is a general purpose library for strongly typed primitives in Python that
|
|
26
|
-
supports serializing to and from BARE messages.
|
|
27
|
-
|
|
28
|
-
```shell
|
|
29
|
-
pip install pybare
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Goals
|
|
33
|
-
|
|
34
|
-
* Provide a declarative structure for defining types
|
|
35
|
-
* Validation on value updates
|
|
36
|
-
* Support streaming messages
|
|
37
|
-
|
|
38
|
-
## Status
|
|
39
|
-
|
|
40
|
-
pybare fully implements all BARE types for both encoding and decoding. This
|
|
41
|
-
includes reading multiple messages from the same `BinaryIO` stream.
|
|
42
|
-
|
|
43
|
-
### TODO
|
|
44
|
-
|
|
45
|
-
- [ ] Codegen based on `.schema` files
|
|
46
|
-
- [ ] Better documentation
|
|
47
|
-
- [ ] More tests
|
|
48
|
-
- [ ] Fast C implementation for encoding
|
|
49
|
-
|
|
50
|
-
## Examples
|
|
51
|
-
|
|
52
|
-
pybare currently requires you define your structures by hand. Examples can be
|
|
53
|
-
found in the
|
|
54
|
-
[tests](https://git.sr.ht/~chiefnoah/pybare/tree/master/bare/test_encoder.py).
|
|
55
|
-
|
|
56
|
-
### Quickstart
|
|
57
|
-
|
|
58
|
-
```python
|
|
59
|
-
from bare import Struct, map, Str, UInt, optional, data, array, Void
|
|
60
|
-
|
|
61
|
-
# Alternatively, class Data(size=64): ...
|
|
62
|
-
PubKey = data(64) # 512 bits
|
|
63
|
-
|
|
64
|
-
class User(Struct):
|
|
65
|
-
username = Filed(Str)
|
|
66
|
-
userid = Field(Int)
|
|
67
|
-
email = Field(optional(Str))
|
|
68
|
-
keys = Field(map(Str, PubKey))
|
|
69
|
-
repos = Field(array(Str)) # variable length array
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
noah = User(username="chiefnoah", userid=1, email=Void(), keys={}, repos=[])
|
|
73
|
-
noah.username == 'chiefnoah'
|
|
74
|
-
noah.username = 'someoneelse'
|
|
75
|
-
noah.username == 'someoneelse'
|
|
76
|
-
noah.userid == 1 # True
|
|
77
|
-
noah.username = 1 # raise: bare.ValidationError
|
|
78
|
-
noah.keys # {} (empty dict)
|
|
79
|
-
noah.keys['my key'] = bytes(64) #\x00\x00...
|
|
80
|
-
noah.keys['oops'] = bytes(1) # raise: bare.ValidationError
|
|
81
|
-
noah.email == Void() # True
|
|
82
|
-
noah.email = 12345 # raise: bare.ValidationError
|
|
83
|
-
noah.pack() # \x00\x01 ... (binary data)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Note, you **must** wrap the desired type in a `Field` to get its 'magic' behavior.
|
|
87
|
-
Class or instance fields that are not wrapped in a `Field` will be ignored by the `pack`
|
|
88
|
-
and `unpack` methods.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
To contribute, send patches to [~chiefnoah/inbox@lists.sr.ht](mailto:~chiefnoah/inbox@lists.sr.ht)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
bare/__init__.py
|
|
5
|
-
bare/barearray.py
|
|
6
|
-
bare/barestruct.py
|
|
7
|
-
bare/baretype.py
|
|
8
|
-
bare/data.py
|
|
9
|
-
bare/map.py
|
|
10
|
-
bare/misc.py
|
|
11
|
-
bare/number.py
|
|
12
|
-
bare/test_barearray.py
|
|
13
|
-
bare/test_barestruct.py
|
|
14
|
-
bare/test_data.py
|
|
15
|
-
bare/test_encoder.py
|
|
16
|
-
bare/test_map.py
|
|
17
|
-
bare/test_misc.py
|
|
18
|
-
bare/test_number.py
|
|
19
|
-
bare/test_union.py
|
|
20
|
-
bare/union.py
|
|
21
|
-
bare/util.py
|
|
22
|
-
pybare.egg-info/PKG-INFO
|
|
23
|
-
pybare.egg-info/SOURCES.txt
|
|
24
|
-
pybare.egg-info/dependency_links.txt
|
|
25
|
-
pybare.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bare
|
pybare-1.2.2/setup.cfg
DELETED
pybare-1.2.2/setup.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import setuptools
|
|
2
|
-
|
|
3
|
-
with open("README.md", "r") as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setuptools.setup(
|
|
7
|
-
name="pybare", # Replace with your own username
|
|
8
|
-
version="1.2.2",
|
|
9
|
-
author="Noah Pederson",
|
|
10
|
-
author_email="noah@packetlost.dev",
|
|
11
|
-
description="A declarative implementation of BARE for Python",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
url="https://sr.ht/~chiefnoah/pybare/",
|
|
15
|
-
packages=setuptools.find_packages(),
|
|
16
|
-
classifiers=[
|
|
17
|
-
"Programming Language :: Python :: 3.10",
|
|
18
|
-
"Programming Language :: Python :: 3.11",
|
|
19
|
-
"Programming Language :: Python :: 3.12",
|
|
20
|
-
"License :: OSI Approved :: MIT License",
|
|
21
|
-
"Operating System :: OS Independent",
|
|
22
|
-
],
|
|
23
|
-
python_requires=">=3.10",
|
|
24
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|