diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 3ec2188265..f6bd455efc 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -34,6 +34,9 @@ from __future__ import print_function import os import sys +import tempfile +import zipfile +from pathlib import Path if hasattr(os, "uname"): platform_system = os.uname()[0].lower() @@ -80,19 +83,32 @@ class SchemaError(Error): pass -def open(fn): - """Loads an IFC dataset from a filepath +def guess_format(path): + """Try to guess format using file extension""" + if path.suffix.lower() in (".ifczip", ".zip"): + return ".ifcZIP" + if path.suffix.lower() in (".ifcxml", ".xml"): + return ".ifcXML" - :param fn: Filepath to the IFC model - :type fn: string - :returns: A file object - :rtype: ifcopenshell.file.file - Example:: - - ifc_file = ifcopenshell.open("/path/to/model.ifc") - """ - f = ifcopenshell_wrapper.open(os.path.abspath(fn)) +def open_path(path: os.PathLike | str, format: str = None) -> file: + path = Path(path) + if format is None: + format = guess_format(path) + if format == ".ifcXML": + f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute())) + if f: + return file(f) + raise IOError(f"Failed to parse .ifcXML file from {path}") + if format == ".ifcZIP": + with tempfile.TemporaryDirectory() as unzipped_path: + with zipfile.ZipFile(path) as zf: + for name in zf.namelist(): + if Path(name).suffix.lower() in (".ifc", ".ifcxml"): + return open_path(zf.extract(name, unzipped_path)) + else: + raise LookupError(f"No .ifc or .ifcXML file found in {path}") + f = ifcopenshell_wrapper.open(str(path.absolute())) if f.good(): return file(f) else: @@ -101,12 +117,27 @@ def open(fn): NO_HEADER: (Error, "Unable to parse IFC SPF header"), UNSUPPORTED_SCHEMA: ( SchemaError, - "Unsupported schema: %s" % ",".join(f.header.file_schema.schema_identifiers), + "Unsupported schema: %s" + % ",".join(f.header.file_schema.schema_identifiers), ), }[f.good().value()] raise exc(msg) +def open(path: os.PathLike | str, format: str = None) -> file: + """Loads an IFC dataset from a filepath + + You can specify a file format. If no format is given, it is guessed from its extension. + Currently supported specified format : .ifc | .ifcZIP | .ifcXML + + Examples: + model = ifcopenshell.open("/path/to/model.ifc") + model = ifcopenshell.open("/path/to/model.ifcXML") + model = ifcopenshell.open("/path/to/model.any_extension", ".ifc") + """ + return open_path(path) + + def create_entity(type, schema="IFC4", *args, **kwargs): """Creates a new IFC entity that does not belong to an IFC file object