Skip to content

Field Type Reference

Field Type Primitives

bydantic comes with a variety of primitive field types that can be used to define bitfields. From these primitives, more complex data structures can be constructed via field type combinators.

uint_field

uint_field(
    n: int, *, default: int | ellipsis = ...
) -> Field[int]

An unsigned integer field type.

Parameters:

Name Type Description Default
n int

The number of bits used to represent the unsigned integer.

required
default int | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[int]

Field[int]: A field that represents an unsigned integer.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: int = bd.uint_field(4)
    b: int = bd.uint_field(4, default=0)

foo = Foo(a=1, b=2)
print(foo) # Foo(a=1, b=2)
print(foo.to_bytes()) # b'\x12'

foo2 = Foo.from_bytes_exact(b'\x34')
print(foo2) # Foo(a=3, b=4)

foo3 = Foo(a = 1) # b is set to 0 by default
print(foo3) # Foo(a=1, b=0)
print(foo3.to_bytes()) # b'\x10'

int_field

int_field(
    n: int, *, default: int | ellipsis = ...
) -> Field[int]

A signed integer field type.

Parameters:

Name Type Description Default
n int

The number of bits used to represent the signed integer.

required
default int | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[int]

Field[int]: A field that represents a signed integer.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: int = bd.int_field(4)
    b: int = bd.int_field(4, default=-1)

foo = Foo(a=1, b=-2)
print(foo) # Foo(a=1, b=-2)
print(foo.to_bytes()) # b'\x16'

foo2 = Foo.from_bytes_exact(b'\x34')
print(foo2) # Foo(a=3, b=4)

foo3 = Foo(a = 1) # b is set to -1 by default
print(foo3) # Foo(a=1, b=-1)
print(foo3.to_bytes()) # b'\x13'

bool_field

bool_field(
    *, default: bool | ellipsis = ...
) -> Field[bool]

A boolean field type. (Bit flag)

Parameters:

Name Type Description Default
default bool | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[bool]

Field[bool]: A field that represents a boolean.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: bool = bd.bool_field()
    b: bool = bd.bool_field(default=True)
    _pad: int = bd.uint_field(6, default=0) # Pad to a full byte

foo = Foo(a=True, b=False)
print(foo) # Foo(a=True, b=False)
print(foo.to_bytes()) # b'\x80'

foo2 = Foo.from_bytes_exact(b'\x40')
print(foo2) # Foo(a=False, b=True)

foo3 = Foo(a = True) # b is set to True by default
print(foo3) # Foo(a=True, b=True)
print(foo3.to_bytes()) # b'À'

bytes_field

bytes_field(
    *, n_bytes: int, default: bytes | ellipsis = ...
) -> Field[bytes]

A bytes field type.

Parameters:

Name Type Description Default
n_bytes int

The number of bytes in the field.

required
default bytes | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[bytes]

Field[bytes]: A field that represents a sequence of bytes.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: bytes = bd.bytes_field(2)
    b: bytes = bd.bytes_field(2, default=b"yz")

foo = Foo(a=b"xy", b=b"uv")
print(foo) # Foo(a=b'xy', b=b'uv')

foo2 = Foo.from_bytes_exact(b'xyuv')
print(foo2) # Foo(a=b'xy', b=b'uv')

foo3 = Foo(a = b"xy") # b is set to b"yz" by default
print(foo3) # Foo(a=b'xy', b=b'yz')
print(foo3.to_bytes()) # b'xyyz'

str_field

str_field(
    *,
    n_bytes: int,
    encoding: str = "utf-8",
    default: str | ellipsis = ...
) -> Field[str]

A string field type.

Parameters:

Name Type Description Default
n_bytes int

The number of bytes in the field.

required
encoding str

The encoding to use when converting the bytes to a string.

'utf-8'
default str | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[str]

Field[str]: A field that represents a string.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: str = bd.str_field(2)
    b: str = bd.str_field(2, default="yz")

foo = Foo(a="xy", b="uv")
print(foo) # Foo(a='xy', b='uv')

foo2 = Foo.from_bytes_exact(b'xyuv')
print(foo2) # Foo(a='xy', b='uv')

