Package bare
pybare is a Python library for writing and reading binary data using the BARE serialization format.
It uses a declarative API that attempts to mimic the schema language provided by the BARE RFC.
Sub-modules
bare.barearraybare.barestructbare.baretypebare.miscbare.numberbare.test_barearraybare.test_barestructbare.test_databare.test_encoderbare.test_mapbare.test_miscbare.test_numberbare.test_unionbare.util
Functions
def array(inner: Type[BAREType[Any]],
size: Optional[int] = None) ‑> Type[Array[Any, ~A]]-
Expand source code
def array( inner: Type[BAREType[Any]], size: Optional[int] = None ) -> Type[Array[Any, A]]: """ A function that defines and returns anonymous BARE `Array` subclass with the provided `inner` type and (optional) `size` arguments. Proper usage of this function is as follows: ``` MyArray = array(UInt, size=10) ``` Note that the name of the class is unspecified, use at your own risk. """ size_name = f"_size_{size}" if size else "" name = f"Array_{inner.__name__}{size_name}_anonymous" namespace = ArrayMeta.__prepare__(name, (Array,), inner=inner, size=size) AnonymousArray = ArrayMeta.__new__( ArrayMeta, name, (Array,), namespace, inner=inner, size=size ) ArrayMeta.__init__(AnonymousArray, name, (Array,), namespace) # type: ignore return AnonymousArrayA function that defines and returns anonymous BARE
Arraysubclass with the providedinnertype and (optional)sizearguments.Proper usage of this function is as follows:
MyArray = array(UInt, size=10)Note that the name of the class is unspecified, use at your own risk.
def data(size: int) ‑> Type[bare.data.Data]-
Expand source code
def data(size: int) -> Type[Data]: """ A function that defines and returns anonymous BARE `Data` subclass with the provided `size` argument. Proper usage of this function is as follows: ``` MyData = data(10) ``` """ size_name = f"_size_{size}" name = f"Data_{size_name}_anonymous" namespace = DataMeta.__prepare__(name, (Data,), size=size) AnonymousData = DataMeta.__new__(DataMeta, name, (Data,), namespace, size=size) DataMeta.__init__(AnonymousData, name, (Data,), namespace) # type: ignore return AnonymousDataA function that defines and returns anonymous BARE
Datasubclass with the providedsizeargument.Proper usage of this function is as follows:
MyData = data(10) def map(key_type: type[BAREType[K]],
value_type: type[BAREType[V]]) ‑> Type[bare.map.Map[~K, ~V]]-
Expand source code
def map(key_type: type[BAREType[K]], value_type: type[BAREType[V]]) -> Type[Map[K, V]]: """ A function that defines and returns anonymous BARE `Map` subclass with the provided `key_type` type and and `value_type` type. Proper usage of this function is as follows: ``` MyMap = map(Str, UInt) ``` Note that the name of the class is unspecified and subject to change. """ name = f"Map_{key_type.__name__}_{value_type.__name__}_anonymous" namespace = MapMeta.__prepare__( name, (Map,), key_type=key_type, value_type=value_type ) AnonymousMap = MapMeta.__new__( MapMeta, name, (Map,), namespace, key_type=key_type, value_type=value_type ) MapMeta.__init__(AnonymousMap, name, (Map,), namespace) # type: ignore return AnonymousMapA function that defines and returns anonymous BARE
Mapsubclass with the providedkey_typetype and andvalue_typetype.Proper usage of this function is as follows:
MyMap = map(Str, UInt)Note that the name of the class is unspecified and subject to change.
def optional(maybe: Type[T]) ‑> Type[bare.union.Union]-
Expand source code
def optional(maybe: Type[T]) -> Type[Union]: """ A simplified version of `union` that implicitly includes a `Void` variant and accepts a single type as an argument. For example: ``` MyOptional = optional(Str) ``` would be equivalent to: class MyOptional(Union, variants=(Void, Str)): ... """ name = f"Optional_{maybe.__name__}" variants = ( Void, maybe, ) namespace = UnionMeta.__prepare__(name, (), variants=variants) AnonymousUnion = UnionMeta.__new__( UnionMeta, name, (Union,), namespace, variants=variants ) UnionMeta.__init__(AnonymousUnion, name, (Union,), namespace, variants=variants) return AnonymousUnion def struct(**kwargs: Type[BAREType]) ‑> Type[Struct]-
Expand source code
def struct(**kwargs: Type[BAREType]) -> Type[Struct]: """ A function that defines and returnes an anonymous BARE `Struct` subclass with the provided `kwargs` as fields. The name of each kwarg becomes the field name, with the value being the field type. The field type is implicitly wrapped in a `Field`.Field Proper usage of this function is as follows: ``` MyStruct = struct(a=UInt, b=Str) ``` Note that the name of the class is unspecified, use at your own risk. """ name = "Struct_{}_anonymous".format("_".join(kwargs.keys())) namespace = StructMeta.__prepare__(name, (Struct,)) namespace.update({field: Field(ty) for field, ty in kwargs.items()}) # type: ignore AnonymousStruct = StructMeta.__new__(StructMeta, name, (Struct,), namespace) StructMeta.__init__(AnonymousStruct, name, (Struct,), namespace) return AnonymousStructA function that defines and returnes an anonymous BARE
Structsubclass with the providedkwargsas fields. The name of each kwarg becomes the field name, with the value being the field type. The field type is implicitly wrapped in aField.FieldProper usage of this function is as follows:
MyStruct = struct(a=UInt, b=Str)Note that the name of the class is unspecified, use at your own risk.
def union(*variants: tuple[type[BAREType[Any]], ...]) ‑> type[bare.union.Union]-
Expand source code
def union(*variants: tuple[type[BAREType[Any]], ...]) -> type[Union]: """ A function that defines and returns an anonymous Union type. The varargs are passed as variants to the Union type. The name of the class is undefined. For example: ``` MyUnion = union(UnionVariant(Str, 3), Int) ``` """ name = "Union_anonymous" namespace = UnionMeta.__prepare__(name, (), variants=variants) AnonymousUnion = UnionMeta.__new__( UnionMeta, name, (Union,), namespace, variants=variants ) UnionMeta.__init__(AnonymousUnion, name, (Union,), namespace, variants=variants) # type: ignore return AnonymousUnionA function that defines and returns an anonymous Union type. The varargs are passed as variants to the Union type.
The name of the class is undefined.
For example:
MyUnion = union(UnionVariant(Str, 3), Int)
Classes
class Array (value: Iterable[T | A])-
Expand source code
class Array(Generic[T, A], BAREType[T], metaclass=ArrayMeta): """ A BARE array type. It's inner type must be a BARE type defined in this package and must be supplied as a metaclass argument to a subclass of the `Array` class using the `inner` kwarg. A `size` kwarg may also be specified, which will make the new subclass a fixed-size array, which does not encode the length of the array in the serialized data. An example that uses both of these: ``` class MyArray(Array, inner=UInt, size=10): ... ``` You do *not* need to specify any fields or methods on the subclass, though you may. The above class `MyArray` is a fixed size array of 10 `UInt` values. """ _type: ClassVar[Type[BAREType]] _size: int | None value: list[A] def __init__(self, value: Iterable[T | A]): inner = [] for v in value: if not isinstance(v, self.__class__._type): v = self.__class__._type(v) else: v = v inner.append(v) self.value = inner def __getitem__(self, key: int): return self.value[key] def append(self, value: T | A): """ Appends a value to the end of the array. The value is validated against the expected type for this `Array`. If the `Array` is of fixed-size, appending will result in a `ValueError` being raised. @value: The value to append to the array """ if self._size and len(self.value) >= self._size: raise ValueError("Appending to fixed sized array is unsupported.") if not isinstance(value, self.__class__._type): # mypy isn't smart enough to tell what type this is value = self.__class__._type(value) # type: ignore self.value.append(value) # type: ignore def __iter__(self): return iter(self.value) def __eq__(self, other: Any) -> bool: if isinstance(other, Array): return other.value == self.value try: values = list(iter(other)) for other_value, self_value in zip(values, self.value): if other_value != self_value: return False return True except TypeError: pass return NotImplemented def pack(self) -> bytes: fp = io.BytesIO() if self._size is None: fp.write(UInt(len(self._inner)).pack()) size = self._size or len(self._inner) for i in range(size): fp.write(self._inner[i].pack()) return fp.getbuffer() @classmethod def unpack(cls, fp: BinaryIO) -> Array: size = cls._size or UInt.unpack(fp).value out = [] for _ in range(size): out.append(cls._type.unpack(fp)) return cls(out) @classmethod def validate(cls, value: Iterable[T]) -> bool: value = list(value) if cls._size: if len(value) != cls._size: return False for v in value: if not cls._type.validate(v): return False return True def __repr__(self) -> str: return f"{self.__class__.__name__}({repr(self.value)})"A BARE array type. It's inner type must be a BARE type defined in this package and must be supplied as a metaclass argument to a subclass of the
Arrayclass using theinnerkwarg.A
sizekwarg may also be specified, which will make the new subclass a fixed-size array, which does not encode the length of the array in the serialized data.An example that uses both of these:
class MyArray(Array, inner=UInt, size=10): ...You do not need to specify any fields or methods on the subclass, though you may. The above class
MyArrayis a fixed size array of 10UIntvalues.Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- abc.Array_Nested_size_1_anonymous
- abc.Array_Order_anonymous
- abc.Array_Str_size_4_anonymous
- bare.test_encoder.MyArray
Class variables
var value : list[~A]
Static methods
def unpack(fp: BinaryIO) ‑> Arraydef validate(value: Iterable[T]) ‑> bool
Methods
def append(self, value: T | A)-
Expand source code
def append(self, value: T | A): """ Appends a value to the end of the array. The value is validated against the expected type for this `Array`. If the `Array` is of fixed-size, appending will result in a `ValueError` being raised. @value: The value to append to the array """ if self._size and len(self.value) >= self._size: raise ValueError("Appending to fixed sized array is unsupported.") if not isinstance(value, self.__class__._type): # mypy isn't smart enough to tell what type this is value = self.__class__._type(value) # type: ignore self.value.append(value) # type: ignore def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: fp = io.BytesIO() if self._size is None: fp.write(UInt(len(self._inner)).pack()) size = self._size or len(self._inner) for i in range(size): fp.write(self._inner[i].pack()) return fp.getbuffer()
class BAREType (*arg, **kwargs)-
Expand source code
class BAREType(Protocol, Generic[T]): @abstractmethod def __init__(self, *arg, **kwargs): ... @abstractmethod def pack(self) -> bytes: ... @classmethod @abstractmethod def unpack(cls, fp: BinaryIO) -> BAREType: ... @classmethod @abstractmethod def validate(cls, value: Any) -> bool: return False @abstractmethod def __eq__(cls, other: Any) -> bool: return NotImplementedBase class for protocol classes.
Protocol classes are defined as::
class Proto(Protocol): def meth(self) -> int: ...Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).
For example::
class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type checkSee PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::
class GenProto[T](Protocol): def meth(self) -> T: ...Ancestors
- typing.Protocol
- typing.Generic
Subclasses
Static methods
def unpack(fp: BinaryIO) ‑> BARETypedef validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
@abstractmethod def pack(self) -> bytes: ...
class Bool (value: T)-
Expand source code
class Bool(NumberMixin[bool]): """ A boolean BARE type, encoded as a single byte. """ def pack(self) -> bytes: return struct.pack("<?", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> Bool: buf = fp.read(struct.calcsize("<?")) return cls(struct.unpack("<?", buf)[0]) # type: ignore @classmethod def validate(cls, value: bool) -> bool: return isinstance(value, (bool, cls))A boolean BARE type, encoded as a single byte.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> Booldef validate(value: bool) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<?", self.value)
class Data (value: bytes)-
Expand source code
class Data(BAREType[bytes], metaclass=DataMeta): """ Represents a BARE data type, which is effectively an alias for an array of u8s. As with the `Array` type, a `size` kwarg may be specified to make the new subclass a fixed-size data type. An example: ``` class MyData(Data, size=10): ... ``` """ _size: Optional[int] def __init__(self, value: bytes): if self._size and len(value) != self._size: raise ValueError( f"Bytes was not expected size ({self._size}). Got {len(value)}" ) self.value = value def pack(self) -> bytes: fp = io.BytesIO() if not self._size: fp.write(UInt(len(self.value)).pack()) fp.write(self.value) return fp.getbuffer() @classmethod def validate(cls, value: ByteString) -> bool: return isinstance(value, (bytes, bytearray, memoryview)) def __eq__(self, other: Any) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.value == other.value @classmethod def unpack(cls, fp: BinaryIO) -> Data: size = cls._size or UInt.unpack(fp).value buf = fp.read(size) return cls(buf)Represents a BARE data type, which is effectively an alias for an array of u8s.
As with the
Arraytype, asizekwarg may be specified to make the new subclass a fixed-size data type.An example:
class MyData(Data, size=10): ...Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- abc.Data__size_128_anonymous
Static methods
def unpack(fp: BinaryIO) ‑> bare.data.Datadef validate(value: ByteString) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: fp = io.BytesIO() if not self._size: fp.write(UInt(len(self.value)).pack()) fp.write(self.value) return fp.getbuffer()
class Enum (*args, **kwds)-
Expand source code
class Enum(IntEnum, metaclass=EnumMeta): """ A BARE enum type. It is a subclass of `IntEnum` and is used to represent a BARE enum type. An example: ``` class MyEnum(Enum): A = 1 B = 2 C = 3 print(MyEnum.A) # prints 1 print(MyEnum.B) # prints 2 print(MyEnum.C) # prints 3 print(MyEnum.A is MyEnum.B) # prints False print(MyEnum.A == 1) # prints True ``` """ def pack(self) -> bytes: return UInt(self.value).pack() @classmethod def unpack(cls, fp: BinaryIO) -> Enum: x = UInt.unpack(fp).value return cls(x) @classmethod def validate(cls, value: Any) -> bool: if isinstance(value, UInt): try: cls(value.value) return True except ValueError: return False elif isinstance(value, int): try: cls(value) return True except ValueError: return False elif isinstance(value, cls): return True else: return FalseA BARE enum type. It is a subclass of
IntEnumand is used to represent a BARE enum type.An example:
class MyEnum(Enum): A = 1 B = 2 C = 3 print(MyEnum.A) # prints 1 print(MyEnum.B) # prints 2 print(MyEnum.C) # prints 3 print(MyEnum.A is MyEnum.B) # prints False print(MyEnum.A == 1) # prints TrueAncestors
- enum.IntEnum
- builtins.int
- enum.ReprEnum
- enum.Enum
Subclasses
- bare.test_encoder.Department
- bare.test_encoder.EnumTest
- bare.test_map.MyEnum
- bare.test_misc.MyEnum
Static methods
def unpack(fp: BinaryIO) ‑> Enumdef validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return UInt(self.value).pack()
class F32 (value: T)-
Expand source code
class F32(NumberMixin[float]): """ A single-precision floating point BARE type. An example: ``` x = F32(123.5) x.pack() # prints b'\x00\x00\xf7B' fp = io.Bytes(x.pack()) F64.unpack(fp) # => F64(123.5) ``` """ def pack(self) -> bytes: return struct.pack("<f", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> F32: buf = fp.read(struct.calcsize("<f")) return cls(struct.unpack("<f", buf)[0]) # type: ignore @classmethod def validate(cls, value: float) -> bool: if isinstance(value, cls): return True try: float(value) return True except (ValueError, TypeError): return FalseA single-precision floating point BARE type.
An example:
x = F32(123.5) x.pack() # prints b' ÷B' fp = io.Bytes(x.pack()) F64.unpack(fp) # => F64(123.5)Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> F32def validate(value: float) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<f", self.value)
class F64 (value: T)-
Expand source code
class F64(NumberMixin[float]): """ A double-precision floating point BARE type. An example: ``` x = F64(123.5) x.pack() # prints b'\x00\x00\x00\x00\x00\xe0^@' fp = io.Bytes(x.pack()) F64.unpack(fp) # => F64(123.5) ``` """ def pack(self) -> bytes: return struct.pack("<d", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> F64: buf = fp.read(struct.calcsize("<d")) return cls(struct.unpack("<d", buf)[0]) @classmethod def validate(cls, value: Any) -> bool: if isinstance(value, cls): return True try: float(value) return True except (ValueError, TypeError): return FalseA double-precision floating point BARE type.
An example:
x = F64(123.5) x.pack() # prints b' à^@' fp = io.Bytes(x.pack()) F64.unpack(fp) # => F64(123.5)Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> F64def validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<d", self.value)
class Field (ty: Type[BAREType | Enum],
attr: str | None = None)-
Expand source code
def __get__(self, inst, _) -> T | Type[BAREType[T] | Enum]: if inst is None: return self.ty return inst.__dict__[self.attr]A descriptor that assists with assigning/retrieving values from a BARE Struct. It is reponsible for performing type validation on assignment of struct fields.
For example:
class MyStruct(Struct): a = Field(UInt) b = Field(Str)Class variables
var attr : strvar name : strvar ty : Type[BAREType | Enum]
class I16 (value: T)-
Expand source code
class I16(NumberMixin[int]): """ A signed 16-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<h", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> I16: buf = fp.read(struct.calcsize("<h")) return cls(struct.unpack("<h", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return -(1 << 15) <= value and value < (1 << 15) except (ValueError, TypeError): return FalseA signed 16-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I16def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<h", self.value)
class I32 (value: T)-
Expand source code
class I32(NumberMixin[int]): """ A signed 32-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<l", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> I32: buf = fp.read(struct.calcsize("<l")) return cls(struct.unpack("<l", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return -(1 << 31) <= value and value < (1 << 31) except (ValueError, TypeError): return FalseA signed 32-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I32def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<l", self.value)
class I64 (value: T)-
Expand source code
class I64(NumberMixin[int]): """ A signed 64-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<q", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> I64: buf = fp.read(struct.calcsize("<q")) return cls(struct.unpack("<q", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return -(1 << 63) <= value and value < (1 << 63) except (ValueError, TypeError): return FalseA signed 64-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I64def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<q", self.value)
class I8 (value: T)-
Expand source code
class I8(NumberMixin[int]): """ A signed 8-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<b", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> I8: buf = fp.read(struct.calcsize("<b")) return cls(struct.unpack("<b", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return -(1 << 7) <= value and value < (1 << 7) except (ValueError, TypeError): return FalseA signed 8-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I8def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<b", self.value)
class Int (value: T)-
Expand source code
class Int(NumberMixin[int]): """ An unsigned varint BARE type. When serialized, it encodes itself as a LEB128 unsigned varint. An example: ``` x = Int(-10) x.pack() # prints b'\x14' fp = io.Bytes(x.pack()) Int.unpack(fp) # => UInt(-10) ``` """ def pack(self) -> bytes: buf = io.BytesIO() _write_varint(buf, self.value, True) return buf.getvalue() @classmethod def unpack(cls, fp: BinaryIO) -> Int: return cls(_read_varint(fp, True)) @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return -(1 << 63) <= value and value <= (1 << 63) except (ValueError, TypeError): return False def __hash__(self): return hash(self.__class__) + hash(self.value)An unsigned varint BARE type. When serialized, it encodes itself as a LEB128 unsigned varint.
An example:
x = Int(-10) x.pack() # prints b'' fp = io.Bytes(x.pack()) Int.unpack(fp) # => UInt(-10)Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> Intdef validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: buf = io.BytesIO() _write_varint(buf, self.value, True) return buf.getvalue()
class Map (initial_values: Mapping[K, V])-
Expand source code
class Map(BAREType[dict[K, V]], metaclass=MapMeta): """ A BARE map type. The type used for keys is declared using the `key_type` metaclass kwarg, and the type used for values is declared using the `value_type` metaclass kwarg. An example: ``` class MyMap(Map, key_type=UInt, value_type=Str): ... ``` `Map` implements a dict-like interface and may be used as such. Both the types of keys and values are validated against their BARE types `validate` class method. All keys and values are normalized to their BARE type wrappers. """ _values: dict def __init__(self, initial_values: Mapping[K, V]): self._values = {} for key, value in initial_values.items(): if not isinstance(key, self._key_type): key = self._key_type(key) if not isinstance(value, self._value_type): value = self._value_type(value) self[key] = value def __getitem__(self, key): if not isinstance(key, self._key_type): key = self._key_type(key) return self._values[key].value def __setitem__(self, key, value): if not isinstance(key, self._key_type): key = self._key_type(key) if not isinstance(value, self._value_type): value = self._value_type(value) self._values[key] = value def update(self, *args, **kwargs): for k, v in dict(*args, **kwargs).items(): self[k] = v def items(self): return self._values.items() @classmethod def validate(cls, value: Mapping) -> bool: for key, value in value.items(): if not cls._key_type.validate(key) or not cls._value_type.validate(value): return False return True def pack(self) -> bytes: fp = io.BytesIO() fp.write(UInt(len(self._values)).pack()) for key, value in self._values.items(): # type: ignore fp.write(key.pack()) fp.write(value.pack()) return fp.getbuffer() @classmethod def unpack(cls, fp: BinaryIO) -> Map: size = UInt.unpack(fp).value out = {} for _ in range(size): key = cls._key_type.unpack(fp) value = cls._value_type.unpack(fp) out[key] = value return cls(out) def __eq__(self, other: Any) -> bool: if isinstance(other, Map): return other._values == self._values try: for other_key, other_value in other.values(): if ( other_key not in self._values and self._key_type(other_key) not in self._values ): return False if other_value != self._values[other_key]: return False return True except TypeError: pass return NotImplementedA BARE map type. The type used for keys is declared using the
key_typemetaclass kwarg, and the type used for values is declared using thevalue_typemetaclass kwarg.An example:
class MyMap(Map, key_type=UInt, value_type=Str): ...Mapimplements a dict-like interface and may be used as such. Both the types of keys and values are validated against their BARE typesvalidateclass method.All keys and values are normalized to their BARE type wrappers.
Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- abc.Map_Int_Str_anonymous
- abc.Map_Str_Data_anonymous
- abc.Map_Str_Data_anonymous
- abc.Map_Str_Int_anonymous
- abc.Map_Str_Str_anonymous
- abc.Map_Str_Str_anonymous
- bare.test_map.MyMap
Static methods
def unpack(fp: BinaryIO) ‑> bare.map.Mapdef validate(value: Mapping) ‑> bool
Methods
def items(self)-
Expand source code
def items(self): return self._values.items() def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: fp = io.BytesIO() fp.write(UInt(len(self._values)).pack()) for key, value in self._values.items(): # type: ignore fp.write(key.pack()) fp.write(value.pack()) return fp.getbuffer() def update(self, *args, **kwargs)-
Expand source code
def update(self, *args, **kwargs): for k, v in dict(*args, **kwargs).items(): self[k] = v
class Str (value: str)-
Expand source code
class Str(BAREType[str]): """ A BARE string type. It should generally be used directly. An example: ``` x = Str("Hello") x.pack() # prints b'\x05Hello' Str.unpack(x.pack()) # => Str("Hello") ``` """ value: str def __init__(self, value: str): if not self.validate(value): raise TypeError(f"Str must wrap a python str. Got {type(value)}") if isinstance(value, self.__class__): self.value = value.value else: self.value = value def pack(self) -> bytes: fp = io.BytesIO() encoded = self.value.encode("utf-8") fp.write(UInt(len(encoded)).pack()) fp.write(encoded) return fp.getbuffer() @classmethod def validate(cls, value: str) -> bool: return isinstance(value, (str, cls)) @classmethod def unpack(cls, fp: BinaryIO) -> Str: size = UInt.unpack(fp).value buf = fp.read(size) if len(buf) != size: raise EOFError return cls(buf.decode("utf-8")) def __eq__(self, other: Any) -> bool: if isinstance(other, Str): return other.value == self.value elif isinstance(other, str): return other == self.value else: return NotImplemented def __hash__(self): return hash(self.__class__) def __repr__(self) -> str: name = self.__class__.__name__ return f'{name}("{self.value})"'A BARE string type.
It should generally be used directly.
An example:
x = Str("Hello") x.pack() # prints b'Hello' Str.unpack(x.pack()) # => Str("Hello")Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- bare.test_encoder.Time
Class variables
var value : str
Static methods
def unpack(fp: BinaryIO) ‑> Strdef validate(value: str) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: fp = io.BytesIO() encoded = self.value.encode("utf-8") fp.write(UInt(len(encoded)).pack()) fp.write(encoded) return fp.getbuffer()
class Struct (**kwargs)-
Expand source code
class Struct(BAREType, metaclass=StructMeta): """ A BARE struct type. Declare fields using the `Field` type. Each field is serialized in the same order as declared in the subclass. Fields that are not wrapped in a `Field` type will be ignored for (de)serierialization. An example: ``` class MyStruct(Struct): a = Field(UInt) b = Field(Str) c = Field(map(Str, Int)) ``` An `__init__` is generated based on the `Field` declarations. This may be overridden, but please remember to call `super().__init__` in your implementation. Fields may be accessed as normal: ``` my_struct = MyStruct(a=1, b="hello", c={"a": 1, "b": 2}) print(my_struct.a) # prints 1 print(my_struct.b) # prints "hello" print(my_struct.c) # prints {"a": 1, "b": 2} ``` Assignments will be validated against their BARE types `validate` class method. """ _fields: dict[str, type] def __init__(self, **kwargs): name = self.__class__.__name__ for key, value in kwargs.items(): if key not in self._fields: raise ValueError(f"Got unexpected field for Struct {name}: {key}") if not isinstance(value, self._fields[key]): value = self._fields[key](value) setattr(self, key, value) missing_fields = set(kwargs.keys()) - set(self._fields.keys()) if len(missing_fields) > 0: raise ValueError( f"Missing fields for {name}.__init__. " f"Expected: {', '.join(missing_fields)}" ) def pack(self: Struct) -> bytes: buf = io.BytesIO() for name, ty in self._fields.items(): val = getattr(self, name) # coerce to the wrapped type if not isinstance(val, ty): val = ty(val) buf.write(ty.pack(val)) return buf.getbuffer() @classmethod def unpack(cls, fp: BinaryIO) -> Struct: fields = {} for name, ty in cls._fields.items(): fields[name] = ty.unpack(fp) return cls(**fields) @classmethod def validate(cls, value: Any) -> bool: if not isinstance(value, cls): return False for name, ty in cls._fields.items(): if not ty.validate(getattr(value, name)): return False return True def __repr__(self) -> str: clsname = self.__class__.__name__ fields = [] for name, field in self._fields.items(): fields.append(f"{name}={getattr(self, name)}") return f"{clsname}({', '.join(fields)})" def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): for name in self._fields.keys(): if getattr(self, name) != getattr(other, name): return False return True return NotImplementedA BARE struct type. Declare fields using the
Fieldtype. Each field is serialized in the same order as declared in the subclass.Fields that are not wrapped in a
Fieldtype will be ignored for (de)serierialization.An example:
class MyStruct(Struct): a = Field(UInt) b = Field(Str) c = Field(map(Str, Int))An
__init__is generated based on theFielddeclarations. This may be overridden, but please remember to callsuper().__init__in your implementation.Fields may be accessed as normal:
my_struct = MyStruct(a=1, b="hello", c={"a": 1, "b": 2}) print(my_struct.a) # prints 1 print(my_struct.b) # prints "hello" print(my_struct.c) # prints {"a": 1, "b": 2}Assignments will be validated against their BARE types
validateclass method.Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- bare.test_encoder.Address
- bare.test_encoder.ArrayTest
- bare.test_encoder.B
- bare.test_encoder.Customer
- bare.test_encoder.Employee
- bare.test_encoder.EnumTestStruct
- bare.test_encoder.Example
- bare.test_encoder.ExampleMapStruct
- bare.test_encoder.Nested
- bare.test_encoder.OptionalStruct
- bare.test_encoder.Order
- bare.test_encoder.UnionTest
- bare.test_encoder.X
Static methods
def unpack(fp: BinaryIO) ‑> Structdef validate(value: Any) ‑> bool
Methods
def pack(self: Struct) ‑> bytes-
Expand source code
def pack(self: Struct) -> bytes: buf = io.BytesIO() for name, ty in self._fields.items(): val = getattr(self, name) # coerce to the wrapped type if not isinstance(val, ty): val = ty(val) buf.write(ty.pack(val)) return buf.getbuffer()
class U16 (value: T)-
Expand source code
class U16(NumberMixin[int]): """ An unsigned 16-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<H", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> U16: buf = fp.read(struct.calcsize("<H")) return cls(struct.unpack("<H", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return 0 <= value and value < (1 << 16) except (ValueError, TypeError): return FalseAn unsigned 16-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U16def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<H", self.value)
class U32 (value: T)-
Expand source code
class U32(NumberMixin[int]): """ An unnsigned 32-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<L", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> U32: buf = fp.read(struct.calcsize("<L")) return cls(struct.unpack("<L", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return 0 <= value and value < (1 << 32) except (ValueError, TypeError): return FalseAn unnsigned 32-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U32def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<L", self.value)
class U64 (value: T)-
Expand source code
class U64(NumberMixin[int]): """ An unsigned 64-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<Q", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> U64: buf = fp.read(struct.calcsize("<Q")) return cls(struct.unpack("<Q", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return 0 <= value and value < (1 << 64) except (ValueError, TypeError): return False def __hash__(self): return hash(self.__class__)An unsigned 64-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U64def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<Q", self.value)
class U8 (value: T)-
Expand source code
class U8(NumberMixin[int]): """ An unsigned 8-bit integer BARE type. """ def pack(self) -> bytes: return struct.pack("<B", self.value) @classmethod def unpack(cls, fp: BinaryIO) -> U8: buf = fp.read(struct.calcsize("<B")) return cls(struct.unpack("<B", buf)[0]) # type: ignore @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return 0 <= value and value < (1 << 8) except (ValueError, TypeError): return FalseAn unsigned 8-bit integer BARE type.
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U8def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return struct.pack("<B", self.value)
class UInt (value: T)-
Expand source code
class UInt(NumberMixin[int], BAREType[int]): """ An unsigned varint BARE type. When serialized, it encodes itself as a LEB128 unsigned varint. An example: ``` x = UInt(10) x.pack() # prints b'\x0a' fp = io.Bytes(x.pack()) UInt.unpack(fp) # => UInt(10) ``` """ def pack(self) -> bytes: buf = io.BytesIO() _write_varint(buf, self.value, False) return buf.getvalue() @classmethod def unpack(cls, fp: BinaryIO) -> UInt: return cls(_read_varint(fp, False)) @classmethod def validate(cls, value: int) -> bool: if isinstance(value, cls): return True try: value = int(value) return 0 <= value and value < (1 << 64) except (ValueError, TypeError): return False def __hash__(self): return hash(self.__class__)An unsigned varint BARE type. When serialized, it encodes itself as a LEB128 unsigned varint.
An example: ``` x = UInt(10) x.pack() # prints b'' fp = io.Bytes(x.pack()) UInt.unpack(fp) # => UInt(10) ```
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> UIntdef validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: buf = io.BytesIO() _write_varint(buf, self.value, False) return buf.getvalue()
class Union (value: Any, *_args, **_kwargs)-
Expand source code
class Union(metaclass=UnionMeta, variants=()): """ A validated and tagged BARE Union type. Variants are specified as a tuple of types passed as the metaclass kwargs. An explicit discriminant may be specified by wrapping the type in a `UnionVariant` object. For example: ``` class MyUnion(Union, variants=(Str, Int)): ... ``` NOTE: You *must* specify the wrapped BARE type when performing assignments, as the underlying type cannot be safely coerced into the BARE type. For example: ``` x = MyUnion(Str("test")) # will work x = MyUnion(123) # will raise a TypeError ``` The wrapped value may be access using `.value`, note that it is not unwrapped implicitly (you will always recieve the BARE type wrapper, ex. UInt(123) as opposed to 123) """ # TODO: type annotations based on this value: Any _variants: dict[int, type] _discriminants: dict[type, int] def __init__(self, value: Any, *_args, **_kwargs): self.value = UnionValidator(self._variants) if value is None: # coerce None to Void for sanity value = Void() if not self.validate(value): union_member_error(value, self._variants.values()) self.value = value def pack(self: Union) -> bytes: buf = io.BytesIO() ty = type(self.value) discriminant = self._type_to_discriminant(ty) buf.write(UInt(discriminant).pack()) buf.write(self.value.pack()) return buf.getbuffer() @classmethod def unpack(cls, fp: BinaryIO) -> Union: discriminant = UInt.unpack(fp).value if discriminant not in cls._variants: raise TypeError( f"Got unexpected discriminant for {cls.__name__}: {discriminant}" ) ty = cls._variants[discriminant] return cls(ty.unpack(fp)) @classmethod def validate(cls, value: Any) -> bool: if isinstance(value, cls): return True return type(value) in cls._variants.values() def __eq__(self, other: Any) -> bool: if type(other) in self._discriminants: return self.value == other elif isinstance(other, self.__class__): return other.value == self.value for ty in self._variants.values(): if ty.validate(other): return other == ty(other) return NotImplemented def __repr__(self) -> str: return f"{self.__class__.__name__}({self.value})" def _type_to_discriminant(self, ty: type) -> int: try: return self._discriminants[ty] except KeyError: union_member_error(ty, self._variants.values()) raiseA validated and tagged BARE Union type. Variants are specified as a tuple of types passed as the metaclass kwargs. An explicit discriminant may be specified by wrapping the type in a
UnionVariantobject.For example:
class MyUnion(Union, variants=(Str, Int)): ...NOTE: You must specify the wrapped BARE type when performing assignments, as the underlying type cannot be safely coerced into the BARE type.
For example:
x = MyUnion(Str("test")) # will work x = MyUnion(123) # will raise a TypeErrorThe wrapped value may be access using
.value, note that it is not unwrapped implicitly (you will always recieve the BARE type wrapper, ex. UInt(123) as opposed to 123)Subclasses
- bare.test_encoder.ExampleUnion
- bare.test_encoder.Person
- bare.test_union.AllPrimativeTypes
- bare.test_union.MyUnion
- bare.union.Optional_Data__size_128_anonymous
- bare.union.Optional_Nested
- bare.union.Optional_Str
- bare.union.Union_anonymous
- bare.union.Union_anonymous
- bare.union.Union_anonymous
Class variables
var value : Any
Static methods
def unpack(fp: BinaryIO) ‑> bare.union.Uniondef validate(value: Any) ‑> bool
Methods
def pack(self: Union) ‑> bytes-
Expand source code
def pack(self: Union) -> bytes: buf = io.BytesIO() ty = type(self.value) discriminant = self._type_to_discriminant(ty) buf.write(UInt(discriminant).pack()) buf.write(self.value.pack()) return buf.getbuffer()
class UnionVariant (variant, discriminant: int)-
Expand source code
class UnionVariant(Generic[T]): """ Represents a variant of a union with an explici discriminator. Use in any place you would otherwise use a BARE type when defining variants of a `Union` For example: ``` class MyUnion(Union, variants=(UnionVariant(Str, 3), Int)): ... ``` """ def __init__(self, variant, discriminant: int): self.variant = variant self.discriminant = discriminantRepresents a variant of a union with an explici discriminator. Use in any place you would otherwise use a BARE type when defining variants of a
UnionFor example:
class MyUnion(Union, variants=(UnionVariant(Str, 3), Int)): ...Ancestors
- typing.Generic
class Void-
Expand source code
class Void(BAREType[None]): """ A Void type. It is similar to the `None` type, but is used to represent a BARE void type. If should *generally* be used directly, but is also used implicitly in `option`. """ def __init__(self): ... def pack(self) -> bytes: return bytes() @classmethod def unpack(cls, fp: BinaryIO) -> Void: return cls() def __eq__(self, other: Any) -> bool: if isinstance(other, Void): return True return NotImplemented @classmethod def validate(cls, value: Any) -> bool: return value is None or isinstance(value, Void) or value is Void def __hash__(self): return hash(self.__class__)A Void type. It is similar to the
Nonetype, but is used to represent a BARE void type.If should generally be used directly, but is also used implicitly in
option.Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- bare.test_encoder.TerminatedEmployee
- bare.test_misc.MyVoid
Static methods
def unpack(fp: BinaryIO) ‑> Voiddef validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes-
Expand source code
def pack(self) -> bytes: return bytes()