pybare 1.2.4__tar.gz → 1.3.0__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 → pybare-1.3.0}/PKG-INFO +22 -19
- {pybare-1.2.4 → pybare-1.3.0}/README.md +2 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/barestruct.py +19 -7
- {pybare-1.2.4 → pybare-1.3.0}/bare/baretype.py +6 -3
- {pybare-1.2.4 → pybare-1.3.0}/bare/data.py +2 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/number.py +31 -21
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_barearray.py +8 -3
- pybare-1.3.0/bare/test_barestruct.py +108 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_data.py +21 -3
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_encoder.py +12 -9
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_map.py +6 -2
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_misc.py +4 -1
- pybare-1.3.0/bare/test_number.py +93 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/test_union.py +10 -4
- {pybare-1.2.4 → pybare-1.3.0}/bare/union.py +1 -0
- pybare-1.3.0/bare/util.py +65 -0
- {pybare-1.2.4 → pybare-1.3.0}/pyproject.toml +1 -1
- pybare-1.2.4/bare/test_barestruct.py +0 -35
- pybare-1.2.4/bare/test_number.py +0 -49
- pybare-1.2.4/bare/util.py +0 -42
- {pybare-1.2.4 → pybare-1.3.0}/LICENSE +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/__init__.py +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/_examples/customer.bin +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/_examples/employee.bin +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/_examples/people.bin +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/_examples/terminated.bin +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/barearray.py +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/map.py +0 -0
- {pybare-1.2.4 → pybare-1.3.0}/bare/misc.py +0 -0
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pybare
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A BARE Encoding Library and Data Validation Library for Python.
|
|
5
5
|
Author-Email: Noah Pederson <noah@packetlost.dev>
|
|
6
6
|
License: Copyright 2020 Noah Pederson
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
|
|
25
26
|
Classifier: Development Status :: 4 - Beta
|
|
26
27
|
Classifier: Intended Audience :: Developers
|
|
27
28
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -38,6 +39,8 @@ Description-Content-Type: text/markdown
|
|
|
38
39
|
|
|
39
40
|
# PyBARE
|
|
40
41
|
[](https://builds.sr.ht/~chiefnoah/pybare?)
|
|
42
|
+
[](https://pybare.ngp.computer)
|
|
41
44
|
|
|
42
45
|
A declarative implementation of the [BARE](https://baremessages.org/) message
|
|
43
46
|
format for Python 3.10+
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# PyBARE
|
|
2
2
|
[](https://builds.sr.ht/~chiefnoah/pybare?)
|
|
3
|
+
[](https://pybare.ngp.computer)
|
|
3
5
|
|
|
4
6
|
A declarative implementation of the [BARE](https://baremessages.org/) message
|
|
5
7
|
format for Python 3.10+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
|
-
from typing import Any, BinaryIO,
|
|
4
|
+
from typing import Any, BinaryIO, Type, TypeVar, _ProtocolMeta
|
|
5
5
|
|
|
6
6
|
from .baretype import BAREType
|
|
7
7
|
from .util import Field
|
|
@@ -67,14 +67,26 @@ class Struct(BAREType, metaclass=StructMeta):
|
|
|
67
67
|
|
|
68
68
|
def __init__(self, **kwargs):
|
|
69
69
|
name = self.__class__.__name__
|
|
70
|
-
for key
|
|
70
|
+
for key in kwargs:
|
|
71
71
|
if key not in self._fields:
|
|
72
72
|
raise ValueError(f"Got unexpected field for Struct {name}: {key}")
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
|
|
74
|
+
missing_fields = []
|
|
75
|
+
for key, ty in self._fields.items():
|
|
76
|
+
field = self.__class__.__dict__[key]
|
|
77
|
+
if key in kwargs:
|
|
78
|
+
value = kwargs[key]
|
|
79
|
+
elif field.has_default():
|
|
80
|
+
value = field.get_default()
|
|
81
|
+
elif getattr(ty, "_optional", False):
|
|
82
|
+
value = None
|
|
83
|
+
else:
|
|
84
|
+
missing_fields.append(key)
|
|
85
|
+
continue
|
|
86
|
+
if not isinstance(value, ty):
|
|
87
|
+
value = ty(value)
|
|
75
88
|
setattr(self, key, value)
|
|
76
|
-
missing_fields
|
|
77
|
-
if len(missing_fields) > 0:
|
|
89
|
+
if missing_fields:
|
|
78
90
|
raise ValueError(
|
|
79
91
|
f"Missing fields for {name}.__init__. "
|
|
80
92
|
f"Expected: {', '.join(missing_fields)}"
|
|
@@ -122,7 +134,7 @@ class Struct(BAREType, metaclass=StructMeta):
|
|
|
122
134
|
return NotImplemented
|
|
123
135
|
|
|
124
136
|
|
|
125
|
-
def struct(**kwargs:
|
|
137
|
+
def struct(**kwargs: Type[BAREType]) -> Type[Struct]:
|
|
126
138
|
"""
|
|
127
139
|
A function that defines and returnes an anonymous BARE `Struct` subclass with the
|
|
128
140
|
provided `kwargs` as fields. The name of each kwarg becomes the field name, with the
|
|
@@ -7,14 +7,17 @@ T = TypeVar("T", covariant=True)
|
|
|
7
7
|
|
|
8
8
|
class BAREType(Protocol, Generic[T]):
|
|
9
9
|
@abstractmethod
|
|
10
|
-
def __init__(self, *arg, **kwargs):
|
|
10
|
+
def __init__(self, *arg, **kwargs):
|
|
11
|
+
...
|
|
11
12
|
|
|
12
13
|
@abstractmethod
|
|
13
|
-
def pack(self) -> bytes:
|
|
14
|
+
def pack(self) -> bytes:
|
|
15
|
+
...
|
|
14
16
|
|
|
15
17
|
@classmethod
|
|
16
18
|
@abstractmethod
|
|
17
|
-
def unpack(cls, fp: BinaryIO) -> BAREType:
|
|
19
|
+
def unpack(cls, fp: BinaryIO) -> BAREType:
|
|
20
|
+
...
|
|
18
21
|
|
|
19
22
|
@classmethod
|
|
20
23
|
@abstractmethod
|
|
@@ -68,6 +68,8 @@ class Data(BAREType[bytes], metaclass=DataMeta):
|
|
|
68
68
|
def unpack(cls, fp: BinaryIO) -> Data:
|
|
69
69
|
size = cls._size or UInt.unpack(fp).value
|
|
70
70
|
buf = fp.read(size)
|
|
71
|
+
if len(buf) != size:
|
|
72
|
+
raise EOFError(f"Expected {size} data bytes, got {len(buf)}")
|
|
71
73
|
return cls(buf)
|
|
72
74
|
|
|
73
75
|
|
|
@@ -113,7 +113,7 @@ class Int(NumberMixin[int]):
|
|
|
113
113
|
return True
|
|
114
114
|
try:
|
|
115
115
|
value = int(value)
|
|
116
|
-
return -(1 << 63) <= value and value
|
|
116
|
+
return -(1 << 63) <= value and value < (1 << 63)
|
|
117
117
|
except (ValueError, TypeError):
|
|
118
118
|
return False
|
|
119
119
|
|
|
@@ -139,7 +139,7 @@ class F64(NumberMixin[float]):
|
|
|
139
139
|
|
|
140
140
|
@classmethod
|
|
141
141
|
def unpack(cls, fp: BinaryIO) -> F64:
|
|
142
|
-
buf = fp
|
|
142
|
+
buf = _read_exact(fp, struct.calcsize("<d"))
|
|
143
143
|
return cls(struct.unpack("<d", buf)[0])
|
|
144
144
|
|
|
145
145
|
@classmethod
|
|
@@ -172,7 +172,7 @@ class F32(NumberMixin[float]):
|
|
|
172
172
|
|
|
173
173
|
@classmethod
|
|
174
174
|
def unpack(cls, fp: BinaryIO) -> F32:
|
|
175
|
-
buf = fp
|
|
175
|
+
buf = _read_exact(fp, struct.calcsize("<f"))
|
|
176
176
|
return cls(struct.unpack("<f", buf)[0]) # type: ignore
|
|
177
177
|
|
|
178
178
|
@classmethod
|
|
@@ -196,7 +196,7 @@ class U8(NumberMixin[int]):
|
|
|
196
196
|
|
|
197
197
|
@classmethod
|
|
198
198
|
def unpack(cls, fp: BinaryIO) -> U8:
|
|
199
|
-
buf = fp
|
|
199
|
+
buf = _read_exact(fp, struct.calcsize("<B"))
|
|
200
200
|
return cls(struct.unpack("<B", buf)[0]) # type: ignore
|
|
201
201
|
|
|
202
202
|
@classmethod
|
|
@@ -220,7 +220,7 @@ class Bool(NumberMixin[bool]):
|
|
|
220
220
|
|
|
221
221
|
@classmethod
|
|
222
222
|
def unpack(cls, fp: BinaryIO) -> Bool:
|
|
223
|
-
buf = fp
|
|
223
|
+
buf = _read_exact(fp, struct.calcsize("<?"))
|
|
224
224
|
return cls(struct.unpack("<?", buf)[0]) # type: ignore
|
|
225
225
|
|
|
226
226
|
@classmethod
|
|
@@ -238,7 +238,7 @@ class I8(NumberMixin[int]):
|
|
|
238
238
|
|
|
239
239
|
@classmethod
|
|
240
240
|
def unpack(cls, fp: BinaryIO) -> I8:
|
|
241
|
-
buf = fp
|
|
241
|
+
buf = _read_exact(fp, struct.calcsize("<b"))
|
|
242
242
|
return cls(struct.unpack("<b", buf)[0]) # type: ignore
|
|
243
243
|
|
|
244
244
|
@classmethod
|
|
@@ -262,7 +262,7 @@ class U16(NumberMixin[int]):
|
|
|
262
262
|
|
|
263
263
|
@classmethod
|
|
264
264
|
def unpack(cls, fp: BinaryIO) -> U16:
|
|
265
|
-
buf = fp
|
|
265
|
+
buf = _read_exact(fp, struct.calcsize("<H"))
|
|
266
266
|
return cls(struct.unpack("<H", buf)[0]) # type: ignore
|
|
267
267
|
|
|
268
268
|
@classmethod
|
|
@@ -286,7 +286,7 @@ class I16(NumberMixin[int]):
|
|
|
286
286
|
|
|
287
287
|
@classmethod
|
|
288
288
|
def unpack(cls, fp: BinaryIO) -> I16:
|
|
289
|
-
buf = fp
|
|
289
|
+
buf = _read_exact(fp, struct.calcsize("<h"))
|
|
290
290
|
return cls(struct.unpack("<h", buf)[0]) # type: ignore
|
|
291
291
|
|
|
292
292
|
@classmethod
|
|
@@ -310,7 +310,7 @@ class U32(NumberMixin[int]):
|
|
|
310
310
|
|
|
311
311
|
@classmethod
|
|
312
312
|
def unpack(cls, fp: BinaryIO) -> U32:
|
|
313
|
-
buf = fp
|
|
313
|
+
buf = _read_exact(fp, struct.calcsize("<L"))
|
|
314
314
|
return cls(struct.unpack("<L", buf)[0]) # type: ignore
|
|
315
315
|
|
|
316
316
|
@classmethod
|
|
@@ -334,7 +334,7 @@ class I32(NumberMixin[int]):
|
|
|
334
334
|
|
|
335
335
|
@classmethod
|
|
336
336
|
def unpack(cls, fp: BinaryIO) -> I32:
|
|
337
|
-
buf = fp
|
|
337
|
+
buf = _read_exact(fp, struct.calcsize("<l"))
|
|
338
338
|
return cls(struct.unpack("<l", buf)[0]) # type: ignore
|
|
339
339
|
|
|
340
340
|
@classmethod
|
|
@@ -358,7 +358,7 @@ class U64(NumberMixin[int]):
|
|
|
358
358
|
|
|
359
359
|
@classmethod
|
|
360
360
|
def unpack(cls, fp: BinaryIO) -> U64:
|
|
361
|
-
buf = fp
|
|
361
|
+
buf = _read_exact(fp, struct.calcsize("<Q"))
|
|
362
362
|
return cls(struct.unpack("<Q", buf)[0]) # type: ignore
|
|
363
363
|
|
|
364
364
|
@classmethod
|
|
@@ -385,7 +385,7 @@ class I64(NumberMixin[int]):
|
|
|
385
385
|
|
|
386
386
|
@classmethod
|
|
387
387
|
def unpack(cls, fp: BinaryIO) -> I64:
|
|
388
|
-
buf = fp
|
|
388
|
+
buf = _read_exact(fp, struct.calcsize("<q"))
|
|
389
389
|
return cls(struct.unpack("<q", buf)[0]) # type: ignore
|
|
390
390
|
|
|
391
391
|
@classmethod
|
|
@@ -399,6 +399,13 @@ class I64(NumberMixin[int]):
|
|
|
399
399
|
return False
|
|
400
400
|
|
|
401
401
|
|
|
402
|
+
def _read_exact(fp: BinaryIO, size: int) -> bytes:
|
|
403
|
+
buf = fp.read(size)
|
|
404
|
+
if len(buf) != size:
|
|
405
|
+
raise EOFError(f"Expected {size} bytes, got {len(buf)}")
|
|
406
|
+
return buf
|
|
407
|
+
|
|
408
|
+
|
|
402
409
|
# This is adapted from https://git.sr.ht/~martijnbraam/bare-py/tree/master/bare/__init__.py#L29
|
|
403
410
|
def _write_varint(fp: BinaryIO, val: int, signed=True) -> int:
|
|
404
411
|
written = 0
|
|
@@ -416,19 +423,22 @@ def _write_varint(fp: BinaryIO, val: int, signed=True) -> int:
|
|
|
416
423
|
|
|
417
424
|
def _read_varint(fp: BinaryIO, signed=True) -> int:
|
|
418
425
|
output = 0
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
426
|
+
for index in range(10):
|
|
427
|
+
buf = fp.read(1)
|
|
428
|
+
if not buf:
|
|
429
|
+
raise EOFError("Not enough bytes in buffer to decode varint")
|
|
430
|
+
b = buf[0]
|
|
431
|
+
if index == 9 and b > 1:
|
|
432
|
+
raise ValueError("Varint exceeds 64 bits")
|
|
425
433
|
if b < 0x80:
|
|
426
|
-
|
|
434
|
+
if index > 0 and b == 0:
|
|
435
|
+
raise ValueError("Varint is not canonically encoded")
|
|
436
|
+
value = output | b << (index * 7)
|
|
427
437
|
if signed:
|
|
428
438
|
sign = value % 2
|
|
429
439
|
value = value // 2
|
|
430
440
|
if sign:
|
|
431
441
|
value = -(value + 1)
|
|
432
442
|
return value
|
|
433
|
-
output |= (b & 0x7F) <<
|
|
434
|
-
|
|
443
|
+
output |= (b & 0x7F) << (index * 7)
|
|
444
|
+
raise ValueError("Varint exceeds 64 bits")
|
|
@@ -5,11 +5,15 @@ import bare
|
|
|
5
5
|
import pytest
|
|
6
6
|
import io
|
|
7
7
|
|
|
8
|
+
__all__ = []
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
def test_2d_array_unsized():
|
|
10
|
-
class InnerArray(Array, inner=UInt):
|
|
12
|
+
class InnerArray(Array, inner=UInt):
|
|
13
|
+
...
|
|
11
14
|
|
|
12
|
-
class My2DArray(Array, inner=InnerArray):
|
|
15
|
+
class My2DArray(Array, inner=InnerArray):
|
|
16
|
+
...
|
|
13
17
|
|
|
14
18
|
input = [[1, 2], [3, 4]]
|
|
15
19
|
x = My2DArray(input)
|
|
@@ -45,7 +49,8 @@ def test_anonymous_array_sized():
|
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
def test_array_sized():
|
|
48
|
-
class MyArry(Array, inner=UInt, size=3):
|
|
52
|
+
class MyArry(Array, inner=UInt, size=3):
|
|
53
|
+
...
|
|
49
54
|
|
|
50
55
|
input = [1, 2, 3]
|
|
51
56
|
x = MyArry(input)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from bare import Struct, UInt, Str, Void, Field, array, optional, struct
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
__all__ = []
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_struct_basic():
|
|
9
|
+
class MyStruct(Struct):
|
|
10
|
+
a = Field(UInt)
|
|
11
|
+
b = Field(Str)
|
|
12
|
+
c: int
|
|
13
|
+
|
|
14
|
+
x = MyStruct(a=123, b="Hello")
|
|
15
|
+
b = x.pack()
|
|
16
|
+
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
17
|
+
assert x == x2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_struct_requires_all_fields():
|
|
21
|
+
class MyStruct(Struct):
|
|
22
|
+
a = Field(UInt)
|
|
23
|
+
b = Field(Str)
|
|
24
|
+
|
|
25
|
+
with pytest.raises(ValueError, match="Missing fields"):
|
|
26
|
+
MyStruct(a=1)
|
|
27
|
+
|
|
28
|
+
with pytest.raises(ValueError, match="Missing fields"):
|
|
29
|
+
MyStruct()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_struct_field_default():
|
|
33
|
+
class MyStruct(Struct):
|
|
34
|
+
name = Field(Str)
|
|
35
|
+
count = Field(UInt, default=0)
|
|
36
|
+
|
|
37
|
+
value = MyStruct(name="example")
|
|
38
|
+
|
|
39
|
+
assert value.count == 0
|
|
40
|
+
assert MyStruct.unpack(io.BytesIO(value.pack())) == value
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_struct_field_default_factory_is_fresh_per_instance():
|
|
44
|
+
Values = array(UInt)
|
|
45
|
+
|
|
46
|
+
class MyStruct(Struct):
|
|
47
|
+
values = Field(Values, default_factory=list)
|
|
48
|
+
|
|
49
|
+
first = MyStruct()
|
|
50
|
+
second = MyStruct()
|
|
51
|
+
first.values.append(1)
|
|
52
|
+
|
|
53
|
+
assert first.values == [1]
|
|
54
|
+
assert second.values == []
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_struct_field_default_is_validated():
|
|
58
|
+
class MyStruct(Struct):
|
|
59
|
+
count = Field(UInt, default=-1)
|
|
60
|
+
|
|
61
|
+
with pytest.raises(TypeError):
|
|
62
|
+
MyStruct()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_field_rejects_default_and_default_factory():
|
|
66
|
+
with pytest.raises(TypeError, match="both default and default_factory"):
|
|
67
|
+
Field(UInt, default=0, default_factory=lambda: 0)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_optional_struct_field_defaults_to_void():
|
|
71
|
+
class MyStruct(Struct):
|
|
72
|
+
name = Field(Str)
|
|
73
|
+
description = Field(optional(Str))
|
|
74
|
+
|
|
75
|
+
value = MyStruct(name="example")
|
|
76
|
+
|
|
77
|
+
assert value.description == Void()
|
|
78
|
+
assert MyStruct.unpack(io.BytesIO(value.pack())) == value
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_explicit_optional_struct_field_overrides_implicit_default():
|
|
82
|
+
class MyStruct(Struct):
|
|
83
|
+
description = Field(optional(Str))
|
|
84
|
+
|
|
85
|
+
value = MyStruct(description=Str("provided"))
|
|
86
|
+
|
|
87
|
+
assert value.description == "provided"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_nested_struct():
|
|
91
|
+
class InnerStruct(Struct):
|
|
92
|
+
i = Field(UInt)
|
|
93
|
+
|
|
94
|
+
class MyStruct(Struct):
|
|
95
|
+
inner = Field(InnerStruct)
|
|
96
|
+
|
|
97
|
+
x = MyStruct(inner=InnerStruct(i=123))
|
|
98
|
+
b = x.pack()
|
|
99
|
+
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
100
|
+
assert x == x2
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_anonymous_struct():
|
|
104
|
+
MyStruct = struct(name=Str, age=UInt)
|
|
105
|
+
x = MyStruct(name="Noah", age=27)
|
|
106
|
+
b = x.pack()
|
|
107
|
+
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
108
|
+
assert x == x2
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
from bare import Data
|
|
1
|
+
from bare import Data
|
|
2
2
|
import io
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
__all__ = []
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
def test_data_sized():
|
|
6
|
-
class SizedData(Data, size=5):
|
|
9
|
+
class SizedData(Data, size=5):
|
|
10
|
+
...
|
|
7
11
|
|
|
8
12
|
x = SizedData(b"12345")
|
|
9
13
|
b = x.pack()
|
|
@@ -13,7 +17,8 @@ def test_data_sized():
|
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
def test_data_unsized():
|
|
16
|
-
class SizedData(Data):
|
|
20
|
+
class SizedData(Data):
|
|
21
|
+
...
|
|
17
22
|
|
|
18
23
|
x = SizedData(b"12345")
|
|
19
24
|
b = x.pack()
|
|
@@ -28,3 +33,16 @@ def test_data_raw():
|
|
|
28
33
|
assert len(b) == 6
|
|
29
34
|
x2 = Data.unpack(io.BytesIO(b))
|
|
30
35
|
assert x == x2
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_data_rejects_truncated_input():
|
|
39
|
+
with pytest.raises(EOFError):
|
|
40
|
+
Data.unpack(io.BytesIO(b"\x05abc"))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_sized_data_rejects_truncated_input():
|
|
44
|
+
class SizedData(Data, size=5):
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
with pytest.raises(EOFError):
|
|
48
|
+
SizedData.unpack(io.BytesIO(b"abc"))
|
|
@@ -27,6 +27,8 @@ from bare import (
|
|
|
27
27
|
UnionVariant,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
__all__ = []
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
class Nested(Struct):
|
|
32
34
|
s = Field(Str)
|
|
@@ -42,7 +44,7 @@ class Example(Struct):
|
|
|
42
44
|
|
|
43
45
|
def test_example_struct():
|
|
44
46
|
n = Nested(s="nested")
|
|
45
|
-
ex = Example(testint=11, teststr="a test", n=n)
|
|
47
|
+
ex = Example(testint=11, teststr="a test", testuint=0, n=n)
|
|
46
48
|
assert hasattr(ex, "testint")
|
|
47
49
|
assert hasattr(ex, "teststr")
|
|
48
50
|
assert hasattr(ex, "n")
|
|
@@ -50,7 +52,7 @@ def test_example_struct():
|
|
|
50
52
|
assert ex.testint == 11
|
|
51
53
|
assert ex.teststr == "a test"
|
|
52
54
|
assert ex.n.s == "nested"
|
|
53
|
-
ex2 = Example(testint=12, teststr="another test")
|
|
55
|
+
ex2 = Example(testint=12, teststr="another test", testuint=0, n=n)
|
|
54
56
|
assert ex2.testint == 12
|
|
55
57
|
assert ex2.teststr == "another test"
|
|
56
58
|
# Check that the values in the original instance haven't been modified
|
|
@@ -100,7 +102,8 @@ def test_map():
|
|
|
100
102
|
assert map2.value["another"] == "test"
|
|
101
103
|
|
|
102
104
|
|
|
103
|
-
class MyArray(Array, inner=Int):
|
|
105
|
+
class MyArray(Array, inner=Int):
|
|
106
|
+
...
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
class ArrayTest(Struct):
|
|
@@ -109,9 +112,7 @@ class ArrayTest(Struct):
|
|
|
109
112
|
|
|
110
113
|
|
|
111
114
|
def test_array_struct():
|
|
112
|
-
ex = ArrayTest()
|
|
113
|
-
ex.a = [1, 2, 3]
|
|
114
|
-
ex.n = [Nested(s="test")]
|
|
115
|
+
ex = ArrayTest(a=[1, 2, 3], n=[Nested(s="test")])
|
|
115
116
|
assert ex.a == [1, 2, 3]
|
|
116
117
|
with pytest.raises(TypeError):
|
|
117
118
|
ex.a = ["a", "b", "c"]
|
|
@@ -141,7 +142,8 @@ def test_optional():
|
|
|
141
142
|
ex.nested = "test"
|
|
142
143
|
|
|
143
144
|
|
|
144
|
-
class ExampleUnion(Union, variants=(Str, Int)):
|
|
145
|
+
class ExampleUnion(Union, variants=(Str, Int)):
|
|
146
|
+
...
|
|
145
147
|
|
|
146
148
|
|
|
147
149
|
class UnionTest(Struct):
|
|
@@ -234,7 +236,8 @@ class TerminatedEmployee(Void):
|
|
|
234
236
|
pass
|
|
235
237
|
|
|
236
238
|
|
|
237
|
-
class Person(Union, variants=(Customer, Employee, TerminatedEmployee)):
|
|
239
|
+
class Person(Union, variants=(Customer, Employee, TerminatedEmployee)):
|
|
240
|
+
...
|
|
238
241
|
|
|
239
242
|
|
|
240
243
|
@pytest.mark.parametrize(
|
|
@@ -247,7 +250,7 @@ def test_people(file):
|
|
|
247
250
|
try:
|
|
248
251
|
p = Person.unpack(f)
|
|
249
252
|
people.append(p)
|
|
250
|
-
except
|
|
253
|
+
except EOFError:
|
|
251
254
|
break
|
|
252
255
|
f.seek(0)
|
|
253
256
|
f = f.read()
|
|
@@ -4,8 +4,11 @@ import io
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
+
__all__ = []
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
class MyMap(Map, key_type=Str, value_type=Int):
|
|
11
|
+
...
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
def test_basic_map():
|
|
@@ -30,7 +33,8 @@ def test_anonymous_map():
|
|
|
30
33
|
def test_nested_map():
|
|
31
34
|
InnerMap = map(Str, Str)
|
|
32
35
|
|
|
33
|
-
class NestedMap(Map, key_type=Str, value_type=InnerMap):
|
|
36
|
+
class NestedMap(Map, key_type=Str, value_type=InnerMap):
|
|
37
|
+
...
|
|
34
38
|
|
|
35
39
|
x = NestedMap({"outer": {"hello": "world"}})
|
|
36
40
|
b = x.pack()
|
|
@@ -3,6 +3,8 @@ from bare import Enum, UInt, Void, Str
|
|
|
3
3
|
import io
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
+
__all__ = []
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class MyEnum(Enum):
|
|
8
10
|
A = 1
|
|
@@ -19,7 +21,8 @@ def test_enum_basic():
|
|
|
19
21
|
MyEnum.unpack(io.BytesIO(b))
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class MyVoid(Void):
|
|
24
|
+
class MyVoid(Void):
|
|
25
|
+
...
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
def test_custom_void():
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import bare
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
__all__ = []
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
"type_, good_values, bad_values",
|
|
10
|
+
[
|
|
11
|
+
(bare.UInt, [0, 1, 2**64 - 1], [-1, 2**64, 2**64 + 1]),
|
|
12
|
+
(
|
|
13
|
+
bare.Int,
|
|
14
|
+
[-(2**63), -(2**63) + 1, 0, 2**63 - 1],
|
|
15
|
+
[-(2**63) - 1, 2**63, 2**63 + 1],
|
|
16
|
+
),
|
|
17
|
+
(bare.U8, [0, 1, 2**8 - 1], [-1, 2**8]),
|
|
18
|
+
(bare.I8, [-(2**7), 0, 2**7 - 1], [-(2**7) - 1, 2**7]),
|
|
19
|
+
(bare.U16, [0, 1, 2**16 - 1], [-1, 2**16]),
|
|
20
|
+
(bare.I16, [-(2**15), 0, 2**15 - 1], [-(2**15) - 1, 2**15]),
|
|
21
|
+
(bare.U32, [0, 1, 2**32 - 1], [-1, 2**32]),
|
|
22
|
+
(bare.I32, [-(2**31), 0, 2**31 - 1], [-(2**31) - 1, 2**31]),
|
|
23
|
+
(bare.U64, [0, 1, 2**64 - 1], [-1, 2**64]),
|
|
24
|
+
(bare.I64, [-(2**63), 0, 2**63 - 1], [-(2**63) - 1, 2**63]),
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
def test_bounds(type_, good_values, bad_values):
|
|
28
|
+
for value in good_values:
|
|
29
|
+
x = type_(value)
|
|
30
|
+
encoded = x.pack()
|
|
31
|
+
decoded = type_.unpack(io.BytesIO(encoded))
|
|
32
|
+
assert decoded == x
|
|
33
|
+
assert decoded.value == value
|
|
34
|
+
for value in bad_values:
|
|
35
|
+
with pytest.raises(TypeError):
|
|
36
|
+
type_(value)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("value", [-(2**63), -(2**63) + 1, 0, 2**63 - 1])
|
|
40
|
+
def test_int_boundary_values_roundtrip(value):
|
|
41
|
+
encoded = bare.Int(value).pack()
|
|
42
|
+
decoded = bare.Int.unpack(io.BytesIO(encoded))
|
|
43
|
+
|
|
44
|
+
assert decoded.value == value
|
|
45
|
+
assert decoded.pack() == encoded
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.mark.parametrize("value", [-(2**63) - 1, 2**63])
|
|
49
|
+
def test_int_rejects_values_that_cannot_roundtrip(value):
|
|
50
|
+
with pytest.raises(TypeError):
|
|
51
|
+
bare.Int(value).pack()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_f32():
|
|
55
|
+
x = bare.F32(123.5)
|
|
56
|
+
b = x.pack()
|
|
57
|
+
assert len(b) == 4
|
|
58
|
+
x2 = bare.F32.unpack(io.BytesIO(b))
|
|
59
|
+
assert x == x2
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_f64():
|
|
63
|
+
x = bare.F64(123.5)
|
|
64
|
+
b = x.pack()
|
|
65
|
+
assert len(b) == 8
|
|
66
|
+
x2 = bare.F64.unpack(io.BytesIO(b))
|
|
67
|
+
assert x == x2
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.parametrize("encoded", [b"\x80\x00", b"\x80" * 10 + b"\x00"])
|
|
71
|
+
def test_uint_rejects_noncanonical_or_oversized_varints(encoded):
|
|
72
|
+
with pytest.raises(ValueError):
|
|
73
|
+
bare.UInt.unpack(io.BytesIO(encoded))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize(
|
|
77
|
+
"type_,size",
|
|
78
|
+
[
|
|
79
|
+
(bare.F32, 4),
|
|
80
|
+
(bare.F64, 8),
|
|
81
|
+
(bare.U8, 1),
|
|
82
|
+
(bare.I8, 1),
|
|
83
|
+
(bare.U16, 2),
|
|
84
|
+
(bare.I16, 2),
|
|
85
|
+
(bare.U32, 4),
|
|
86
|
+
(bare.I32, 4),
|
|
87
|
+
(bare.U64, 8),
|
|
88
|
+
(bare.I64, 8),
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
def test_fixed_width_number_rejects_truncated_input(type_, size):
|
|
92
|
+
with pytest.raises(EOFError):
|
|
93
|
+
type_.unpack(io.BytesIO(b"\x00" * (size - 1)))
|
|
@@ -3,8 +3,11 @@ import bare
|
|
|
3
3
|
import io
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
+
__all__ = []
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
class MyUnion(Union, variants=(Str, Int)):
|
|
10
|
+
...
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
def test_union_basic():
|
|
@@ -37,7 +40,8 @@ def test_anonymous_union():
|
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
def test_explicit_discriminant_implicit_following():
|
|
40
|
-
class MyUnion(Union, variants=(UnionVariant(Str, 4), Int)):
|
|
43
|
+
class MyUnion(Union, variants=(UnionVariant(Str, 4), Int)):
|
|
44
|
+
...
|
|
41
45
|
|
|
42
46
|
x = MyUnion(Str("Hello"))
|
|
43
47
|
b = x.pack()
|
|
@@ -57,7 +61,8 @@ def test_explicit_discriminant_implicit_following():
|
|
|
57
61
|
|
|
58
62
|
|
|
59
63
|
def test_explicit_discriminant_all_cases():
|
|
60
|
-
class MyUnion(Union, variants=(UnionVariant(Str, 4), UnionVariant(Int, 10))):
|
|
64
|
+
class MyUnion(Union, variants=(UnionVariant(Str, 4), UnionVariant(Int, 10))):
|
|
65
|
+
...
|
|
61
66
|
|
|
62
67
|
x = MyUnion(Str("Hello"))
|
|
63
68
|
b = x.pack()
|
|
@@ -96,7 +101,8 @@ class AllPrimativeTypes(
|
|
|
96
101
|
bare.Data,
|
|
97
102
|
bare.Void,
|
|
98
103
|
),
|
|
99
|
-
):
|
|
104
|
+
):
|
|
105
|
+
...
|
|
100
106
|
|
|
101
107
|
|
|
102
108
|
@pytest.mark.parametrize(
|
|
@@ -210,6 +210,7 @@ def optional(maybe: Type[T]) -> Type[Union]:
|
|
|
210
210
|
maybe,
|
|
211
211
|
)
|
|
212
212
|
namespace = UnionMeta.__prepare__(name, (), variants=variants)
|
|
213
|
+
namespace["_optional"] = True
|
|
213
214
|
AnonymousUnion = UnionMeta.__new__(
|
|
214
215
|
UnionMeta, name, (Union,), namespace, variants=variants
|
|
215
216
|
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Any, Callable, Type, TypeVar
|
|
2
|
+
from .misc import Enum
|
|
3
|
+
|
|
4
|
+
from .baretype import BAREType
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T", bound=BAREType)
|
|
7
|
+
_MISSING = object()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Field:
|
|
11
|
+
"""
|
|
12
|
+
A descriptor that assists with assigning/retrieving values from a BARE Struct.
|
|
13
|
+
It is reponsible for performing type validation on assignment of struct fields.
|
|
14
|
+
|
|
15
|
+
For example:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
class MyStruct(Struct):
|
|
19
|
+
a = Field(UInt)
|
|
20
|
+
b = Field(Str)
|
|
21
|
+
```
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
attr: str
|
|
25
|
+
name: str
|
|
26
|
+
ty: Type[BAREType | Enum]
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
ty: Type[BAREType | Enum],
|
|
31
|
+
attr: str | None = None,
|
|
32
|
+
*,
|
|
33
|
+
default: Any = _MISSING,
|
|
34
|
+
default_factory: Callable[[], Any] | None = None,
|
|
35
|
+
):
|
|
36
|
+
if default is not _MISSING and default_factory is not None:
|
|
37
|
+
raise TypeError("Field cannot specify both default and default_factory")
|
|
38
|
+
if default_factory is not None and not callable(default_factory):
|
|
39
|
+
raise TypeError("Field default_factory must be callable")
|
|
40
|
+
# ignore the typing here because these will always be set when assigned to an
|
|
41
|
+
# object
|
|
42
|
+
self.attr = attr # type: ignore
|
|
43
|
+
self.ty = ty
|
|
44
|
+
self.default = default
|
|
45
|
+
self.default_factory = default_factory
|
|
46
|
+
|
|
47
|
+
def has_default(self) -> bool:
|
|
48
|
+
return self.default is not _MISSING or self.default_factory is not None
|
|
49
|
+
|
|
50
|
+
def get_default(self) -> Any:
|
|
51
|
+
if self.default_factory is not None:
|
|
52
|
+
return self.default_factory()
|
|
53
|
+
return self.default
|
|
54
|
+
|
|
55
|
+
def __get__(self, inst, _) -> T | Type[BAREType[T] | Enum]:
|
|
56
|
+
if inst is None:
|
|
57
|
+
return self.ty
|
|
58
|
+
return inst.__dict__[self.attr]
|
|
59
|
+
|
|
60
|
+
def __set__(self, inst, value: T):
|
|
61
|
+
if not isinstance(value, self.ty) and not self.ty.validate(value):
|
|
62
|
+
raise TypeError(
|
|
63
|
+
f"type {type(value)} is invalid for field of BARE type {self.ty}"
|
|
64
|
+
)
|
|
65
|
+
inst.__dict__[self.attr] = value
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import io
|
|
2
|
-
from bare import Struct, UInt, Str, Field, struct
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def test_struct_basic():
|
|
6
|
-
class MyStruct(Struct):
|
|
7
|
-
a = Field(UInt)
|
|
8
|
-
b = Field(Str)
|
|
9
|
-
c: int
|
|
10
|
-
|
|
11
|
-
x = MyStruct(a=123, b="Hello")
|
|
12
|
-
b = x.pack()
|
|
13
|
-
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
14
|
-
assert x == x2
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def test_nested_struct():
|
|
18
|
-
class InnerStruct(Struct):
|
|
19
|
-
i = Field(UInt)
|
|
20
|
-
|
|
21
|
-
class MyStruct(Struct):
|
|
22
|
-
inner = Field(InnerStruct)
|
|
23
|
-
|
|
24
|
-
x = MyStruct(inner=InnerStruct(i=123))
|
|
25
|
-
b = x.pack()
|
|
26
|
-
x2 = MyStruct.unpack(io.BytesIO(b))
|
|
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
|
pybare-1.2.4/bare/test_number.py
DELETED
|
@@ -1,49 +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
|
-
(
|
|
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
|
pybare-1.2.4/bare/util.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
from typing import Generic, Type, TypeVar
|
|
2
|
-
|
|
3
|
-
from .baretype import BAREType
|
|
4
|
-
|
|
5
|
-
T = TypeVar("T", bound=BAREType)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Field:
|
|
9
|
-
"""
|
|
10
|
-
A descriptor that assists with assigning/retrieving values from a BARE Struct.
|
|
11
|
-
It is reponsible for performing type validation on assignment of struct fields.
|
|
12
|
-
|
|
13
|
-
For example:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
class MyStruct(Struct):
|
|
17
|
-
a = Field(UInt)
|
|
18
|
-
b = Field(Str)
|
|
19
|
-
```
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
attr: str
|
|
23
|
-
name: str
|
|
24
|
-
ty: Type[BAREType]
|
|
25
|
-
|
|
26
|
-
def __init__(self, ty: Type[BAREType], attr: str | None = None):
|
|
27
|
-
# ignore the typing here because these will always be set when assigned to an
|
|
28
|
-
# object
|
|
29
|
-
self.attr = attr # type: ignore
|
|
30
|
-
self.ty = ty
|
|
31
|
-
|
|
32
|
-
def __get__(self, inst, _) -> T | Type[BAREType[T]]:
|
|
33
|
-
if inst is None:
|
|
34
|
-
return self.ty
|
|
35
|
-
return inst.__dict__[self.attr]
|
|
36
|
-
|
|
37
|
-
def __set__(self, inst, value: T):
|
|
38
|
-
if not isinstance(value, self.ty) and not self.ty.validate(value):
|
|
39
|
-
raise TypeError(
|
|
40
|
-
f"type {type(value)} is invalid for field of BARE type {self.ty}"
|
|
41
|
-
)
|
|
42
|
-
inst.__dict__[self.attr] = value
|
|
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
|