foo3 = Foo(a = "xy") # b is set to "yz" by default
print(foo3) # Foo(a='xy', b='yz')
print(foo3.to_bytes()) # b'xyyz'

uint_enum_field

uint_enum_field(
    enum: Type[IntEnumT],
    n: int,
    *,
    default: IntEnumT | ellipsis = ...
) -> Field[IntEnumT]

An unsigned integer enum field type.

Parameters:

Name Type Description Default
enum Type[IntEnumT]

The enum class to use for the field. (Must be a subclass of IntEnum or IntFlag)

required
n int

The number of bits used to represent the enum.

required
default IntEnumT | ellipsis

An optional default value to use when constructing the field in a new object (Must match the enum type passed in the enum arg).

...

Returns:

Type Description
Field[IntEnumT]

Field[IntEnumT]: A field that represents an unsigned integer enum.

Example
import bydantic as bd
from enum import IntEnum

class Color(IntEnum):
    RED = 1
    GREEN = 2
    BLUE = 3
    PURPLE = 4

class Foo(bd.Bitfield):
    a: Color = bd.uint_enum_field(Color, 4)
    b: Color = bd.uint_enum_field(Color, 4, default=Color.GREEN)

foo = Foo(a=Color.RED, b=Color.BLUE)
print(foo) # Foo(a=<Color.RED: 1>, b=<Color.BLUE: 3>)
print(foo.to_bytes()) # b'\x13'

foo2 = Foo.from_bytes_exact(b'\x24')
print(foo2) # Foo(a=<Color.GREEN: 2>, b=<Color.PURPLE: 4>)

foo3 = Foo(a = Color.RED) # b is set to Color.GREEN by default
print(foo3) # Foo(a=<Color.RED: 1>, b=<Color.GREEN: 2>)
print(foo3.to_bytes()) # b'\x12'

int_enum_field

int_enum_field(
    enum: Type[IntEnumT],
    n: int,
    *,
    default: IntEnumT | ellipsis = ...
) -> Field[IntEnumT]

An signed integer enum field type.

Parameters:

Name Type Description Default
n int

The number of bits used to represent the enum.

required
enum Type[IntEnumT]

The enum class to use for the field. (Must be a subclass of IntEnum or IntFlag)

required
default IntEnumT | ellipsis

An optional default value to use when constructing the field in a new object (Must match the enum type passed in the enum arg).

...

Returns:

Type Description
Field[IntEnumT]

Field[IntEnumT]: A field that represents an unsigned integer enum.

Example
import bydantic as bd
from enum import IntEnum

class Color(IntEnum):
    RED = -2
    GREEN = -1
    BLUE = 0
    PURPLE = 1

class Foo(bd.Bitfield):
    a: Color = bd.int_enum_field(Color, 4)
    b: Color = bd.int_enum_field(Color, 4, default=Color.GREEN)

foo = Foo(a=Color.RED, b=Color.BLUE)
print(foo) # Foo(a=<Color.RED: -2>, b=<Color.BLUE: 0>)
print(foo.to_bytes()) # b'\xe0'

foo2 = Foo.from_bytes_exact(b'\xfe')
print(foo2) # Foo(a=<Color.GREEN: -1>, b=<Color.RED: -2>)

none_field

none_field(
    *, default: None | ellipsis = ...
) -> Field[None]

A field type that represents no data.

This field type is most useful when paired with dynamic_field to create optional values in a Bitfield.

Parameters:

Name Type Description Default
default None | ellipsis

The optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[None]

Field[None]: A field that represents no data.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: int = bd.uint_field(8)
    b: int | None = bd.dynamic_field(lambda x: bd.uint_field(8) if x.a else bd.none_field())

foo = Foo.from_bytes_exact(b'\x01\x02')
print(foo) # Foo(a=1, b=2)

foo2 = Foo.from_bytes_exact(b'\x00')
print(foo2) # Foo(a=0, b=None)

bits_field

bits_field(
    n: int, *, default: Sequence[bool] | ellipsis = ...
) -> Field[t.Tuple[bool, ...]]

