From b35f99e63f75510a3c09e567cc14d9f460ee5092 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Sat, 18 Jul 2026 20:35:10 +0500 Subject: [PATCH] ty: detect unresolved references --- nix/cache_dependencies.py | 1 + pyproject.toml | 4 --- src/bcf/bcf/v3/bcfapi.py | 6 ++++ src/bonsai/bonsai/bim/import_ifc.py | 2 ++ .../bonsai/bim/module/boundary/operator.py | 9 +++-- src/bonsai/bonsai/bim/module/cad/operator.py | 2 ++ .../bim/module/classification/operator.py | 2 ++ src/bonsai/bonsai/bim/module/cost/data.py | 2 ++ .../bonsai/bim/module/drawing/decoration.py | 33 +++++++++++++++---- .../bonsai/bim/module/drawing/helper.py | 5 +++ .../bonsai/bim/module/drawing/operator.py | 9 ++++- .../bonsai/bim/module/drawing/scheduler.py | 7 ++++ .../bonsai/bim/module/drawing/shaders.py | 13 +++++--- .../bonsai/bim/module/drawing/svgwriter.py | 2 ++ src/bonsai/bonsai/bim/module/drawing/ui.py | 4 +-- .../bonsai/bim/module/geometry/helper.py | 11 ++++++- .../bonsai/bim/module/geometry/operator.py | 5 +++ .../bonsai/bim/module/light/operator.py | 4 +++ src/bonsai/bonsai/bim/module/model/array.py | 1 + src/bonsai/bonsai/bim/module/model/mep.py | 4 +++ src/bonsai/bonsai/bim/module/model/product.py | 2 +- src/bonsai/bonsai/bim/module/model/profile.py | 6 ++++ src/bonsai/bonsai/bim/module/model/slab.py | 2 ++ src/bonsai/bonsai/bim/module/model/wall.py | 2 ++ .../bonsai/bim/module/project/operator.py | 4 +++ src/bonsai/bonsai/bim/module/pset/operator.py | 2 ++ src/bonsai/bonsai/bim/module/pset/prop.py | 2 ++ src/bonsai/bonsai/bim/module/pset/ui.py | 1 + src/bonsai/bonsai/bim/module/root/operator.py | 9 +++-- .../bonsai/bim/module/search/operator.py | 3 ++ src/bonsai/bonsai/bim/module/sequence/ui.py | 6 ++-- .../bonsai/bim/module/spatial/operator.py | 2 +- src/bonsai/bonsai/bim/module/spatial/ui.py | 2 ++ src/bonsai/bonsai/bim/module/style/ui.py | 1 + src/bonsai/bonsai/bim/module/void/operator.py | 2 ++ src/bonsai/bonsai/bim/ui.py | 4 +++ src/bonsai/bonsai/core/covering.py | 10 ++++-- src/bonsai/bonsai/core/drawing.py | 1 + src/bonsai/bonsai/tool/cad.py | 2 ++ src/bonsai/bonsai/tool/cost.py | 2 ++ src/bonsai/bonsai/tool/drawing.py | 6 ++-- src/bonsai/bonsai/tool/feature.py | 4 +-- src/bonsai/bonsai/tool/geometry.py | 6 ++++ src/bonsai/bonsai/tool/loader.py | 13 ++++++-- src/bonsai/bonsai/tool/misc.py | 2 ++ src/bonsai/bonsai/tool/parametric.py | 2 ++ src/bonsai/bonsai/tool/polyline.py | 2 ++ src/bonsai/bonsai/tool/project.py | 9 +++-- src/bonsai/bonsai/tool/search.py | 4 +++ src/bonsai/bonsai/tool/sequence.py | 7 ++-- src/bonsai/bonsai/tool/snap.py | 5 +++ src/bonsai/bonsai/tool/spatial.py | 9 ++++- src/bonsai/bonsai/tool/style.py | 5 +++ src/bonsai/scripts/bonsai_translations.py | 1 + .../classifications/brick_classifiction.py | 1 + .../scripts/generate_furniture_library.py | 2 ++ .../generate_steel_profiles_library.py | 2 ++ src/ifc2ca/ca2ifc.py | 8 +++++ src/ifc2ca/ifc2ca.py | 21 ++++++++++-- src/ifc5d/ifc5d/ifc2json.py | 2 ++ src/ifc5d/ifc5d/ifc5Dspreadsheet.py | 4 +++ src/ifcfm/ifcfm/__init__.py | 14 +++++--- src/ifcfm/ifcfm/cobie24.py | 2 ++ src/ifcfm/pyproject.toml | 1 + .../api/alignment/_add_segment_to_curve.py | 2 ++ .../_get_segment_start_point_label.py | 2 ++ .../api/alignment/create_as_polyline.py | 2 ++ .../api/alignment/create_from_csv.py | 6 ++++ .../api/classification/add_reference.py | 3 ++ .../ifcopenshell/api/cogo/bearing2dd.py | 2 ++ .../ifcopenshell/api/feature/add_feature.py | 2 ++ .../api/feature/remove_feature.py | 2 ++ .../api/geometry/add_representation.py | 8 +++++ .../api/geometry/disconnect_path.py | 6 ++++ .../api/georeference/edit_true_north.py | 2 ++ .../ifcopenshell/api/georeference/edit_wcs.py | 2 ++ .../add_structural_boundary_condition.py | 2 ++ .../api/style/assign_representation_styles.py | 2 ++ .../ifcopenshell/api/system/assign_system.py | 5 +-- .../ifcopenshell/api/unit/assign_unit.py | 4 +++ src/ifcopenshell-python/ifcopenshell/draw.py | 7 ++++ .../ifcopenshell/entity_instance.py | 2 +- src/ifcopenshell-python/ifcopenshell/file.py | 3 ++ .../ifcopenshell/geom/main.py | 9 ++--- .../ifcopenshell/ifcopenshell_wrapper.pyi | 5 ++- .../ifcopenshell/util/cost.py | 2 ++ .../ifcopenshell/util/element.py | 5 +++ .../ifcopenshell/util/geolocation.py | 2 ++ .../ifcopenshell/util/mvd_info.py | 18 +++++----- .../ifcopenshell/util/placement.py | 3 ++ .../ifcopenshell/util/schema.py | 5 ++- .../ifcopenshell/util/selector.py | 2 ++ .../ifcopenshell/util/shape_builder.py | 2 ++ .../ifcopenshell/validate.py | 20 ++++++----- src/ifcopenshell-python/test/test_file_gc.py | 6 ++-- .../recipes/AssignConstituentFractions.py | 2 ++ src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py | 2 +- src/ifctester/ifctester/facet.py | 11 +++++++ src/ifctester/ifctester/reporter.py | 4 +++ 99 files changed, 401 insertions(+), 84 deletions(-) diff --git a/nix/cache_dependencies.py b/nix/cache_dependencies.py index 7d231779ee..3ee03382ea 100644 --- a/nix/cache_dependencies.py +++ b/nix/cache_dependencies.py @@ -68,6 +68,7 @@ def unpack_dependencies(install_dir: Path) -> None: if __name__ == "__main__": + action = None if len(sys.argv) != 2 or (action := sys.argv[1].lower()) not in ("pack", "unpack"): print(__doc__) sys.exit(1) diff --git a/pyproject.toml b/pyproject.toml index 7d7f6719fc..4a7d22b0f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,8 +82,6 @@ ignore = [ all = "error" # Structural rules (no deep type inference needed, easier to adapt). -# Has false positives due to ty walrus operator bug. -possibly-unresolved-reference = "ignore" # Maybe later, requires to specify element types for all generics. missing-type-argument = "ignore" # Conflicts with `bpy` props defined using annotations. @@ -194,7 +192,6 @@ format.sequence = ["black", "ruff"] cmake-format = "gersemi . --in-place" [tool.poe.tasks.ty-ios] -# --ignore unresolved-reference: walrus operator false positives in ty. cmd = """ ty check nix/ @@ -212,7 +209,6 @@ cmd = """ src/ifcpatch src/ifctester --python=src/ifcopenshell-python/.venv - --ignore unresolved-reference """ [tool.poe.tasks.bonsai-deps] diff --git a/src/bcf/bcf/v3/bcfapi.py b/src/bcf/bcf/v3/bcfapi.py index 368c294cd0..515b2f2fe2 100644 --- a/src/bcf/bcf/v3/bcfapi.py +++ b/src/bcf/bcf/v3/bcfapi.py @@ -188,6 +188,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason @@ -206,6 +208,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason @@ -222,6 +226,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py index 06f4c4afff..4598c50099 100644 --- a/src/bonsai/bonsai/bim/import_ifc.py +++ b/src/bonsai/bonsai/bim/import_ifc.py @@ -1103,12 +1103,14 @@ class IfcImporter: vertices = [[v[i], v[i + 1], v[i + 2], 1] for i in range(0, len(v), 3)] edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)] v2 = None + polyline = None for edge in edges: v1 = vertices[edge[0]] if v1 != v2: polyline = curve.splines.new("POLY") polyline.points[-1].co = mathutils.Vector(v1) v2 = vertices[edge[1]] + assert polyline is not None polyline.points.add(1) polyline.points[-1].co = mathutils.Vector(v2) edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(geometry).tolist() diff --git a/src/bonsai/bonsai/bim/module/boundary/operator.py b/src/bonsai/bonsai/bim/module/boundary/operator.py index 6d6404a1cb..9a89b2ae20 100644 --- a/src/bonsai/bonsai/bim/module/boundary/operator.py +++ b/src/bonsai/bonsai/bim/module/boundary/operator.py @@ -1059,6 +1059,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator): return tool.Ifc.get().createIfcConnectionSurfaceGeometry(surface) def export_surface(self, polygon, target_face_matrix): + ifc_file = tool.Ifc.get() x_axis = target_face_matrix.col[0][:3] z_axis = target_face_matrix.col[2][:3] p1 = target_face_matrix.translation @@ -1071,18 +1072,20 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator): placement = builder.create_axis2_placement_3d([o / self.unit_scale for o in p1], z_axis, x_axis) surface.BasisSurface = tool.Ifc.get().create_entity("IfcPlane", placement) - if tool.Ifc.get().schema != "IFC2X3": + schema = ifc_file.schema + if schema != "IFC2X3": points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords] point_list = tool.Ifc.get().createIfcCartesianPointList2D(points) outer_boundary = tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False) - inner_boundaries = [] + inner_boundaries: list[ifcopenshell.entity_instance] = [] for interior in polygon.interiors: points = [tool.Model.convert_si_to_unit(list(co)) for co in interior.coords] point_list = tool.Ifc.get().createIfcCartesianPointList2D(points) inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False)) else: - pass # TODO + # TODO: + raise NotImplementedError(schema) surface.OuterBoundary = outer_boundary surface.InnerBoundaries = inner_boundaries diff --git a/src/bonsai/bonsai/bim/module/cad/operator.py b/src/bonsai/bonsai/bim/module/cad/operator.py index 5d74857822..d30665ae4f 100644 --- a/src/bonsai/bonsai/bim/module/cad/operator.py +++ b/src/bonsai/bonsai/bim/module/cad/operator.py @@ -400,6 +400,7 @@ class CadOffset(bpy.types.Operator): [verts.update(e.verts) for e in edges] # Use the viewport angle to determine the offset direction + wp = None for area in bpy.context.screen.areas: if area.type == "VIEW_3D": # Don't ask me, I don't know. @@ -409,6 +410,7 @@ class CadOffset(bpy.types.Operator): z = area.spaces.active.region_3d.view_rotation @ Vector((0, 0, 1)) wp = Matrix([x, y, z, Vector((0, 0, 0))]).to_4x4().transposed() break + assert wp is not None rotation = Matrix.Rotation(pi / 2, 2, "Z") rotation_i = Matrix.Rotation(-pi / 2, 2, "Z") diff --git a/src/bonsai/bonsai/bim/module/classification/operator.py b/src/bonsai/bonsai/bim/module/classification/operator.py index 77b77ba541..7c07f4ab5a 100644 --- a/src/bonsai/bonsai/bim/module/classification/operator.py +++ b/src/bonsai/bonsai/bim/module/classification/operator.py @@ -478,6 +478,7 @@ class ChangeClassificationLevel(bpy.types.Operator): def execute(self, context): props = tool.Classification.get_classification_props() props.available_library_references.clear() + reference = None for reference in IfcStore.classification_file.by_id(self.parent_id).HasReferences: new = props.available_library_references.add() new.identification = reference.Identification or "" @@ -485,6 +486,7 @@ class ChangeClassificationLevel(bpy.types.Operator): new.ifc_definition_id = reference.id() new.has_references = bool(reference.HasReferences) new.referenced_source + assert reference if reference.ReferencedSource.is_a("IfcClassificationReference"): props.active_library_referenced_source = reference.ReferencedSource.ReferencedSource.id() else: diff --git a/src/bonsai/bonsai/bim/module/cost/data.py b/src/bonsai/bonsai/bim/module/cost/data.py index 32299b8e41..6b4e1f5814 100644 --- a/src/bonsai/bonsai/bim/module/cost/data.py +++ b/src/bonsai/bonsai/bim/module/cost/data.py @@ -156,6 +156,8 @@ class CostSchedulesData: values = root_element.CostValues elif root_element.is_a("IfcConstructionResource"): values = root_element.BaseCosts + else: + assert False, root_element for cost_value in values or []: cls._load_cost_value(root_element, data, cost_value) # data["CostValues"].append(cost_value.id()) diff --git a/src/bonsai/bonsai/bim/module/drawing/decoration.py b/src/bonsai/bonsai/bim/module/drawing/decoration.py index b9b4dd40cb..57cafab07b 100644 --- a/src/bonsai/bonsai/bim/module/drawing/decoration.py +++ b/src/bonsai/bonsai/bim/module/drawing/decoration.py @@ -425,10 +425,12 @@ class BaseDecorator: blf.size(font_id, font_size_px) + w, h = None, None if box_alignment or center or vcenter: w, h = blf.dimensions(font_id, text) if box_alignment: + assert w is not None and h is not None box_alignment_offset = Vector((0, 0)) if "bottom" in box_alignment: pass @@ -450,10 +452,12 @@ class BaseDecorator: else: # horizontal centering if center: + assert w is not None pos -= Vector((cos, sin)) * w * 0.5 # vertical centering if vcenter: + assert h is not None pos -= Vector((-sin, cos)) * h * 0.5 # side-shifting @@ -1001,6 +1005,8 @@ class FallDecorator(BaseDecorator): O = A.copy() O.z = B.z run = (B - O).length + + angle_tg = None if run != 0: angle_tg = rise / run angle = round(degrees(atan(angle_tg))) @@ -1018,6 +1024,7 @@ class FallDecorator(BaseDecorator): elif object_type == "SLOPE_PERCENT": if angle == 90: return "-" + assert angle_tg is not None return f"{round(angle_tg * 100)} %" return "NO DATA" @@ -1249,6 +1256,7 @@ class SectionLevelDecorator(BaseDecorator): } # process edges + text_position, text_dir = None, None for edge in edges_original: v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]] start_i = len(output_verts) @@ -1554,32 +1562,39 @@ class SectionDecorator(BaseDecorator): v0, v1 = winspace_verts[edge[0]], winspace_verts[edge[1]] start_i = len(output_verts) + circle_head = None if display_start_circle or display_end_circle: circle_head = get_circle_head(circle_size) - if display_start_symbol or display_end_symbol or connect_markers: + triangle_head, divider_offset, edge_dir_circle = None, None, None + display_symbol = display_start_symbol or display_end_symbol + if display_symbol or connect_markers: edge_dir = (v1 - v0).normalized() side = (edge_dir.yx * Vector((1, -1))).to_3d() edge_dir_circle = edge_dir * circle_size - if display_start_symbol or display_end_symbol: - triangle_head = get_triangle_head(edge_dir, -side, triangle_length, triangle_width) - divider_offset = [] - divider_offset.append(edge_dir_circle if connect_markers else edge_dir_circle * 3) - divider_offset.append(edge_dir_circle) + if display_symbol: + triangle_head = get_triangle_head(edge_dir, -side, triangle_length, triangle_width) + divider_offset = [] + divider_offset.append(edge_dir_circle if connect_markers else edge_dir_circle * 3) + divider_offset.append(edge_dir_circle) if display_start_circle: + assert circle_head is not None start_i = add_verts_sequence([v + v0 for v in circle_head], start_i, **out_kwargs, closed=True) # circle middle divider if not display_start_symbol: + assert divider_offset is not None start_i = add_verts_sequence( [v0 + divider_offset[0], v0 - divider_offset[1]], start_i, **out_kwargs ) if display_start_symbol: + assert triangle_head is not None start_i = add_verts_sequence([v + v0 for v in triangle_head], start_i, **out_kwargs, closed=True) if display_end_circle: + assert circle_head is not None start_i = add_verts_sequence([v + v1 for v in circle_head], start_i, **out_kwargs, closed=True) # circle middle divider if not display_end_symbol: @@ -1588,9 +1603,11 @@ class SectionDecorator(BaseDecorator): ) if display_end_symbol: + assert triangle_head is not None start_i = add_verts_sequence([v + v1 for v in triangle_head], start_i, **out_kwargs, closed=True) if connect_markers: + assert edge_dir_circle is not None gap = [] gap.append(edge_dir_circle if display_start_symbol else Vector((0, 0, 0))) gap.append(edge_dir_circle if display_end_symbol else Vector((0, 0, 0))) @@ -1871,6 +1888,8 @@ class CutDecorator: layer_set = material offset = 0 sense_factor = 1 + else: + assert False, material if len(layer_set.MaterialLayers) == 1: material = layer_set.MaterialLayers[0].Material @@ -1897,6 +1916,8 @@ class CutDecorator: co = Vector((0.0, 0.0, offset)) no = tool.Drawing.get_extrusion_vector(element).normalized() no = Vector([1.0, 0.0, 0.0]) + else: + assert False, usage no *= sense_factor last_i = len(layer_set.MaterialLayers) - 1 diff --git a/src/bonsai/bonsai/bim/module/drawing/helper.py b/src/bonsai/bonsai/bim/module/drawing/helper.py index 4c707b81a8..73b1f9257d 100644 --- a/src/bonsai/bonsai/bim/module/drawing/helper.py +++ b/src/bonsai/bonsai/bim/module/drawing/helper.py @@ -225,9 +225,11 @@ def format_distance( unit_system, unit_length, unit_fraction = unit_mapping[custom_unit] value *= unit_scale + tx_dist = None # Imperial Formatting if unit_system == "IMPERIAL": + toInches = None if in_unit_length: if unit_length == "INCHES": toInches = 1 @@ -241,6 +243,7 @@ def format_distance( toInches = 1550 inPerFoot = 144 + assert toInches is not None decInches = value * toInches decFeet = decInches / 12 @@ -383,6 +386,7 @@ def format_distance( if precision and isinstance(precision, float): value = precision * round(float(value) / precision) + fmt = None if decimal_places is not None: fmt = "%1." + str(decimal_places) + "f" @@ -465,6 +469,7 @@ def format_distance( assert f"Unexpected unit_system - '{unit_system}'." # tx_dist = fmt % value + assert tx_dist is not None return tx_dist diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index b1c4c69c3b..4a08a3209b 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -698,6 +698,8 @@ class CreateDrawing(bpy.types.Operator): layer_set = material offset = 0 sense_factor = 1 + else: + assert False, material camera_matrix_i = context.scene.camera.matrix_world.inverted() @@ -722,7 +724,6 @@ class CreateDrawing(bpy.types.Operator): bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.000001) bmesh.ops.triangle_fill(bm, use_dissolve=True, edges=bm.edges) - prev_co = None if not usage: sense_factor = 1 # Assume the extrusion vector points in the direction sense no = tool.Drawing.get_extrusion_vector(element).normalized() @@ -739,6 +740,8 @@ class CreateDrawing(bpy.types.Operator): co = Vector((0.0, 0.0, offset)) no = tool.Drawing.get_extrusion_vector(element).normalized() no = Vector([1.0, 0.0, 0.0]) + else: + assert False, usage no *= sense_factor last_i = len(layer_set.MaterialLayers) - 1 for i, layer in enumerate(layer_set.MaterialLayers): @@ -906,6 +909,10 @@ class CreateDrawing(bpy.types.Operator): if os.path.isfile(svg_path) and self.props.should_use_linework_cache: return svg_path + ifc = tool.Ifc.get() + semantics = None + pairs = None + # in case of printing multiple drawings we need to sync just once if self.sync and self.drawing_index == 0: with profile("sync"): diff --git a/src/bonsai/bonsai/bim/module/drawing/scheduler.py b/src/bonsai/bonsai/bim/module/drawing/scheduler.py index a1610020c4..a13d718e73 100644 --- a/src/bonsai/bonsai/bim/module/drawing/scheduler.py +++ b/src/bonsai/bonsai/bim/module/drawing/scheduler.py @@ -110,12 +110,14 @@ class Scheduler: y = self.margin rows = list(sheet.iter_rows()) total_rows = len(rows) + x = None for i, row in enumerate(rows): # The last row may contain only null values if i == (total_rows - 1) and not [c for c in row if c.value is not None]: continue x = self.margin + unmerged_height = None for cell in row: if isinstance(cell, openpyxl.cell.cell.MergedCell): column_letter = openpyxl.utils.get_column_letter(cell.column) @@ -230,8 +232,11 @@ class Scheduler: ) x += unmerged_width + + assert unmerged_height is not None y += unmerged_height + assert x is not None total_width = x + self.margin total_height = y + self.margin self.svg["width"] = "{}mm".format(total_width) @@ -375,6 +380,7 @@ class Scheduler: tri = 0 stop_iterating_over_rows = False # TODO: row spans support? + x = None for tr in table.getElementsByType(TableRow): if stop_iterating_over_rows: break @@ -491,6 +497,7 @@ class Scheduler: tri += 1 y += height + assert x is not None total_width = x + self.margin total_height = y + self.margin self.svg["width"] = "{}mm".format(total_width) diff --git a/src/bonsai/bonsai/bim/module/drawing/shaders.py b/src/bonsai/bonsai/bim/module/drawing/shaders.py index c050cf0f41..817491b282 100644 --- a/src/bonsai/bonsai/bim/module/drawing/shaders.py +++ b/src/bonsai/bonsai/bim/module/drawing/shaders.py @@ -102,16 +102,16 @@ void angle_circle_head( in vec4 circle_start, in float circle_angle, in bool counterclockwise, out vec4 head[CIRCLE_SEGS+1], out float angle_segs) { - + // 1 added to CIRCLE_SEGS because we're number of vertices // for n segments is n+1 - + float angle_d; angle_d = PI * 2 / CIRCLE_SEGS; // 30d // need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve angle_segs = max(1, ceil(circle_angle / angle_d)); angle_d = circle_angle / angle_segs; - + for(int i = 0; i < (angle_segs + 1); i++) { float angle = angle_d * i; if (counterclockwise) { @@ -143,7 +143,7 @@ void cross_head(in vec4 dir, in float size, out vec4 head[3]) { #define do_vertex(pos, e) (do_vertex_util(pos, vec2(-(e).y, (e).x) / winsize.xy)) #define do_vertex_win(pos, e) ( do_vertex( WIN2CLIP( pos ), e ) ) -// if vertex is shared by two segments of the line still need to emit it twice +// if vertex is shared by two segments of the line still need to emit it twice // to avoid smoothing artifacts // don't forget to initialize `vec2 EDGE_DIR` for macro to work // `pos0` / `pos1` - vertex position in clip space @@ -197,10 +197,13 @@ void do_circle_head(vec4 pos_w, vec4 head[CIRCLE_SEGS]) { def add_verts_sequence(verts, start_i, output_verts, output_edges, closed=False): """Add sequence of verts to output lists, returns next vertex index""" + i = None for i, v in enumerate(verts[:-1], start_i): output_verts.append(v) output_edges.append((i, i + 1)) output_verts.append(verts[-1]) + assert i is not None + if closed: output_edges.append((i + 1, start_i)) return i + 2 @@ -273,7 +276,7 @@ class BaseShader: FRAG_GLSL = """ uniform vec4 color; uniform float lineWidth; - + in float smoothline; out vec4 fragColor; void main() { diff --git a/src/bonsai/bonsai/bim/module/drawing/svgwriter.py b/src/bonsai/bonsai/bim/module/drawing/svgwriter.py index 8093a5e72a..e6cb797738 100644 --- a/src/bonsai/bonsai/bim/module/drawing/svgwriter.py +++ b/src/bonsai/bonsai/bim/module/drawing/svgwriter.py @@ -1449,6 +1449,7 @@ class SvgWriter: angle_tg = rise / run angle = round(degrees(atan(angle_tg))) else: + angle_tg = None angle = 90 # ues SLOPE_ANGLE as default @@ -1462,6 +1463,7 @@ class SvgWriter: elif object_type == "SLOPE_PERCENT": if angle == 90: return "-" + assert angle_tg is not None return f"{round(angle_tg * 100)} %" tag = element.Description or get_label_text() diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index e0df93a4a6..c65b651d36 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -964,14 +964,14 @@ class BIM_UL_sheets(bpy.types.UIList): if self.filter_name: filter_name = self.filter_name.lower() - active_sheet = None + active_sheet_index = None for sheet in data.sheets: if sheet.is_sheet: - active_sheet = sheet active_sheet_index = len(flt_flags) if filter_name in sheet.name.lower() or filter_name in sheet.identification.lower(): flt_flags.append(self.bitflag_filter_item) if not sheet.is_sheet: + assert active_sheet_index is not None flt_flags[active_sheet_index] = self.bitflag_filter_item else: flt_flags.append(0) diff --git a/src/bonsai/bonsai/bim/module/geometry/helper.py b/src/bonsai/bonsai/bim/module/geometry/helper.py index 3b14003435..defbd014b1 100644 --- a/src/bonsai/bonsai/bim/module/geometry/helper.py +++ b/src/bonsai/bonsai/bim/module/geometry/helper.py @@ -75,9 +75,13 @@ class Helper: for face in bm.faces: if len(face.verts) > 4: potential_faces.append(face) + + # TODO: replace with next(..., None) + face = None for face in potential_faces: if face.normal.z < -0.1: break + assert face is not None profile = [l.vert.index for l in face.loops] extrusion = self.detect_extrusion_edge(bm, face) @@ -108,10 +112,12 @@ class Helper: if not potential_faces: potential_faces = bm.faces + # TODO: replace with next(..., None) + face = None for face in potential_faces: if face.normal.z < -0.1: break - + assert face is not None profile = [l.vert.index for l in face.loops] extrusion = self.detect_extrusion_edge(bm, face) @@ -145,9 +151,12 @@ class Helper: if total_verts > 4: potential_faces.append(face) + # TODO: replace with next(..., None) + face = None for face in potential_faces: if face.normal.z < -0.1: break + assert face is not None end_faces = [] end_face_normal = face.normal diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 32428280c2..e8d2db55af 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -3527,12 +3527,15 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator): if props.representation_item_shape_aspect == "NEW": active_representation = tool.Geometry.get_active_representation(obj) # find IfcProductRepresentationSelect based on current representation + product_shape = None if hasattr(element, "Representation"): # IfcProduct product_shape = element.Representation else: # IfcTypeProduct for representation_map in element.RepresentationMaps: if representation_map.MappedRepresentation == active_representation: product_shape = representation_map + assert product_shape is not None + previous_shape_aspect_id = props.active_item.shape_aspect_id # will be None if item didn't had a shape aspect previous_shape_aspect = tool.Ifc.get_entity_by_id(previous_shape_aspect_id) @@ -3882,6 +3885,8 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator): curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale) elif self.shape == "CYLINDER": curve = builder.circle(radius=0.25 / unit_scale) + else: + assert False, self.shape item = builder.extrude( curve, magnitude=0.5 / unit_scale, diff --git a/src/bonsai/bonsai/bim/module/light/operator.py b/src/bonsai/bonsai/bim/module/light/operator.py index c3f377e9b9..a8920b83fe 100644 --- a/src/bonsai/bonsai/bim/module/light/operator.py +++ b/src/bonsai/bonsai/bim/module/light/operator.py @@ -156,6 +156,7 @@ class RadianceRender(bpy.types.Operator): print(f"Quality: {quality}, Detail: {detail}, Variability: {variability}") print(f"Output directory: {output_dir}") + hdr_image_path, hdr_mask_path, sky_map_cal_path = None, None if use_hdr: hdr_image = "noon_grass_2k.hdr" hdr_mask = "noon_grass_2k_mask.hdr" @@ -254,6 +255,9 @@ class RadianceRender(bpy.types.Operator): # 4 0 0 -1 180 if use_hdr and choose_hdr_image == "Noon": + assert hdr_image_path is not None + assert hdr_mask_path is not None + assert sky_map_cal_path is not None with open(sky_file_path, "w") as f: f.write(sky_description_str) diff --git a/src/bonsai/bonsai/bim/module/model/array.py b/src/bonsai/bonsai/bim/module/model/array.py index 5972189405..848d9aa98d 100644 --- a/src/bonsai/bonsai/bim/module/model/array.py +++ b/src/bonsai/bonsai/bim/module/model/array.py @@ -564,6 +564,7 @@ class SelectAllArrayObjects(bpy.types.Operator): except RuntimeError: self.report({"ERROR"}, f"Objects that don't have an array parent, were deselected.") object.select_set(False) + continue array_objects = tool.Array.get_all_objects(parent_element) tool.Blender.set_objects_selection( diff --git a/src/bonsai/bonsai/bim/module/model/mep.py b/src/bonsai/bonsai/bim/module/model/mep.py index 2a906d4ec4..7e12573497 100644 --- a/src/bonsai/bonsai/bim/module/model/mep.py +++ b/src/bonsai/bonsai/bim/module/model/mep.py @@ -408,6 +408,8 @@ class MEPGenerator: compare = tool.Cad.is_x(requested_value, fitting_value, compare_precision) elif isinstance(fitting_value, list): compare = tool.Cad.are_vectors_equal(requested_value, Vector(fitting_value), precision) + else: + assert False, f"{key} {second_key}" return compare ignore_keys = [] @@ -476,11 +478,13 @@ class MEPGenerator: if predefined_type == "OBSTRUCTION": return packed_data + start_port = None for port in ports: port_local_position = V(*port.ObjectPlacement.RelativePlacement.Location.Coordinates) if tool.Cad.is_x(port_local_position.length, 0.0): start_port = port break + assert start_port is not None connected_port = tool.System.get_connected_port(start_port) connected_element = tool.System.get_port_relating_element(connected_port) diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 75f5441827..b10d0f3387 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -325,7 +325,7 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator): if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]: props.relating_type_id = str(self.relating_type_id) - building_obj = None + building_obj, building_element = None, None if len(context.selected_objects) == 1 and context.active_object: building_obj = context.active_object building_element = tool.Ifc.get_entity(building_obj) diff --git a/src/bonsai/bonsai/bim/module/model/profile.py b/src/bonsai/bonsai/bim/module/model/profile.py index efbf41900d..97edc1cd20 100644 --- a/src/bonsai/bonsai/bim/module/model/profile.py +++ b/src/bonsai/bonsai/bim/module/model/profile.py @@ -593,6 +593,8 @@ class DumbProfileJoiner: axisl = (profile2.matrix_world.inverted() @ axis1[1]) - (profile2.matrix_world.inverted() @ axis1[0]) elif connection1 == "ATSTART": axisl = (profile2.matrix_world.inverted() @ axis1[0]) - (profile2.matrix_world.inverted() @ axis1[1]) + else: + assert False, connection1 xy_angle = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d())) if xy_angle >= -135 and xy_angle <= -45: closest_plane = "bottom" @@ -617,6 +619,8 @@ class DumbProfileJoiner: axisl = (profile1.matrix_world.inverted() @ axis2[1]) - (profile1.matrix_world.inverted() @ axis2[0]) elif connection2 == "ATSTART": axisl = (profile1.matrix_world.inverted() @ axis2[0]) - (profile1.matrix_world.inverted() @ axis2[1]) + else: + assert False, connection2 xy_angle2 = degrees(Vector((1, 0)).angle_signed(axisl.normalized().to_2d())) if xy_angle2 >= -135 and xy_angle2 <= -45: closest_plane2 = "bottom" @@ -844,6 +848,8 @@ class DumbProfileJoiner: else: y_axis = obj.matrix_world.to_quaternion() @ Vector((0, 1, 0)) z_axis = obj.matrix_world.to_quaternion() @ Vector((-1, 0, 0)) + else: + assert False, plane return self.create_matrix(p, x_axis, y_axis, z_axis) def create_matrix(self, p: Vector, x: Vector, y: Vector, z: Vector) -> Matrix: diff --git a/src/bonsai/bonsai/bim/module/model/slab.py b/src/bonsai/bonsai/bim/module/model/slab.py index 4f47740e6d..892fbb89cc 100644 --- a/src/bonsai/bonsai/bim/module/model/slab.py +++ b/src/bonsai/bonsai/bim/module/model/slab.py @@ -508,6 +508,7 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): converter.run() profile = tool.Ifc.get().createIfcArbitraryClosedProfileDef("AREA") + curve = None for path in converter.paths: points = [] lines = path[0] @@ -517,6 +518,7 @@ class EditSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): points.append(tool.Ifc.get().createIfcCartesianPoint(local_point)) points.append(points[0]) curve = tool.Ifc.get().createIfcPolyline(points) + assert curve profile.OuterCurve = curve old_profile = extrusion.SweptArea diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index eaf4c356be..5b73f5641b 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -1577,6 +1577,7 @@ class DumbWallJoiner: # Get the ATEND connection from wall1 to use it in wall2 relating_element = None connections = element1.ConnectedTo + relating_connection, description = ..., ... for conn in connections: if conn.is_a("IfcRelConnectsPathElements") and conn.RelatingConnectionType == "ATEND": relating_element = conn.RelatedElement @@ -1591,6 +1592,7 @@ class DumbWallJoiner: description = conn.Description bonsai.core.geometry.remove_connection(tool.Geometry, connection=conn) if relating_element: + assert relating_connection is not ... and description is not ... ifcopenshell.api.geometry.connect_path( tool.Ifc.get(), relating_element=relating_element, diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 032fa3e40c..02ae75792b 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -714,6 +714,8 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator): representations = element.RepresentationMaps or [] elif element.is_a("IfcProduct"): representations = [element.Representation] if element.Representation else [] + else: + assert False, element for representation in representations or []: for element in self.file.traverse(representation): if not element.is_a("IfcRepresentationItem") or not element.StyledByItem: @@ -2029,6 +2031,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper): project_props = tool.Project.get_project_props() prefs = tool.Blender.get_addon_preferences() project_props.use_relative_project_path = self.use_relative_path + old_history_size, old_undo_steps = None, None if prefs.should_disable_undo_on_save: old_history_size = tool.Ifc.get().history_size old_undo_steps = context.preferences.edit.undo_steps @@ -2036,6 +2039,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper): context.preferences.edit.undo_steps = 0 IfcStore.execute_ifc_operator(self, context) if prefs.should_disable_undo_on_save: + assert old_history_size is not None and old_undo_steps is not None tool.Ifc.get().history_size = old_history_size context.preferences.edit.undo_steps = old_undo_steps return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/pset/operator.py b/src/bonsai/bonsai/bim/module/pset/operator.py index ea1d39fb96..f820038807 100644 --- a/src/bonsai/bonsai/bim/module/pset/operator.py +++ b/src/bonsai/bonsai/bim/module/pset/operator.py @@ -113,6 +113,8 @@ class EditPset(bpy.types.Operator, tool.Ifc.Operator): elif props.active_pset_type == "QTO": pset = ifcopenshell.api.pset.add_qto(self.file, product=element, name=props.active_pset_name) props.active_pset_id = pset.id() + else: + assert False if self.properties: properties = json.loads(self.properties) diff --git a/src/bonsai/bonsai/bim/module/pset/prop.py b/src/bonsai/bonsai/bim/module/pset/prop.py index 786e6a62a2..6d1d59be1d 100644 --- a/src/bonsai/bonsai/bim/module/pset/prop.py +++ b/src/bonsai/bonsai/bim/module/pset/prop.py @@ -228,6 +228,8 @@ def get_qto_name(self: "PsetProperties", context: bpy.types.Context) -> tool.Ble if "bpy.data.objects" in pset_type: if prop_type == "PsetProperties": results = get_object_qto_name(self, context) + else: + assert False elif prop_type == "TaskPsetProperties": results = get_task_qto_names(self, context) elif prop_type == "ResourcePsetProperties": diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index addd03a504..df99acd73c 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -480,6 +480,7 @@ class BIM_PT_material_psets(Panel): def draw(self, context): assert self.layout props = tool.Material.get_material_props() + ifc_definition_id = None if material := props.active_material: ifc_definition_id = material.ifc_definition_id diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index 993d3bfb4b..b1c13a8368 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -630,10 +630,13 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator): local_z = wall_matrix.to_3x3() @ Vector((0, 0, 1)) direction_sense = getattr(usage, "DirectionSense", "POSITIVE") - if usage.LayerSetDirection == "AXIS2": + layer_set_direction = usage.LayerSetDirection + if layer_set_direction == "AXIS2": z_axis = tuple(local_y) if direction_sense == "POSITIVE" else tuple(-local_y) - elif usage.LayerSetDirection == "AXIS3": + elif layer_set_direction == "AXIS3": z_axis = tuple(local_z) if direction_sense == "POSITIVE" else tuple(-local_z) + else: + assert False, layer_set_direction item = builder.extrude( profile, @@ -763,6 +766,8 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator): WebThickness=default_web_thickness / unit_scale, FlangeThickness=default_flange_thickness / unit_scale, ) + else: + assert False, representation_template rel = ifcopenshell.api.material.assign_material( tool.Ifc.get(), products=[element], type="IfcMaterialProfileSet" diff --git a/src/bonsai/bonsai/bim/module/search/operator.py b/src/bonsai/bonsai/bim/module/search/operator.py index f55dcf31b7..b17019f5f2 100644 --- a/src/bonsai/bonsai/bim/module/search/operator.py +++ b/src/bonsai/bonsai/bim/module/search/operator.py @@ -1009,6 +1009,7 @@ class ColourByProperty(Operator): palette = props.palette is_qualitative = palette in ("tab10", "paired") + colours = None if is_qualitative: colours = tool.Search.get_qualitative_palette(palette) @@ -1035,6 +1036,7 @@ class ColourByProperty(Operator): if value in colourscheme: colourscheme[value]["total"] += 1 else: + assert colours is not None colourscheme[value] = {"colour": next(colours)[0:3], "total": 1} obj.color = (*colourscheme[value]["colour"], 1) else: @@ -1139,6 +1141,7 @@ class SelectByProperty(Operator): is_qualitative = palette in ("tab10", "paired") + values = None if not is_qualitative: values = [] for colour in props.colourscheme: diff --git a/src/bonsai/bonsai/bim/module/sequence/ui.py b/src/bonsai/bonsai/bim/module/sequence/ui.py index 56847fa750..e393c425c2 100644 --- a/src/bonsai/bonsai/bim/module/sequence/ui.py +++ b/src/bonsai/bonsai/bim/module/sequence/ui.py @@ -281,11 +281,11 @@ class BIM_PT_work_schedules(Panel): def draw_task_operators(self) -> None: row = self.layout.row(align=True) row.alignment = "RIGHT" - ifc_definition_id = None + task, ifc_definition_id = None, None if self.tprops.tasks and self.props.active_task_index < len(self.tprops.tasks): task = self.tprops.tasks[self.props.active_task_index] ifc_definition_id = task.ifc_definition_id - if ifc_definition_id: + if task and ifc_definition_id: if self.props.active_task_id: if self.props.editing_task_type == "TASKTIME": row.operator("bim.edit_task_time", text="", icon="CHECKMARK") @@ -341,6 +341,8 @@ class BIM_PT_work_schedules(Panel): row.prop(self.props, "other_columns", text="") column_type, name = self.props.other_columns.split(".") data_type = "string" + else: + assert False, column_type row.operator("bim.set_task_sort_column", text="", icon="SORTALPHA").column = f"{column_type}.{name}" row.prop( self.props, "is_sort_reversed", text="", icon="SORT_DESC" if self.props.is_sort_reversed else "SORT_ASC" diff --git a/src/bonsai/bonsai/bim/module/spatial/operator.py b/src/bonsai/bonsai/bim/module/spatial/operator.py index 7279b3b6c4..b97665c6c3 100644 --- a/src/bonsai/bonsai/bim/module/spatial/operator.py +++ b/src/bonsai/bonsai/bim/module/spatial/operator.py @@ -516,7 +516,7 @@ class SetContainerVisibility(bpy.types.Operator): if self.mode == "ISOLATE": if tool.Ifc.get_schema() == "IFC2X3": containers = tool.Ifc.get().by_type("IfcSpatialStructureElement") - elif tool.Ifc.get_schema() != "IFC2X3": + else: containers = set(tool.Ifc.get().by_type("IfcSpatialElement")) containers -= set(tool.Ifc.get().by_type("IfcSpatialZone")) for container in containers: diff --git a/src/bonsai/bonsai/bim/module/spatial/ui.py b/src/bonsai/bonsai/bim/module/spatial/ui.py index 3a4a771e01..00fe3110a8 100644 --- a/src/bonsai/bonsai/bim/module/spatial/ui.py +++ b/src/bonsai/bonsai/bim/module/spatial/ui.py @@ -125,6 +125,7 @@ class BIM_PT_spatial_decomposition(Panel): row.label(text="Warning: No Default Container", icon="ERROR") row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="") + ifc_definition_id = None if self.props.active_container: ifc_definition_id = self.props.active_container.ifc_definition_id row = self.layout.row(align=True) @@ -170,6 +171,7 @@ class BIM_PT_spatial_decomposition(Panel): if not self.props.active_container: return + assert ifc_definition_id is not None container_has_elements = bool(self.props.total_elements) if container_has_elements: diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index 4e3a1daccb..45120ae1ed 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -102,6 +102,7 @@ class BIM_PT_styles(Panel): # style ui tools if active_style: + style = active_style row = self.layout.row(align=True) if material := style.blender_material: msprops = tool.Style.get_material_style_props(material) diff --git a/src/bonsai/bonsai/bim/module/void/operator.py b/src/bonsai/bonsai/bim/module/void/operator.py index 433ea3a06b..bf24b5c1a9 100644 --- a/src/bonsai/bonsai/bim/module/void/operator.py +++ b/src/bonsai/bonsai/bim/module/void/operator.py @@ -72,6 +72,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): opening_objects = [obj for obj in selected_objects if obj != target_object] + obj1 = ... for opening_obj in opening_objects: element1 = tool.Ifc.get_entity(target_object) obj1 = target_object @@ -196,6 +197,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): bpy.data.objects.remove(obj2) tool.Model.purge_scene_openings() + assert obj1 is not ... context.view_layer.objects.active = obj1 return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 42092ff558..5e79cba3aa 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -284,11 +284,13 @@ class GizmoPreferences(bpy.types.PropertyGroup): draw_gizmos_in_3d_viewport: bool +_gizmo_pref_entry = None for _gizmo_pref_entry in tool.Parametric.EDIT_TYPES: GizmoPreferences.__annotations__[_gizmo_pref_entry.name] = BoolProperty( name=_gizmo_pref_entry.name.replace("_", " ").title(), default=True, ) +assert _gizmo_pref_entry is not None del _gizmo_pref_entry @@ -394,12 +396,14 @@ class DefaultParameters(bpy.types.PropertyGroup): and gives the create operator a preset to copy from.""" +_default_params_entry = None for _default_params_entry in tool.Parametric.EDIT_TYPES: if not _default_params_entry.has_default_parameters: continue DefaultParameters.__annotations__[_default_params_entry.name] = bpy.props.PointerProperty( type=getattr(_model_prop, _default_params_entry.props_attr), ) +assert _default_params_entry is not None del _default_params_entry diff --git a/src/bonsai/bonsai/core/covering.py b/src/bonsai/bonsai/core/covering.py index 2ab0d39cc4..cd43313658 100644 --- a/src/bonsai/bonsai/core/covering.py +++ b/src/bonsai/bonsai/core/covering.py @@ -74,10 +74,11 @@ def add_instance_ceiling_covering_from_cursor( if not relating_type.is_a("IfcCoveringType"): relating_type = None + ceiling_height = None if selected_objects and active_obj: - x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj) + x, y, z, _, _ = spatial.get_x_y_z_h_mat_from_obj(active_obj) else: - x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor() + x, y, z, _, _ = spatial.get_x_y_z_h_mat_from_cursor() ceiling_height = covering.get_z_from_ceiling_height() space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y) @@ -87,6 +88,7 @@ def add_instance_ceiling_covering_from_cursor( obj = spatial.create_object("Covering") spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj) + assert ceiling_height is not None spatial.translate_obj_to_z_location(obj, z + ceiling_height) spatial.assign_type_to_obj(obj) spatial.set_covering_representation_from_polygon(obj, space_polygon, polygon_is_si=True) @@ -100,7 +102,9 @@ def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spa selected_objects = spatial.get_selected_objects() if selected_objects and active_obj: - x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj) + x, y, _, _, _ = spatial.get_x_y_z_h_mat_from_obj(active_obj) + else: + assert False, "Object has to be active and selected." space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y) diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index 5192aeaa37..c6a7231273 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -497,6 +497,7 @@ def add_annotation( drawing_tool.show_decorations() obj = drawing_tool.create_annotation_object(drawing, object_type) element = ifc.get_entity(obj) + relating_type_rep = None if not element: # Brand new annotation relating_type_rep = drawing_tool.get_annotation_representation(relating_type) if relating_type else None element = drawing_tool.run_root_assign_class( diff --git a/src/bonsai/bonsai/tool/cad.py b/src/bonsai/bonsai/tool/cad.py index 2a1f5f9c69..ad23f55a5d 100644 --- a/src/bonsai/bonsai/tool/cad.py +++ b/src/bonsai/bonsai/tool/cad.py @@ -981,6 +981,7 @@ class Cad: has_found_connected_edge = True loops.append(loop) + new_verts = None for loop in loops: all_verts = {v.index for e in loop for v in e.verts} possible_v1s = [] @@ -1084,6 +1085,7 @@ class Cad: break v1 = v2 + assert new_verts is not None return new_verts diff --git a/src/bonsai/bonsai/tool/cost.py b/src/bonsai/bonsai/tool/cost.py index fc07a629a6..a0ee797e0b 100644 --- a/src/bonsai/bonsai/tool/cost.py +++ b/src/bonsai/bonsai/tool/cost.py @@ -280,6 +280,8 @@ class Cost(bonsai.core.tool.Cost): new = props.cost_item_processes.add() elif related_object.is_a("IfcResource"): new = props.cost_item_resources.add() + else: + assert False, related_object new.ifc_definition_id = related_object.id() new.name = related_object.Name or "Unnamed" diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 7493b02d33..c309444473 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -2575,16 +2575,15 @@ class Drawing(bonsai.core.tool.Drawing): if not obj: continue current_representation = tool.Geometry.get_active_representation(obj) + current_representation_subcontext = None if current_representation: subcontext = current_representation.ContextOfItems current_representation_subcontext = tool.Geometry.get_subcontext_parameters(subcontext) - has_context = False for subcontext in subcontexts: # prioritize already active representation if it matches the subcontext # (element could have multiple representations in the same subcontext) - if current_representation and subcontext == current_representation_subcontext: - has_context = True + if current_representation_subcontext and subcontext == current_representation_subcontext: break priority_representation = ifcopenshell.util.representation.get_representation(element, *subcontext) if priority_representation: @@ -2594,7 +2593,6 @@ class Drawing(bonsai.core.tool.Drawing): obj=obj, representation=priority_representation, ) - has_context = True break linked_handles: set[bpy.types.Object] = set() diff --git a/src/bonsai/bonsai/tool/feature.py b/src/bonsai/bonsai/tool/feature.py index 3a06cbf625..7bc9a875fd 100644 --- a/src/bonsai/bonsai/tool/feature.py +++ b/src/bonsai/bonsai/tool/feature.py @@ -23,7 +23,6 @@ from typing import TYPE_CHECKING import bpy import ifcopenshell.api.feature -import ifcopenshell.util.representation import bonsai.core.geometry import bonsai.core.tool @@ -50,6 +49,7 @@ class Feature(bonsai.core.tool.Feature): has_visible_openings = True break + element_had_openings = None for feature_obj in feature_objs: feature_element = tool.Ifc.get_entity(feature_obj) @@ -58,7 +58,6 @@ class Feature(bonsai.core.tool.Feature): bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=featured_obj) element_had_openings = tool.Geometry.has_openings(featured_element) - body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body") ifcopenshell.api.feature.add_feature(tool.Ifc.get(), feature=feature_element, element=featured_element) if tool.Ifc.is_moved(feature_obj): @@ -73,6 +72,7 @@ class Feature(bonsai.core.tool.Feature): if voided_obj.data: if tool.Ifc.is_edited(voided_obj): voided_element_ = tool.Ifc.get_entity(voided_obj) + assert element_had_openings is not None if element_had_openings or (voided_element_ != featured_element and voided_element_.HasOpenings): voided_obj.scale = (1.0, 1.0, 1.0) tool.Ifc.finish_edit(voided_obj) diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 08abd38b22..2475f6c9e9 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -757,6 +757,7 @@ class Geometry(bonsai.core.tool.Geometry): # its centroid not obscured (tested via raycasting) by any other # face. distance = max(obj.dimensions.xyz) + min_y, max_z = None, None if axis == "+Z": max_z = max([co[2] for co in obj.bound_box]) + 0.002 direction = Vector((0, 0, -1)) @@ -771,8 +772,10 @@ class Geometry(bonsai.core.tool.Geometry): if direction.dot(face.normal) > 0: continue if axis == "+Z": + assert max_z is not None face_centroid_at_max = Vector((*face.calc_center_median().xy, max_z)) elif axis == "-Y": + assert min_y is not None centroid = face.calc_center_median() face_centroid_at_max = Vector((centroid.x, min_y, centroid.z)) face_centroid_at_max = obj.matrix_world @ face_centroid_at_max @@ -1885,6 +1888,7 @@ class Geometry(bonsai.core.tool.Geometry): """NOTE: we assume that all items belonged to the same representation and to the same shape aspect""" ifc_file = tool.Ifc.get() previous_shape_aspect = None + base_representation = None for inverse in ifc_file.get_inverse(representation_items[0]): if inverse.is_a("IfcShapeRepresentation"): if inverse.OfShapeAspect: @@ -1894,6 +1898,7 @@ class Geometry(bonsai.core.tool.Geometry): previous_shape_aspect = inverse.OfShapeAspect[0] else: base_representation = inverse + assert base_representation # remove item from previous shape aspect if previous_shape_aspect: @@ -2211,6 +2216,7 @@ class Geometry(bonsai.core.tool.Geometry): assert item obj.data.clear_geometry() + cartesian_point_offset = None if item.is_a("IfcHalfSpaceSolid"): bm = bmesh.new() bmesh.ops.create_grid(bm, size=0.5) diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 383be821e2..d661f3205b 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -1087,18 +1087,21 @@ class Loader(bonsai.core.tool.Loader): bm = bmesh.new() bm.from_mesh(mesh) prev_co = None - if usage.LayerSetDirection == "AXIS2": + layer_set_direction = usage.LayerSetDirection + if layer_set_direction == "AXIS2": co = Vector((0.0, offset, 0.0)) no = cls.get_extrusion_vector(element).normalized() no = no.cross(Vector([1.0, 0.0, 0.0])) - elif usage.LayerSetDirection == "AXIS3": + elif layer_set_direction == "AXIS3": co = Vector((0.0, 0.0, offset)) no = cls.get_extrusion_vector(element).normalized() no = Vector([0.0, 0.0, 1.0]) - elif usage.LayerSetDirection == "AXIS1": + elif layer_set_direction == "AXIS1": co = Vector((0.0, 0.0, offset)) no = cls.get_extrusion_vector(element).normalized() no = Vector([1.0, 0.0, 0.0]) + else: + assert False, layer_set_direction no *= sense_factor # Cache this body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") @@ -1108,6 +1111,7 @@ class Loader(bonsai.core.tool.Loader): if style := tool.Ifc.get_entity(material): styles[style] = i last_i = len(layer_set.MaterialLayers) - 1 + bisect_geom = None for i, layer in enumerate(layer_set.MaterialLayers): if i != last_i: prev_co = co.copy() @@ -1121,6 +1125,7 @@ class Loader(bonsai.core.tool.Loader): if (material_index := styles.get(style, None)) is None: material_index = len(mesh.materials) mesh.materials.append(tool.Ifc.get_object(style)) + assert bisect_geom is not None if i == last_i: for face in bisect_geom["geom"]: if isinstance(face, bmesh.types.BMFace): @@ -1286,6 +1291,7 @@ class Loader(bonsai.core.tool.Loader): polyline.material_index = material_index return polyline + item = None for item_data, item_style in zip(rep_items, item_styles): item = item_data["item"] @@ -1313,6 +1319,7 @@ class Loader(bonsai.core.tool.Loader): polyline.points.add(1) polyline.points[-1].co = native_data["matrix"] @ Vector(v2) + assert item is not None curve.bevel_depth = unit_scale * item.Radius thickness = None if (inner_radius := item.InnerRadius) and (thickness := max(item.Radius - inner_radius, 0)): diff --git a/src/bonsai/bonsai/tool/misc.py b/src/bonsai/bonsai/tool/misc.py index 893b37ce31..8e42133941 100644 --- a/src/bonsai/bonsai/tool/misc.py +++ b/src/bonsai/bonsai/tool/misc.py @@ -220,10 +220,12 @@ class Misc(bonsai.core.tool.Misc): related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element))) related_objects = sorted(related_objects, key=lambda e: e[1]) storey_elevation = None + i = None for i, related_object in enumerate(related_objects): if related_object[0] == storey: storey_elevation = related_object[1] break + assert i is not None if i + total_storeys < len(related_objects): next_storey_elevation = related_objects[i + total_storeys][1] unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) diff --git a/src/bonsai/bonsai/tool/parametric.py b/src/bonsai/bonsai/tool/parametric.py index 623405fe16..adf019874a 100644 --- a/src/bonsai/bonsai/tool/parametric.py +++ b/src/bonsai/bonsai/tool/parametric.py @@ -641,6 +641,8 @@ del _edit_type_names # call sites can reference ``tool.Parametric.ROOF`` directly. Renaming a # registry entry renames the constant; a typo at the call site surfaces as # AttributeError at module load. +_entry = None for _entry in Parametric.EDIT_TYPES: setattr(Parametric, _entry.name.upper(), _entry) +assert _entry is not None del _entry diff --git a/src/bonsai/bonsai/tool/polyline.py b/src/bonsai/bonsai/tool/polyline.py index 542bc23005..883d811448 100644 --- a/src/bonsai/bonsai/tool/polyline.py +++ b/src/bonsai/bonsai/tool/polyline.py @@ -168,6 +168,7 @@ class Polyline(bonsai.core.tool.Polyline): distance = (mouse_vector - last_point).length if distance < 0: return + angle, orientation_angle, angle_round_threshold = None, None if distance > 0: angle = tool.Cad.angle_3_vectors( second_to_last_point, last_point, mouse_vector, new_angle=None, degrees=True @@ -188,6 +189,7 @@ class Polyline(bonsai.core.tool.Polyline): angle = 0 orientation_angle = 0 if input_ui: + assert angle is not None and orientation_angle is not None and angle_round_threshold is not None if should_round: angle_snap = tool.Snap.get_angle_snap_value(context) angle = angle_snap * round(angle / angle_snap) if distance < angle_round_threshold else angle diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index 7199c125cf..1d7a2dc2e0 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -370,18 +370,21 @@ class Project(bonsai.core.tool.Project): props = cls.get_project_props() active_library_breadcrumb = props.get_active_library_breadcrumb() change_back = False + breadcrumb = None if active_library_breadcrumb: name = active_library_breadcrumb.name breadcrumb_type = active_library_breadcrumb.breadcrumb_type library_id = active_library_breadcrumb.library_id + breadcrumb = (name, breadcrumb_type, library_id) change_back = True bpy.ops.bim.rewind_library() if change_back: + assert breadcrumb bpy.ops.bim.change_library_element( - element_name=name, - breadcrumb_type=breadcrumb_type, - library_id=library_id, + element_name=breadcrumb[0], + breadcrumb_type=breadcrumb[1], + library_id=breadcrumb[2], ) @classmethod diff --git a/src/bonsai/bonsai/tool/search.py b/src/bonsai/bonsai/tool/search.py index 356addee3a..85c1ecac70 100644 --- a/src/bonsai/bonsai/tool/search.py +++ b/src/bonsai/bonsai/tool/search.py @@ -505,6 +505,8 @@ class Search(bonsai.core.tool.Search): (0.773, 0.922, 0.816), (0.871, 0.957, 0.894), ] + else: + assert False, theme if value < min_val: value = min_val @@ -574,8 +576,10 @@ class ImportFilterQueryTransformer(lark.Transformer): new = self.filter_groups.add() global_ids = [] is_first_group = len(self.filter_groups) == 1 + new2 = None for filter_index, arg in enumerate(args): if arg["type"] == "instance" and global_ids: + assert new2 if "bpy.data.texts" in new2.value: data_name = new2.value.split("bpy.data.texts")[1][2:-2] bpy.data.texts[data_name].write("," + arg["value"]) diff --git a/src/bonsai/bonsai/tool/sequence.py b/src/bonsai/bonsai/tool/sequence.py index 2afed8741a..cc6c08b8da 100644 --- a/src/bonsai/bonsai/tool/sequence.py +++ b/src/bonsai/bonsai/tool/sequence.py @@ -23,7 +23,7 @@ import re from collections.abc import Iterable from datetime import datetime from datetime import time as datetime_time -from typing import TYPE_CHECKING, Any, Literal, Optional, Union +from typing import TYPE_CHECKING, Any, Literal, Optional, Union, assert_never import bpy import ifcopenshell @@ -1127,7 +1127,8 @@ class Sequence(bonsai.core.tool.Sequence): @classmethod def load_default_animation_color_scheme(cls): - groups = { + GroupType = Literal["CREATION", "OPERATION", "MOVEMENT_TO", "DESTRUCTION", "MOVEMENT_FROM", "USERDEFINED"] + groups: dict[GroupType, dict[str, Any]] = { "CREATION": { "PredefinedType": ["CONSTRUCTION", "INSTALLATION"], "Color": (0.0, 1.0, 0.0), @@ -1167,6 +1168,8 @@ class Sequence(bonsai.core.tool.Sequence): predefined_type_item2 = props.task_output_colors.add() predefined_type_item2.name = predefined_type predefined_type_item2.color = data["Color"] + else: + assert_never(group) # TO DO: consider cases where users confuses inputs and outputs predefined_type_item.name = predefined_type predefined_type_item.color = data["Color"] diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index 5755c02ba7..a9723c7436 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -225,6 +225,7 @@ class Snap(bonsai.core.tool.Snap): # Get axis that are closer than the stick factor threshold elegible_axis = [] + axis = None for axis in snap_axis: if not axis: continue @@ -326,6 +327,7 @@ class Snap(bonsai.core.tool.Snap): detected_snaps: list[dict[str, Any]] = [] def select_plane_method(): + plane_origin, plane_normal = None, None if not last_polyline_point: plane_origin = Vector((0, 0, 0)) plane_normal = Vector((0, 0, 1)) @@ -357,6 +359,7 @@ class Snap(bonsai.core.tool.Snap): plane_origin = Vector((last_polyline_point.x, last_polyline_point.y, last_polyline_point.z)) plane_normal = Vector((1, 0, 0)) + assert plane_origin and plane_normal plane_normal = tool.Polyline.use_transform_orientations(plane_normal) return plane_origin, plane_normal @@ -583,6 +586,7 @@ class Snap(bonsai.core.tool.Snap): snaps_by_group = filter_snapping_points_by_group(detected_snaps) edges = [] # Get edges to create edge-intersection snap + axis_start, axis_end = ..., ... for snapping_point in snaps_by_group: if snapping_point["group"] in {"Polyline", "Measure", "Wireframe", "Object"}: if snapping_point["type"] == "Edge": @@ -607,6 +611,7 @@ class Snap(bonsai.core.tool.Snap): if point["type"] == "Axis": if ordered_snaps[0]["type"] not in {"Axis", "Plane"}: obj = ordered_snaps[0]["object"] + assert axis_start is not ... and axis_end is not ... mixed_snap = cls.mix_snap_and_axis(ordered_snaps[0], axis_start, axis_end) for mixed_point in mixed_snap: snap_point = { diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index df87d493c7..875023d34b 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -304,12 +304,14 @@ class Spatial(bonsai.core.tool.Spatial): while True: has_parent = None + new_current_results = None for key in current_results: if flat_key.startswith(key): has_parent = True new_current_results = current_results[key]["children"] break if has_parent: + assert new_current_results is not None current_results = new_current_results else: break @@ -978,19 +980,24 @@ class Spatial(bonsai.core.tool.Spatial): interiors_list = [] if union_geom.geom_type == "MultiPolygon": + poly = None for poly in union_geom.geoms: interiors_list = cls.get_poly_valid_interior_list( poly=poly, min_area=min_area, interiors_list=interiors_list ) + assert poly new_poly = Polygon(poly.exterior.coords, holes=interiors_list) - if union_geom.geom_type == "Polygon": + elif union_geom.geom_type == "Polygon": interiors_list = cls.get_poly_valid_interior_list( poly=union_geom, min_area=min_area, interiors_list=interiors_list ) new_poly = Polygon(union_geom.exterior.coords, holes=interiors_list) + else: + assert False, union_geom.geom_type + return new_poly @classmethod diff --git a/src/bonsai/bonsai/tool/style.py b/src/bonsai/bonsai/tool/style.py index 83f1751e96..aa3e508ef0 100644 --- a/src/bonsai/bonsai/tool/style.py +++ b/src/bonsai/bonsai/tool/style.py @@ -360,6 +360,10 @@ class Style(bonsai.core.tool.Style): material_output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL", {"is_active_output": True}) surface_output = get_input_node(material_output, "Surface") + # TODO: this variable is not really needed, + # just workaround a for ty issue detecting unresolved refs. + bsdf = None + if surface_output and surface_output.type == "MIX_SHADER": mix_shader = surface_output if ( @@ -388,6 +392,7 @@ class Style(bonsai.core.tool.Style): and (bsdf := get_input_node(surface_output, input_index=1, of_type="BSDF_PRINCIPLED")) ) ): + assert bsdf report(f"Because of {BLUE}BSDF_PRINCIPLED{R} node reflectance method identified as {BLUE}PHYSICAL{R}") attributes["ReflectanceMethod"] = "NOTDEFINED" if tool.Ifc.get_schema() != "IFC4X3" else "PHYSICAL" diff --git a/src/bonsai/scripts/bonsai_translations.py b/src/bonsai/scripts/bonsai_translations.py index 7b6ae960db..bb7c6ae569 100644 --- a/src/bonsai/scripts/bonsai_translations.py +++ b/src/bonsai/scripts/bonsai_translations.py @@ -216,6 +216,7 @@ def update_translations_from_po(po_directory: Path, translations_module: Path): if BPY_IS_LOADED: + import bpy class SetupTranslationUI(bpy.types.Operator): bl_idname = "bim.setup_translation_ui" diff --git a/src/bonsai/scripts/classifications/brick_classifiction.py b/src/bonsai/scripts/classifications/brick_classifiction.py index 17246940c7..feaaf8b36d 100644 --- a/src/bonsai/scripts/classifications/brick_classifiction.py +++ b/src/bonsai/scripts/classifications/brick_classifiction.py @@ -82,6 +82,7 @@ class Generator: } """.replace("{entity}", location.split("#")[-1])) # filter parents for the brick entity + parent = None for row in query: parent = row.get("parent").toPython() if "brickschema.org" in parent and parent in references.keys(): diff --git a/src/bonsai/scripts/generate_furniture_library.py b/src/bonsai/scripts/generate_furniture_library.py index 7b9d411772..710372fc20 100644 --- a/src/bonsai/scripts/generate_furniture_library.py +++ b/src/bonsai/scripts/generate_furniture_library.py @@ -1036,6 +1036,7 @@ class LibraryGenerator: seat_width_offset = 0.7 * width / 2 if cistern_depth else width / 2 seat_start_width_offset = 0.6 * width + cistern_3d = None if cistern_height: cistern = builder.rectangle(size=V(width, cistern_depth), position=shift_to_center) cistern_3d = ifcopenshell.util.element.copy_deep(self.file, cistern) @@ -1118,6 +1119,7 @@ class LibraryGenerator: # cistern if cistern_height: + assert cistern_3d cistern_3d = builder.extrude( cistern_3d, cistern_height + seat_level / 2, position=V(0, 0, seat_level / 2) ) diff --git a/src/bonsai/scripts/generate_steel_profiles_library.py b/src/bonsai/scripts/generate_steel_profiles_library.py index ca45648122..7db5135686 100644 --- a/src/bonsai/scripts/generate_steel_profiles_library.py +++ b/src/bonsai/scripts/generate_steel_profiles_library.py @@ -143,6 +143,7 @@ class LibraryGenerator: if "unused" in ifc_params: del ifc_params["unused"] + profiles_gap = ... if prof_type == "profile_hollow*_square": ifc_params["YDim"] = ifc_params["XDim"] elif ifc_profile_name == "IfcCircleHollowProfileDef": @@ -160,6 +161,7 @@ class LibraryGenerator: profile = self.file.create_entity(ifc_profile_name, ProfileName=prof_name, ProfileType="AREA", **ifc_params) if prof_type == "profile_l*lbeam_2l": + assert profiles_gap is not ... profile.ProfileName = None # to avoid name confusion mode = "SLBB" if prof_name.endswith("_SLBB") else "LLBB" profile = self.create_double_l_profile(profile, prof_name, profiles_gap, mode) diff --git a/src/ifc2ca/ca2ifc.py b/src/ifc2ca/ca2ifc.py index f2be1bf0cc..7b706cf334 100644 --- a/src/ifc2ca/ca2ifc.py +++ b/src/ifc2ca/ca2ifc.py @@ -27,10 +27,12 @@ flatten = itertools.chain.from_iterable def get_element_data(model, name, element): if element["geometry_type"] == "Edge": + cell_tags, cell_block = None, None for i, cell_block in enumerate(model.cells): if cell_block.type == "line": cell_tags = model.cell_data["cell_tags"][i] break + assert cell_tags is not None and cell_block is not None rows = [] for i_row, i in enumerate(cell_tags): if i == 0: @@ -59,6 +61,7 @@ def get_element_data(model, name, element): elif element["geometry_type"] == "Face": triangle_cell_tags = None quad_cell_tags = None + points, cell_block = None, None for i, cell_block in enumerate(model.cells): if cell_block.type == "triangle": triangle_cell_tags = model.cell_data["cell_tags"][i] @@ -78,8 +81,10 @@ def get_element_data(model, name, element): if not len(rows): points = [] else: + assert cell_block is not None points = list(flatten([cell_block.data[c] for c in rows])) + cell_block = None for i, cell_block in enumerate(model.cells): if cell_block.type == "quad": quad_cell_tags = model.cell_data["cell_tags"][i] @@ -97,6 +102,7 @@ def get_element_data(model, name, element): rows.append(i_row) break if len(rows): + assert cell_block is not None and points is not None points.extend(list(flatten([cell_block.data[c] for c in rows]))) points = list(set(points)) @@ -172,6 +178,8 @@ def results_to_ifc(ifc_file, ifc_model, rmed_path, global_case, field_types, dat model_cases = data["load_cases"] elif global_case == "COMB": model_cases = data["load_combinations"] + else: + assert False, global_case for field in field_types: if field == "InternalForces": _parsed_data = internal_forces_to_ifc(ifc_file, ifc_model, result, model_cases, data["elements"]) diff --git a/src/ifc2ca/ifc2ca.py b/src/ifc2ca/ifc2ca.py index 728df67d45..e3a1cde993 100644 --- a/src/ifc2ca/ifc2ca.py +++ b/src/ifc2ca/ifc2ca.py @@ -283,7 +283,7 @@ class Ifc2CA: geometry = [x.EdgeStart.VertexGeometry.Coordinates for x in repr_item.Bounds[0].Bound.EdgeList] else: - print(representation) + assert False, representation return geometry def parse_material(self, material: ios.entity_instance): @@ -399,6 +399,9 @@ class Ifc2CA: elif element.is_a("IfcStructuralSurfaceMember"): placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position) + else: + assert False, element + origin, orientation = self.parse_transformation_matrix(placement) data["origin"] = origin data["orientation"] = orientation @@ -436,7 +439,7 @@ class Ifc2CA: for i, v in enumerate(placement[:3]): v[3] = data["geometry"][i] - if connection.is_a("IfcStructuralCurveConnection"): + elif connection.is_a("IfcStructuralCurveConnection"): placement = ifcopenshell.util.placement.a2p( data["geometry"][0], connection.Axis.DirectionRatios, @@ -446,6 +449,9 @@ class Ifc2CA: elif connection.is_a("IfcStructuralSurfaceConnection"): placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position) + else: + assert False, connection + origin, orientation = self.parse_transformation_matrix(placement) data["origin"] = origin data["orientation"] = orientation @@ -552,6 +558,9 @@ class Ifc2CA: }, } + else: + assert False, element["geometry_type"] + for action in actions: self.add_action_loads(element, action, data, load_cases) @@ -586,6 +595,7 @@ class Ifc2CA: data["actions"].append(action.get_info() | {"AppliedLoad": action.AppliedLoad.get_info()}) if element["geometry_type"] in ["Vertex", "Edge"]: + force_projection_coeff, moment_projection_coeff = None, None if action.is_a("IfcStructuralPointAction") and load.is_a("IfcStructuralLoadSingleForce"): FX = tempFX = load.ForceX if load.ForceX is not None else 0.0 FY = tempFY = load.ForceY if load.ForceY is not None else 0.0 @@ -639,8 +649,12 @@ class Ifc2CA: force_projection_coeff = 1.0 moment_projection_coeff = 1.0 + else: + assert False, action + for iLC, load_case in enumerate(load_cases): if load_case.id() in active_load_case_ids: + assert force_projection_coeff is not None and moment_projection_coeff is not None load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient data["loadGroups"].append(load_group.Name) data["loadsLC"]["FX"][iLC] += FX * load_group_coeff * load_case_coeff * force_projection_coeff @@ -672,6 +686,9 @@ class Ifc2CA: else: force_projection_coeff = 1.0 + else: + assert False, action + for iLC, load_case in enumerate(load_cases): if load_case.id() in active_load_case_ids: load_case_coeff = 1.0 if load_case.Coefficient is None else load_case.Coefficient diff --git a/src/ifc5d/ifc5d/ifc2json.py b/src/ifc5d/ifc5d/ifc2json.py index 3b184ca1c7..d4450c7541 100644 --- a/src/ifc5d/ifc5d/ifc2json.py +++ b/src/ifc5d/ifc5d/ifc2json.py @@ -109,6 +109,8 @@ class ifc5D2json: values = root_element.CostValues elif root_element.is_a("IfcConstructionResource"): values = root_element.BaseCosts + else: + assert False, root_element for cost_value in values or []: self.extract_cost_value(root_element, data, cost_value) # data["CostValues"].append(cost_value.id()) diff --git a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py index bd38a8f53b..a015f4206b 100644 --- a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py +++ b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py @@ -478,6 +478,8 @@ class Ifc5DOdsWriter(Ifc5Dwriter): cell.addElement(P(text=value)) elif type == "formula": cell = TableCell(formula=value, stylename=style) + else: + assert False, type row.addElement(cell) def add_cost_item_rows(table, cost_data): @@ -715,6 +717,8 @@ if __name__ == "__main__": writer = Ifc5DOdsWriter(args["input"], args["output"]) elif args["format"] == "XLSX": writer = Ifc5DXlsxWriter(args["input"], args["output"]) + else: + assert False, args writer.write() logger.info("Finished conversion in %ss", time.time() - start) diff --git a/src/ifcfm/ifcfm/__init__.py b/src/ifcfm/ifcfm/__init__.py index ced960305c..9525de8f15 100644 --- a/src/ifcfm/ifcfm/__init__.py +++ b/src/ifcfm/ifcfm/__init__.py @@ -25,9 +25,10 @@ import re from collections import defaultdict from collections.abc import Callable from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal, Union +from typing import TYPE_CHECKING, Any, Literal, Union, cast import ifcopenshell.util.selector +from typing_extensions import assert_never try: from openpyxl import Workbook @@ -100,8 +101,9 @@ class Parser: def parse(self, ifc_file: ifcopenshell.file, name=None): for category_name, category_config in self.config["categories"].items(): for element in category_config["get_category_elements"](ifc_file): - get_element_data: Union[GetElementDataCallBack, dict[str, Any]] - get_element_data = category_config["get_element_data"] + get_element_data = cast( + Union[GetElementDataCallBack, dict[str, Any]], category_config["get_element_data"] + ) if isinstance(get_element_data, dict): data = {} @@ -109,14 +111,18 @@ class Parser: data[key] = ifcopenshell.util.selector.get_element_value(element, query) elif isinstance(get_element_data, Callable): data = get_element_data(ifc_file, element) or {} + else: + assert_never(get_element_data) - get_custom_element_data = self.get_custom_element_data.get(category_name, lambda x, y: None) + get_custom_element_data = self.get_custom_element_data.get(category_name, lambda *_: None) if isinstance(get_custom_element_data, dict): custom_data = {} for key, query in get_custom_element_data.items(): custom_data[key] = ifcopenshell.util.selector.get_element_value(element, query) elif isinstance(get_custom_element_data, Callable): custom_data = get_custom_element_data(ifc_file, element) or {} + else: + assert_never(get_custom_element_data) data.update(custom_data) diff --git a/src/ifcfm/ifcfm/cobie24.py b/src/ifcfm/ifcfm/cobie24.py index 40533bb3bb..ac36fb48de 100644 --- a/src/ifcfm/ifcfm/cobie24.py +++ b/src/ifcfm/ifcfm/cobie24.py @@ -271,6 +271,8 @@ def get_contact_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_i pao = the_actor person = the_actor.ThePerson organization = the_actor.TheOrganization + else: + assert False, the_actor email = get_email_from_pao(person, organization) diff --git a/src/ifcfm/pyproject.toml b/src/ifcfm/pyproject.toml index 741bbb91cc..c44f869129 100644 --- a/src/ifcfm/pyproject.toml +++ b/src/ifcfm/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ "openpyxl", "odfpy", "pandas", + "typing-extensions", ] [project.urls] diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/_add_segment_to_curve.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/_add_segment_to_curve.py index 74129e6fce..659a3f314b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/_add_segment_to_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/_add_segment_to_curve.py @@ -159,8 +159,10 @@ def _add_segment_to_curve( else: assert False + end_point = ... for mapped_segment in mapped_segments: if mapped_segment: end_point = _add_curve_segment_to_composite_curve(file, layout_segment, mapped_segment, curve) + assert end_point is not ... return end_point diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/_get_segment_start_point_label.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/_get_segment_start_point_label.py index 6e5453489b..db162d7d33 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/_get_segment_start_point_label.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/_get_segment_start_point_label.py @@ -308,5 +308,7 @@ def _get_segment_start_point_label(prev_segment: entity_instance, segment: entit label = _cant_callback(prev_segment, segment) else: label = _cant_label(prev_segment, segment) + else: + assert False, s.DesignParameters return label diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/create_as_polyline.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/create_as_polyline.py index b786b80396..1348402d03 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/create_as_polyline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/create_as_polyline.py @@ -46,6 +46,7 @@ def _create_layout(file: ifcopenshell.file, alignment: entity_instance, points: ifcopenshell.api.nest.assign_object(file, related_objects=alignment_layouts, relating_object=alignment) start_dist_along = 0.0 + gradient = None for p1, p2 in zip(points, points[1:]): x1, y1, z1 = p1.Coordinates x2, y2, z2 = p2.Coordinates @@ -100,6 +101,7 @@ def _create_layout(file: ifcopenshell.file, alignment: entity_instance, points: ifcopenshell.api.nest.assign_object(file, related_objects=[hsegment], relating_object=alignment_layouts[0]) if include_vertical: + assert gradient is not None vsegment = file.createIfcAlignmentSegment( ifcopenshell.guid.new(), DesignParameters=file.createIfcAlignmentVerticalSegment( diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/create_from_csv.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/create_from_csv.py index 3bd8ed275d..f092b273ba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/create_from_csv.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/create_from_csv.py @@ -57,6 +57,7 @@ def create_from_csv(file: ifcopenshell.file, filepath: str) -> entity_instance: :param filepath: path the to CSV file :return: IfcAlignment """ + alignment = None with open(filepath, newline="") as csvfile: reader = csv.reader(csvfile) row_count = 0 @@ -89,9 +90,14 @@ def create_from_csv(file: ifcopenshell.file, filepath: str) -> entity_instance: ) else: # add all subsequent vertical alignments + assert alignment is not None vertical_layout = ifcopenshell.api.alignment.add_vertical_layout(file, alignment) ifcopenshell.api.alignment.layout_vertical_alignment_by_pi_method( file, vertical_layout, coordinates, radii ) + if row_count == 0: + raise ValueError(f"CSV file '{filepath}' is empty; expected at least one row for the horizontal alignment.") + + assert alignment is not None return alignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index 10177821b4..decc5b4580 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -182,6 +182,8 @@ class Usecase: if not reference: migrator = ifcopenshell.util.schema.Migrator() + old_referenced_source = ... + existing_classification = None if self.settings["is_lightweight"]: old_referenced_source = self.settings["reference"].ReferencedSource self.settings["reference"].ReferencedSource = None @@ -194,6 +196,7 @@ class Usecase: reference = migrator.migrate(self.settings["reference"], self.file) if self.settings["is_lightweight"]: + assert old_referenced_source is not ... reference.ReferencedSource = self.settings["classification"] self.settings["reference"].ReferencedSource = old_referenced_source elif existing_classification: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cogo/bearing2dd.py b/src/ifcopenshell-python/ifcopenshell/api/cogo/bearing2dd.py index 895215f066..0e8451fbf5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cogo/bearing2dd.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cogo/bearing2dd.py @@ -88,6 +88,8 @@ def bearing2dd(bearing: str) -> float: elif cY == "S" and cX == "W": angle = 270.0 sign = -1.0 + else: + assert False, (cY, cX) try: dms = ifcopenshell.util.geolocation.dms2dd(d, m, s, ms) diff --git a/src/ifcopenshell-python/ifcopenshell/api/feature/add_feature.py b/src/ifcopenshell-python/ifcopenshell/api/feature/add_feature.py index cfdff3819c..3c5e4b4e98 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/feature/add_feature.py +++ b/src/ifcopenshell-python/ifcopenshell/api/feature/add_feature.py @@ -116,6 +116,8 @@ def add_feature( return ifcopenshell.api.aggregate.assign_object(file, [feature], element) rels = feature.AdheresToElement ifc_class = "IfcRelAdheresToElement" + else: + assert False, feature if rels: if rels[0][4] == element: diff --git a/src/ifcopenshell-python/ifcopenshell/api/feature/remove_feature.py b/src/ifcopenshell-python/ifcopenshell/api/feature/remove_feature.py index 100e886214..998c1d70c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/feature/remove_feature.py +++ b/src/ifcopenshell-python/ifcopenshell/api/feature/remove_feature.py @@ -54,6 +54,8 @@ def remove_feature(file: ifcopenshell.file, feature: ifcopenshell.entity_instanc rels = [] else: rels = feature.ProjectsElements + else: + assert False, feature for rel in rels: history = rel.OwnerHistory file.remove(rel) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 16dc7a8345..5dc9fcc6dc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -436,6 +436,7 @@ class Usecase: def create_curve_bounded_planes(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]: items = [] + points = None if self.file.schema != "IFC2X3": points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=False) for polygon in self.settings["geometry"].polygons: @@ -443,6 +444,7 @@ class Usecase: if self.file.schema == "IFC2X3": curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=False) else: + assert points is not None curve = self.create_curve_from_polygon(points, polygon, is_2d=False) items.append(self.file.createIfcCurveBoundedPlane(BasisSurface=plane, OuterBoundary=curve)) return items @@ -457,12 +459,14 @@ class Usecase: def create_annotation_fill_areas(self, is_2d: bool = False) -> list[ifcopenshell.entity_instance]: items = [] + points = None if self.file.schema != "IFC2X3": points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=is_2d) for polygon in self.settings["geometry"].polygons: if self.file.schema == "IFC2X3": curve = self.create_curve_from_polygon_ifc2x3(polygon, is_2d=is_2d) else: + assert points is not None curve = self.create_curve_from_polygon(points, polygon, is_2d=is_2d) items.append(self.file.createIfcAnnotationFillArea(OuterBoundary=curve)) return items @@ -813,17 +817,20 @@ class Usecase: def create_triangulated_face_set(self) -> ifcopenshell.entity_instance: ifc_raw_items = [None] * self.settings["total_items"] + ifc_raw_uv_items = None if self.settings["should_generate_uvs"]: ifc_raw_uv_items = [None] * self.settings["total_items"] for i, value in enumerate(ifc_raw_items): ifc_raw_items[i] = [] if self.settings["should_generate_uvs"]: + assert ifc_raw_uv_items is not None ifc_raw_uv_items[i] = [] for polygon in self.settings["geometry"].polygons: ifc_raw_items[polygon.material_index % self.settings["total_items"]].append( [v + 1 for v in polygon.vertices] ) if self.settings["should_generate_uvs"]: + assert ifc_raw_uv_items is not None ifc_raw_uv_items[polygon.material_index % self.settings["total_items"]].append( [uv + 1 for uv in polygon.loop_indices] ) @@ -831,6 +838,7 @@ class Usecase: coordinates = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices) if self.settings["should_generate_uvs"]: + assert ifc_raw_uv_items is not None # Blender supports multiple UV layers. We don't. Too bad. tex_coords = self.file.createIfcTextureVertexList( [tuple(x.uv) for x in self.settings["geometry"].uv_layers[0].data] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py index 625bfd329c..bd87b737d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py @@ -50,6 +50,12 @@ def disconnect_path( for r in relating_element.ConnectedTo if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == related_element ] + else: + raise ValueError( + "Either provide `element` and `connection_type`, or provide `relating_element` and `related_element`. " + f"Got: element={element}, connection_type={connection_type}, " + f"relating_element={relating_element}, related_element={related_element}." + ) for connection in set(connections): history = connection.OwnerHistory diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_true_north.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_true_north.py index 58ab003e98..c0cb3149ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_true_north.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_true_north.py @@ -52,6 +52,7 @@ def edit_true_north(file: ifcopenshell.file, true_north: Optional[Union[tuple[fl # This unsets true north ifcopenshell.api.georeference.edit_true_north(model, true_north=None) """ + x, y = None, None if isinstance(true_north, (float, int)): x, y = ifcopenshell.util.geolocation.angle2yaxis(true_north) elif true_north is not None: @@ -73,4 +74,5 @@ def edit_true_north(file: ifcopenshell.file, true_north: Optional[Union[tuple[fl context.TrueNorth = file.create_entity("IfcDirection") else: context.TrueNorth = file.create_entity("IfcDirection") + assert x is not None and y is not None context.TrueNorth.DirectionRatios = (x, y) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py index 126072e254..38c61b2a6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py @@ -90,6 +90,8 @@ def edit_wcs( point, file.createIfcDirection((xaxis_x, xaxis_y)), ) + else: + assert False, context context.WorldCoordinateSystem = placement if file.get_total_inverses(old_wcs) == 0: ifcopenshell.util.element.remove_deep2(file, old_wcs) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py index c78bba2b68..7f7f70785f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py @@ -61,6 +61,8 @@ def add_structural_boundary_condition( boundary_class = "IfcBoundaryEdgeCondition" elif related_connection.is_a("IfcStructuralSurfaceConnection"): boundary_class = "IfcBoundaryFaceCondition" + else: + assert False, related_connection condition = file.create_entity(boundary_class, Name=name) connection.AppliedCondition = condition diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py index 2182d354c1..b26d106017 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py @@ -126,6 +126,7 @@ class Usecase: use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"] replace_previous_same_type_style = self.settings["replace_previous_same_type_style"] + style: ifcopenshell.entity_instance | None = None for element in self.file.traverse(self.settings["shape_representation"]): if not element.is_a("IfcShapeModel"): continue @@ -137,6 +138,7 @@ class Usecase: if self.settings["styles"]: # If there are more items than styles, fallback to using the last style style = self.settings["styles"].pop(0) + assert style is not None name = style.Name current_style_type = style.is_a() diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py index ee1acff2c4..3512b540ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py @@ -51,7 +51,8 @@ def assign_system( # This duct is part of the system ifcopenshell.api.system.assign_system(model, products=[duct], system=system) """ - if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products): - raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}") + for product in products: + if not ifcopenshell.util.system.is_assignable(product, system): + raise TypeError(f"You cannot assign an {product.is_a()} to an {system.is_a()}") return ifcopenshell.api.group.assign_group(file, products=products, group=system) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index 7b5c2b6ae4..89e923b11b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -144,6 +144,8 @@ class Usecase: elif unit_type == "volume": dimensional_exponents = self.file.createIfcDimensionalExponents(3, 0, 0, 0, 0, 0, 0) name_prefix = "cubic" + else: + assert False, unit_type si_unit = self.file.createIfcSIUnit( None, @@ -159,6 +161,8 @@ class Usecase: name = "{}mile".format(name_prefix + " " if name_prefix else "") elif data["raw"] == "THOU": name = "{}thou".format(name_prefix + " " if name_prefix else "") + else: + assert False, data value_component = self.file.create_entity( "IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]} ) diff --git a/src/ifcopenshell-python/ifcopenshell/draw.py b/src/ifcopenshell-python/ifcopenshell/draw.py index bbcfa48aea..0b317a148f 100644 --- a/src/ifcopenshell-python/ifcopenshell/draw.py +++ b/src/ifcopenshell-python/ifcopenshell/draw.py @@ -296,6 +296,7 @@ def main( else: num_passes = 0 + g2 = None for iteration in range(num_passes + 1): # initialize empty group, note that in the current approach only one @@ -316,6 +317,7 @@ def main( plt.fill(numpy.array(x.boundary).T[0], numpy.array(x.boundary).T[1]) """ + semantics, pairs = None, None if iteration != num_passes: pairs = svgfill_context.get_face_pairs() semantics = [None] * (max(pairs) + 1) @@ -377,6 +379,7 @@ def main( if inside_elements: elements = None if iteration != num_passes: + assert semantics is not None semantics[pi] = (inside_elements[0], -1) else: elements = tree.select_ray(pythonize(a), pythonize(b - a)) @@ -409,6 +412,7 @@ def main( svg_fill = "rgb(%s)" % ", ".join(str(f * 255.0) for f in clr[0:3]) if iteration != num_passes: + assert semantics is not None semantics[pi] = elements[0] else: svg_fill = "none" @@ -418,6 +422,8 @@ def main( if iteration != num_passes: to_remove = [] + assert pairs is not None + assert semantics is not None for he_idx in range(0, len(pairs), 2): # @todo instead of ray_distance, better do (x.point - y.point).dot(x.normal) # to see if they're coplanar, because ray-distance will be different in case @@ -445,6 +451,7 @@ def main( # Swap the XML nodes from the files # Remove the original hidden line node we still have in the serializer output + assert g2 is not None g1.removeChild(projection) g2.setAttribute("class", "projection") # Find the children of the projection node parent diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 46fef0a37f..32fcf5c101 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -192,7 +192,7 @@ class entity_instance: return @property - def file(self): + def file(self) -> "ifcopenshell.file": # ugh circular imports, name collisions from . import file diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index e900a0e72f..e3d7990745 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -734,6 +734,7 @@ class file: # Don't store these attributes as transactions # as the creation it self is already stored with # it's arguments + transaction = None if attrs: transaction = self.transaction self.transaction = None @@ -849,11 +850,13 @@ class file: :returns: An ifcopenshell.entity_instance """ + max_id = None if self.transaction: max_id = self.wrapped_data.getMaxId() inst.wrapped_data.this.disown() result = entity_instance(self.wrapped_data.add(inst.wrapped_data, -1 if _id is None else _id), self) if self.transaction: + assert max_id is not None added_elements = [e for e in self.traverse(result) if e.id() > max_id] [self.transaction.store_create(e) for e in reversed(added_elements)] return result diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index b3c9afc869..0640c8eb96 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -326,10 +326,11 @@ class iterator(ifcopenshell_wrapper.Iterator): if include_or_exclude_type == {"entity_instance"}: include_or_exclude = cast(set[entity_instance], include_or_exclude) - if not all((last_inst := inst).is_a("IfcProduct") for inst in include_or_exclude): - raise ValueError( - f"include and exclude need to be an aggregate of IfcProduct. Violating element: '{last_inst}'." - ) + for inst in include_or_exclude: + if not inst.is_a("IfcProduct"): + raise ValueError( + f"include and exclude need to be an aggregate of IfcProduct. Violating element: '{inst}'." + ) initializer = ifcopenshell_wrapper.construct_iterator_with_include_exclude_id diff --git a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi index 5c4d635b2a..323200b9fb 100644 --- a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi +++ b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi @@ -1070,7 +1070,10 @@ class file: """ ... - def getMaxId(self): ... + def getMaxId(self) -> int: + """Get the highest instance id currently in use in the file.""" + ... + def get_total_inverses_by_id(self, instance_id: int) -> int: ... def getUnit(self, unit_type): ... def get_inverse(self, e: entity_instance) -> tuple[entity_instance, ...]: ... diff --git a/src/ifcopenshell-python/ifcopenshell/util/cost.py b/src/ifcopenshell-python/ifcopenshell/util/cost.py index 4354e49e90..4ac048c221 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/cost.py +++ b/src/ifcopenshell-python/ifcopenshell/util/cost.py @@ -112,6 +112,8 @@ def sum_child_root_elements(root_element: ifcopenshell.entity_instance, category values = new_child_root_element.CostValues elif root_element.is_a("IfcConstructionResource"): values = child_root_element.BaseCosts + else: + assert False, root_element for child_cost_value in values or []: if category_filter and child_cost_value.Category != category_filter: continue diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 6be8ef9e0d..cf6c52e37d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -329,6 +329,8 @@ def get_quantity( data["properties"] = get_quantities(quantity.HasQuantities, verbose=verbose) del data["HasQuantities"] result = data + else: + assert False, quantity if verbose: result = {"id": quantity.id(), "class": quantity.is_a(), "value": result} return result @@ -385,6 +387,7 @@ def get_property( if prop.Name != name: continue is_single_value = False # For now we pass value type only for single values. + result_type = None if prop.is_a("IfcPropertySingleValue"): # 2 IfcPropertySingleValue.NominalValue result = v.wrappedValue if (v := prop[2]) else None @@ -407,6 +410,8 @@ def get_property( data["properties"] = get_properties(prop.HasProperties, verbose=verbose) del data["HasProperties"] result = data + else: + assert False, prop if verbose: result = {"id": prop.id(), "class": prop.is_a(), "value": result} if is_single_value: diff --git a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py index 47c8886691..1926f88caf 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py @@ -260,6 +260,8 @@ def get_helmert_transformation_parameters(ifc_file: ifcopenshell.file) -> Option xaa = 1.0 xao = 0.0 scale = factor_x = factor_y = factor_z = 1 + else: + assert False, conversion if not xaa and not xao: xaa = 1.0 diff --git a/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py b/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py index a3bdb07337..ce3cd44e45 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py +++ b/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py @@ -18,18 +18,16 @@ from __future__ import annotations -try: - from lark import Lark, Transformer - from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken - - LARK_AVAILABLE = True -except ImportError: - LARK_AVAILABLE = False - +import importlib.util import re from typing import Union +LARK_AVAILABLE = importlib.util.find_spec("lark") is not None + if LARK_AVAILABLE: + from lark import Lark, Transformer + from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken + mvd_grammar = r""" start: entry+ @@ -92,9 +90,9 @@ if LARK_AVAILABLE: self.store_text_attribute(args, "options") def dynamic_option(self, args): + original_keyword = str(args[0]) + key = original_keyword.lower() try: - original_keyword = str(args[0]) - key = original_keyword.lower() raw_text = args[1].children[0].value parsed_value = parse_semicolon_separated_kv(raw_text) self._dynamic[key] = (parsed_value, original_keyword) diff --git a/src/ifcopenshell-python/ifcopenshell/util/placement.py b/src/ifcopenshell-python/ifcopenshell/util/placement.py index cc37ab9906..fc577b4f2f 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/placement.py +++ b/src/ifcopenshell-python/ifcopenshell/util/placement.py @@ -89,6 +89,9 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> MatrixType: x = np.array((1, 0, 0)) o = placement.Location.Coordinates + else: + assert False, placement + return a2p(o, z, x) diff --git a/src/ifcopenshell-python/ifcopenshell/util/schema.py b/src/ifcopenshell-python/ifcopenshell/util/schema.py index 5aa9b26fab..d8c865e438 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/schema.py +++ b/src/ifcopenshell-python/ifcopenshell/util/schema.py @@ -584,6 +584,7 @@ class Migrator: # NOTE: `attribute` is an attribute in new file schema # print("Migrating attribute", element, new_element, attribute.name()) old_file = element.wrapped_data.file + value = ... if hasattr(element, attribute.name()): value = getattr(element, attribute.name()) # print("Attribute names matched", value) @@ -622,9 +623,7 @@ class Migrator: except: # We tried our best return - try: - value - except UnboundLocalError: + if value is ...: print( f"Couldn't match attribute {attribute.name()} by name to migrate from {element} " f"to {new_element} and there is no special mapping to handle migration " diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index e6333395a7..f83d3cd3d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -1279,6 +1279,8 @@ class FacetTransformer(lark.Transformer): result = bool(value.match(element_value)) if element_value is not None else False elif value in (None, True, False): result = element_value is value + else: + assert False, value if comparison.startswith("!"): return not result diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py index c3c33e2054..1a133e041a 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py @@ -210,6 +210,8 @@ def np_rotation_matrix( matrix = np.array([[cos_theta, 0, sin_theta], [0, 1, 0], [-sin_theta, 0, cos_theta]]) elif axis == "Z": matrix = np.array([[cos_theta, -sin_theta, 0], [sin_theta, cos_theta, 0], [0, 0, 1]]) + else: + assert False, axis else: # Assume axis is a vector. axis = axis / np.linalg.norm(axis) diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py index 524dc78d35..46269fe33b 100644 --- a/src/ifcopenshell-python/ifcopenshell/validate.py +++ b/src/ifcopenshell-python/ifcopenshell/validate.py @@ -320,6 +320,7 @@ def log_internal_cpp_errors( if log_content is None: log_content = ifcopenshell.get_log() + lines = None msgs = list(map(json.loads, filter(None, log_content.split("\n")))) chr_offsets = [chr_offset_re.findall(m["message"]) for m in msgs] instance_messages = [for_instance_re.findall(m["message"]) for m in msgs] @@ -356,6 +357,7 @@ def log_internal_cpp_errors( except: inst = None else: + assert lines is not None inst = next( ( l.decode("ascii", errors="ignore").strip() @@ -691,14 +693,16 @@ def validate_ifc_header( if not value: log_error(header_entity, name, index, AGGREGATE_TYPE, "EMPTY LIST") return - if not all(isinstance(last_value := v, str) for v in value): - log_error( - header_entity, - name, - index, - AGGREGATE_TYPE, - f"LIST with {type(last_value).__name__} (value: {last_value})", - ) + for v in value: + if not isinstance(v, str): + log_error( + header_entity, + name, + index, + AGGREGATE_TYPE, + f"LIST with {type(v).__name__} (value: {v})", + ) + break return if not isinstance(value, str): diff --git a/src/ifcopenshell-python/test/test_file_gc.py b/src/ifcopenshell-python/test/test_file_gc.py index 532fa664ec..fd082b3ea5 100644 --- a/src/ifcopenshell-python/test/test_file_gc.py +++ b/src/ifcopenshell-python/test/test_file_gc.py @@ -26,6 +26,8 @@ def test_file_gc(args): inst = f.createIfcPerson() elif api in (1, 2): inst = f.createIfcSite() + else: + assert False, api r = weakref.ref(f) @@ -68,9 +70,9 @@ def test_file_gc(args): assert r() if not file_first: - del f + del f # ty: ignore[possibly-unresolved-reference] else: - del inst + del inst # ty: ignore[possibly-unresolved-reference] # With both deleted we should have no longer access to the file. assert r() is None diff --git a/src/ifcpatch/ifcpatch/recipes/AssignConstituentFractions.py b/src/ifcpatch/ifcpatch/recipes/AssignConstituentFractions.py index 0b105a52f7..b88c942ac3 100644 --- a/src/ifcpatch/ifcpatch/recipes/AssignConstituentFractions.py +++ b/src/ifcpatch/ifcpatch/recipes/AssignConstituentFractions.py @@ -71,11 +71,13 @@ class Patcher: # Sort elements by GlobalId to ensure consistent order elements_sorted = sorted(elements, key=lambda x: x.GlobalId) + element_quantities = None for element in elements_sorted: quantities = self.get_element_quantities(element) if quantities: element_quantities = quantities break + assert element_quantities is not None if not element_quantities: continue diff --git a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py index d1405e9a33..3733bc9520 100644 --- a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py +++ b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py @@ -521,7 +521,7 @@ class Patcher(ifcpatch.BasePatcher): data_type = "JSON" json_attrs.append(i) else: - print("Possibly not implemented attribute data type:", attribute, primitive) + assert False, f"{attribute}, {primitive}" if not self.is_strict or derived[i]: optional = "DEFAULT NULL" else: diff --git a/src/ifctester/ifctester/facet.py b/src/ifctester/ifctester/facet.py index b8c3ebad5d..4339a3eea1 100644 --- a/src/ifctester/ifctester/facet.py +++ b/src/ifctester/ifctester/facet.py @@ -142,6 +142,8 @@ class Facet: templates = [ t.replace("shall", "may").replace("Shall", "May").replace("must", "may") for t in templates ] + else: + assert False, clause_type for template in templates: total_variables = len(template) - len(template.replace("{", "")) @@ -242,6 +244,7 @@ class Entity(Facet): elif not is_pass: reason = {"type": "NAME", "actual": inst.is_a().upper()} + predefined_type = None if is_pass and self.predefinedType: if self.predefinedType == "USERDEFINED": is_pass = ifcopenshell.util.element.is_userdefined_type(inst) @@ -616,6 +619,8 @@ class PartOf(Facet): if predefined_type != self.predefinedType: is_pass = False reason = {"type": "PREDEFINEDTYPE", "actual": predefined_type} + else: + assert False, self.relation if self.cardinality == "prohibited": return PartOfResult(not is_pass, {"type": "PROHIBITED"}) @@ -800,11 +805,13 @@ class Property(Facet): ] elif prop_entity.is_a("IfcPropertyBoundedValue"): values = [] + data_type = None for attribute in ["UpperBoundValue", "LowerBoundValue", "SetPointValue"]: value = getattr(prop_entity, attribute) if value is not None: data_type = value.is_a() values.append(value.wrappedValue) + assert data_type is not None, prop_entity if self.dataType and data_type.lower() != self.dataType.lower(): is_pass = False reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType} @@ -825,6 +832,7 @@ class Property(Facet): elif prop_entity.is_a("IfcPropertyTableValue"): values = [] units = ifcopenshell.util.unit.get_property_table_unit(prop_entity, inst.wrapped_data.file) + data_type = None for attribute in ["Defining", "Defined"]: column_values = props[pset_name][prop_entity.Name][f"{attribute}Values"] if not column_values: @@ -847,6 +855,7 @@ class Property(Facet): values.extend(column_values) if not values: is_pass = False + assert data_type is not None, prop_entity reason = {"type": "DATATYPE", "actual": data_type, "dataType": self.dataType} break props[pset_name][prop_entity.Name] = values @@ -984,6 +993,8 @@ class Material(Facet): values.update( [item.Name, item.Category, item.Material.Name, getattr(item.Material, "Category", None)] ) + else: + assert False, material is_pass = False for value in values: diff --git a/src/ifctester/ifctester/reporter.py b/src/ifctester/ifctester/reporter.py index de70879f09..661a3c5e83 100644 --- a/src/ifctester/ifctester/reporter.py +++ b/src/ifctester/ifctester/reporter.py @@ -343,6 +343,8 @@ class Json(Reporter): elif requirement.value: label = "Reference" value = requirement.value + else: + assert False, requirement elif facet_type == "PartOf": label = requirement.relation if requirement.predefinedType: @@ -357,6 +359,8 @@ class Json(Reporter): label = "Name / Category" if requirement.value: value = requirement.value + else: + assert False, facet_type requirements.append( ResultsRequirement( facet_type=facet_type,