Hacks and fixes to get python code back in reasonable state

This commit is contained in:
Thomas Krijnen
2026-01-10 10:21:09 +01:00
parent 69a4ad35a1
commit 5c9213426f
5 changed files with 60 additions and 31 deletions
@@ -87,10 +87,6 @@ class entity_instance_mixin:
print(wall.__class__) # <class 'ifcopenshell.entity_instance'> print(wall.__class__) # <class 'ifcopenshell.entity_instance'>
""" """
@property
def file(self):
raise NotImplementedError
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
if name in ("this", "thisown") or name.startswith("_swig_"): if name in ("this", "thisown") or name.startswith("_swig_"):
return object.__getattr__(self, name) return object.__getattr__(self, name)
@@ -300,15 +296,6 @@ class entity_instance_mixin:
__rge__ = functools.partialmethod(compare, op=operator.ge, reverse=True) __rge__ = functools.partialmethod(compare, op=operator.ge, reverse=True)
__rgt__ = functools.partialmethod(compare, op=operator.gt, 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 <type, value, file pointer>
if id_ := self.id():
return hash((id_, self.file_pointer()))
else:
return hash((self.is_a(), self[0], self.file_pointer()))
def __dir__(self): def __dir__(self):
return sorted( return sorted(
set( set(
+23 -6
View File
@@ -504,13 +504,30 @@ class file_mixin:
to_delete: Union[set[ifcopenshell.entity_instance], None] = None to_delete: Union[set[ifcopenshell.entity_instance], None] = None
"""Entities for batch removal.""" """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): @transaction.setter
self.history = [] def transaction(self, v):
self.future = [] self.state[2] = v
self.transaction: Optional[Transaction] = None
def set_history_size(self, size: int) -> None: def set_history_size(self, size: int) -> None:
self.history_size = size self.history_size = size
while len(self.history) > self.history_size: while len(self.history) > self.history_size:
@@ -670,7 +687,7 @@ class file_mixin:
if attr[0:6] == "create": if attr[0:6] == "create":
return functools.partial(self.create_entity, attr[6:]) return functools.partial(self.create_entity, attr[6:])
else: else:
return getattr(self, attr) raise AttributeError
def __getitem__(self, key: Union[numbers.Integral, str, bytes]) -> entity_instance: def __getitem__(self, key: Union[numbers.Integral, str, bytes]) -> entity_instance:
if isinstance(key, numbers.Integral): if isinstance(key, numbers.Integral):
@@ -20,7 +20,7 @@
import time import time
import uuid import uuid
from .file import file from ifcopenshell import file
from .guid import compress from .guid import compress
from .ifcopenshell_wrapper import version from .ifcopenshell_wrapper import version
from typing import Optional from typing import Optional
@@ -22,7 +22,7 @@ import ifcopenshell
import ifcopenshell.ifcopenshell_wrapper as W import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.schema import ifcopenshell.util.schema
import ifcopenshell.util.type import ifcopenshell.util.type
from ifcopenshell.entity_instance import entity_instance from ifcopenshell import entity_instance
from functools import lru_cache from functools import lru_cache
from typing import Optional, Literal, NamedTuple, Union from typing import Optional, Literal, NamedTuple, Union
+35 -10
View File
@@ -255,8 +255,8 @@ private:
express::Base create(const std::string& entity_name) { express::Base create(const std::string& entity_name) {
const IfcParse::declaration* decl = $self->schema()->declaration_by_name(entity_name); const IfcParse::declaration* decl = $self->schema()->declaration_by_name(entity_name);
if (!decl || !decl->as_entity()) { if (!decl || !(decl->as_entity() || decl->as_type_declaration())) {
throw IfcParse::IfcException("No such entity declaration: '" + entity_name + "' in schema '" + $self->schema()->name()); throw IfcParse::IfcException("No such entity or type declaration: '" + entity_name + "' in schema '" + $self->schema()->name());
} }
return $self->create(decl); return $self->create(decl);
} }
@@ -343,21 +343,22 @@ private:
%pythoncode %{ %pythoncode %{
schema = property(schema_name) schema = property(schema_name)
header = property(header) header = property(header)
_registry = {}
old_init = __init__ _old_init = __init__
def __init__(self, schema=None, schema_version=None): def __init__(self, schema=None, schema_version=None):
self.old_init(*filter(None, (schema, schema_version))) self._old_init(*filter(None, (schema, schema_version)))
self.post_init()
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 { %extend express::Base {
%pythoncode %{
# Will be assigned when `ifcopenshell.entity_instance` is created.
file = None
%}
// 0 = not found // 0 = not found
// 1 = regular forward attribute // 1 = regular forward attribute
// 2 = inverse attribute // 2 = inverse attribute
@@ -479,6 +480,22 @@ private:
return $self->identity() == other.identity(); return $self->identity() == other.identity();
} }
size_t __hash__() const {
if (!self->declaration().as_entity()) {
return boost::hash<std::tuple<uint32_t, void*>>{}({self->identity(), self->file()});
} else {
return self->get_attribute_value(0).apply_visitor([&](const auto& val){
using U = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<U, Blank> || std::is_same_v<U, Derived> || std::is_same_v<U, boost::logic::tribool> || std::is_same_v<U, EnumerationReference> || std::is_same_v<U, empty_aggregate_t> || std::is_same_v<U, empty_aggregate_of_aggregate_t>) {
// @todo
return boost::hash<std::tuple<size_t, size_t, void*>>{}({self->declaration().index_in_schema(), 0, self->file()});
} else {
return boost::hash<std::tuple<size_t, decltype(val), void*>>{}({self->declaration().index_in_schema(), val, self->file()});
}
});
}
}
std::string __repr__() const { std::string __repr__() const {
std::ostringstream oss; std::ostringstream oss;
$self->toString(oss); $self->toString(oss);
@@ -820,6 +837,14 @@ private:
throw IfcParse::IfcException("Attribute not set"); throw IfcParse::IfcException("Attribute not set");
} }
} }
IfcParse::IfcFile* file_py() const {
return $self->file();
}
%pythoncode %{
file = property(file_py)
%}
} }
%extend IfcParse::IfcSpfHeader { %extend IfcParse::IfcSpfHeader {