A field type that represents a sequence of bits. (A tuple of booleans).

Parameters:

Name Type Description Default
n int

The number of bits in the field.

required
default Sequence[bool] | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[Tuple[bool, ...]]

Field[t.Tuple[bool, ...]]: A field that represents a sequence of bits.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: t.Tuple[bool, ...] = bd.bits_field(4)
    b: t.Tuple[bool, ...] = bd.bits_field(4, default=(True, False, True, False))

foo = Foo(a=(True, False, True, False), b=(False, True, False, True))
print(foo) # Foo(a=(True, False, True, False), b=(False, True, False, True))
print(foo.to_bytes()) # b'\xaa\xaa'

foo2 = Foo.from_bytes_exact(b'\xaa\xaa')
print(foo2) # Foo(a=(True, False, True, False), b=(False, True, False, True))

foo3 = Foo(a=(True, False, True, False)) # b is set to (True, False, True, False) by default
print(foo3) # Foo(a=(True, False, True, False), b=(True, False, True, False))
print(foo3.to_bytes()) # b'\xaa\xaa'

Literal Field Types

These field types are used to define fields with literal types. They are most often useful for fixed-length headers, padding, etc.

lit_uint_field

lit_uint_field(n: int, *, default: T) -> Field[T]

A literal unsigned integer field type.

Parameters:

Name Type Description Default
n int

The number of bits used to represent the unsigned integer.

required
default LiteralIntT

The literal default value to use when constructing the field in a new object. (Required to infer the literal type).

required

Returns:

Type Description
Field[T]

Field[LiteralIntT]: A field that represents a literal unsigned integer.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: t.Literal[1] = bd.lit_uint_field(4, default=1)
    b: t.Literal[2] = bd.lit_uint_field(4, default=2)

foo = Foo()
print(foo) # Foo(a=1, b=2)
print(foo.to_bytes()) # b'\x12'

foo2 = Foo.from_bytes_exact(b'\x12')
print(foo2) # Foo(a=1, b=2)

lit_int_field

lit_int_field(n: int, *, default: T) -> Field[T]

A literal signed integer field type.

Parameters:

Name Type Description Default
n int

The number of bits used to represent the signed integer.

required
default LiteralIntT

The literal default value to use when constructing the field in a new object. (Required to infer the literal type).

required

Returns:

Type Description
Field[T]

Field[LiteralIntT]: A field that represents a literal signed integer.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: t.Literal[-1] = bd.lit_int_field(4, default=-1)
    b: t.Literal[2] = bd.lit_int_field(4, default=2)

foo = Foo()
print(foo) # Foo(a=-1, b=2)
print(foo.to_bytes()) # b'\xf2'

foo2 = Foo.from_bytes_exact(b'\xf2')
print(foo2) # Foo(a=-1, b=2)

lit_bytes_field

lit_bytes_field(*, default: T) -> Field[T]

A literal bytes field type.

Parameters:

Name Type Description Default
default LiteralBytesT

The literal default value to use when constructing the field in a new object. (Required to infer the literal type).

required

Returns:

Type Description
Field[T]

Field[LiteralBytesT]: A field that represents a literal bytes.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: t.Literal[b'xy'] = bd.lit_bytes_field(default=b'xy')
    b: t.Literal[b'uv'] = bd.lit_bytes_field(default=b'uv')

foo = Foo()
print(foo) # Foo(a=b'xy', b=b'uv')
print(foo.to_bytes()) # b'xyuv'

foo2 = Foo.from_bytes_exact(b'xyuv')
print(foo2) # Foo(a=b'xy', b=b'uv')

# Note that the following shortcut may be alternatively used
# in definitions:

class Shortcut(bd.Bitfield):
    a: t.Literal[b'xy'] = b'xy'
    b: t.Literal[b'uv'] = b'uv'

lit_str_field

lit_str_field(
    *,
    n_bytes: int | None = None,
    encoding: str = "utf-8",
    default: T
) -> Field[T]

A literal string field type.

Parameters:

Name Type Description Default
n_bytes int

The number of bytes used to represent the string.

None
default LiteralStrT

