The previous commit guarded import_item against tool.Loader.create_generic_shape
returning None, but only assigned cartesian_point_offset inside the walrus
operator of the non-None branch. When geometry is None and the item is also an
IfcSweptAreaSolid, the code after the if/else unconditionally reads
cartesian_point_offset to adjust item_matrix, raising an UnboundLocalError
instead of the intended graceful error log.
Move the cartesian_point_offset lookup out of the walrus operator so it is
always computed, matching its use later in the function regardless of which
branch ran.
Verified live in headless Blender: reproduced both the original AttributeError
('NoneType' has no attribute 'verts_buffer', matching the issue's traceback)
and this UnboundLocalError with a synthetic IfcExtrudedAreaSolid item whose
create_generic_shape was made to return None (the reporter's real .ifc file
was shared privately with maintainers and was not available to us); confirmed
the fixed code path completes without crashing and logs the error instead.
Fixes#6693.
Generated with the assistance of an AI coding tool.
Editing an IfcRepresentationItem and exiting item edit mode re-imports
its geometry through tool.Geometry.import_item, which calls
tool.Loader.create_generic_shape(item) and immediately passes the
result to ifcopenshell.util.shape.get_vertices without checking it.
The geometry kernel can legitimately return None for an item, for
example when a boolean or clipping result is invalid or has no
remaining volume, which is exactly what every other caller of
create_generic_shape already guards against (import_ifc.py and
tool/loader.py both check the result before using it). Without the
same guard here, get_vertices(None) raises an unhandled
AttributeError partway through import_item, after
obj.data.clear_geometry() has already run, leaving the item's mesh
cleared and the operator aborted instead of failing gracefully.
This adds the same None check used elsewhere in the codebase: on
failure we log a clear error naming the offending item instead of
crashing, and skip only the geometry-dependent steps.
Fixes#6693.
Generated with the assistance of an AI coding tool.
Error was:
```
configure: error: could not find a working compiler, see config.log for details
```
config.log:
```
conftest.c: In function 'f':
conftest.c:12:48: error: too many arguments to function 'g'; expected 0, have 6
12 | for(i=0;i<1;i++){if(e(got,got,9,d[i].n)==0)h();g(i,d[i].src,d[i].n,got,d[i].want,9);if(d[i].n)h();}}
| ^ ~
```
Last reference to this file was dropped in 7ae685dbf, though the ref was
pointing to `/patches/opencollada/pr622.patch`, so IIUC
`patches/pr622.patch` was never used.
AST parser has changed a bit and there are some minor differences in the .py output. Updating files just to avoid seeing these diffs when rerunning rule compiler.
Example error:
```
ast.Str(s=node.attr),
^^^^^^^
AttributeError: module 'ast' has no attribute 'Str'
```
`ast.Str` was deprecated since 3.8 and was removed in 3.14, see https://docs.python.org/3/whatsnew/3.14.html#id9
Because uv was always trying to install when starting a venv in `ifcopenshell` folder, though they might be already available globally. And also they were listed twice - in pyproject and in the ci-lint.yml, now there's a single source of truth.
versionURLs in brand.html used http:// while the docs sites are
served over https://, so currentURL.includes(url) never matched and
the <select> never reflected/switched to Unstable. Fixes#8023.
Generated with the assistance of an AI coding tool.
Two TestImplementsTool failures on v0.8.0:
- test_cost.py: Cost could not be instantiated because
core.tool.Cost declared abstract get_direct_cost_item_products, which
tool.cost.Cost never implements. The method is dead (zero call sites;
get_cost_item_products(is_deep=False) already covers the 'direct'
case), so remove the abstract declaration.
- test_ifcgit.py: tool.ifcgit.IfcGit was not declared as a subclass of
its core.tool.IfcGit interface (unlike every sibling tool class), so
the isinstance check failed. Add the base class (and the
bonsai.core.tool import it needs). All 50 interface methods are
already implemented on the concrete class.
No behaviour change. Verified in headless Blender: isinstance(Cost(), core.tool.Cost) and isinstance(IfcGit(), core.tool.IfcGit) both True (were TypeError / False); repo abstract-vs-impl diff confirms all IfcGit abstracts are implemented.
This change was made with the assistance of an AI tool.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
black (the version CI's psf/black@stable resolves to) flags three spots
in util/selector.py: the chained .replace() in FormatTransformer.number,
the suppress_zero_inches kwarg in format_length, and the long
`elif key in (...) and hasattr(...)` placement-key tuple in
set_element_value. Reformat all three to black's multi-line style.
Formatting only, no behavioural change (all keys preserved).
This change was made with the assistance of an AI tool.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
generate_annotation built the annotation list from a set union and sorted it by
ZIndex and TEXT-ness only. Annotations that tied on that key kept set iteration
order, which follows entity hash (step id plus the process memory address), so
the order of tied annotations (for example a label and its background fill)
shuffled between Blender restarts and flipped their draw order.
Add the stable IFC step id as a final tiebreaker so the order is total and
session independent. Behavior preserving, no z-layer semantics changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
coerce_value assumed value_str was always a CLI string, but ifcmcp
passes JSON-decoded native types (int, None) straight through. Guard
the Union/Optional "none" check so it only calls .lower() on strings,
and handle native None explicitly.
IfcSpace is not a subtype of IfcElement, so quantify.run_quantify()'s
default selector silently skipped all spaces, reporting
elements_quantified: 0 with no error or warning.
Generated with the assistance of an AI coding tool.