construct Binary Data Parser
construct is a powerful declarative symmetric parser/builder for binary data, supporting Python 3.6+. It allows you to define the structure of binary data using Python objects and then parse or build data according to that structure. The current version is 2.10.70, and it maintains a fairly active release cadence, often releasing minor fixes and improvements.
Common errors
-
ModuleNotFoundError: No module named 'construct'
cause The 'construct' library is not installed in the Python environment, or the environment where the code is being run does not have access to the installed package.fixInstall the library using pip: `pip install construct`. -
construct.core.StreamError: could not read enough bytes
cause This error occurs when the parsing operation expects to read a certain number of bytes from the input stream, but the stream ends prematurely or contains fewer bytes than the defined construct requires.fixEnsure the input binary data provided to the `parse()` method is complete and matches the structure defined by the construct. You might need to check the source of your binary data or adjust the construct's definition if the data length is variable. -
construct.core.ConstError: expected b'VALUE_A' but parsed b'VALUE_B'
cause A `Const` field in the construct definition enforces that a specific byte sequence (or value) must be present at that point in the stream. This error means the data being parsed does not match the expected constant value.fixVerify the binary data being parsed against the expected constant value defined in the `Const` construct. If the data is correct and the `Const` definition is incorrect, update the `Const` value. If the data is faulty, correct the data source. -
TypeError: 'bytes' object cannot be interpreted as an integer
cause This common Python error often arises when working with `construct` because you are attempting to use a `bytes` object (which `construct` frequently returns) in a context where an integer value is expected, or vice-versa, without proper conversion.fixExplicitly convert the `bytes` object to an integer using methods like `int.from_bytes(byte_object, byteorder)` with the correct byte order, or access individual bytes as integers (e.g., `byte_object[0]`). Conversely, convert integers to bytes using `int.to_bytes()` when building. -
construct.core.SizeofError: SizeofError
cause This error is raised when the `sizeof()` method of a construct cannot determine its size upfront, often because it depends on a value from the context dictionary that is not available or because the construct has a variable size that cannot be computed statically.fixIf the size depends on a context value, ensure that the necessary key is present in the context dictionary when calling `sizeof()` or when building. For dynamically sized constructs, `sizeof()` might not be determinable, and you may need to rely on parsing or building to calculate the actual size.
Warnings
- breaking Major API overhaul occurred between construct 2.0 and 2.5/2.6. This was a near-complete rewrite, deprecating and removing many constructs (e.g., `Buffer`, `BitStruct`), changing import paths (`construct.core`), and modifying core behaviors. Code written for construct 2.0 is highly unlikely to work with 2.5+.
- breaking Breaking changes were introduced in versions 2.8 and 2.9. Notably, `Subconstruct` was removed in 2.8 (use `Construct` directly), `RepeatUntil` was renamed to `GreedyRange`, and `Context.error_when` behavior changed slightly in 2.9.
- gotcha Construct objects (e.g., `Struct`, `Array`) are often mutable. If you create an instance of a construct and then modify its internal properties or reuse it in different contexts without care, you might encounter unexpected side effects due to shared state.
- gotcha The `this` (or `ctx`) variable, used for accessing context and previously parsed fields within `lambda` functions or methods, can be a source of confusion. Misunderstanding its scope or when certain fields become available can lead to errors.
Install
-
pip install construct
Imports
- Struct
from construct.core import Struct
from construct import Struct
- Int8ub
from construct import Int8ub
- Bytes
from construct import Bytes
- Array
from construct import Array
- GreedyRange
from construct import GreedyRange
- StreamError
from construct import StreamError
Quickstart
from construct import Struct, Int8ub, Bytes, Array, GreedyRange, StreamError
# Define a simple binary structure
# A header, an array of 8-bit unsigned integers, and a variable-length data block
packet_struct = Struct(
"header" / Bytes(4), # 4-byte header
"count" / Int8ub, # 1-byte count of following integers
"data" / Array(lambda ctx: ctx.count, Int8ub), # Array of 'count' integers
"payload" / GreedyRange(Int8ub) # Remaining bytes as a list of integers
)
# Example data to build
my_data = {
"header": b"\xde\xad\xbe\xef",
"count": 3,
"data": [10, 20, 30],
"payload": [1, 2, 3, 4, 5]
}
# Build binary data from Python object
built_binary = packet_struct.build(my_data)
print(f"Built binary: {built_binary.hex()}")
# Parse binary data into Python object
# Using a different example binary for parsing
binary_to_parse = b"\xaa\xbb\xcc\xdd\x02\x01\x02\xff\xee\xdd"
try:
parsed_object = packet_struct.parse(binary_to_parse)
print(f"Parsed object: {parsed_object}")
# Accessing fields
print(f"Parsed header: {parsed_object.header.hex()}")
print(f"Parsed data: {parsed_object.data}")
except StreamError as e:
print(f"Error parsing data: {e}")