structo 0.0.10__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.
structo/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """
2
+ Structify
3
+ """
4
+
5
+ __version__ = "0.0.10"
6
+
7
+ from .interfaces import Serializer, Serializable
8
+ from .serialise import get_serializer
9
+ from .objects import SerializableObject, PackedInt, PackedInts
10
+ from .serializers import (
11
+ uint64_BE,
12
+ uint64_LE,
13
+ uint64,
14
+ uint32_BE,
15
+ uint32_LE,
16
+ uint32,
17
+ uint16_BE,
18
+ uint16_LE,
19
+ uint16,
20
+ uint8,
21
+ int64_BE,
22
+ int64_LE,
23
+ int64,
24
+ int32_BE,
25
+ int32_LE,
26
+ int32,
27
+ int16_BE,
28
+ int16_LE,
29
+ int16,
30
+ int8,
31
+ float64_BE,
32
+ float64_LE,
33
+ float64,
34
+ float32_BE,
35
+ float32_LE,
36
+ float32,
37
+ Array,
38
+ Buffer,
39
+ List,
40
+ String,
41
+ Blob,
42
+ Literal,
43
+ Optional,
44
+ )
structo/interfaces.py ADDED
@@ -0,0 +1,46 @@
1
+ import io
2
+ import typing as t
3
+
4
+
5
+ class Serializable:
6
+ @classmethod
7
+ def serializer(cls) -> Serializer[t.Self]: ...
8
+
9
+ @classmethod
10
+ def sizeof(cls) -> int | None:
11
+ return cls.serializer().sizeof()
12
+
13
+ def write(self, f: t.IO[bytes]):
14
+ return self.serializer().write(f, self)
15
+
16
+ @classmethod
17
+ def read(cls, f: t.IO[bytes]) -> t.Self:
18
+ return cls.serializer().read(f)
19
+
20
+ def to_bytes(self) -> bytes:
21
+ return self.serializer().to_bytes(self)
22
+
23
+ @classmethod
24
+ def from_bytes(cls, data: bytes) -> t.Self:
25
+ return cls.serializer().from_bytes(data)
26
+
27
+
28
+ class Serializer[T]:
29
+ def sizeof(self) -> int | None:
30
+ return None
31
+
32
+ def write(self, f: t.IO[bytes], value: T):
33
+ raise NotImplementedError(f"{type(self).__name__} can't be serialized")
34
+
35
+ def read(self, f: t.IO[bytes]) -> T:
36
+ raise NotImplementedError(f"{type(self).__name__} can't be deserialized")
37
+
38
+ def to_bytes(self, value: T) -> bytes:
39
+ buf = io.BytesIO()
40
+ self.write(buf, value)
41
+ buf.seek(0)
42
+ return buf.getvalue()
43
+
44
+ def from_bytes(self, data: bytes) -> T:
45
+ buf = io.BytesIO(data)
46
+ return self.read(buf)
@@ -0,0 +1,2 @@
1
+ from .packed import PackedInt, PackedInts
2
+ from .serializable_object import SerializableObject
@@ -0,0 +1,50 @@
1
+ from ..interfaces import Serializable, Serializer
2
+
3
+ import typing as t
4
+ from dataclasses import dataclass
5
+ import annotationlib
6
+
7
+
8
+ class PackedInt:
9
+ bits: int
10
+
11
+ def __init__(self, *, bits: int) -> None:
12
+ self.bits = bits
13
+
14
+
15
+ class PackedIntsMeta(type):
16
+ def __new__(cls, name, bases, dct):
17
+ new_class = t.cast(type, super().__new__(cls, name, bases, dct))
18
+ if name == "PackedInts":
19
+ return new_class
20
+
21
+ annotate = annotationlib.get_annotate_from_class_namespace(dct)
22
+ assert annotate, f"Annotations not found for {name}"
23
+
24
+ annotations = annotate(annotationlib.Format.VALUE_WITH_FAKE_GLOBALS)
25
+
26
+ bits: dict[str, int] = {}
27
+ for key, value in annotations.items():
28
+ err_msg = f"expected Annotated[int, PackedInt(...)] on {name}.{key}"
29
+ assert t.get_origin(value) is t.Annotated, err_msg
30
+
31
+ ints = [arg for arg in t.get_args(value) if isinstance(arg, PackedInt)]
32
+ assert len(ints) <= 1, err_msg
33
+ assert len(ints) == 1, err_msg
34
+ bits[key] = ints[0].bits
35
+
36
+ obj = t.cast(PackedInts, dataclass(new_class))
37
+ obj._bits = bits
38
+ return obj
39
+
40
+
41
+ # This is very messed up since we
42
+ @t.dataclass_transform()
43
+ class PackedInts(Serializable, metaclass=PackedIntsMeta):
44
+ _bits: dict[str, int]
45
+
46
+ @classmethod
47
+ def serializer(cls) -> Serializer[t.Self]:
48
+ from ..serializers import PackedIntSerializer
49
+
50
+ return PackedIntSerializer(cls)
@@ -0,0 +1,37 @@
1
+ import annotationlib
2
+ import typing as t
3
+ from dataclasses import dataclass
4
+
5
+ from ..interfaces import Serializable, Serializer
6
+ from ..serialise import get_serializer
7
+
8
+
9
+ class SerializableObjectMeta(type):
10
+ def __new__(cls, name, bases, dct):
11
+ new_class = t.cast(type, super().__new__(cls, name, bases, dct))
12
+ if name == "SerializableObject":
13
+ return new_class
14
+
15
+ annotate = annotationlib.get_annotate_from_class_namespace(dct)
16
+
17
+ assert annotate, "No annotations method available"
18
+ annotations = annotate(annotationlib.Format.VALUE_WITH_FAKE_GLOBALS)
19
+
20
+ fields: dict[str, Serializer] = {}
21
+ for key, annotation in annotations.items():
22
+ try:
23
+ fields[key] = get_serializer(annotation)
24
+ except Exception as e:
25
+ raise ValueError(f"Invalid attribute defintion for {name}.{key}") from e
26
+
27
+ return t.cast(SerializableObject, dataclass(new_class))
28
+
29
+
30
+ # This is very messed up since we
31
+ @t.dataclass_transform()
32
+ class SerializableObject(Serializable, metaclass=SerializableObjectMeta):
33
+ @classmethod
34
+ def serializer(cls) -> Serializer[t.Self]:
35
+ from ..serializers import ObjectSerializer
36
+
37
+ return ObjectSerializer(cls)
structo/serialise.py ADDED
@@ -0,0 +1,46 @@
1
+ import typing as t
2
+ from .interfaces import Serializer, Serializable
3
+
4
+
5
+ def to_serializer[T](format: type[T] | Serializer[T]) -> Serializer[T]:
6
+ if isinstance(format, Serializer):
7
+ return format
8
+
9
+ if isinstance(format, type) and issubclass(format, Serializable):
10
+ return format.serializer()
11
+
12
+ raise AssertionError(f"Invalid value_type: {format}")
13
+
14
+
15
+ def get_serializer(format: type | Serializer) -> Serializer:
16
+ if t.get_origin(format) is t.Annotated:
17
+ args = t.get_args(format)
18
+ serializers = [arg for arg in args if isinstance(arg, Serializer)]
19
+ assert len(serializers) != 0, f"No serializers for {format} found"
20
+ assert len(serializers) == 1, f"More than one serializers for {format} found"
21
+ serializer = serializers[0]
22
+
23
+ return serializer
24
+
25
+ if isinstance(format, type) and issubclass(format, Serializable):
26
+ return format.serializer()
27
+
28
+ # Nicely formatted errors:
29
+ if format == int:
30
+ raise ValueError(
31
+ f"No serializer for int, you need to use structo.int32, structo.int32 or similar instead"
32
+ )
33
+ if format == float:
34
+ raise ValueError(
35
+ f"No serializer for float, you need to use structo.float32 or structo.float64 instead"
36
+ )
37
+ if format == bytes:
38
+ raise ValueError(
39
+ f"No serializer for bytes, you need to use structo.Buffer or structo.Blob instead"
40
+ )
41
+ if format == list:
42
+ raise ValueError(
43
+ f"No serializer for list, you need to use structo.List or structo.Array instead"
44
+ )
45
+
46
+ raise NotImplementedError(f"No serializer found for {format}")
@@ -0,0 +1,37 @@
1
+ from .numbers import (
2
+ uint64_BE,
3
+ uint64_LE,
4
+ uint64,
5
+ uint32_BE,
6
+ uint32_LE,
7
+ uint32,
8
+ uint16_BE,
9
+ uint16_LE,
10
+ uint16,
11
+ uint8,
12
+ int64_BE,
13
+ int64_LE,
14
+ int64,
15
+ int32_BE,
16
+ int32_LE,
17
+ int32,
18
+ int16_BE,
19
+ int16_LE,
20
+ int16,
21
+ int8,
22
+ float64_BE,
23
+ float64_LE,
24
+ float64,
25
+ float32_BE,
26
+ float32_LE,
27
+ float32,
28
+ )
29
+ from .buffer import Buffer
30
+ from .array import Array
31
+ from .blob import Blob
32
+ from .list import List
33
+ from .string import String
34
+ from .literal import Literal
35
+ from .optional import Optional
36
+ from .object import ObjectSerializer
37
+ from .packed import PackedIntSerializer
@@ -0,0 +1,34 @@
1
+ from ..serialise import to_serializer
2
+ from ..interfaces import Serializer
3
+
4
+
5
+ class Array[T](Serializer[list[T]]):
6
+ _length: int
7
+ _value_type: Serializer[T]
8
+
9
+ def __init__(self, value: Serializer[T] | type[T], length: int) -> None:
10
+ assert length > 0, "Array must be longer than 0"
11
+ self._length = length
12
+ self._value_type = to_serializer(value)
13
+
14
+ def write(self, f, value):
15
+ assert (
16
+ len(value) == self._length
17
+ ), f"expected array with {self._length} length, receieved {len(value)}"
18
+
19
+ for item in value:
20
+ self._value_type.write(f, item)
21
+
22
+ def read(self, f):
23
+ items = []
24
+ for _ in range(self._length):
25
+ items.append(self._value_type.read(f))
26
+
27
+ return items
28
+
29
+ def sizeof(self):
30
+ element_length = self._value_type.sizeof()
31
+ if element_length is None:
32
+ return None
33
+ else:
34
+ return element_length * self._length
@@ -0,0 +1,20 @@
1
+ from .numbers import uint32_LE
2
+ from ..interfaces import Serializer
3
+
4
+
5
+ class Blob(Serializer[bytes]):
6
+ "A set of arbitrary bytes, prefixed with it's length"
7
+
8
+ _length_type: Serializer[int]
9
+
10
+ def __init__(self, length_type: Serializer[int] | None = None) -> None:
11
+ self._length_type = length_type or uint32_LE
12
+
13
+ def write(self, f, value):
14
+ self._length_type.write(f, len(value))
15
+ f.write(value)
16
+
17
+ def read(self, f):
18
+ length = self._length_type.read(f)
19
+ data = f.read(length)
20
+ return data
@@ -0,0 +1,21 @@
1
+ from ..interfaces import Serializer
2
+
3
+
4
+ class Buffer(Serializer[bytes]):
5
+ _length: int
6
+
7
+ def __init__(self, length: int) -> None:
8
+ self._length = length
9
+
10
+ def write(self, f, value):
11
+ assert (
12
+ len(value) == self._length
13
+ ), f"expected data with length {self._length}, received {len(value)}"
14
+ f.write(value)
15
+
16
+ def read(self, f):
17
+ data = f.read(self._length)
18
+ return data
19
+
20
+ def sizeof(self):
21
+ return self._length
@@ -0,0 +1,29 @@
1
+ from .numbers import uint32_LE
2
+ from ..serialise import to_serializer
3
+ from ..interfaces import Serializer
4
+
5
+
6
+ class List[T](Serializer[list[T]]):
7
+ "A list of items, prefixed with it's length"
8
+
9
+ _length_type: Serializer[int]
10
+ _value_type: Serializer
11
+
12
+ def __init__(self, value: Serializer[T] | type[T], length: Serializer[int] | None = None) -> None:
13
+ self._length_type = length or uint32_LE
14
+ self._value_type = to_serializer(value)
15
+
16
+ def write(self, f, value):
17
+ length = len(value)
18
+ self._length_type.write(f, length)
19
+ for item in value:
20
+ self._value_type.write(f, item)
21
+
22
+ def read(self, f):
23
+ length = self._length_type.read(f)
24
+ values = []
25
+ for _ in range(length):
26
+ value = self._value_type.read(f)
27
+ values.append(value)
28
+
29
+ return values
@@ -0,0 +1,30 @@
1
+ from ..interfaces import Serializer
2
+
3
+
4
+ class Literal(Serializer[bytes]):
5
+ _values: list[bytes]
6
+ _length: int
7
+
8
+ def __init__(self, *values: bytes) -> None:
9
+ assert len(values) > 0, "foo"
10
+ length = len(values[0])
11
+
12
+ for value in values:
13
+ assert (
14
+ len(value) == length
15
+ ), f"All values in structo.Literal have to be the same length, expected {value} to be length {length}"
16
+
17
+ self._length = length
18
+ self._values = list(values)
19
+
20
+ def write(self, f, value):
21
+ assert value in self._values, f"{value} not in {b", ".join(self._values)}"
22
+ f.write(value)
23
+
24
+ def read(self, f):
25
+ value = f.read(self._length)
26
+ assert value in self._values, f"{value} not in {b", ".join(self._values)}"
27
+ return value
28
+
29
+ def sizeof(self):
30
+ return self._length
@@ -0,0 +1,100 @@
1
+ import io
2
+ import struct
3
+ from ..interfaces import Serializer
4
+
5
+
6
+ def struct_serializer(sformat: str, bytes: int) -> Serializer:
7
+ class StructSerializer(Serializer):
8
+ def sizeof(self):
9
+ return bytes
10
+
11
+ def write(self, f, value):
12
+ data = struct.pack(sformat, value)
13
+ return f.write(data)
14
+
15
+ def read(self, f):
16
+ data = f.read(bytes)
17
+ return struct.unpack(sformat, data)[0]
18
+
19
+ return StructSerializer()
20
+
21
+
22
+ uint64_BE: Serializer[int] = struct_serializer(">Q", bytes=8)
23
+ "**unsigned 64bit integer - big endian**"
24
+
25
+ uint64_LE: Serializer[int] = struct_serializer("<Q", bytes=8)
26
+ "**unsigned 64bit integer - little endian**"
27
+
28
+ uint64 = uint64_BE
29
+ "**unsigned 64bit integer - big endian**"
30
+
31
+ uint32_BE: Serializer[int] = struct_serializer(">I", bytes=4)
32
+ "**unsigned 32bit integer - big endian**"
33
+
34
+ uint32_LE: Serializer[int] = struct_serializer("<I", bytes=4)
35
+ "**unsigned 32bit integer - little endian**"
36
+
37
+ uint32 = uint32_BE
38
+ "**unsigned 32bit integer - big endian**"
39
+
40
+ uint16_BE: Serializer[int] = struct_serializer(">H", bytes=2)
41
+ "**unsigned 16bit integer - big endian**"
42
+
43
+ uint16_LE: Serializer[int] = struct_serializer("<H", bytes=2)
44
+ "**unsigned 16bit integer - little endian**"
45
+
46
+ uint16 = uint16_BE
47
+ "**unsigned 16bit integer - big endian**"
48
+
49
+ uint8: Serializer[int] = struct_serializer("B", bytes=1)
50
+ "**unsigned 8bit integer**"
51
+
52
+ int64_BE: Serializer[int] = struct_serializer(">q", bytes=8)
53
+ "**signed 64bit integer - big endian**"
54
+
55
+ int64_LE: Serializer[int] = struct_serializer("<q", bytes=8)
56
+ "**signed 64bit integer - little endian**"
57
+
58
+ int64 = int64_BE
59
+ "**signed 64bit integer - big endian**"
60
+
61
+ int32_BE: Serializer[int] = struct_serializer(">i", bytes=4)
62
+ "**signed 32bit integer - big endian**"
63
+
64
+ int32_LE: Serializer[int] = struct_serializer("<i", bytes=4)
65
+ "**signed 32bit integer - little endian**"
66
+
67
+ int32 = int32_BE
68
+ "**signed 32bit integer - big endian**"
69
+
70
+ int16_BE: Serializer[int] = struct_serializer(">h", bytes=2)
71
+ "**signed 16bit integer - big endian**"
72
+
73
+ int16_LE: Serializer[int] = struct_serializer("<h", bytes=2)
74
+ "**signed 16bit integer - little endian**"
75
+
76
+ int16 = int16_BE
77
+ "**signed 16bit integer - big endian**"
78
+
79
+ int8: Serializer[int] = struct_serializer("b", bytes=1)
80
+ "**signed 8bit integer**"
81
+
82
+
83
+ float64_BE: Serializer[float] = struct_serializer(">d", bytes=8)
84
+ "**64bit float - big endian**"
85
+
86
+ float64_LE: Serializer[float] = struct_serializer("<d", bytes=8)
87
+ "**64bit float - little endian**"
88
+
89
+ float64 = float64_BE
90
+ "**64bit float - big endian**"
91
+
92
+ float32_BE: Serializer[float] = struct_serializer(">f", bytes=4)
93
+ "**32bit float - big endian**"
94
+
95
+ float32_LE: Serializer[float] = struct_serializer("<f", bytes=4)
96
+ "**32bit float - little endian**"
97
+
98
+ float32 = float32_BE
99
+ "**32bit float - big endian**"
100
+
@@ -0,0 +1,52 @@
1
+ import typing as t
2
+ import io
3
+ import annotationlib
4
+
5
+ from ..interfaces import Serializer
6
+ from ..objects import SerializableObject
7
+ from ..serialise import get_serializer
8
+
9
+
10
+ class ObjectSerializer[T: SerializableObject](Serializer[T]):
11
+ _annotations: dict[str, Serializer] = {}
12
+ _type: type
13
+
14
+ def __init__(self, type: type[T]) -> None:
15
+ annotations: dict[str, Serializer] = {}
16
+ self._type = type
17
+
18
+ attrs = annotationlib.get_annotations(type)
19
+ for key, value in attrs.items():
20
+ try:
21
+ annotations[key] = get_serializer(value)
22
+ except Exception as e:
23
+ raise ValueError(
24
+ f"Invalid attribute defintion for {type.__name__}.{key}"
25
+ ) from e
26
+
27
+ self._annotations = annotations
28
+
29
+ def sizeof(self) -> int | None:
30
+ total_size = 0
31
+ for serializer in self._annotations.values():
32
+ field_size = serializer.sizeof()
33
+ if field_size is None:
34
+ return None
35
+ else:
36
+ total_size += field_size
37
+
38
+ return total_size
39
+
40
+ def write(self, f, value):
41
+ for field_key, field_format in self._annotations.items():
42
+ field_value = getattr(value, field_key)
43
+ field_format.write(f, field_value)
44
+
45
+ def read(self, f):
46
+ attrs = {}
47
+
48
+ for field_key, field_format in self._annotations.items():
49
+ field_value = field_format.read(f)
50
+ attrs[field_key] = field_value
51
+
52
+ return self._type(**attrs)
@@ -0,0 +1,39 @@
1
+ from ..interfaces import Serializer
2
+
3
+
4
+ NONE_TYPE_BYTE = b""
5
+ VALUE_TYPE_BYTE = b""
6
+
7
+
8
+ class Optional[T](Serializer[T | None]):
9
+ _value_type: Serializer[T]
10
+
11
+ def __init__(self, value: Serializer[T]) -> None:
12
+ self._value_type = value
13
+
14
+ def write(self, f, value):
15
+ if value is None:
16
+ f.write(NONE_TYPE_BYTE)
17
+ else:
18
+ f.write(VALUE_TYPE_BYTE)
19
+ self._value_type.write(f, value)
20
+
21
+ def read(self, f):
22
+ type_byte = f.read(1)
23
+
24
+ assert type_byte in (
25
+ NONE_TYPE_BYTE,
26
+ VALUE_TYPE_BYTE,
27
+ ), f"Unexpected type byte {type_byte}"
28
+
29
+ if type_byte == NONE_TYPE_BYTE:
30
+ return None
31
+ else:
32
+ return self._value_type.read(f)
33
+
34
+ def sizeof(self):
35
+ value_length = self._value_type.sizeof()
36
+ if value_length is None:
37
+ return None
38
+ else:
39
+ return value_length + 1
@@ -0,0 +1,57 @@
1
+ from math import ceil
2
+
3
+ from ..objects import PackedInts
4
+ from ..interfaces import Serializer
5
+
6
+ import typing as t
7
+
8
+
9
+ class PackedIntSerializer[T: PackedInts](Serializer[T]):
10
+ _size: int
11
+ _bits: dict[str, int]
12
+ _cls: type[type[T]]
13
+
14
+ def __init__(self, cls: type[T]) -> None:
15
+ self._cls = cls
16
+ self._bits = cls._bits
17
+ self._size = self.sizeof()
18
+
19
+ def write(self, f, value):
20
+ output = 0
21
+ offset = 0
22
+ for field_key, bits in self._bits.items():
23
+ field_value = getattr(value, field_key)
24
+
25
+ max_value = (2**bits) - 1
26
+ assert (
27
+ field_value <= max_value
28
+ ), f"{self._cls.__name__}{field_key} exceed max value, {field_value} > {max_value}"
29
+ output += field_value << offset
30
+ offset += bits
31
+
32
+ data = bytearray(self._size)
33
+ for x in range(self._size):
34
+ byte = output & 255
35
+ output >>= 8
36
+ data[x] = byte
37
+
38
+ f.write(data)
39
+
40
+ def read(self, f):
41
+ data = f.read(self._size)
42
+ integer = int.from_bytes(data, "little")
43
+ attrs: dict[str, t.Any] = {}
44
+
45
+ offset = 0
46
+ for field_key, bits in self._bits.items():
47
+
48
+ mask = (2**bits) - 1
49
+ value = (integer >> offset) & mask
50
+
51
+ offset += bits
52
+ attrs[field_key] = value
53
+
54
+ return self._cls(**attrs)
55
+
56
+ def sizeof(self) -> int:
57
+ return ceil(sum(self._bits.values()) / 8)
@@ -0,0 +1,27 @@
1
+ import typing as t
2
+ from .numbers import uint32_LE
3
+ from ..interfaces import Serializer
4
+
5
+
6
+ class String(Serializer[str]):
7
+ "A unicode string, prefixed with it's byte length"
8
+
9
+ _length_type: Serializer[int]
10
+
11
+ def __init__(self, length_type: Serializer[int] | None = None) -> None:
12
+ """_summary_
13
+
14
+ Args:
15
+ length_type (Serializer[int], optional): Defaults to uint32_LE
16
+ """
17
+ self._length_type = length_type or uint32_LE
18
+
19
+ def write(self, f, value):
20
+ data = value.encode("utf-8")
21
+ self._length_type.write(f, len(data))
22
+ f.write(data)
23
+
24
+ def read(self, f):
25
+ length = self._length_type.read(f)
26
+ data = f.read(length)
27
+ return data.decode("utf-8")
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: structo
3
+ Version: 0.0.10
4
+ Summary: Structify
5
+ Author-email: Ben Brady <benbradybusiness@gmail.com>
6
+ Requires-Python: >=3.14
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Dist: black>=26.1.0 ; extra == "dev"
10
+ Requires-Dist: pytest>=9.0.2 ; extra == "dev"
11
+ Requires-Dist: black ; extra == "docs"
12
+ Requires-Dist: mkdocs ; extra == "docs"
13
+ Requires-Dist: mkdocs-material ; extra == "docs"
14
+ Requires-Dist: mkdocstrings[crystal, python] ; extra == "docs"
15
+ Project-URL: Home, https://nnilky.site
16
+ Provides-Extra: dev
17
+ Provides-Extra: docs
@@ -0,0 +1,21 @@
1
+ structo/__init__.py,sha256=-QD1o5pnWc1lpWW6mvdUXVtoswjhJzJ97grj1uFr-ds,658
2
+ structo/interfaces.py,sha256=ecmIOKz4Z06UNlUdspXzIjjCLiPJfZrahsFhEXSFjt8,1188
3
+ structo/serialise.py,sha256=GsrxqQGanqVmaM6s1J3T8PSdit-AmrGhdmyGfEi-uwI,1640
4
+ structo/objects/__init__.py,sha256=0FMm6WXU3PERdq_kU30u6ofTYAfQGmw_JQ6z4mC5Ru4,94
5
+ structo/objects/packed.py,sha256=9Fr3xjme9wvuh51jsGlM9pPb58zVO3-o_PqdVKXU9VI,1486
6
+ structo/objects/serializable_object.py,sha256=MsuJU1nrWULG7vXenUgkxB71xu9XKU4KaEEk0waOU_U,1250
7
+ structo/serializers/__init__.py,sha256=wd5FxsqqqT8vOAKhvqLp3qL0PoOXhJhkrzc7f-8edrM,641
8
+ structo/serializers/array.py,sha256=uYSEqTa2LYTINxgT2VWhi8dK5KgJyg-5_8kZx9sZZeA,964
9
+ structo/serializers/blob.py,sha256=63wABDOVnbeUD2Tga2hO35eSu8quLEJZfFGvxhAciqY,542
10
+ structo/serializers/buffer.py,sha256=7mIvqWVB679Sf9DpngnvVD1xMq-XnuWkq57W3QI8ccM,484
11
+ structo/serializers/list.py,sha256=-1mUYWpN22uVcvOf2Igdt4cw9RNNbjz7xD2ZnmCz1S8,849
12
+ structo/serializers/literal.py,sha256=ZQQ_SVFTwSe8cfOE9eeaw9j12ytYgvav0PPrve12JWY,857
13
+ structo/serializers/numbers.py,sha256=bNyzRtpXRmHIPx93JOG_B2CqrfkOk09Dfb01LYBpEaY,2767
14
+ structo/serializers/object.py,sha256=FH5w5GDwvo2P3HjWgmNDql_y0OlvpOVhGEjV0OqRxgs,1535
15
+ structo/serializers/optional.py,sha256=nUyZDaB_UIDb4dPxDQSkSEzi387AOERwskR2zwoZGDQ,924
16
+ structo/serializers/packed.py,sha256=RkhgXXfpOMYysolnL0mvezkyPje86ukOIYzUraAZ7k8,1471
17
+ structo/serializers/string.py,sha256=A-wFOGgNbnHZHDkOfwetb-O1m59hvOIehTzfq66n2bU,732
18
+ structo-0.0.10.dist-info/licenses/LICENSE,sha256=lOeFTSB86KGSl3-qqiI0zhIy0Nv-2USUp_93ANudE14,1076
19
+ structo-0.0.10.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
20
+ structo-0.0.10.dist-info/METADATA,sha256=DNRhNUEVH2KDrTjD4-uHL--VTElJFNdWH_Bmr1m7C6Y,557
21
+ structo-0.0.10.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: flit 3.12.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Ben Brady
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.