tsrkit-types 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
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.
- tsrkit_types/__init__.py +95 -76
- tsrkit_types/bits.py +39 -20
- tsrkit_types/bool.py +4 -8
- tsrkit_types/bytearray.py +37 -0
- tsrkit_types/bytes.py +24 -56
- tsrkit_types/bytes_common.py +68 -0
- tsrkit_types/choice.py +4 -4
- tsrkit_types/dictionary.py +1 -1
- tsrkit_types/enum.py +3 -3
- tsrkit_types/integers.py +17 -0
- tsrkit_types/itf/codable.py +2 -1
- tsrkit_types/null.py +3 -3
- tsrkit_types/option.py +20 -2
- tsrkit_types/sequences.py +4 -4
- tsrkit_types/struct.py +16 -10
- {tsrkit_types-0.1.1.dist-info → tsrkit_types-0.1.3.dist-info}/METADATA +102 -23
- tsrkit_types-0.1.3.dist-info/RECORD +21 -0
- tsrkit_types-0.1.1.dist-info/RECORD +0 -19
- {tsrkit_types-0.1.1.dist-info → tsrkit_types-0.1.3.dist-info}/WHEEL +0 -0
- {tsrkit_types-0.1.1.dist-info → tsrkit_types-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {tsrkit_types-0.1.1.dist-info → tsrkit_types-0.1.3.dist-info}/top_level.txt +0 -0
tsrkit_types/__init__.py
CHANGED
|
@@ -1,76 +1,95 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
#
|
|
76
|
-
|
|
1
|
+
"""
|
|
2
|
+
TSRKit Types - Performant Python types for binary serialization and JSON encoding.
|
|
3
|
+
|
|
4
|
+
This module provides a comprehensive set of typed data structures with built-in
|
|
5
|
+
serialization capabilities, including integers, strings, containers, and more.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Core interfaces
|
|
9
|
+
from .itf.codable import Codable
|
|
10
|
+
|
|
11
|
+
# Integer types
|
|
12
|
+
from .integers import Uint, U8, U16, U32, U64
|
|
13
|
+
|
|
14
|
+
# String types
|
|
15
|
+
from .string import String
|
|
16
|
+
|
|
17
|
+
# Boolean types
|
|
18
|
+
from .bool import Bool
|
|
19
|
+
|
|
20
|
+
# Null types
|
|
21
|
+
from .null import Null, NullType
|
|
22
|
+
|
|
23
|
+
# Choice and Option types
|
|
24
|
+
from .choice import Choice
|
|
25
|
+
from .option import Option
|
|
26
|
+
|
|
27
|
+
# Container types
|
|
28
|
+
from .sequences import (
|
|
29
|
+
Seq, Vector, Array,
|
|
30
|
+
TypedVector, TypedArray,
|
|
31
|
+
BoundedVector, TypedBoundedVector
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Dictionary types
|
|
35
|
+
from .dictionary import Dictionary
|
|
36
|
+
|
|
37
|
+
# Bytes types
|
|
38
|
+
from .bytes import Bytes, ByteArray16, ByteArray32, ByteArray64, ByteArray128, ByteArray256, ByteArray512, ByteArray1024
|
|
39
|
+
from .bytearray import ByteArray
|
|
40
|
+
|
|
41
|
+
# Bit types
|
|
42
|
+
from .bits import Bits
|
|
43
|
+
|
|
44
|
+
# Enum types
|
|
45
|
+
from .enum import Enum
|
|
46
|
+
|
|
47
|
+
# Structure decorator
|
|
48
|
+
from .struct import structure, struct
|
|
49
|
+
|
|
50
|
+
# Export all public types
|
|
51
|
+
__all__ = [
|
|
52
|
+
# Core interfaces
|
|
53
|
+
"Codable",
|
|
54
|
+
|
|
55
|
+
# Integer types
|
|
56
|
+
"Uint", "U8", "U16", "U32", "U64",
|
|
57
|
+
|
|
58
|
+
# String types
|
|
59
|
+
"String",
|
|
60
|
+
|
|
61
|
+
# Boolean types
|
|
62
|
+
"Bool",
|
|
63
|
+
|
|
64
|
+
# Null types
|
|
65
|
+
"Null", "NullType",
|
|
66
|
+
|
|
67
|
+
# Choice and Option types
|
|
68
|
+
"Choice", "Option",
|
|
69
|
+
|
|
70
|
+
# Container types
|
|
71
|
+
"Seq", "Vector", "Array",
|
|
72
|
+
"TypedVector", "TypedArray",
|
|
73
|
+
"BoundedVector", "TypedBoundedVector",
|
|
74
|
+
|
|
75
|
+
# Dictionary types
|
|
76
|
+
"Dictionary",
|
|
77
|
+
|
|
78
|
+
# Bytes types
|
|
79
|
+
"Bytes", "ByteArray16", "ByteArray32", "ByteArray64", "ByteArray128", "ByteArray256", "ByteArray512", "ByteArray1024",
|
|
80
|
+
"ByteArray",
|
|
81
|
+
|
|
82
|
+
# Bit types
|
|
83
|
+
"Bits",
|
|
84
|
+
|
|
85
|
+
# Enum types
|
|
86
|
+
"Enum",
|
|
87
|
+
|
|
88
|
+
# Structure decorator
|
|
89
|
+
"structure", "struct",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Version information
|
|
93
|
+
__version__ = "1.0.0"
|
|
94
|
+
__author__ = "TSRKit Team"
|
|
95
|
+
__license__ = "MIT"
|
tsrkit_types/bits.py
CHANGED
|
@@ -34,7 +34,13 @@ class Bits(Seq):
|
|
|
34
34
|
|
|
35
35
|
@classmethod
|
|
36
36
|
def from_json(cls, json_str: str) -> "Bits":
|
|
37
|
-
|
|
37
|
+
bits = Bytes.from_json(json_str).to_bits(bit_order=cls._order)
|
|
38
|
+
|
|
39
|
+
# For fixed-length types, trim to exact size
|
|
40
|
+
if cls._min_length == cls._max_length and cls._min_length > 0:
|
|
41
|
+
bits = bits[:cls._min_length]
|
|
42
|
+
|
|
43
|
+
return cls(bits)
|
|
38
44
|
|
|
39
45
|
# ---------------------------------------------------------------------------- #
|
|
40
46
|
# Serialization #
|
|
@@ -43,7 +49,9 @@ class Bits(Seq):
|
|
|
43
49
|
def encode_size(self) -> int:
|
|
44
50
|
# Calculate the number of bytes needed
|
|
45
51
|
bit_enc = 0
|
|
46
|
-
if
|
|
52
|
+
# Check if this is a variable-length type (needs length prefix)
|
|
53
|
+
is_fixed_length = (self._min_length == self._max_length and self._min_length > 0)
|
|
54
|
+
if not is_fixed_length:
|
|
47
55
|
bit_enc = Uint(len(self)).encode_size()
|
|
48
56
|
|
|
49
57
|
return bit_enc + ((len(self) + 7) // 8)
|
|
@@ -54,17 +62,18 @@ class Bits(Seq):
|
|
|
54
62
|
total_size = self.encode_size()
|
|
55
63
|
self._check_buffer_size(buffer, total_size, offset)
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
current_offset = offset
|
|
66
|
+
|
|
67
|
+
# Check if this is a variable-length type (needs length prefix)
|
|
68
|
+
is_fixed_length = (self._min_length == self._max_length and self._min_length > 0)
|
|
69
|
+
|
|
70
|
+
if not is_fixed_length:
|
|
62
71
|
# Encode the bit length first
|
|
63
|
-
|
|
72
|
+
current_offset += Uint(len(self)).encode_into(buffer, current_offset)
|
|
64
73
|
else:
|
|
65
|
-
# Ensure bit length
|
|
66
|
-
if len(self) != self.
|
|
67
|
-
raise ValueError("Bit sequence length mismatch")
|
|
74
|
+
# Ensure bit length matches expected size for fixed-length types
|
|
75
|
+
if len(self) != self._min_length:
|
|
76
|
+
raise ValueError(f"Bit sequence length mismatch: expected {self._min_length}, got {len(self)}")
|
|
68
77
|
|
|
69
78
|
if not all(
|
|
70
79
|
isinstance(bit, (bool, int)) and bit in (0, 1, True, False)
|
|
@@ -72,9 +81,9 @@ class Bits(Seq):
|
|
|
72
81
|
):
|
|
73
82
|
raise ValueError(f"Bit sequence must contain only 0s and 1s, got an sequence of {self}")
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
84
|
+
# Convert bits to bytes and write to buffer
|
|
85
|
+
bit_bytes = Bytes.from_bits(self, bit_order=self._order)
|
|
86
|
+
buffer[current_offset : current_offset + len(bit_bytes)] = bit_bytes
|
|
78
87
|
|
|
79
88
|
return total_size
|
|
80
89
|
|
|
@@ -98,18 +107,28 @@ class Bits(Seq):
|
|
|
98
107
|
Raises:
|
|
99
108
|
DecodeError: If buffer too small or bit_length not specified
|
|
100
109
|
"""
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
# Check if this is a fixed-length Bits type
|
|
111
|
+
is_fixed_length = (cls._min_length == cls._max_length and cls._min_length > 0)
|
|
112
|
+
|
|
113
|
+
original_offset = offset
|
|
114
|
+
|
|
115
|
+
if is_fixed_length:
|
|
116
|
+
_len = cls._min_length
|
|
117
|
+
else:
|
|
118
|
+
# Variable length - decode length from buffer
|
|
104
119
|
_len, size = Uint.decode_from(buffer, offset)
|
|
105
120
|
offset += size
|
|
106
121
|
|
|
107
122
|
if _len == 0:
|
|
108
|
-
return [],
|
|
123
|
+
return cls([]), offset - original_offset
|
|
109
124
|
|
|
110
125
|
# Calculate required bytes
|
|
111
126
|
byte_count = (_len + 7) // 8
|
|
112
127
|
cls._check_buffer_size(buffer, byte_count, offset)
|
|
113
128
|
|
|
114
|
-
|
|
115
|
-
|
|
129
|
+
result_bits = Bytes(buffer[offset : offset + byte_count]).to_bits(bit_order=cls._order)
|
|
130
|
+
# Trim to exact bit length
|
|
131
|
+
result_bits = result_bits[:_len]
|
|
132
|
+
|
|
133
|
+
total_bytes_read = offset + byte_count - original_offset
|
|
134
|
+
return cls(result_bits), total_bytes_read
|
tsrkit_types/bool.py
CHANGED
|
@@ -29,13 +29,9 @@ class Bool(Codable):
|
|
|
29
29
|
# JSON Parse #
|
|
30
30
|
# ---------------------------------------------------------------------------- #
|
|
31
31
|
|
|
32
|
-
def to_json(self) ->
|
|
33
|
-
return
|
|
32
|
+
def to_json(self) -> bool:
|
|
33
|
+
return bool(self)
|
|
34
34
|
|
|
35
35
|
@classmethod
|
|
36
|
-
def from_json(cls,
|
|
37
|
-
|
|
38
|
-
return cls(True)
|
|
39
|
-
if json_str == "false":
|
|
40
|
-
return cls(False)
|
|
41
|
-
raise ValueError("Invalid JSON string for Bool")
|
|
36
|
+
def from_json(cls, data: bool) -> "Bool":
|
|
37
|
+
return cls(data)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Tuple, Union
|
|
2
|
+
from tsrkit_types.integers import Uint
|
|
3
|
+
from tsrkit_types.itf.codable import Codable
|
|
4
|
+
from tsrkit_types.bytes_common import BytesMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ByteArray(bytearray, Codable, BytesMixin):
|
|
8
|
+
"""Variable Size ByteArray"""
|
|
9
|
+
|
|
10
|
+
# Bit conversion and JSON methods inherited from BytesMixin
|
|
11
|
+
|
|
12
|
+
# ---------------------------------------------------------------------------- #
|
|
13
|
+
# Serialization #
|
|
14
|
+
# ---------------------------------------------------------------------------- #
|
|
15
|
+
def encode_size(self) -> int:
|
|
16
|
+
return Uint(len(self)).encode_size() + len(self)
|
|
17
|
+
|
|
18
|
+
def encode_into(self, buf: bytearray, offset: int = 0) -> int:
|
|
19
|
+
current_offset = offset
|
|
20
|
+
_len = len(self)
|
|
21
|
+
current_offset += Uint(_len).encode_into(buf, current_offset)
|
|
22
|
+
buf[current_offset:current_offset+_len] = self
|
|
23
|
+
current_offset += _len
|
|
24
|
+
return current_offset - offset
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], offset: int = 0) -> Tuple["ByteArray", int]:
|
|
28
|
+
current_offset = offset
|
|
29
|
+
_len, _inc_offset = Uint.decode_from(buffer, offset)
|
|
30
|
+
current_offset += _inc_offset
|
|
31
|
+
return cls(buffer[current_offset:current_offset+_len]), current_offset + _len - offset
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------- #
|
|
34
|
+
# JSON Serialization #
|
|
35
|
+
# ---------------------------------------------------------------------------- #
|
|
36
|
+
# JSON methods inherited from BytesMixin
|
|
37
|
+
|
tsrkit_types/bytes.py
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import abc
|
|
2
|
+
from typing import Tuple, Union, ClassVar
|
|
4
3
|
from tsrkit_types.integers import Uint
|
|
5
4
|
from tsrkit_types.itf.codable import Codable
|
|
5
|
+
from tsrkit_types.bytes_common import BytesMixin
|
|
6
|
+
|
|
7
|
+
class BytesCheckMeta(abc.ABCMeta):
|
|
8
|
+
"""Meta class to check if the instance is a bytes with the same key and value types"""
|
|
9
|
+
def __instancecheck__(cls, instance):
|
|
10
|
+
# TODO - This needs more false positive testing
|
|
11
|
+
_matches_length = str(getattr(cls, "_length", None)) == str(getattr(instance, "_length", None))
|
|
12
|
+
return isinstance(instance, bytes) and _matches_length
|
|
13
|
+
|
|
6
14
|
|
|
7
|
-
class Bytes(bytes, Codable):
|
|
15
|
+
class Bytes(bytes, Codable, BytesMixin, metaclass=BytesCheckMeta):
|
|
16
|
+
"""Fixed Size Bytes"""
|
|
8
17
|
|
|
9
18
|
_length: ClassVar[Union[None, int]] = None
|
|
10
19
|
|
|
@@ -18,47 +27,7 @@ class Bytes(bytes, Codable):
|
|
|
18
27
|
"_length": _len,
|
|
19
28
|
})
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
return f"{self.__class__.__name__}({self.hex()})"
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def from_bits(cls, bits: list[bool], bit_order = "msb") -> Self:
|
|
26
|
-
# Sanitize input: make sure bits are 0 or 1
|
|
27
|
-
bits = [int(bool(b)) for b in bits]
|
|
28
|
-
n = len(bits)
|
|
29
|
-
# Pad with zeros to multiple of 8
|
|
30
|
-
pad = (8 - n % 8) % 8
|
|
31
|
-
bits += [0] * pad
|
|
32
|
-
|
|
33
|
-
byte_arr = []
|
|
34
|
-
for i in range(0, len(bits), 8):
|
|
35
|
-
byte_bits = bits[i:i + 8]
|
|
36
|
-
if bit_order == "msb":
|
|
37
|
-
# Most significant bit first
|
|
38
|
-
val = 0
|
|
39
|
-
for bit in byte_bits:
|
|
40
|
-
val = (val << 1) | bit
|
|
41
|
-
elif bit_order == "lsb":
|
|
42
|
-
# Least significant bit first
|
|
43
|
-
val = 0
|
|
44
|
-
for bit in reversed(byte_bits):
|
|
45
|
-
val = (val << 1) | bit
|
|
46
|
-
else:
|
|
47
|
-
raise ValueError(f"Unknown bit_order: {bit_order}")
|
|
48
|
-
# noinspection PyUnreachableCode
|
|
49
|
-
byte_arr.append(val)
|
|
50
|
-
return cls(bytes(byte_arr))
|
|
51
|
-
|
|
52
|
-
def to_bits(self, bit_order="msb") -> list[bool]:
|
|
53
|
-
bits = []
|
|
54
|
-
for byte in self:
|
|
55
|
-
if bit_order == "msb":
|
|
56
|
-
bits.extend([(byte >> i) & 1 for i in reversed(range(8))])
|
|
57
|
-
elif bit_order == "lsb":
|
|
58
|
-
bits.extend([(byte >> i) & 1 for i in range(8)])
|
|
59
|
-
else:
|
|
60
|
-
raise ValueError(f"Unknown bit_order: {bit_order}")
|
|
61
|
-
return bits
|
|
30
|
+
# Bit conversion methods inherited from BytesMixin
|
|
62
31
|
|
|
63
32
|
# ---------------------------------------------------------------------------- #
|
|
64
33
|
# Serialization #
|
|
@@ -79,7 +48,7 @@ class Bytes(bytes, Codable):
|
|
|
79
48
|
return current_offset - offset
|
|
80
49
|
|
|
81
50
|
@classmethod
|
|
82
|
-
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], offset: int = 0) -> Tuple[
|
|
51
|
+
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], offset: int = 0) -> Tuple["Bytes", int]:
|
|
83
52
|
current_offset = offset
|
|
84
53
|
_len = cls._length
|
|
85
54
|
if _len is None:
|
|
@@ -90,13 +59,12 @@ class Bytes(bytes, Codable):
|
|
|
90
59
|
# ---------------------------------------------------------------------------- #
|
|
91
60
|
# JSON Serialization #
|
|
92
61
|
# ---------------------------------------------------------------------------- #
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
62
|
+
# JSON methods inherited from BytesMixin
|
|
63
|
+
|
|
64
|
+
ByteArray16 = Bytes[16]
|
|
65
|
+
ByteArray32 = Bytes[32]
|
|
66
|
+
ByteArray64 = Bytes[64]
|
|
67
|
+
ByteArray128 = Bytes[128]
|
|
68
|
+
ByteArray256 = Bytes[256]
|
|
69
|
+
ByteArray512 = Bytes[512]
|
|
70
|
+
ByteArray1024 = Bytes[1024]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common functionality for Bytes and ByteArray types.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BytesMixin:
|
|
9
|
+
"""Mixin providing common functionality for bytes-like types."""
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def from_bits(cls, bits: list[bool], bit_order: str = "msb"):
|
|
13
|
+
"""Convert a list of bits to bytes with specified bit order."""
|
|
14
|
+
# Sanitize input: make sure bits are 0 or 1
|
|
15
|
+
bits = [int(bool(b)) for b in bits]
|
|
16
|
+
n = len(bits)
|
|
17
|
+
# Pad with zeros to multiple of 8
|
|
18
|
+
pad = (8 - n % 8) % 8
|
|
19
|
+
bits += [0] * pad
|
|
20
|
+
|
|
21
|
+
byte_arr = []
|
|
22
|
+
for i in range(0, len(bits), 8):
|
|
23
|
+
byte_bits = bits[i:i + 8]
|
|
24
|
+
if bit_order == "msb":
|
|
25
|
+
# Most significant bit first
|
|
26
|
+
val = 0
|
|
27
|
+
for bit in byte_bits:
|
|
28
|
+
val = (val << 1) | bit
|
|
29
|
+
elif bit_order == "lsb":
|
|
30
|
+
# Least significant bit first
|
|
31
|
+
val = 0
|
|
32
|
+
for bit in reversed(byte_bits):
|
|
33
|
+
val = (val << 1) | bit
|
|
34
|
+
else:
|
|
35
|
+
raise ValueError(f"Unknown bit_order: {bit_order}")
|
|
36
|
+
byte_arr.append(val)
|
|
37
|
+
return cls(bytes(byte_arr))
|
|
38
|
+
|
|
39
|
+
def to_bits(self, bit_order: str = "msb") -> list[bool]:
|
|
40
|
+
"""Convert bytes to a list of bits with specified bit order."""
|
|
41
|
+
bits = []
|
|
42
|
+
for byte in self:
|
|
43
|
+
if bit_order == "msb":
|
|
44
|
+
bits.extend([bool((byte >> i) & 1) for i in reversed(range(8))])
|
|
45
|
+
elif bit_order == "lsb":
|
|
46
|
+
bits.extend([bool((byte >> i) & 1) for i in range(8)])
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError(f"Unknown bit_order: {bit_order}")
|
|
49
|
+
return bits
|
|
50
|
+
|
|
51
|
+
def to_json(self):
|
|
52
|
+
"""Convert bytes to hex string for JSON serialization."""
|
|
53
|
+
return self.hex()
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_json(cls, data: str):
|
|
57
|
+
"""Create instance from hex string."""
|
|
58
|
+
data = data.replace("0x", "")
|
|
59
|
+
return cls(bytes.fromhex(data))
|
|
60
|
+
|
|
61
|
+
def __str__(self):
|
|
62
|
+
return f"{self.__class__.__name__}({self.hex()})"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_bit_order(bit_order: str) -> None:
|
|
66
|
+
"""Validate bit order parameter."""
|
|
67
|
+
if bit_order not in ("msb", "lsb"):
|
|
68
|
+
raise ValueError(f"Unknown bit_order: {bit_order}. Must be 'msb' or 'lsb'")
|
tsrkit_types/choice.py
CHANGED
|
@@ -112,13 +112,13 @@ class Choice(Codable):
|
|
|
112
112
|
|
|
113
113
|
def encode_into(self, buf: bytearray, offset: int = 0) -> int:
|
|
114
114
|
current_offset = offset
|
|
115
|
-
# Find the
|
|
116
|
-
for i,
|
|
117
|
-
if isinstance(self._value,
|
|
115
|
+
# Find the index of the (key, type) pair that matches our current choice
|
|
116
|
+
for i, (key, choice_type) in enumerate(self._opt_types):
|
|
117
|
+
if self._choice_key == key and isinstance(self._value, choice_type):
|
|
118
118
|
current_offset += Uint(i).encode_into(buf, current_offset)
|
|
119
119
|
break
|
|
120
120
|
else:
|
|
121
|
-
raise ValueError(f"Value {self._value} is not a valid choice")
|
|
121
|
+
raise ValueError(f"Value {self._value} with key {self._choice_key} is not a valid choice")
|
|
122
122
|
current_offset += self._value.encode_into(buf, current_offset)
|
|
123
123
|
return current_offset - offset
|
|
124
124
|
|
tsrkit_types/dictionary.py
CHANGED
|
@@ -102,7 +102,7 @@ class Dictionary(dict, Codable, Generic[K, V], metaclass=DictCheckMeta):
|
|
|
102
102
|
if not isinstance(data, dict):
|
|
103
103
|
_value = cls({})
|
|
104
104
|
for val in data:
|
|
105
|
-
_value[cls.
|
|
105
|
+
_value[cls._key_type.from_json(val[cls._key_name])] = cls._value_type.from_json(val[cls._value_name])
|
|
106
106
|
return _value
|
|
107
107
|
else:
|
|
108
108
|
return cls(
|
tsrkit_types/enum.py
CHANGED
|
@@ -20,8 +20,8 @@ class Enum(metaclass=EnumMeta):
|
|
|
20
20
|
>>> assert decoded == value
|
|
21
21
|
>>> assert bytes_read == 1
|
|
22
22
|
>>>
|
|
23
|
-
>>> assert MyEnum.from_json(1
|
|
24
|
-
>>> assert MyEnum.from_json("A"
|
|
23
|
+
>>> assert MyEnum.from_json(1) == MyEnum.A
|
|
24
|
+
>>> assert MyEnum.from_json("A") == MyEnum.A
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
@property
|
|
@@ -106,7 +106,7 @@ class Enum(metaclass=EnumMeta):
|
|
|
106
106
|
The corresponding enum value
|
|
107
107
|
|
|
108
108
|
Raises:
|
|
109
|
-
|
|
109
|
+
ValueError: If the value is invalid
|
|
110
110
|
"""
|
|
111
111
|
for v in cls.__members__.values():
|
|
112
112
|
if v._value_ == data or v._name_ == data:
|
tsrkit_types/integers.py
CHANGED
|
@@ -195,6 +195,23 @@ class Uint(int, Codable, metaclass=IntCheckMeta):
|
|
|
195
195
|
beta = int.from_bytes(buffer[offset + 1 : offset + 1 + _l], "little")
|
|
196
196
|
value = alpha * 2 ** (_l * 8) + beta
|
|
197
197
|
return cls(value), _l + 1
|
|
198
|
+
|
|
199
|
+
def to_bits(self, bit_order: str = "msb") -> list[bool]:
|
|
200
|
+
"""Convert an int to bits"""
|
|
201
|
+
if bit_order == "msb":
|
|
202
|
+
return [bool((self >> i) & 1) for i in reversed(range(self.byte_size * 8 if self.byte_size > 0 else 64))]
|
|
203
|
+
elif bit_order == "lsb":
|
|
204
|
+
return [bool((self >> i) & 1) for i in range(self.byte_size * 8 if self.byte_size > 0 else 64)]
|
|
205
|
+
else:
|
|
206
|
+
raise ValueError(f"Invalid bit order: {bit_order}")
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
def from_bits(cls, bits: list[bool], bit_order: str = "msb") -> "Uint":
|
|
210
|
+
"""Convert bits to an int"""
|
|
211
|
+
if bit_order == "msb":
|
|
212
|
+
return cls(int("".join(str(int(b)) for b in bits), 2))
|
|
213
|
+
elif bit_order == "lsb":
|
|
214
|
+
return cls(int("".join(str(int(b)) for b in reversed(bits)), 2))
|
|
198
215
|
|
|
199
216
|
|
|
200
217
|
U8 = Uint[8]
|
tsrkit_types/itf/codable.py
CHANGED
|
@@ -69,7 +69,8 @@ class Codable(ABC, Generic[T]):
|
|
|
69
69
|
value, bytes_read = cls.decode_from(buffer, offset)
|
|
70
70
|
return value
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
@classmethod
|
|
73
|
+
def _check_buffer_size(cls, buffer: bytearray, size: int, offset: int) -> None:
|
|
73
74
|
"""
|
|
74
75
|
Check if the buffer has enough space to encode the value.
|
|
75
76
|
|
tsrkit_types/null.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Optional,
|
|
1
|
+
from typing import Optional, Tuple, Union
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class NullType:
|
|
@@ -22,7 +22,7 @@ class NullType:
|
|
|
22
22
|
return 0
|
|
23
23
|
|
|
24
24
|
@classmethod
|
|
25
|
-
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], offset: int = 0) -> Tuple[
|
|
25
|
+
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], offset: int = 0) -> Tuple['NullType', int]:
|
|
26
26
|
return cls(), 0
|
|
27
27
|
|
|
28
28
|
# ---------------------------------------------------------------------------- #
|
|
@@ -33,7 +33,7 @@ class NullType:
|
|
|
33
33
|
return None
|
|
34
34
|
|
|
35
35
|
@classmethod
|
|
36
|
-
def from_json(cls, json_str: Optional[str]) ->
|
|
36
|
+
def from_json(cls, json_str: Optional[str]) -> 'NullType':
|
|
37
37
|
if json_str is None:
|
|
38
38
|
return cls()
|
|
39
39
|
raise ValueError("Invalid JSON string for NullType")
|
tsrkit_types/option.py
CHANGED
|
@@ -16,7 +16,7 @@ class Option(Choice, Generic[T]):
|
|
|
16
16
|
name = f"Option[{opt_t.__class__.__name__}]"
|
|
17
17
|
return type(name,
|
|
18
18
|
(Option,),
|
|
19
|
-
{"_opt_types": ((None,
|
|
19
|
+
{"_opt_types": ((None, NullType), (None, opt_t))})
|
|
20
20
|
|
|
21
21
|
def __init__(self, val: T|NullType = Null):
|
|
22
22
|
super().__init__(val)
|
|
@@ -27,4 +27,22 @@ class Option(Choice, Generic[T]):
|
|
|
27
27
|
super().set(value, key)
|
|
28
28
|
|
|
29
29
|
def __bool__(self):
|
|
30
|
-
return self._value != Null
|
|
30
|
+
return self._value != Null
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------- #
|
|
33
|
+
# JSON Serde #
|
|
34
|
+
# ---------------------------------------------------------------------------- #
|
|
35
|
+
|
|
36
|
+
def to_json(self):
|
|
37
|
+
"""Convert Option to JSON. Returns None for empty Options."""
|
|
38
|
+
if self._value == Null:
|
|
39
|
+
return None
|
|
40
|
+
return self._value.to_json()
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_json(cls, data):
|
|
44
|
+
"""Create Option from JSON data. None creates empty Option."""
|
|
45
|
+
if data is None:
|
|
46
|
+
return cls() # Empty Option
|
|
47
|
+
# Try to create the wrapped type from the data
|
|
48
|
+
return cls(cls._opt_types[1][1].from_json(data))
|
tsrkit_types/sequences.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import abc
|
|
2
|
-
from typing import TypeVar, Type, ClassVar, Tuple, Generic
|
|
2
|
+
from typing import TypeVar, Type, ClassVar, Tuple, Generic, Optional
|
|
3
3
|
from tsrkit_types.integers import Uint
|
|
4
4
|
from tsrkit_types.itf.codable import Codable
|
|
5
5
|
|
|
@@ -84,7 +84,7 @@ class Seq(list, Codable, Generic[T], metaclass=SeqCheckMeta):
|
|
|
84
84
|
raise TypeError(f"{value!r} is not an instance of {self._element_type!r}")
|
|
85
85
|
|
|
86
86
|
def _validate_self(self):
|
|
87
|
-
"""For Resultant
|
|
87
|
+
"""For Resultant self check - added to fns that alter size"""
|
|
88
88
|
if len(self) < self._min_length:
|
|
89
89
|
raise ValueError(f"Vector: Expected sequence size to be >= {self._min_length}, resultant size {len(self)}")
|
|
90
90
|
elif len(self) > self._max_length:
|
|
@@ -118,7 +118,7 @@ class Seq(list, Codable, Generic[T], metaclass=SeqCheckMeta):
|
|
|
118
118
|
return f"{self.__class__.__name__}({list(self)})"
|
|
119
119
|
|
|
120
120
|
@property
|
|
121
|
-
def _length(self):
|
|
121
|
+
def _length(self) -> Optional[int]:
|
|
122
122
|
if self._min_length == self._max_length:
|
|
123
123
|
return self._min_length
|
|
124
124
|
return None
|
|
@@ -143,7 +143,7 @@ class Seq(list, Codable, Generic[T], metaclass=SeqCheckMeta):
|
|
|
143
143
|
def encode_into(self, buffer: bytearray, offset: int = 0) -> int:
|
|
144
144
|
current_offset = offset
|
|
145
145
|
# If length is not defined
|
|
146
|
-
if(self._min_length !=
|
|
146
|
+
if(self._min_length != self._max_length):
|
|
147
147
|
current_offset += Uint(len(self)).encode_into(buffer, current_offset)
|
|
148
148
|
|
|
149
149
|
for item in self:
|
tsrkit_types/struct.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass, fields
|
|
2
2
|
from typing import Any, Tuple, Union
|
|
3
3
|
from tsrkit_types.itf.codable import Codable
|
|
4
|
+
from tsrkit_types.null import NullType
|
|
5
|
+
from tsrkit_types.option import Option
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def structure(_cls=None, *, frozen=False, **kwargs):
|
|
@@ -58,22 +60,26 @@ def structure(_cls=None, *, frozen=False, **kwargs):
|
|
|
58
60
|
for field in fields(cls):
|
|
59
61
|
k = field.metadata.get("name", field.name)
|
|
60
62
|
v = data.get(k)
|
|
61
|
-
if v is None:
|
|
62
|
-
|
|
63
|
-
init_data[field.name] = field.metadata.get("default")
|
|
64
|
-
else:
|
|
65
|
-
raise ValueError(f"Field {k} not found in {cls}")
|
|
63
|
+
if v is None and field.metadata.get("default") is not None:
|
|
64
|
+
init_data[field.name] = field.metadata.get("default")
|
|
66
65
|
else:
|
|
67
66
|
init_data[field.name] = field.type.from_json(v)
|
|
68
67
|
return cls(**init_data)
|
|
69
68
|
|
|
70
69
|
|
|
71
70
|
new_cls.__init__ = __init__
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
new_cls.
|
|
75
|
-
|
|
76
|
-
new_cls.
|
|
71
|
+
|
|
72
|
+
# Only overwrite if the method is not already defined
|
|
73
|
+
if not new_cls.__dict__.get("encode_size"):
|
|
74
|
+
new_cls.encode_size = encode_size
|
|
75
|
+
if not new_cls.__dict__.get("decode_from"):
|
|
76
|
+
new_cls.decode_from = decode_from
|
|
77
|
+
if not new_cls.__dict__.get("encode_into"):
|
|
78
|
+
new_cls.encode_into = encode_into
|
|
79
|
+
if not new_cls.__dict__.get("to_json"):
|
|
80
|
+
new_cls.to_json = to_json
|
|
81
|
+
if not new_cls.__dict__.get("from_json"):
|
|
82
|
+
new_cls.from_json = from_json
|
|
77
83
|
|
|
78
84
|
new_cls = type(new_cls.__name__, (Codable, new_cls), dict(new_cls.__dict__))
|
|
79
85
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tsrkit-types
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Performant Python Typings library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies
|
|
5
5
|
Author-email: chainscore-labs <hello@chainscore.finance>, prasad-kumkar <prasad@chainscore.finance>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: Developers
|
|
|
15
15
|
Classifier: Intended Audience :: System Administrators
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -28,7 +27,7 @@ Classifier: Topic :: Utilities
|
|
|
28
27
|
Classifier: Topic :: System :: Archiving
|
|
29
28
|
Classifier: Topic :: Communications
|
|
30
29
|
Classifier: Typing :: Typed
|
|
31
|
-
Requires-Python: >=3.
|
|
30
|
+
Requires-Python: >=3.11
|
|
32
31
|
Description-Content-Type: text/markdown
|
|
33
32
|
License-File: LICENSE
|
|
34
33
|
Provides-Extra: dev
|
|
@@ -300,7 +299,9 @@ restored = StringToInt.from_json(json_data)
|
|
|
300
299
|
|
|
301
300
|
### Bytes Types
|
|
302
301
|
|
|
303
|
-
|
|
302
|
+
The library provides two complementary byte array types: immutable `Bytes` (extending Python's built-in `bytes`) and mutable `ByteArray` (extending Python's `bytearray`). Both types share common functionality through a mixin architecture for bit conversion, JSON serialization, and binary encoding.
|
|
303
|
+
|
|
304
|
+
#### Bytes (Immutable - extension of Python bytes)
|
|
304
305
|
|
|
305
306
|
```python
|
|
306
307
|
from tsrkit_types.bytes import Bytes
|
|
@@ -308,47 +309,115 @@ from tsrkit_types.bytes import Bytes
|
|
|
308
309
|
# Creation
|
|
309
310
|
data = Bytes(b"Hello, binary world!")
|
|
310
311
|
data = Bytes([0x01, 0x02, 0x03, 0x04])
|
|
312
|
+
data = Bytes("48656c6c6f") # From hex string
|
|
311
313
|
|
|
312
|
-
#
|
|
313
|
-
data.to_bits()
|
|
314
|
-
Bytes.from_bits(
|
|
314
|
+
# Shared Operations (via BytesMixin)
|
|
315
|
+
bits = data.to_bits() # Convert to bit list [True, False, True, ...]
|
|
316
|
+
data2 = Bytes.from_bits(bits) # Create from bit list
|
|
315
317
|
|
|
316
|
-
#
|
|
318
|
+
# Properties
|
|
317
319
|
length = len(data) # Byte length
|
|
318
320
|
raw_bytes = bytes(data) # Convert to Python bytes
|
|
319
321
|
|
|
320
|
-
# Encoding
|
|
322
|
+
# Encoding/Decoding
|
|
321
323
|
encoded = data.encode() # [length][raw_bytes]
|
|
322
324
|
decoded = Bytes.decode(encoded)
|
|
323
325
|
|
|
324
326
|
# JSON serialization (hex encoded)
|
|
325
327
|
json_str = data.to_json() # "48656c6c6f2c2062696e61727920776f726c6421"
|
|
326
328
|
restored = Bytes.from_json(json_str)
|
|
329
|
+
restored2 = Bytes.from_json("0x48656c6c6f") # Supports 0x prefix
|
|
327
330
|
```
|
|
328
331
|
|
|
329
|
-
####
|
|
332
|
+
#### ByteArray (Mutable - extension of Python bytearray)
|
|
330
333
|
|
|
331
334
|
```python
|
|
332
|
-
from tsrkit_types.
|
|
335
|
+
from tsrkit_types.bytearray import ByteArray
|
|
333
336
|
|
|
334
337
|
# Creation
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
data = ByteArray(b"Hello, binary world!")
|
|
339
|
+
data = ByteArray([0x01, 0x02, 0x03, 0x04])
|
|
340
|
+
data = ByteArray("48656c6c6f") # From hex string
|
|
341
|
+
|
|
342
|
+
# Mutable Operations
|
|
343
|
+
data.append(0xFF) # Add single byte
|
|
344
|
+
data.extend([0xAB, 0xCD]) # Add multiple bytes
|
|
345
|
+
data.insert(0, 0x00) # Insert byte at position
|
|
346
|
+
data.pop() # Remove and return last byte
|
|
347
|
+
data.remove(0xFF) # Remove first occurrence
|
|
348
|
+
data.clear() # Remove all bytes
|
|
349
|
+
data.reverse() # Reverse in-place
|
|
350
|
+
|
|
351
|
+
# Indexing and Slicing (mutable)
|
|
352
|
+
data[0] = 0x42 # Set byte at index
|
|
353
|
+
data[1:3] = [0x43, 0x44] # Set slice
|
|
354
|
+
del data[0] # Delete byte at index
|
|
355
|
+
|
|
356
|
+
# Shared Operations (via BytesMixin) - same as Bytes
|
|
357
|
+
bits = data.to_bits() # Convert to bit list
|
|
358
|
+
data2 = ByteArray.from_bits(bits) # Create from bit list
|
|
359
|
+
|
|
360
|
+
# Properties and Conversion
|
|
361
|
+
length = len(data) # Byte length
|
|
362
|
+
raw_bytes = bytes(data) # Convert to immutable bytes
|
|
363
|
+
immutable = Bytes(data) # Convert to immutable Bytes
|
|
364
|
+
|
|
365
|
+
# Encoding/Decoding (same interface as Bytes)
|
|
366
|
+
encoded = data.encode() # [length][raw_bytes]
|
|
367
|
+
decoded = ByteArray.decode(encoded)
|
|
368
|
+
|
|
369
|
+
# JSON serialization (same interface as Bytes)
|
|
370
|
+
json_str = data.to_json() # "48656c6c6f..."
|
|
371
|
+
restored = ByteArray.from_json(json_str)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Key Differences:**
|
|
375
|
+
- **Bytes**: Immutable, extends `bytes`, memory-efficient for read-only data
|
|
376
|
+
- **ByteArray**: Mutable, extends `bytearray`, suitable for dynamic byte manipulation
|
|
377
|
+
- **Shared Functionality**: Both support identical bit conversion, JSON serialization, and binary encoding through `BytesMixin`
|
|
378
|
+
|
|
379
|
+
**Common Features (Both Types):**
|
|
380
|
+
- Bit-level conversion with MSB/LSB support
|
|
381
|
+
- Hex string JSON serialization with 0x prefix support
|
|
382
|
+
- Efficient binary encoding with length prefix
|
|
383
|
+
- String representation and validation
|
|
384
|
+
- Memory-efficient operations
|
|
385
|
+
|
|
386
|
+
#### Bits (Bit Arrays - Sequence of bool)
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
from tsrkit_types.bits import Bits
|
|
390
|
+
|
|
391
|
+
# Creation
|
|
392
|
+
bits = Bits([True, False, True, True, False])
|
|
393
|
+
bits = Bits.from_hex("1A3F") # From hex string
|
|
394
|
+
bits = Bits.from_int(42, 8) # From integer with bit width
|
|
395
|
+
|
|
396
|
+
# Fixed-size parameterized bits
|
|
397
|
+
FixedBits = Bits[8] # Exactly 8 bits
|
|
398
|
+
fixed = FixedBits([True, False, True, True, False, False, True, False])
|
|
338
399
|
|
|
339
400
|
# Operations
|
|
340
|
-
bit_val = bits[0]
|
|
341
|
-
bits[1] = True
|
|
342
|
-
bits.append(False)
|
|
343
|
-
bits.extend([True, False])
|
|
401
|
+
bit_val = bits[0] # Get bit at index
|
|
402
|
+
bits[1] = True # Set bit at index
|
|
403
|
+
bits.append(False) # Add bit
|
|
404
|
+
bits.extend([True, False]) # Add multiple bits
|
|
344
405
|
|
|
345
406
|
# Conversion
|
|
346
|
-
hex_str = bits.to_hex()
|
|
347
|
-
int_val = bits.to_int()
|
|
407
|
+
hex_str = bits.to_hex() # Convert to hex string
|
|
408
|
+
int_val = bits.to_int() # Convert to integer
|
|
409
|
+
|
|
410
|
+
# Bit order specification
|
|
411
|
+
bits_msb = Bits([True, False], bit_order="MSB") # Most significant bit first
|
|
412
|
+
bits_lsb = Bits([True, False], bit_order="LSB") # Least significant bit first
|
|
348
413
|
|
|
349
414
|
# Encoding
|
|
350
|
-
encoded = bits.encode()
|
|
351
|
-
decoded =
|
|
415
|
+
encoded = bits.encode() # [length][packed_bits]
|
|
416
|
+
decoded = Bits.decode(encoded)
|
|
417
|
+
|
|
418
|
+
# JSON serialization
|
|
419
|
+
json_str = bits.to_json() # Hex string representation
|
|
420
|
+
restored = Bits.from_json(json_str)
|
|
352
421
|
```
|
|
353
422
|
|
|
354
423
|
### Enum (Extension of Python Enum, with Codable + JSON support)
|
|
@@ -559,6 +628,16 @@ buffer = bytearray(1024)
|
|
|
559
628
|
offset = 0
|
|
560
629
|
offset += value1.encode_into(buffer, offset)
|
|
561
630
|
offset += value2.encode_into(buffer, offset)
|
|
631
|
+
|
|
632
|
+
# Choose appropriate byte type for your use case
|
|
633
|
+
# Use Bytes for immutable binary data (memory efficient, read-only)
|
|
634
|
+
config_data = Bytes(b"static configuration")
|
|
635
|
+
|
|
636
|
+
# Use ByteArray for dynamic binary buffers (mutable, growing data)
|
|
637
|
+
dynamic_buffer = ByteArray()
|
|
638
|
+
dynamic_buffer.extend([0x01, 0x02])
|
|
639
|
+
dynamic_buffer.append(0x03)
|
|
640
|
+
dynamic_buffer.insert(0, 0x00) # Result: [0x00, 0x01, 0x02, 0x03]
|
|
562
641
|
```
|
|
563
642
|
|
|
564
643
|
## Examples
|
|
@@ -735,6 +814,6 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gui
|
|
|
735
814
|
|
|
736
815
|
## Requirements
|
|
737
816
|
|
|
738
|
-
- **Python**: >= 3.
|
|
817
|
+
- **Python**: >= 3.11
|
|
739
818
|
- **Runtime Dependencies**: None (zero dependencies!)
|
|
740
819
|
- **Development Dependencies**: pytest and plugins (see `pyproject.toml`)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
tsrkit_types/__init__.py,sha256=dPiakZ3q_YZLcj3nW5CLjI8CA9rE8umqtv2camWl3Cs,1956
|
|
2
|
+
tsrkit_types/bits.py,sha256=33WLfuUYdzGscoc-E3InTYdbJRIDeixoe_pG0XAv71c,4213
|
|
3
|
+
tsrkit_types/bool.py,sha256=Veu6KLIeiRFnIW1sbawJ0haF0veyaL-As5mBiypq-uE,1280
|
|
4
|
+
tsrkit_types/bytearray.py,sha256=DaMnACq-7pzEW-pRO2f_IAny_8ldhSt3G5D24Soywk4,1662
|
|
5
|
+
tsrkit_types/bytes.py,sha256=ayuD8AUTbZ4LFnceG8TBzAGAYXGht0pYQcmX-7ZtwyI,2762
|
|
6
|
+
tsrkit_types/bytes_common.py,sha256=b1Zqh_NhkCX718QaZC52J3nEzKAi1Fe8E0nefWVwwmo,2301
|
|
7
|
+
tsrkit_types/choice.py,sha256=zNLvB1jSZERot6Qo9cxxkPcY84DAJss8o0ULyp9D--4,5196
|
|
8
|
+
tsrkit_types/dictionary.py,sha256=GcUvaHC1VOEc3OzUVAH0id--ncLqTJ6ZRz-lucMNRdY,5618
|
|
9
|
+
tsrkit_types/enum.py,sha256=3MyLW15_ToQQdctJjcMY8Xb3OsS_Ad997OOEo8FjVeA,4256
|
|
10
|
+
tsrkit_types/integers.py,sha256=w8WCbzzQP0YX3ky4iP-fbNeOBHsIydsU0Tady8iGQPQ,8026
|
|
11
|
+
tsrkit_types/null.py,sha256=OwHVhWLdazaFLXXR8iHPaHe67NPmYHDCavBxUfcZ53s,1318
|
|
12
|
+
tsrkit_types/option.py,sha256=xwtaAFN20GjaGqzYZAAo1fZqqUZnCYHdy2bjshhY620,1631
|
|
13
|
+
tsrkit_types/sequences.py,sha256=KOT5-4mia5Lc0CMjIF7Yqe34Sei92ZQ2s6GaP13soUg,8090
|
|
14
|
+
tsrkit_types/string.py,sha256=8rvg0BwvyhQnbMW3qornmBFTQHeTQFUfwD7OC0eVgcY,2575
|
|
15
|
+
tsrkit_types/struct.py,sha256=GoXWFc1OqO9CXnxFS7Xkms7J-rtZe8h32LkAIBlzdSM,3497
|
|
16
|
+
tsrkit_types/itf/codable.py,sha256=agx8YSVWGBeEiLrU9eqh3ZoeTgpJsmmIlW622xIKqPI,2810
|
|
17
|
+
tsrkit_types-0.1.3.dist-info/licenses/LICENSE,sha256=TwnDvVCPwHadHWLUuY1sPx03XNw1jzh_ZmoDBNai9Uc,1072
|
|
18
|
+
tsrkit_types-0.1.3.dist-info/METADATA,sha256=W_kNQJPjZnMV4wh3tqG7ggWcec8tL1d6gLWx2K4jvVM,22755
|
|
19
|
+
tsrkit_types-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
tsrkit_types-0.1.3.dist-info/top_level.txt,sha256=pnVhnUsnZ_A0FIj1zHwDw3suMGrfMJwusp-4GPVY1CM,13
|
|
21
|
+
tsrkit_types-0.1.3.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
tsrkit_types/__init__.py,sha256=Vw03Fv9RnfmZGqzBpFhuglyewh4MxKR8T5bdWdal1VU,1870
|
|
2
|
-
tsrkit_types/bits.py,sha256=ggx2lDRWtBBIYQBtKEx-XirwYTJdajctCEpd3VmuXrc,3275
|
|
3
|
-
tsrkit_types/bool.py,sha256=anu5RquxV8xdDMj7aIwOOCP3WnK6vMNst4WUPlfeKm4,1460
|
|
4
|
-
tsrkit_types/bytes.py,sha256=t4GeqbYMloKrTu4XyGSRquMhQL4Ayr-S4vgZhZBS9BU,3771
|
|
5
|
-
tsrkit_types/choice.py,sha256=L4Pb0ZBvaoCW2h9VBLclmmgIJvfO0Yeb2khPniFN2Xo,5113
|
|
6
|
-
tsrkit_types/dictionary.py,sha256=EktyMKH27HVoR1xNhrkV8fW6KfcxpbHiiYPYoCrFdoI,5614
|
|
7
|
-
tsrkit_types/enum.py,sha256=0PZUuydhGVN6zmAscQNzq4Vnn5mUUPIxlDoQBodqf7g,4272
|
|
8
|
-
tsrkit_types/integers.py,sha256=GT9xVZYBNMcPpShJIlSazwiZYGhoBowhR0MA_cGO5Q0,7200
|
|
9
|
-
tsrkit_types/null.py,sha256=IcYYY5ZT7cZ5q5oyc0mCFGUCVCdhAXB_ftyAM9z5_Ek,1312
|
|
10
|
-
tsrkit_types/option.py,sha256=V4J8SmhUDjINqMM6Ml3ho0YwTy2UT-avrjoYh32Swyg,879
|
|
11
|
-
tsrkit_types/sequences.py,sha256=2vJeO47rmIN7WQjFGKudDhzDVifTNmrj1whE03npz3c,8092
|
|
12
|
-
tsrkit_types/string.py,sha256=8rvg0BwvyhQnbMW3qornmBFTQHeTQFUfwD7OC0eVgcY,2575
|
|
13
|
-
tsrkit_types/struct.py,sha256=ni8jwZlfT_wehardfImBJ0AsvnO-oCW-3APplLXsqGY,3206
|
|
14
|
-
tsrkit_types/itf/codable.py,sha256=jWfoYZ31MZ6k3nlHb2IwJdjD3G0pVPyZWlF0Sv9MAWs,2794
|
|
15
|
-
tsrkit_types-0.1.1.dist-info/licenses/LICENSE,sha256=TwnDvVCPwHadHWLUuY1sPx03XNw1jzh_ZmoDBNai9Uc,1072
|
|
16
|
-
tsrkit_types-0.1.1.dist-info/METADATA,sha256=Yuq6ylZlPzHnNpfEGIio0FFMS13G6oMPHkFwGVAjkm0,19404
|
|
17
|
-
tsrkit_types-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
-
tsrkit_types-0.1.1.dist-info/top_level.txt,sha256=pnVhnUsnZ_A0FIj1zHwDw3suMGrfMJwusp-4GPVY1CM,13
|
|
19
|
-
tsrkit_types-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|