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.barearray
bare.barestruct
bare.baretype
bare.misc
bare.number
bare.test_barearray
bare.test_barestruct
bare.test_data
bare.test_encoder
bare.test_map
bare.test_misc
bare.test_number
bare.test_union
bare.util
Functions
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 providedinner
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.
def data(size: int) ‑> Type[bare.data.Data]
-
A function that defines and returns anonymous BARE
Data
subclass with the providedsize
argument.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]]
-
A function that defines and returns anonymous BARE
Map
subclass with the providedkey_type
type and andvalue_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.
def optional(maybe: Type[T]) ‑> Type[bare.union.Union]
def struct(**kwargs: Dict[str, Type[BAREType]]) ‑> Type[Struct]
-
A function that defines and returnes an anonymous BARE
Struct
subclass with the providedkwargs
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 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]
-
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)
Classes
class Array (value: Iterable[T | A])
-
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 theinner
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 10UInt
values.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)})"
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) ‑> Array
def validate(value: Iterable[T]) ‑> bool
Methods
def append(self, value: T | A)
def pack(self) ‑> bytes
class BAREType (*arg, **kwargs)
-
Base 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 check
See 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: ...
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 NotImplemented
Ancestors
- typing.Protocol
- typing.Generic
Subclasses
Static methods
def unpack(fp: BinaryIO) ‑> BAREType
def validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes
class Bool (value: T)
-
A boolean BARE type, encoded as a single byte.
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))
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> Bool
def validate(value: bool) ‑> bool
Methods
def pack(self) ‑> bytes
class Data (value: bytes)
-
Represents a BARE data type, which is effectively an alias for an array of u8s.
As with the
Array
type, asize
kwarg may be specified to make the new subclass a fixed-size data type.An example:
class MyData(Data, size=10): ...
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)
Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- abc.Data__size_128_anonymous
Static methods
def unpack(fp: BinaryIO) ‑> bare.data.Data
def validate(value: ByteString) ‑> bool
Methods
def pack(self) ‑> bytes
class Enum (*args, **kwds)
-
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
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 False
Ancestors
- 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) ‑> Enum
def validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes
class F32 (value: T)
-
A 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)
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> F32
def validate(value: float) ‑> bool
Methods
def pack(self) ‑> bytes
class F64 (value: T)
-
A 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)
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> F64
def validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes
class Field (ty: Type[BAREType], attr: str | None = None)
-
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)
Expand source code
def __get__(self, inst, _) -> T | Type[BAREType[T]]: if inst is None: return self.ty return inst.__dict__[self.attr]
Class variables
var attr : str
var name : str
var ty : Type[BAREType]
class I16 (value: T)
-
A signed 16-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I16
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class I32 (value: T)
-
A signed 32-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I32
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class I64 (value: T)
-
A signed 64-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I64
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class I8 (value: T)
-
A signed 8-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> I8
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class Int (value: T)
-
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)
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)
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> Int
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class Map (initial_values: Mapping[K, V])
-
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 thevalue_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 typesvalidate
class method.All keys and values are normalized to their BARE type wrappers.
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 NotImplemented
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.Map
def validate(value: Mapping) ‑> bool
Methods
def items(self)
def pack(self) ‑> bytes
def update(self, *args, **kwargs)
class Str (value: str)
-
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")
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})"'
Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- bare.test_encoder.Time
Class variables
var value : str
Static methods
def unpack(fp: BinaryIO) ‑> Str
def validate(value: str) ‑> bool
Methods
def pack(self) ‑> bytes
class Struct (**kwargs)
-
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 theField
declarations. 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
validate
class method.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 NotImplemented
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) ‑> Struct
def validate(value: Any) ‑> bool
Methods
def pack(self: Struct) ‑> bytes
class U16 (value: T)
-
An unsigned 16-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U16
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class U32 (value: T)
-
An unnsigned 32-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U32
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class U64 (value: T)
-
An unsigned 64-bit integer BARE type.
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__)
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U64
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class U8 (value: T)
-
An unsigned 8-bit integer BARE type.
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 False
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> U8
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class UInt (value: T)
-
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) ```
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__)
Ancestors
- bare.number.NumberMixin
- BAREType
- typing.Protocol
- typing.Generic
Static methods
def unpack(fp: BinaryIO) ‑> UInt
def validate(value: int) ‑> bool
Methods
def pack(self) ‑> bytes
class Union (value: Any, *_args, **_kwargs)
-
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)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()) raise
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.Union
def validate(value: Any) ‑> bool
Methods
def pack(self: Union) ‑> bytes
class UnionVariant (variant, discriminant: int)
-
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)): ...
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 = discriminant
Ancestors
- typing.Generic
class Void
-
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
.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__)
Ancestors
- BAREType
- typing.Protocol
- typing.Generic
Subclasses
- bare.test_encoder.TerminatedEmployee
- bare.test_misc.MyVoid
Static methods
def unpack(fp: BinaryIO) ‑> Void
def validate(value: Any) ‑> bool
Methods
def pack(self) ‑> bytes