Skip to content

openbim-ifcx-py ​

Python bindings for openbim-ifcx: read, write, validate, compose and export IFC5 / IFCX files from Python.

Crateopenbim-ifcx-py 0.1.2 on main
Latest release0.1.2 (2026-10-03)
Distributed asPyPI openbim-ifcx
Installpip install openbim-ifcx
RequiresPython >=3.9
LicenseMIT
API documentationrustdoc
Sourcecrates/openbim-ifcx-py/

About ​

From the crate's README.md, which is also its registry page:

Read, write, validate and compose IFC5 / IFCX files, and export them as GLB, from Python. A thin binding over the openbim-ifcx Rust crate, from the openbim-ifcx-py crate in openbimrs/ifcx: one abi3 wheel for CPython 3.9 and later.

The documentation has a Python guide and the API reference; the viewer shows an IFCX file in the browser.

Targets the ifcx_alpha draft of buildingSMART's IFC5. IFCX is still a moving draft; see the capabilities.

sh
pip install openbim-ifcx

Example ​

python
from pathlib import Path
from openbim_ifcx import IfcxFile, compose, export_glb

model = Path("model.ifcx").read_bytes()
file = IfcxFile.parse(model)              # str or bytes
print(file.header["id"], file.node_count)

report = file.validate()
for f in report["failures"]:
    print(f["node"], f["attribute"], f["pointer"], f["kind"], f["message"])
Path("copy.ifcx").write_text(file.write())  # unchanged from the input

# Layers weakest first: the last layer's opinions win.
tree = compose([model, Path("overlay.ifcx").read_bytes()])
print(list(tree["children"]))              # the root nodes

# Any glTF viewer opens the result.
Path("model.glb").write_bytes(export_glb([model, Path("overlay.ifcx").read_bytes()]))

API ​

CallReturns
IfcxFile.parse(data)an IfcxFile; data is JSON text or UTF-8 bytes
file.write(pretty=False)the file as JSON text, lossless
file.to_dict()the file as plain dicts and lists
file.headerthe header dict
file.node_countnumber of entries in data
file.validate()a validation report against the file's own schemas
compose(layers, imports=None)the composed tree as nested dicts
validate(layers, imports=None)a validation report over all layers and resolved imports
export_glb(layers, imports=None, *, y_up=True, origin_on_root=True)the composed model as binary glTF 2.0 bytes

layers is one file or an iterable of files, weakest first. The package ships type hints (py.typed).

Lossless round trip. write() keeps key order, fields the draft does not define, null deletions and every number exactly as read.

Composed tree. compose returns the artificial root (path "") over every root node. Each node is {"path", "attributes", "children"}, the shape of upstream's PostCompositionNode, with children keyed by child name. Nodes shared through inheritance appear once per place they are used.

Imports. Without imports, imports are not resolved: the layers' schemas and data are concatenated in order, as upstream's Federate does. With imports (a mapping from the exact import uri to a file, possibly empty), the layers become the imports of a synthetic main layer, as upstream's ifcx compose command builds it, and every import must be supplied. integrity values are checked against the supplied bytes. A layer overrides the layers it imports, as agreed upstream in buildingSMART/IFC5-development#144, and a later layer overrides both. Nothing is fetched from the network or the filesystem.

GLB export. export_glb writes the render scene of the composed tree: transformed, instanced meshes, lines and points with their materials, one glTF node per instance named by its IFCX path. The root node turns IFCX's Z-up into glTF's Y-up (y_up=False keeps Z-up) and carries the scene origin, so georeferenced models keep their coordinates (origin_on_root=False centres the model on the glTF origin instead). Geometry values that do not decode are left out.

Validation report. {"valid": bool, "failures": [...]}, each failure {"node", "attribute", "pointer", "kind", "message"}: the node path, the attribute id, an RFC 6901 pointer into the value ("" for the value itself), a stable kind such as missing-schema, type-mismatch, not-an-integer, not-an-option, missing-key, too-few-elements or too-many-elements, and a human message.

Errors ​

Every failure raises IfcxError with a stable code:

codeMeaning
readnot an IFCX file: invalid JSON or the wrong shape; the message gives line and column
writethe file could not be written
layerimports could not be resolved: a missing file, an import cycle, an integrity mismatch
composea reference cycle, or a reference to a node no layer defines
invalid-argumentan argument of the wrong type, or no layers
glbthe scene could not be written as GLB (over 4 GiB)

The codes are shared with the JavaScript binding (@openbim/ifcx); a code is never renamed or reused.

Threads ​

Parsing, composing, validating and GLB export release the GIL. An IfcxFile is immutable and can be shared between threads.

Build from source ​

sh
uv venv && . .venv/bin/activate
uv pip install maturin
maturin develop --release     # or: scripts/check-python.sh to build + test

License ​

MIT, like the rest of openbimrs/ifcx.

Python API ​

IfcxFile ​

One IFCX file.

MemberDescription
IfcxFile.parse(data: Input) -> IfcxFileRead an IFCX file from its JSON text or UTF-8 bytes.
file.write(pretty: bool = False) -> strThe file as JSON text; pretty indents it by two spaces.
file.to_dict() -> Dict[str, Any]The file as plain dicts and lists, in file order.
file.header: Dict[str, Any]The header object.
file.node_count: intNumber of entries in data; several may share a path.
file.validate() -> Dict[str, Any]Check every attribute against this file's own schemas.

Functions and types ​

NameDescription
IfcxErrorThe exception every failure raises; its code is stable.
Input = Union[str, bytes, bytearray, memoryview]An IFCX file as JSON text or as its UTF-8 bytes.
compose(layers: Union[Input, Iterable[Input]], imports: Optional[Mapping[str, Input]] = None) -> Dict[str, Any]Compose layers, weakest first, into a tree of plain dicts.
export_glb(layers: Union[Input, Iterable[Input]], imports: Optional[Mapping[str, Input]] = None, *, y_up: bool = True, origin_on_root: bool = True) -> bytesCompose layers, weakest first, and write them as a binary glTF 2.0 file.
validate(layers: Union[Input, Iterable[Input]], imports: Optional[Mapping[str, Input]] = None) -> Dict[str, Any]Check the attributes of the layers against the schemas of every layer.

Within the workspace ​

Changes ​

Unreleased, on main:

Changed ​

  • Breaking: with imports resolved, a layer now overrides the layers it imports, as agreed upstream in buildingSMART/IFC5-development#144 (was: an import overrode the layer importing it). compose, validate and GLB export give different results when an import carries data or redefines a schema that a layer also sets. Layers given without imports, and imports that carry only schemas, compose as before; a later layer still overrides an earlier one and everything it imports (#36).

Latest release, 0.1.2 (2026-10-03):

Changed ​

  • Built on openbim-ifcx 0.1.1 and openbim-ifcx-geometry 0.1.1: validation with imports checks every layer against the schemas of all layers, flattening and composition no longer copy attribute values, and the fuzzing fixes apply (deep schema inheritance, oversized coordinates, exponential scene walks, malformed PCD headers).

  • The README, which is the PyPI page, links the documentation site (https://openbimrs.github.io/ifcx/) and the viewer at its new address, https://openbimrs.github.io/ifcx/viewer/ (#64).

Full history: crates/openbim-ifcx-py/CHANGELOG.md, and every release of every crate on the project changelog.

Released under the MIT licence. No buildingSMART files or texts are published here.