From 5c9213426f03e40bd635af4888f1d4a6bdb2ccc2 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sat, 10 Jan 2026 10:21:09 +0100 Subject: [PATCH] Hacks and fixes to get python code back in reasonable state --- .../ifcopenshell/entity_instance.py | 13 ------ src/ifcopenshell-python/ifcopenshell/file.py | 29 +++++++++--- .../ifcopenshell/template.py | 2 +- .../ifcopenshell/util/pset.py | 2 +- src/ifcwrap/IfcParseWrapper.i | 45 ++++++++++++++----- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 3dd785092b..b8e597b8aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -87,10 +87,6 @@ class entity_instance_mixin: print(wall.__class__) # """ - @property - def file(self): - raise NotImplementedError - def __getattr__(self, name: str) -> Any: if name in ("this", "thisown") or name.startswith("_swig_"): return object.__getattr__(self, name) @@ -300,15 +296,6 @@ class entity_instance_mixin: __rge__ = functools.partialmethod(compare, op=operator.ge, reverse=True) __rgt__ = functools.partialmethod(compare, op=operator.gt, reverse=True) - def __hash__(self): - # Proper entity instances have a stable identity by means of the numeric - # step id. Selected type instances (such as IfcPropertySingleValue.NominalValue - # always have id=0, so we hash - if id_ := self.id(): - return hash((id_, self.file_pointer())) - else: - return hash((self.is_a(), self[0], self.file_pointer())) - def __dir__(self): return sorted( set( diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 291a3e8255..02ca4b9d64 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -504,13 +504,30 @@ class file_mixin: to_delete: Union[set[ifcopenshell.entity_instance], None] = None """Entities for batch removal.""" + registry = {} + + def post_init(self, iden): + if state := self.registry.get(iden): + self.state = state + else: + self.state = self.registry[iden] = [[],[],None] + + @property + def history(self): + return self.state[0] + + @property + def future(self): + return self.state[1] + @property + def transaction(self): + return self.state[2] - def post_init(self): - self.history = [] - self.future = [] - self.transaction: Optional[Transaction] = None - + @transaction.setter + def transaction(self, v): + self.state[2] = v + def set_history_size(self, size: int) -> None: self.history_size = size while len(self.history) > self.history_size: @@ -670,7 +687,7 @@ class file_mixin: if attr[0:6] == "create": return functools.partial(self.create_entity, attr[6:]) else: - return getattr(self, attr) + raise AttributeError def __getitem__(self, key: Union[numbers.Integral, str, bytes]) -> entity_instance: if isinstance(key, numbers.Integral): diff --git a/src/ifcopenshell-python/ifcopenshell/template.py b/src/ifcopenshell-python/ifcopenshell/template.py index d31b125279..42cb1e25ac 100644 --- a/src/ifcopenshell-python/ifcopenshell/template.py +++ b/src/ifcopenshell-python/ifcopenshell/template.py @@ -20,7 +20,7 @@ import time import uuid -from .file import file +from ifcopenshell import file from .guid import compress from .ifcopenshell_wrapper import version from typing import Optional diff --git a/src/ifcopenshell-python/ifcopenshell/util/pset.py b/src/ifcopenshell-python/ifcopenshell/util/pset.py index df906a2e57..db1b4736c9 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/pset.py +++ b/src/ifcopenshell-python/ifcopenshell/util/pset.py @@ -22,7 +22,7 @@ import ifcopenshell import ifcopenshell.ifcopenshell_wrapper as W import ifcopenshell.util.schema import ifcopenshell.util.type -from ifcopenshell.entity_instance import entity_instance +from ifcopenshell import entity_instance from functools import lru_cache from typing import Optional, Literal, NamedTuple, Union diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index b4a81943cb..6cae0fc2ca 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -255,8 +255,8 @@ private: express::Base create(const std::string& entity_name) { const IfcParse::declaration* decl = $self->schema()->declaration_by_name(entity_name); - if (!decl || !decl->as_entity()) { - throw IfcParse::IfcException("No such entity declaration: '" + entity_name + "' in schema '" + $self->schema()->name()); + if (!decl || !(decl->as_entity() || decl->as_type_declaration())) { + throw IfcParse::IfcException("No such entity or type declaration: '" + entity_name + "' in schema '" + $self->schema()->name()); } return $self->create(decl); } @@ -343,21 +343,22 @@ private: %pythoncode %{ schema = property(schema_name) header = property(header) + _registry = {} - old_init = __init__ + _old_init = __init__ def __init__(self, schema=None, schema_version=None): - self.old_init(*filter(None, (schema, schema_version))) - self.post_init() + self._old_init(*filter(None, (schema, schema_version))) + + def __setattr__(self, k, v): + object.__setattr__(self, k, v) + if k == 'this': + # only now we know the identity of the object and we set our python-side attributes based on a python-side map + self.post_init(int(v)) %} } %extend express::Base { - %pythoncode %{ - # Will be assigned when `ifcopenshell.entity_instance` is created. - file = None - %} - // 0 = not found // 1 = regular forward attribute // 2 = inverse attribute @@ -479,6 +480,22 @@ private: return $self->identity() == other.identity(); } + size_t __hash__() const { + if (!self->declaration().as_entity()) { + return boost::hash>{}({self->identity(), self->file()}); + } else { + return self->get_attribute_value(0).apply_visitor([&](const auto& val){ + using U = std::decay_t; + if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) { + // @todo + return boost::hash>{}({self->declaration().index_in_schema(), 0, self->file()}); + } else { + return boost::hash>{}({self->declaration().index_in_schema(), val, self->file()}); + } + }); + } + } + std::string __repr__() const { std::ostringstream oss; $self->toString(oss); @@ -820,6 +837,14 @@ private: throw IfcParse::IfcException("Attribute not set"); } } + + IfcParse::IfcFile* file_py() const { + return $self->file(); + } + + %pythoncode %{ + file = property(file_py) + %} } %extend IfcParse::IfcSpfHeader {