Skip to content

Fix StopIteration when computing mass of a compound containing an empty compound (#2078) - #2079

Draft
youdie006 wants to merge 1 commit into
CadQuery:masterfrom
youdie006:fix/2078-nested-empty-compound-mass
Draft

Fix StopIteration when computing mass of a compound containing an empty compound (#2078)#2079
youdie006 wants to merge 1 commit into
CadQuery:masterfrom
youdie006:fix/2078-nested-empty-compound-mass

Conversation

@youdie006

Copy link
Copy Markdown

Fixes #2078.

Repro

from cadquery.occ_impl.shapes import compound

compound().Volume()            # -> 0.0  (fine)
compound(compound()).Volume()  # -> raises StopIteration

A bare empty compound reports a mass of 0.0, but a non-empty compound whose first child is an empty compound raises StopIteration instead.

Root cause

Shape._mass_calc_function picks the mass function from the first non-compound descendant of a compound. It guarded the top-level empty case with if obj: but then descended with a bare next(iter(child)):

while child.ShapeType() == "Compound":
    child = next(iter(child))   # empty child compound -> bare StopIteration

Shape.__iter__ yields nothing for an empty compound, so next() raises. This is self-inconsistent: a bare empty compound already resolves to 0.0, so nesting it inside another compound must not flip that into a crash.

Fix

Use the two-argument next(..., None) sentinel so both the top-level-empty case (unchanged) and the nested-empty case resolve to SOLID -> 0.0. Non-empty compounds still descend to their first non-compound child exactly as before.

The same helper backs Volume(), Area(), and centerOfMass(), so all three are fixed (verified: nested-empty centerOfMass() now returns (0, 0, 0)).

Test

Adds test_nested_empty_compound_mass to tests/test_shapes.py, asserting the bare-empty control plus Volume()/Area() of a nested-empty compound are 0.0. Fails with StopIteration before the fix, passes after.

Thanks to @johnbeard for the clear report and minimal repro.


AI-assisted: this fix was prepared with the help of an AI coding assistant and reviewed by me.

…ty compound

A bare empty compound reports a mass of 0.0, but a non-empty compound whose
first child is an empty compound raised StopIteration instead.

Shape._mass_calc_function picks the mass function from the first non-compound
descendant of a compound. It guarded the top-level empty case with `if obj:` but
then descended with a bare next(iter(child)); Shape.__iter__ yields nothing for
an empty compound, so next() raised. This is self-inconsistent: a bare empty
compound already resolves to 0.0, so nesting it inside another compound must not
flip that into a crash.

Use the two-argument next(..., None) sentinel so both the top-level-empty case
(unchanged) and the nested-empty case resolve to SOLID -> 0.0. Non-empty
compounds still descend to their first non-compound child exactly as before. The
same helper backs Volume(), Area() and centerOfMass(), so all three are fixed.

Fixes CadQuery#2078.
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.71%. Comparing base (d6729f5) to head (e426890).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2079      +/-   ##
==========================================
- Coverage   95.72%   95.71%   -0.01%     
==========================================
  Files          30       30              
  Lines        9533     9531       -2     
  Branches     1421     1420       -1     
==========================================
- Hits         9125     9123       -2     
  Misses        253      253              
  Partials      155      155              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@adam-urbanczyk
adam-urbanczyk marked this pull request as draft August 19, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compound containing empty Compound raises during Volume()

1 participant