API reference¶
Everything below is importable from bytestruct (the exceptions also from bytestruct.exceptions).
Declarative binary records and composable codecs, on the standard library alone.
Fixed-size records are declared as dataclasses deriving from :class:BinaryStruct, with each field's wire
type given by its annotation::
@dataclass
class Header(BinaryStruct, byte_order="<"):
magic: Annotated[u32, Const(0x1F3D5B79)] = field(default=0x1F3D5B79, init=False)
size: u32
name: Annotated[str, PaddedString(16)]
kind: Annotated[SomeIntEnum, u16] # parsed into the enum type
header = Header.parse(data) # short input -> TruncatedDataError, wrong magic -> UnexpectedConstantError
data = header.build() # an unencodable value or a wrong constant -> BinaryBuildError
The layout is compiled into struct.Struct objects once, when the class is defined, so a parse is one
unpack_from per byte-order run. Wire fields are positional __init__ parameters in wire order; a field
declared = field(default=..., init=False) is left out of __init__: parse skips its bytes (padding) and
build packs its default (on a Const field, the constant). Fields without a wire type may follow the wire
fields and are ignored by parse/build. Records cannot be subclassed.
Variable-length values are described by codecs, which compose like construct's::
STRING = Aligned(4, Prefixed(U32_LE, CSTRING)) # u32 length, NUL-terminated UTF-8, padded to 4 bytes
OBJECT = Switch(U32_LE, {1: STRING, 2: U64_LE}) # parses to (tag, value)
A codec is anything with read(reader) and build(value): the :class:Codec combinators, the scalars
(U32_LE, ...), Bytes(n), and record classes themselves. A record field annotated with a codec
(name: Annotated[str, CSTRING]) makes the record variable-length; such fields follow the fixed-size ones.
Formats with no declarative shape are read by hand with :class:ByteReader.
BytesLike
module-attribute
¶
Any buffer the parsers accept: bytes, bytearray or memoryview.
u16
module-attribute
¶
An unsigned 16-bit integer field, in the record's byte order.
u32
module-attribute
¶
An unsigned 32-bit integer field, in the record's byte order.
u64
module-attribute
¶
An unsigned 64-bit integer field, in the record's byte order.
i16
module-attribute
¶
A signed 16-bit integer field, in the record's byte order.
i32
module-attribute
¶
A signed 32-bit integer field, in the record's byte order.
i64
module-attribute
¶
A signed 64-bit integer field, in the record's byte order.
u32le
module-attribute
¶
An unsigned 32-bit integer field, always little-endian.
u32be
module-attribute
¶
An unsigned 32-bit integer field, always big-endian.
f32
module-attribute
¶
An IEEE 754 single-precision float field, in the record's byte order.
f64
module-attribute
¶
An IEEE 754 double-precision float field, in the record's byte order.
u16le
module-attribute
¶
An unsigned 16-bit integer field, always little-endian.
u16be
module-attribute
¶
An unsigned 16-bit integer field, always big-endian.
u64le
module-attribute
¶
An unsigned 64-bit integer field, always little-endian.
u64be
module-attribute
¶
An unsigned 64-bit integer field, always big-endian.
i16le
module-attribute
¶
A signed 16-bit integer field, always little-endian.
i16be
module-attribute
¶
A signed 16-bit integer field, always big-endian.
i32le
module-attribute
¶
A signed 32-bit integer field, always little-endian.
i32be
module-attribute
¶
A signed 32-bit integer field, always big-endian.
i64le
module-attribute
¶
A signed 64-bit integer field, always little-endian.
i64be
module-attribute
¶
A signed 64-bit integer field, always big-endian.
f32le
module-attribute
¶
An IEEE 754 single-precision float field, always little-endian.
f32be
module-attribute
¶
An IEEE 754 single-precision float field, always big-endian.
f64le
module-attribute
¶
An IEEE 754 double-precision float field, always little-endian.
f64be
module-attribute
¶
An IEEE 754 double-precision float field, always big-endian.
uuid_t
module-attribute
¶
A 16-byte UUID field, parsed into a :class:uuid.UUID.
ipv4
module-attribute
¶
A 4-byte IPv4 address field, parsed into an :class:ipaddress.IPv4Address.
ipv6
module-attribute
¶
A 16-byte IPv6 address field, parsed into an :class:ipaddress.IPv6Address.
ByteReader ¶
Sequential reader over the window data[offset:end] of an in-memory buffer; every read*/unpack*
returns exactly what was asked for or raises :class:TruncatedDataError.
Readers made by :meth:sub share data (nothing is copied) and have a narrower end. base is the
absolute offset of data[0] in the outermost buffer, so errors report absolute offsets.
Source code in bytestruct/__init__.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
absolute
property
¶
The current position as an absolute offset in the outermost buffer.
read ¶
Consume and return the next size bytes.
Source code in bytestruct/__init__.py
skip ¶
Consume the next size bytes without returning them.
sub ¶
Consume the next size bytes and return a reader confined to them.
Source code in bytestruct/__init__.py
read_all ¶
unpack ¶
Consume fmt.size bytes and return all the values fmt unpacks from them.
Source code in bytestruct/__init__.py
unpack_one ¶
Consume fmt.size bytes and return the single value fmt unpacks from them.
Source code in bytestruct/__init__.py
read_cstring ¶
Read up to (and consume) a NUL terminator; the terminator is not returned.
Source code in bytestruct/__init__.py
Codec ¶
Bases: Protocol[T]
Parses one wire value from a :class:ByteReader (read) and encodes one (build).
Anything with these two methods is a codec: the combinators below, the scalars (U32_LE, ...), Bytes(n),
and :class:BinaryStruct record classes. Errors raised inside a nested value record the enclosing cases,
elements and fields in :attr:BinaryDataError.path.
Source code in bytestruct/__init__.py
CodecBase ¶
Bases: Generic[T]
Base of the combinators: a codec with a parse shortcut.
Source code in bytestruct/__init__.py
Scalar ¶
Bases: Struct
A struct.Struct holding a single value, usable as a codec and with ByteReader.unpack_one.
Source code in bytestruct/__init__.py
Pass ¶
Bases: CodecBase[None]
No bytes; parses to None.
Source code in bytestruct/__init__.py
read ¶
GreedyBytes ¶
Bases: CodecBase[bytes]
Every remaining byte (of the enclosing :class:Prefixed, or of the buffer).
Source code in bytestruct/__init__.py
CString ¶
Bases: CodecBase[str]
A NUL-terminated UTF-8 string. Building one with an embedded NUL (the peer would stop there) is an error.
errors is the UTF-8 decoding error handler (e.g. "replace" for names cut mid-character).
Source code in bytestruct/__init__.py
read ¶
Parse one value at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
GreedyString ¶
Bases: CodecBase[str]
Every remaining byte (of the enclosing :class:Prefixed, or of the buffer), as a UTF-8 string.
Prefixed(U16_BE, GREEDY_STRING) is a length-prefixed string (construct's PascalString). errors
is the UTF-8 decoding error handler (e.g. "replace").
Source code in bytestruct/__init__.py
read ¶
Parse one value at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
Prefixed ¶
Bases: CodecBase[T]
inner, confined to the byte count that length precedes it with.
Source code in bytestruct/__init__.py
read ¶
Parse one value at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
Aligned ¶
Bases: CodecBase[T]
inner, followed by NUL padding to a multiple of modulus bytes counted from where it starts.
Source code in bytestruct/__init__.py
PrefixedArray ¶
Bases: CodecBase[list[T]]
An element count in count, followed by that many elements.
Source code in bytestruct/__init__.py
read ¶
Parse one value at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
Tuple ¶
Bases: CodecBase[tuple[Any, ...]]
Consecutive values of different codecs, parsed into a tuple.
Source code in bytestruct/__init__.py
read ¶
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
Switch ¶
Bases: CodecBase[tuple[Any, Any]]
A tag, followed by a value in the codec it selects from cases; parses to (tag, value).
A tag without a case uses default, or is an error when there is none.
Source code in bytestruct/__init__.py
read ¶
Parse one value at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode value, raising :class:BinaryBuildError when it cannot be encoded.
Source code in bytestruct/__init__.py
OpenIntEnum ¶
Bases: IntEnum
IntEnum that maps unknown values to cached pseudo-members instead of raising, so a value from a newer
peer (an unknown opcode, status, level, ...) still parses. Declare at least one member.
Source code in bytestruct/__init__.py
Format ¶
Field annotation metadata: a single-value struct code, optionally pinned to a byte order.
Annotated[int, Format("H")] is how u16 is defined. Subclasses define custom fixed-size field types by
overriding decode (unpacked value -> field value, raising :class:BinaryParseError) and/or encode
(the reverse, raising :class:BinaryBuildError); only overridden ones are called.
Source code in bytestruct/__init__.py
decode ¶
Bytes ¶
Bases: Format
A fixed-size raw byte field; building it from a value of any other length is an error.
Source code in bytestruct/__init__.py
PaddedString ¶
Bases: Format
A fixed-size field holding a NUL-padded UTF-8 string of at most size bytes.
A string that fills the field has no terminator; a shorter one ends at its first NUL, and the bytes after it
are ignored. errors is the UTF-8 decoding error handler (e.g. "replace" for names cut mid-character).
Building NUL-pads the encoded string to the field size.
Source code in bytestruct/__init__.py
decode ¶
Decode the bytes before the first NUL (the whole field when there is none).
Source code in bytestruct/__init__.py
encode ¶
Encode the string, raising :class:BinaryBuildError when it cannot be encoded, does not fit or contains a
NUL byte.
Source code in bytestruct/__init__.py
Uuid ¶
Bases: Format
A 16-byte UUID field, parsed into a :class:uuid.UUID.
Source code in bytestruct/__init__.py
decode ¶
IPAddress ¶
Bases: Format
A packed IPv4 (4-byte) or IPv6 (16-byte) address field, parsed into an :mod:ipaddress object.
Source code in bytestruct/__init__.py
decode ¶
Array ¶
Bases: Format
A fixed-length array of a plain number type, parsed into a list:
Annotated[list[int], Array(u64, 6)]. Elements use the record's byte order unless their alias pins one.
Source code in bytestruct/__init__.py
in_record ¶
This array with unpinned elements in the enclosing record's byte_order.
decode ¶
encode ¶
Convert the field value to the wire bytes, raising :class:BinaryBuildError when it does not fit.
Source code in bytestruct/__init__.py
Const ¶
Field annotation metadata: the field must always hold value.
A wrong value raises :class:UnexpectedConstantError on parse and :class:BinaryBuildError on build. Only for
fields whose value is not converted (numbers, Bytes, int enums): parse compares the raw unpacked value.
Source code in bytestruct/__init__.py
BinaryStruct ¶
Base for dataclasses describing a binary record; see the module docstring.
Record classes are codecs themselves (read/build), so they nest inside other records and combinators.
Source code in bytestruct/__init__.py
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | |
parse
classmethod
¶
Parse the record from the start of data; trailing bytes are ignored.
read
classmethod
¶
Parse the record at the reader's offset and advance past it.
Source code in bytestruct/__init__.py
build ¶
Encode the record (as a codec, Record.build(record)).
Source code in bytestruct/__init__.py
bytestruct.exceptions ¶
Errors raised while parsing or building binary data.
BinaryDataError ¶
Bases: ValueError
Base for binary wire-format errors: data that could not be parsed, or a value that could not be encoded.
record, field and offset (absolute, in the outermost buffer) locate the failure when known, and
path lists the enclosing cases, elements and fields, outermost first. They prefix the message, e.g.
Header.magic at offset 0: expected 0x29b00b92, got 0x0 or
DICTIONARY -> [2] -> STRING at offset 104: expected 200 bytes, found 16.
Source code in bytestruct/exceptions.py
located ¶
located(*, record: Optional[str] = None, field: Optional[str] = None, offset: Optional[int] = None) -> BinaryDataError
Return a copy of this error (same type) with any missing location filled in.
Source code in bytestruct/exceptions.py
within ¶
Record that the failure happened inside step (a case, element or field), as the error propagates.
BinaryParseError ¶
Bases: BinaryDataError
Binary wire data could not be parsed.
Source code in bytestruct/exceptions.py
BinaryBuildError ¶
Bases: BinaryDataError
A value could not be encoded into its binary wire format.
Source code in bytestruct/exceptions.py
TruncatedDataError ¶
Bases: BinaryParseError
Binary wire data ended before the structure being parsed was complete.
Source code in bytestruct/exceptions.py
UnexpectedConstantError ¶
Bases: BinaryParseError
A constant field (magic, version, ...) in binary wire data did not have its expected value.