The literal default value to use when constructing the field in a new object. (Required to infer the literal type).

required

Returns:

Type Description
Field[T]

Field[LiteralStrT]: A field that represents a literal string.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: t.Literal["xy"] = bd.lit_str_field(encoding="utf-8", default="xy")
    b: t.Literal["uv"] = bd.lit_str_field(default="uv")

foo = Foo()
print(foo) # Foo(a='xy', b='uv')
print(foo.to_bytes()) # b'xyuv'

foo2 = Foo.from_bytes_exact(b'xyuv')
print(foo2) # Foo(a="xy", b="uv")

# Note that the following shortcut may be alternatively used
# in definitions:

class Shortcut(bd.Bitfield):
    a: t.Literal["xy"] = "xy"
    b: t.Literal["uv"] = "uv"

Field Type Combinators

These field types can be used build new field types from other field types.

mapped_field

mapped_field(
    field: Field[T],
    vm: ValueMapper[T, P],
    *,
    default: P | ellipsis = ...
) -> Field[P]

A field type for creating transformations of values.

Transformations are done via the ValueMapper protocol, an object defined with deserialize and serialize methods. The deserialize method is used to transform the value when deserializing the field from the provided field type, and the serialize method is used to reverse this transformation when serializing the field back.

Several built-in value mappers are provided, including Scale and IntScale, for scaling values by a given float or int factor, respectively.

Parameters:

Name Type Description Default
field Field[T]

The field to transform.

required
vm ValueMapper[T, P]

The value mapper to use for the transformation.

required
default P | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[P]

Field[P]: A field that represents the transformed value.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: int = bd.uint_field(4)
    b: float = bd.map_field(
        bd.uint_field(4),
        bd.Scale(0.5),
        default=0.5
    )

foo = Foo(a=1, b=1.5)
print(foo) # Foo(a=1, b=1.5)
print(foo.to_bytes()) # b'\x13'

foo2 = Foo.from_bytes_exact(b'\x13')
print(foo2) # Foo(a=1, b=1.5)

foo3 = Foo(a=1) # b is set to 0.5 by default
print(foo3) # Foo(a=1, b=0.5)
print(foo3.to_bytes()) # b'\x11'

list_field

list_field(
    item: Type[T] | Field[T],
    n_items: int,
    *,
    default: List[T] | ellipsis = ...
) -> Field[t.List[T]]

A field type that represents a list of items.

Parameters:

Name Type Description Default
item Type[T] | Field[T]

