Construct
A Python library where a binary format is an object built from small pieces, which can both parse bytes into Python values and build values back into bytes.
- What it is
- Binary parsing and encoding (declarative binary layouts)
- Type
- Open source
- Made by
- Open-source project by Tomer Filiba, Corbin Simpson and Arkadiusz Bulski
- License
- MIT
- Facts checked
- 9 October 2026
What it is
A format is a value you assemble in Python. Struct("width" / Int8ub, "height" / Int8ub, "pixels" / Array(this.width * this.height, Byte)) names each field with /, and this expressions let a size or condition depend on fields read earlier. Pieces cover integers of every size and byte order, Const magic bytes, BitStruct for bit fields, PrefixedArray and PascalString for length-prefixed data, If and Switch for branches, Pointer to jump to an offset, Lazy for fields parsed only when accessed, and tunnels that compress or length-prefix a whole substructure. Because the description is data rather than code, parse turns bytes into a Container and build turns a dict back into bytes, from the same object. Its docs list network formats (Ethernet, IP, TCP, DNS, DHCP), images (PNG, GIF, BMP), executables (ELF, PE) and file systems (ext2, FAT16, MBR) among the things it has parsed.
Interpreting the object tree field by field has a cost, so since 2.9 any construct can compile() itself into a generated Python module that parses faster, at the cost of some features and some safety checks. The docs’ own example goes from 129 to 45 microseconds per parse, and they recommend PyPy for more speed still. Construct runs on CPython 3.8 to 3.12 and PyPy with no required dependencies, can export a definition as a Kaitai Struct .ksy file, and gets type hints from the separate construct-typing package.
Why teams pick it
- One definition parses and builds, with bit and byte granularity, offsets, lazy fields and conditional branches
- Pure Python with no required dependencies;
pip install constructand it runs anywhere Python does - Errors name the path to the failing field, such as
(parsing) -> a -> b -> c -> foo, which makes malformed input quick to locate - Interactive: a format can be built up and tested in a Python shell, and the
construct-editorpackage is a visual editor for binary blobs built on it
Watch out for
- Speed takes extra steps: the docs recommend PyPy and
compile(), but compiled parsers drop lambdas, parse hooks and the debugger, and the docs warn not to use them on corrupted or untrusted data - Releases have slowed: 2.10.70 in November 2023 is the latest, though commits continued into 2025
- Big version jumps broke code: 2.8 renamed the integer, float and many field types, and 2.10 removed
Embedded, so old examples often need porting - Python only, and without static types unless you add
construct-typing
Alternatives
What all declarative binary layouts have in common →How it fits with other technologies
One definition that parses a sensor packet and builds a new one · python
from construct import Struct, Const, BitStruct, Nibble, Int16ub, Int16sb, Int8ub, PrefixedArray, Check, this
# "SN" | version: 4 bits | flags: 4 bits | sensor id: u16 | count: u8 | count × i16
Reading = Struct(
"magic" / Const(b"SN"),
"header" / BitStruct("version" / Nibble, "flags" / Nibble),
Check(this.header.version == 1),
"sensor_id" / Int16ub,
"temps" / PrefixedArray(Int8ub, Int16sb), # hundredths of a degree
)
r = Reading.parse(b"SN\x12\x00\x07\x02\x08\x34\xff\x9c")
print(r.header.version, r.header.flags, r.sensor_id, list(r.temps))
# 1 2 7 [2100, -100]
# the same definition builds bytes; the count prefix comes from the list
out = Reading.build(dict(header=dict(version=1, flags=3), sensor_id=7, temps=[2100, -100, 2250]))
print(out.hex(" "))
# 53 4e 13 00 07 03 08 34 ff 9c 08 ca
try:
Reading.parse(b"SN\x22\x00\x07\x00")
except Exception as e:
print(type(e).__name__) # CheckErrorSee it in a real stack
No architecture uses Construct yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from2007–2023
Tomer Filiba, also the author of the RPyC and Plumbum libraries, wrote Construct as a way to define binary data structures in Python instead of parsing them by hand. Filiba recalls giving up on it somewhere in 2007, right after releasing version 2.0, thinking it complete enough to survive on its own; instead code rot set in.
Corbin Simpson took it over in January 2011, moved it to GitHub, fixed bugs and wrote the documentation, and the project built a following on PyPI. Filiba sketched a Construct 3 that would generate efficient C or Python code from definitions, and back-ported its this expressions in 2012. Arkadiusz Bulski, by far its most prolific committer, then drove a run of breaking releases: 2.8 in 2016 with the / naming syntax and new type names, 2.9 in 2018 with compilation to Python, and 2.10 in January 2020, which removed Embedded. The project also began collaborating with Kaitai Struct, whose FAQ calls Construct its closest analogue.
Why it’s built this way
- A format is a data structure, not code
- A description can run in either direction, parsing bytes into objects and building objects into bytes, which hand-written parsing code can’t.
- Build formats from Python objects
- No separate schema language or compiler step: formats are composed, tested and debugged with ordinary Python.
thisexpressions instead of lambdas- An expression like
this.width * this.heightreads like the format’s own spec, and unlike a lambda, 2.9’s compiler can turn it into plain Python. - Compile to Python, not Cython
- The docs say benchmarks showed PyPy running the generated code much faster than Cython, so the Cython work was withdrawn.
Timeline
- 2007
Construct 2.0; Filiba steps back from the project
- 2011
Corbin Simpson takes over maintenance and moves it to GitHub
- 2016
2.8 introduces
"name" / Fieldsyntax and new integer names - 2018
2.9 compiles definitions into Python modules
- 2020
2.10 (January) removes
Embeddedand adds compiled building - 2023
2.10.70, the latest release (November)
- Tomer Filiba: The Future of Construct (May 2012)
- Tomer Filiba: A Survey of Construct 3 (January 2013)
- Construct docs: Transition to 2.8
- Construct docs: Transition to 2.9 (compilation)
- Construct docs: Transition to 2.10
- Construct docs: home page (2.10 released January 2020, Kaitai Struct collaboration)
- Construct docs: Compilation (Cython withdrawn in favour of PyPy)
- PyPI: construct (release history)
- Kaitai Struct FAQ: Construct is the closest analog
- GitHub: construct/construct contributors
Sources6 · facts checked 9 October 2026
- Construct docs: Introduction (features, formats parsed, requirements, extras)
- Construct docs: The Basics (fields, structs, error paths)
- Construct docs: Compilation (restrictions, untrusted data warning, benchmark)
- Construct docs: Transition to 2.10 (Python 3.8 to 3.12)
- PyPI: construct (MIT licence, release history)
- construct/construct on GitHub