Skip to content

Commit b2d5580

Browse files
committed
Allow to open .ifcZIP | .ifcXML IfcOpenShell#2350
Transparently open any .ifc | .ifcXML | .ifcZIP formatted file with any extension : - by guessing format from its extension (eg. .zip | .ifczip | .ifcZIP | .ifcXML | .ifcxml | .IFC | .ifc) zipped file can file can be of any supported format - by specifying a format
1 parent 8386aa0 commit b2d5580

1 file changed

Lines changed: 45 additions & 14 deletions

File tree

src/ifcopenshell-python/ifcopenshell/__init__.py

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434

3535
import os
3636
import sys
37+
import tempfile
38+
import zipfile
39+
from pathlib import Path
3740

3841
if hasattr(os, "uname"):
3942
platform_system = os.uname()[0].lower()
@@ -80,19 +83,32 @@ class SchemaError(Error):
8083
pass
8184

8285

83-
def open(fn):
84-
"""Loads an IFC dataset from a filepath
85-
86-
:param fn: Filepath to the IFC model
87-
:type fn: string
88-
:returns: A file object
89-
:rtype: ifcopenshell.file.file
90-
91-
Example::
92-
93-
ifc_file = ifcopenshell.open("/path/to/model.ifc")
94-
"""
95-
f = ifcopenshell_wrapper.open(os.path.abspath(fn))
86+
def guess_format(path):
87+
"""Try to guess format using file extension"""
88+
if path.suffix.lower() in (".ifczip", ".zip"):
89+
return ".ifcZIP"
90+
if path.suffix.lower() in (".ifcxml", ".xml"):
91+
return ".ifcXML"
92+
93+
94+
def open_path(path: os.PathLike | str, format: str = None) -> file:
95+
path = Path(path)
96+
if format is None:
97+
format = guess_format(path)
98+
if format == ".ifcXML":
99+
f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute()))
100+
if f:
101+
return file(f)
102+
raise IOError(f"Failed to parse .ifcXML file from {path}")
103+
if format == ".ifcZIP":
104+
with tempfile.TemporaryDirectory() as unzipped_path:
105+
with zipfile.ZipFile(path) as zf:
106+
for name in zf.namelist():
107+
if Path(name).suffix.lower() in (".ifc", ".ifcxml"):
108+
return open_path(zf.extract(name, unzipped_path))
109+
else:
110+
raise LookupError(f"No .ifc or .ifcXML file found in {path}")
111+
f = ifcopenshell_wrapper.open(str(path.absolute()))
96112
if f.good():
97113
return file(f)
98114
else:
@@ -101,12 +117,27 @@ def open(fn):
101117
NO_HEADER: (Error, "Unable to parse IFC SPF header"),
102118
UNSUPPORTED_SCHEMA: (
103119
SchemaError,
104-
"Unsupported schema: %s" % ",".join(f.header.file_schema.schema_identifiers),
120+
"Unsupported schema: %s"
121+
% ",".join(f.header.file_schema.schema_identifiers),
105122
),
106123
}[f.good().value()]
107124
raise exc(msg)
108125

109126

127+
def open(path: os.PathLike | str, format: str = None) -> file:
128+
"""Loads an IFC dataset from a filepath
129+
130+
You can specify a file format. If no format is given, it is guessed from its extension.
131+
Currently supported specified format : .ifc | .ifcZIP | .ifcXML
132+
133+
Examples:
134+
model = ifcopenshell.open("/path/to/model.ifc")
135+
model = ifcopenshell.open("/path/to/model.ifcXML")
136+
model = ifcopenshell.open("/path/to/model.any_extension", ".ifc")
137+
"""
138+
return open_path(path)
139+
140+
110141
def create_entity(type, schema="IFC4", *args, **kwargs):
111142
"""Creates a new IFC entity that does not belong to an IFC file object
112143

0 commit comments

Comments
 (0)