The type of items in the list. In addition to fields, Bitfield classes can also be returned (provided they don't have dynamic fields).

required
n_items int

The number of items in the list.

required
default List[T] | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[List[T]]

Field[t.List[T]]: A field that represents a list of items.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: int = bd.uint_field(4)
    b: t.List[int] = bd.list_field(bd.uint_field(4), 3, default=[1, 2, 3])

foo = Foo(a=1, b=[0, 1, 2])
print(foo) # Foo(a=1, b=[0, 1, 2])
print(foo.to_bytes()) # b'\x10\x12'

foo2 = Foo.from_bytes_exact(b'\x10\x12')
print(foo2) # Foo(a=1, b=[0, 1, 2])

foo3 = Foo(a=1) # b is set to [1, 2, 3] by default
print(foo3) # Foo(a=1, b=[1, 2, 3])
print(foo3.to_bytes()) # b'\x11#'

bitfield_field

bitfield_field(
    cls: Type[BitfieldT],
    n: int | ellipsis = ...,
    *,
    default: BitfieldT | ellipsis = ...
) -> Field[BitfieldT]

A field type that represents a Bitfield.

Parameters:

Name Type Description Default
cls Type[BitfieldT]

The Bitfield class to use for the field.

required
n int | ellipsis

The number of bits in the field. Note: this is optional for bitfields without dynamic fields because it can inferred from the class itself.

...
default BitfieldT | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[BitfieldT]

Field[BitfieldT]: A field that represents a Bitfield.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    a: int = bd.uint_field(4)
    b: int = bd.uint_field(4, default=0)

class Bar(bd.Bitfield):
    c: Foo = bd.bitfield_field(Foo, 8)

bar = Bar(c=Foo(a=1, b=2))
print(bar) # Bar(c=Foo(a=1, b=2))
print(bar.to_bytes()) # b'\x12\x02'

bar2 = Bar.from_bytes_exact(b'\x12\x02')
print(bar2) # Bar(c=Foo(a=1, b=2))

bar3 = Bar(c=Foo(a=1)) # c is set to Foo(a=1, b=0) by default
print(bar3) # Bar(c=Foo(a=1, b=0))
print(bar3.to_bytes()) # b'\x10\x00'

# For bitfields without dynamic fields, the size can be inferred:
class Baz(bd.Bitfield):
    c: Foo = bd.bitfield_field(Foo)

# Or, more concisely:
class Baz(bd.Bitfield):
    c: Foo

baz = Baz(c=Foo(a=1, b=2))
print(baz) # Baz(c=Foo(a=1, b=2))
print(baz.to_bytes()) # b'\x12\x02'

dynamic_field

dynamic_field(
    fn: (
        Callable[[Any], Type[T] | Field[T]]
        | Callable[[Any, int], Type[T] | Field[T]]
    ),
    *,
    default: T | ellipsis = ...
) -> Field[T]

A field type that can be decided at runtime, based on the values of already-parsed fields, or the number of bits remaining in the stream.

Note that the discriminator function provided can be either a one-argument or two-argument function. If a one-argument function is provided, it will be called with an intermeditate Bitfield object with the fields that have been parsed so far. If a two-argument function is the first argument will be the intermediate Bitfield object and the second argument will be the number of bits remaining in the stream. The function should use this information to return a valid field type to use for the field.

Parameters:

Name Type Description Default
fn Callable[[Any], Type[T] | Field[T]] | Callable[[Any, int], Type[T] | Field[T]]

A one or two-argument function that returns the field type to use for the field.

required
default T | ellipsis

An optional default value to use when constructing the field in a new object.

...

Returns:

Type Description
Field[T]

Field[T]: A field that represents a dynamic field.

Example
import bydantic as bd
import typing as t

class Foo(bd.Bitfield):
    a: int = bd.uint_field(8)
    b: int | bytes = bd.dynamic_field(
        lambda x: bd.uint_field(8) if x.a else bd.bytes_field(n_bytes=1)
    )

foo = Foo.from_bytes_exact(b'\x01\x02')
print(foo) # Foo(a=1, b=2)

foo2 = Foo.from_bytes_exact(b'\x00A')
print(foo2) # Foo(a=0, b=b'A')

Value Mappers

bydantic.mapped_field uses the ValueMapper protocol for transforming values when serializing and deserializing and deserializing bitfields. This section describes the ValueMapper protocol and the built-in value mappers provided by bydantic.

ValueMapper

Bases: Protocol[T, P]

A protocol for transforming values during serialization / deserialization via map_field.

deserialize

deserialize(x: T) -> P

Transform the value from type T to type P when deserializing the bitfield.

serialize

serialize(y: P) -> T

Transform the value from type P to type T when serializing the bitfield.

Scale

A value mapper that scales a value by a given factor, resulting in a float value.

Attributes:

Name Type Description
by float

The factor to scale by.

n_digits int | None

The number of digits to round to. If None, no rounding is done. Default is None.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    b: float = bd.map_field(bd.uint_field(8), bd.Scale(by=0.1))

foo = Foo(b=2)
print(foo) # Foo(b=0.2)

foo2 = Foo.from_bytes_exact(b'\x02')
print(foo2) # Foo(b=0.2)

IntScale

A value mapper that scales a value by a given factor, resulting in an integer value.

Attributes:

Name Type Description
by int

The factor to scale by.

Example
import bydantic as bd

class Foo(bd.Bitfield):
    b: int = bd.map_field(bd.uint_field(8), bd.IntScale(by=10))

foo = Foo(b=20)
print(foo) # Foo(b=20)
print(foo.to_bytes()) # b'\x02'

foo2 = Foo.from_bytes_exact(b'\x02')
print(foo2) # Foo(b=20)