#2471 work around file gc with instance refs

This commit is contained in:
Thomas Krijnen
2022-10-04 10:21:45 +02:00
parent f81b22240c
commit f41ad0d5de
@@ -107,20 +107,36 @@ class entity_instance(object):
e = ifcopenshell_wrapper.new_IfcBaseClass(*e) e = ifcopenshell_wrapper.new_IfcBaseClass(*e)
super(entity_instance, self).__setattr__("wrapped_data", e) super(entity_instance, self).__setattr__("wrapped_data", e)
super(entity_instance, self).__setattr__("method_list", None) super(entity_instance, self).__setattr__("method_list", None)
# Make sure the file is not gc'ed while we have live instances
self.wrapped_data.file = file self.wrapped_data.file = file
def __del__(self):
"""
#2471 while the precise chain of action is unclear, creating
instance references prevents file gc, even with all instance
refs deleted. This is a work-around for that.
"""
self.wrapped_data.file = None
def __getattr__(self, name): def __getattr__(self, name):
INVALID, FORWARD, INVERSE = range(3) INVALID, FORWARD, INVERSE = range(3)
attr_cat = self.wrapped_data.get_attribute_category(name) attr_cat = self.wrapped_data.get_attribute_category(name)
if attr_cat == FORWARD: if attr_cat == FORWARD:
return entity_instance.wrap_value( return entity_instance.wrap_value(
self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name)), self.wrapped_data.file self.wrapped_data.get_argument(
self.wrapped_data.get_argument_index(name)
),
self.wrapped_data.file,
) )
elif attr_cat == INVERSE: elif attr_cat == INVERSE:
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name), self.wrapped_data.file) return entity_instance.wrap_value(
self.wrapped_data.get_inverse(name), self.wrapped_data.file
)
else: else:
raise AttributeError( raise AttributeError(
"entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(True), name) "entity instance of type '%s' has no attribute '%s'"
% (self.wrapped_data.is_a(True), name)
) )
@staticmethod @staticmethod
@@ -159,7 +175,11 @@ class entity_instance(object):
:type attr: int :type attr: int
:rtype: string :rtype: string
""" """
attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr) attr_idx = (
attr
if isinstance(attr, numbers.Integral)
else self.wrapped_data.get_argument_index(attr)
)
return self.wrapped_data.get_argument_type(attr_idx) return self.wrapped_data.get_argument_type(attr_idx)
def attribute_name(self, attr_idx): def attribute_name(self, attr_idx):
@@ -177,15 +197,23 @@ class entity_instance(object):
def __getitem__(self, key): def __getitem__(self, key):
if key < 0 or key >= len(self): if key < 0 or key >= len(self):
raise IndexError("Attribute index {} out of range for instance of type {}".format(key, self.is_a())) raise IndexError(
return entity_instance.wrap_value(self.wrapped_data.get_argument(key), self.wrapped_data.file) "Attribute index {} out of range for instance of type {}".format(
key, self.is_a()
)
)
return entity_instance.wrap_value(
self.wrapped_data.get_argument(key), self.wrapped_data.file
)
def __setitem__(self, idx, value): def __setitem__(self, idx, value):
if self.wrapped_data.file and self.wrapped_data.file.transaction: if self.wrapped_data.file and self.wrapped_data.file.transaction:
self.wrapped_data.file.transaction.store_edit(self, idx, value) self.wrapped_data.file.transaction.store_edit(self, idx, value)
if self.method_list is None: if self.method_list is None:
super(entity_instance, self).__setattr__("method_list", _method_dict[self.is_a(True)]) super(entity_instance, self).__setattr__(
"method_list", _method_dict[self.is_a(True)]
)
method = self.method_list[idx] method = self.method_list[idx]
@@ -193,7 +221,9 @@ class entity_instance(object):
if method is not set_derived_atribute: if method is not set_derived_atribute:
self.wrapped_data.setArgumentAsNull(idx) self.wrapped_data.setArgumentAsNull(idx)
else: else:
self.method_list[idx](self.wrapped_data, idx, entity_instance.unwrap_value(value)) self.method_list[idx](
self.wrapped_data, idx, entity_instance.unwrap_value(value)
)
return value return value
@@ -240,8 +270,11 @@ class entity_instance(object):
if self.id(): if self.id():
return self.wrapped_data == other.wrapped_data return self.wrapped_data == other.wrapped_data
else: else:
return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == \ return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == (
(other.is_a(), other[0], other.wrapped_data.file_pointer()) other.is_a(),
other[0],
other.wrapped_data.file_pointer(),
)
def __hash__(self): def __hash__(self):
# Proper entity instances have a stable identity by means of the numeric # Proper entity instances have a stable identity by means of the numeric
@@ -263,7 +296,9 @@ class entity_instance(object):
) )
) )
def get_info(self, include_identifier=True, recursive=False, return_type=dict, ignore=()): def get_info(
self, include_identifier=True, recursive=False, return_type=dict, ignore=()
):
"""Return a dictionary of the entity_instance's properties (Python and IFC) and their values. """Return a dictionary of the entity_instance's properties (Python and IFC) and their values.
:param include_identifier: Whether or not to include the STEP numerical identifier :param include_identifier: Whether or not to include the STEP numerical identifier
@@ -293,7 +328,11 @@ class entity_instance(object):
yield "id", self.id() yield "id", self.id()
yield "type", self.is_a() yield "type", self.is_a()
except BaseException: except BaseException:
logging.exception("unhandled exception while getting id / type info on {}".format(self)) logging.exception(
"unhandled exception while getting id / type info on {}".format(
self
)
)
for i in range(len(self)): for i in range(len(self)):
try: try:
if self.wrapped_data.get_attribute_names()[i] in ignore: if self.wrapped_data.get_attribute_names()[i] in ignore:
@@ -316,16 +355,24 @@ class entity_instance(object):
ignore=ignore, ignore=ignore,
) )
attr_value = entity_instance.walk(is_instance, get_info_, attr_value) attr_value = entity_instance.walk(
is_instance, get_info_, attr_value
)
yield self.attribute_name(i), attr_value yield self.attribute_name(i), attr_value
except BaseException: except BaseException:
logging.exception("unhandled exception occurred setting attribute name for {}".format(self)) logging.exception(
"unhandled exception occurred setting attribute name for {}".format(
self
)
)
return return_type(_()) return return_type(_())
__dict__ = property(get_info) __dict__ = property(get_info)
def get_info_2(self, include_identifier=True, recursive=False, return_type=dict, ignore=()): def get_info_2(
self, include_identifier=True, recursive=False, return_type=dict, ignore=()
):
assert include_identifier assert include_identifier
assert recursive assert recursive
assert return_type is dict assert return_type is dict