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 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.

def data(size: int) ‑> Type[bare.data.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)
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 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.

def optional(maybe: Type[T]) ‑> Type[bare.union.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)): …

def struct(**kwargs: Dict[str, 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.

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 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.

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)

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

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, a size 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 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.

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 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.

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