mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-10 06:00:51 +00:00
Compare entity instances in one file by identity again
entity_instance.file is a property backed by a fresh SWIG wrapper on every access, and ifcopenshell::file had no __eq__, so `self.file != other.file` in entity_instance.__eq__ compared two throwaway wrappers and was always true - even for an instance against itself. Every entity comparison therefore took the deep get_info() branch, making distinct but structurally identical instances compare equal and leaving the final `return False` unreachable. Bonsai's TestAddRepresentationItemToShapeAspect showed this as two separate IfcShapeAspects being treated as one, so the stale aspect was never removed. Restore the file_pointer() pair that was commented out on both ifcopenshell::file and express::Base - IfcParseWrapper.i already described it as the way to "trace file ownership of instances on the python side" - and give file the __eq__/__hash__ it was missing. The express::Base one needs $self->file() now that file_ lives on instance_data. This also repairs rocksdb_lazy_instance.__eq__, which already called file_pointer(). EXPRESS `=` is value comparison and `:=:` is instance comparison, but rule_compiler emits `==` for both (see the @todo on process_rel_op), and derived attributes build their operands in the shared global file, so rules compare same-file instances and need value semantics. Restore those for the duration of rule execution with settings.compare_instances_by_value, alongside the existing unpack_non_aggregate_inverses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -263,13 +263,29 @@ private:
|
||||
%newobject ifcopenshell::file::create_uninitialized;
|
||||
|
||||
%extend ifcopenshell::file {
|
||||
/*
|
||||
// Use to correlate to entity_instance.file_pointer, so that we
|
||||
// can trace file ownership of instances on the python side.
|
||||
size_t file_pointer() const {
|
||||
return reinterpret_cast<size_t>($self);
|
||||
}
|
||||
*/
|
||||
|
||||
%pythoncode %{
|
||||
def __eq__(self, other):
|
||||
# Attribute access hands out a new wrapper every time, so identity
|
||||
# of the wrapper says nothing about the file it refers to.
|
||||
if self is other:
|
||||
return True
|
||||
if not isinstance(other, file):
|
||||
return NotImplemented
|
||||
return self.file_pointer() == other.file_pointer()
|
||||
|
||||
def __ne__(self, other):
|
||||
result = self.__eq__(other)
|
||||
return result if result is NotImplemented else not result
|
||||
|
||||
def __hash__(self):
|
||||
return self.file_pointer()
|
||||
%}
|
||||
|
||||
file(const std::string& schema) {
|
||||
return new ifcopenshell::file(ifcopenshell::schema_by_name(schema));
|
||||
@@ -552,12 +568,10 @@ private:
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
/*
|
||||
// Just something to have a somewhat sensible value to hash
|
||||
// Identifies the file owning this instance, zero when it has no owner.
|
||||
size_t file_pointer() const {
|
||||
return reinterpret_cast<size_t>($self->file_);
|
||||
return reinterpret_cast<size_t>($self->file());
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned get_argument_index(const std::string& a) const {
|
||||
if ($self->declaration().as_entity()) {
|
||||
|
||||
Reference in New Issue
Block a user