From 6e1ebb02aca1d435496e0c5c3d6b3be3b5e8d8dd Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 12 Jun 2024 18:13:49 +0500 Subject: [PATCH] file and entity - avoid infinite recursion if they fail to initialize Example of the error: Traceback (most recent call last): File "\test.py", line 133, in ifc_file = ifcopenshell.file.from_string(ifc_str) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\ifcopenshell\file.py", line 648, in from_string return file(ifcopenshell_wrapper.read(s)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\ifcopenshell\file.py", line 264, in __init__ raise exc(msg) ifcopenshell.SchemaError: Unsupported schema: IFC2X3 Exception ignored in: Traceback (most recent call last): File "\ifcopenshell\file.py", line 282, in __del__ del file_dict[self.file_pointer()] ^^^^^^^^^^^^^^^^^ File "\ifcopenshell\file.py", line 424, in __getattr__ return getattr(self.wrapped_data, attr) ^^^^^^^^^^^^^^^^^ File "\ifcopenshell\file.py", line 424, in __getattr__ return getattr(self.wrapped_data, attr) ^^^^^^^^^^^^^^^^^ File "\ifcopenshell\file.py", line 424, in __getattr__ return getattr(self.wrapped_data, attr) ^^^^^^^^^^^^^^^^^ [Previous line repeated 996 more times] RecursionError: maximum recursion depth exceeded Simple way to trigger those recursions was: import ifcopenshell ifcopenshell.file(schema="IFC4x3_RC4_43c3555") ifcopenshell.entity_instance(("IFC4X3", "IfcPresentationStyleAssignment")) --- src/ifcopenshell-python/ifcopenshell/entity_instance.py | 9 ++++++++- src/ifcopenshell-python/ifcopenshell/file.py | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index f43e58bcb8..3c53a96cc1 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -160,7 +160,14 @@ class entity_instance: instance references prevents file gc, even with all instance refs deleted. This is a work-around for that. """ - self.wrapped_data.file = None + # Avoid infinite recursion if entity is failed to initialize + # and wrapped_data is unset. Hacky since we override + # both __dict__ and __dir__. + try: + wrapped_data = super().__getattribute__("wrapped_data") + wrapped_data.file = None + except AttributeError: + return @property def file(self): diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index fd08c36b77..cb50ca6ca1 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -277,6 +277,10 @@ class file: file_dict[self.file_pointer()] = weakref.ref(self) def __del__(self) -> None: + # Avoid infinite recursion if file is failed to initialize + # and wrapped_data is unset. + if "wrapped_data" not in dir(self): + return del file_dict[self.file_pointer()] def set_history_size(self, size: int) -> None: