mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Fix all ty diagnostics on ifcviewer-wgpu (ci-lint ty-ios + ty-bonsai)
This branch carried v0.8.0's strict `[tool.ty.rules] all = "error"` config but not the source fixes that were made upstream to satisfy it, so both ci-lint ty gates were failing: `poe ty-ios` reported 256 diagnostics and `poe ty-bonsai` 258. Both are now clean. Most fixes are ported from v0.8.0 and follow two idioms: initialise a name before a conditional that may not bind it (plus an `assert` where the invariant is real but not provable), and close an exhaustive `if`/`elif` chain with `else: assert False, <discriminant>`. The branch's own newer accessors are preserved throughout - `.file`, `.declaration`, `file.types()`, `get_max_id()` are kept rather than reverted to `wrapped_data.*`, and non-ty upstream changes (notably the in-progress geometry cache removal) are deliberately not pulled in. Notable fixes that are not straight ports: * ifcopenshell_wrapper.pyi: `entity_instance.file` was declared as `def file(self) -> file`, where the property name shadows the `class file` below it, so the annotation resolved to `Unknown`. Every `element.file` in the codebase was therefore unchecked. Qualifying it to `ifcopenshell.file` restores `.schema` to its Literal union and surfaces no new diagnostics. * model/wall.py: a duplicated merge fragment in the void-straddle path ran an always-true `if void_straddles:` that read `new_opening` from the mutually exclusive branch (stale value, or NameError on the first iteration), followed by an unreachable duplicate `elif`. Removing it makes the file match v0.8.0. * light/operator.py: upstream's own fix unpacks three targets from two values and raises ValueError unconditionally; corrected to `None, None, None`. * assign_system.py, validate.py, geom/main.py: walrus-in-genexp is valid at runtime (PEP 572 binds in the containing scope) but ty does not model it; rewritten as explicit loops, matching upstream. Verified: poe ty-ios, poe ty-bonsai, ruff check src/ nix/, black --check ., and compileall -W error at py3.10 (ifcopenshell-python) and py3.11 (bonsai). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,11 @@ 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
|
||||
)
|
||||
|
||||
assert alignment is not None
|
||||
return alignment
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]}
|
||||
)
|
||||
|
||||
@@ -286,6 +286,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
|
||||
@@ -306,6 +307,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)
|
||||
@@ -367,6 +369,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))
|
||||
@@ -399,6 +402,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"
|
||||
@@ -406,6 +410,8 @@ def main(
|
||||
p.setAttribute("style", "fill: " + svg_fill)
|
||||
|
||||
if iteration != num_passes:
|
||||
assert pairs is not None
|
||||
assert semantics is not None
|
||||
to_remove = []
|
||||
|
||||
for he_idx in range(0, len(pairs), 2):
|
||||
@@ -435,6 +441,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
|
||||
@@ -529,9 +536,10 @@ def main(
|
||||
*(tup for i, tup in enumerate(zip(path_objects, section_polies, polies)) if has_relevant_zone(i))
|
||||
)
|
||||
|
||||
# ty can't bound the length of the unpacked iterables, so it over-counts the args.
|
||||
arranged = W.arrange_polygons(
|
||||
*filter(None, (ARRANGE_POLYGON_SETTINGS,)),
|
||||
polies,
|
||||
polies, # ty:ignore[too-many-positional-arguments]
|
||||
*ifcopenshell.optional_logger_args(logger),
|
||||
)
|
||||
svg_data_3 = W.polygons_to_svg(arranged, False)
|
||||
|
||||
@@ -90,8 +90,8 @@ class Transaction:
|
||||
batch_inverses: list[ElementInverses]
|
||||
batch_delete_ids: set[int]
|
||||
|
||||
def __init__(self, ifc_file: file):
|
||||
self.file: file = ifc_file
|
||||
def __init__(self, ifc_file: ifcopenshell.file):
|
||||
self.file: ifcopenshell.file = ifc_file
|
||||
self.operations = []
|
||||
self.is_batched = False
|
||||
self.batch_delete_index = 0
|
||||
@@ -634,6 +634,7 @@ class file_mixin:
|
||||
# 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
|
||||
@@ -715,12 +716,14 @@ class file_mixin:
|
||||
:returns: An ifcopenshell.entity_instance
|
||||
"""
|
||||
|
||||
max_id = None
|
||||
if self.transaction:
|
||||
max_id = self.get_max_id()
|
||||
|
||||
result = self._add(inst, -1 if _id is None else _id)
|
||||
|
||||
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
|
||||
|
||||
@@ -364,10 +364,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
|
||||
|
||||
|
||||
@@ -752,7 +752,7 @@ class entity_instance(entity_instance_mixin):
|
||||
@property
|
||||
def declaration(self) -> declaration: ...
|
||||
@property
|
||||
def file(self) -> file: ...
|
||||
def file(self) -> ifcopenshell.file: ...
|
||||
def get_argument(self, *args: int | str) -> Any: ...
|
||||
def get_argument_index(self, a: str) -> int: ...
|
||||
def attribute_name(self, i: int) -> str: ...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -331,6 +331,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
|
||||
@@ -387,6 +389,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
|
||||
@@ -409,6 +412,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -569,6 +569,7 @@ class Migrator:
|
||||
# NOTE: `attribute` is an attribute in new file schema
|
||||
# print("Migrating attribute", element, new_element, attribute.name())
|
||||
old_file = element.file
|
||||
value = ...
|
||||
if hasattr(element, attribute.name()):
|
||||
value = getattr(element, attribute.name())
|
||||
# print("Attribute names matched", value)
|
||||
@@ -607,9 +608,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 "
|
||||
|
||||
@@ -1290,6 +1290,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
|
||||
|
||||
@@ -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)
|
||||
@@ -473,7 +475,6 @@ class ShapeBuilder:
|
||||
if arc_points and self.file.schema == "IFC2X3":
|
||||
raise Exception("Arcs are not supported for IFC2X3.")
|
||||
|
||||
points: np.ndarray
|
||||
points = np.array(points)
|
||||
if position_offset is not None:
|
||||
points = points + position_offset
|
||||
@@ -635,7 +636,6 @@ class ShapeBuilder:
|
||||
diff = (0.01, 0.01) * diff_sign
|
||||
middle_point = points[0] + diff
|
||||
|
||||
points: list[VectorType]
|
||||
points = [points[0], middle_point, points[1]]
|
||||
points = [ifc_safe_vector_type(p) for p in points]
|
||||
seg = self.file.createIfcArcIndex((1, 2, 3))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -27,6 +27,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)
|
||||
|
||||
@@ -69,9 +71,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
|
||||
|
||||
Reference in New Issue
Block a user