From e57b5ac5edcb127e2f7944461957f577ecc9e73a Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Sun, 19 Jul 2026 15:35:21 +0300 Subject: [PATCH] ifcwrap: rename geometry backref helper to fix stub validation crash The #1124 fix (824c1fc280) named its pythoncode backref helper `_geometry_with_backref` with a leading underscore. validate_stub.py's get_function_node_name() skips any leading-underscore name (its only exception is `_is`), so the helper never gets added to the parsed `subnames` set. When get_names_tree_lines() later processes `geometry = property(_geometry_with_backref)`, find_method_by_name() can't find the backing function and the `assert (wrapped_function := find_method_by_name(arg))` at validate_stub.py:154 raises AssertionError instead of the expected clean stub/wrapper diff, crashing the CI `ci.yml` -> compile-and-test -> "Test ifcopenshell-python" step (`make test-parallel`) on every push since 824c1fc280. Renaming to `geometry_with_backref_` (trailing underscore) matches the existing local convention in the same class bodies for backing- implementation helpers (calc_volume_, calc_surface_area_), which validate_stub.py's heuristic already handles correctly. Also add the six new SVG edge-classification settings-flag classes (SvgEmitFlushEdges, SvgRenderCreaseEdges, SvgRenderSharpEdges, SvgRidgeAngleMinDegrees, SvgUseEdgeClassification, SvgValleyAngleMinDegrees) to ifcopenshell_wrapper.pyi. These were added to the C++/SWIG side by the SVG edge classification commits but never synced to the stub, and validate_stub would report them as a stub/wrapper discrepancy once the crash above is fixed. Reproduced by building ifcopenshell against the exact CI-tested commit (c68e4a0eee) with SWIG 4.1.0 (matching ci.yml's pinned build-from-source version) and running validate_stub.py's actual logic against the generated ifcopenshell_wrapper.py: crashed identically to the CI log before this change, passed cleanly after. test/util/scripts/test_validate_stub.py::TestValidateStub::test_run passes against the rebuilt wrapper. Generated with the assistance of an AI coding tool. --- .../ifcopenshell/ifcopenshell_wrapper.pyi | 30 +++++++++++++++++++ src/ifcwrap/IfcGeomWrapper.i | 12 ++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi index 323200b9fb..8d77d4dd33 100644 --- a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi +++ b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi @@ -484,6 +484,26 @@ class Settings: def set_(self, *args): ... def setting_names(self): ... +class SvgEmitFlushEdges: + defaultvalue: Any + description: Any + name: Any + +class SvgRenderCreaseEdges: + defaultvalue: Any + description: Any + name: Any + +class SvgRenderSharpEdges: + defaultvalue: Any + description: Any + name: Any + +class SvgRidgeAngleMinDegrees: + defaultvalue: Any + description: Any + name: Any + class SvgSerializer(WriteOnlyGeometrySerializer): def __init__(self, out_filename, geometry_settings, settings, logger=None): ... SH_NONE: Any @@ -548,6 +568,16 @@ class SvgSerializer(WriteOnlyGeometrySerializer): def write(self, *args): ... def writeHeader(self): ... +class SvgUseEdgeClassification: + defaultvalue: Any + description: Any + name: Any + +class SvgValleyAngleMinDegrees: + defaultvalue: Any + description: Any + name: Any + class SwigPyIterator: def __init__(self, *args, **kwargs): ... def advance(self, n): ... diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index b6dffb3b6e..b77d8467b1 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -794,11 +794,11 @@ struct ShapeRTTI : public boost::static_visitor %pythoncode %{ # Hide the getters with read-only property implementations # Keep the owning element alive while its geometry is referenced (#1124). - def _geometry_with_backref(self, _f=geometry): + def geometry_with_backref_(self, _f=geometry): result = _f(self) result._parent = self return result - geometry = property(_geometry_with_backref) + geometry = property(geometry_with_backref_) %} }; @@ -806,11 +806,11 @@ struct ShapeRTTI : public boost::static_visitor %pythoncode %{ # Hide the getters with read-only property implementations # Keep the owning element alive while its geometry is referenced (#1124). - def _geometry_with_backref(self, _f=geometry): + def geometry_with_backref_(self, _f=geometry): result = _f(self) result._parent = self return result - geometry = property(_geometry_with_backref) + geometry = property(geometry_with_backref_) %} }; @@ -836,11 +836,11 @@ struct ShapeRTTI : public boost::static_visitor %pythoncode %{ # Hide the getters with read-only property implementations # Keep the owning element alive while its geometry is referenced (#1124). - def _geometry_with_backref(self, _f=geometry): + def geometry_with_backref_(self, _f=geometry): result = _f(self) result._parent = self return result - geometry = property(_geometry_with_backref) + geometry = property(geometry_with_backref_) volume = property(calc_volume_) surface_area = property(calc_surface_area_) %}