Fix running of test/tests.py

This commit is contained in:
Thomas Krijnen
2026-01-08 11:49:29 +01:00
parent f5b2358c2e
commit ae79996eb6
8 changed files with 65 additions and 122 deletions
@@ -169,6 +169,7 @@ def open(
print(products[0].id(), products[0].GlobalId) # 122 2XQ$n5SLP5MBLyL442paFx
print(products[0] == model[122] == model["2XQ$n5SLP5MBLyL442paFx"]) # True
"""
path = Path(path)
if not path.exists():
raise FileNotFoundError(f"Path does not exist: '{path}'.")
@@ -205,7 +206,33 @@ def open(
f = ifcopenshell_wrapper.open(str(path.absolute()), mmap=mmap)
else:
f = ifcopenshell_wrapper.open(str(path.absolute()))
return file(f)
f.post_init()
READ_ERROR = ifcopenshell_wrapper.file_open_status.READ_ERROR
NO_HEADER = ifcopenshell_wrapper.file_open_status.NO_HEADER
UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA
INVALID_SYNTAX = ifcopenshell_wrapper.file_open_status.INVALID_SYNTAX
UNKNOWN = ifcopenshell_wrapper.file_open_status.UNKNOWN
if not f.good():
from . import Error, SchemaError
exc, msg = {
READ_ERROR: lambda: (IOError, "Unable to open file for reading"),
NO_HEADER: lambda: (Error, "Unable to parse IFC SPF header"),
UNSUPPORTED_SCHEMA: lambda: (
SchemaError,
"Unsupported schema: %s" % ",".join(f.header.file_schema.schema_identifiers),
),
INVALID_SYNTAX: lambda: (Error, "Syntax error during parse, check logs"),
# This is the case when passing uninitialized_tag
UNKNOWN: lambda: (None, None),
}[f.good().value()]()
if exc is not None:
raise exc(msg)
return f
def create_entity(type: str, schema: str = "IFC4", *args: Any, **kwargs: Any) -> entity_instance:
@@ -270,7 +270,7 @@ get_attr_stmt_entity = "%(null_check)s return ((express::Base)(get_attribute_val
get_attr_stmt_array = "%(null_check)s std::vector<express::Base> es = get_attribute_value(%(index)d); return cast_vector<%(list_instance_type)s>(es);"
get_attr_stmt_nested_array = "%(null_check)s std::vector<std::vector<express::Base>> es = get_attribute_value(%(index)d); return cast_vector_vector<%(list_instance_type)s>(es);"
get_inverse = "return cast_vector<%(type)s>(data()->file()->getInverse(data()->id(), %(schema_name_upper)s_types[%(type_index)d], %(index)d));"
get_inverse = "return cast_vector<%(type)s>(file()->getInverse(data()->id(), %(schema_name_upper)s_types[%(type_index)d], %(index)d));"
set_attr_stmt = (
"%(check_optional_set_begin)sset_attribute_value(%(index)d, %(star_if_optional)sv);%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s"
+6 -48
View File
@@ -20,11 +20,9 @@ from __future__ import annotations
import os
import re
import numbers
import time
import zipfile
import functools
import ifcopenshell
import weakref
import types
from pathlib import Path
from typing import Any, Optional, TYPE_CHECKING, Union, overload, Literal, TypedDict
@@ -414,13 +412,13 @@ class rocksdb_file_storage:
self._prefix = prefix
def items(self):
it = self.file.wrapped_data.key_value_store_iter(self._prefix)
it = self.file.key_value_store_iter(self._prefix)
while it and it.valid():
yield it.key(), it.value()
it.next()
def read(self, key):
return self.file.wrapped_data.key_value_store_query(key)
return self.file.key_value_store_query(key)
def by_id(self, name):
if isinstance(name, tuple):
@@ -480,25 +478,6 @@ class rocksdb_file_storage:
return "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, version_tuple[0:2]))
class file_header:
def __init__(self, file, header_data):
self.file = file
self.header_data = header_data
# @todo these are probably no longer necessary now as we no longer depend on decoration of file
@property
def file_description(self) -> ifcopenshell.entity_instance:
return self.header_data.file_description_py()
@property
def file_name(self) -> ifcopenshell.entity_instance:
return self.header_data.file_name_py()
@property
def file_schema(self) -> ifcopenshell.entity_instance:
return self.header_data.file_schema_py()
class file_mixin:
"""Base class for containing IFC files.
@@ -611,7 +590,7 @@ class file_mixin:
# @todo we should probably check that values for
# attributes are not passed as duplicates using
# both regular arguments and keyword arguments.
kwargs_attrs = [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
kwargs_attrs = [(e.get_argument_index(name), arg) for name, arg in kwargs.items()]
attrs = list(enumerate(args)) + kwargs_attrs
if len(attrs) > len(e):
@@ -799,11 +778,11 @@ class file_mixin:
if with_attribute_indices and not allow_duplicate:
raise ValueError("with_attribute_indices requires allow_duplicate to be True")
inverses = [entity_instance(e, self) for e in self.get_inverse(inst.wrapped_data)]
inverses = self.get_inverse(inst)
if allow_duplicate:
if with_attribute_indices:
idxs = self.get_inverse_indices(inst.wrapped_data)
idxs = self.get_inverse_indices(inst)
# TODO: include in typing.
return list(zip(inverses, idxs))
else:
@@ -811,17 +790,6 @@ class file_mixin:
return set(inverses)
def get_total_inverses(self, inst: ifcopenshell.entity_instance) -> int:
"""Returns the number of entities that reference this entity
This is equivalent to `len(model.get_inverse(element))`, but
significantly faster.
:param inst: The entity instance to get inverse relationships
:returns: The total number of references
"""
return self.get_total_inverses(inst.wrapped_data)
def remove(self, inst: ifcopenshell.entity_instance) -> None:
"""Deletes an IFC object in the file.
@@ -833,7 +801,7 @@ class file_mixin:
"""
if self.transaction:
self.transaction.store_delete(inst)
return self.remove(inst.wrapped_data)
return self.remove(inst)
def batch(self):
"""Low-level mechanism to speed up deletion of large subgraphs"""
@@ -904,16 +872,6 @@ class file_mixin:
def to_string(self) -> str:
return self.to_string()
@property
def header(self) -> file_header:
# TODO: Workaround for old builds, remove after build stabilizes.
# TODO: No need for `wrapped_data.header` to be a method - should use `@property`?
header = self.header
if isinstance(header, types.MethodType):
return file_header(self, self.header())
else:
return self.header
@property
def storage(self) -> Optional[rocksdb_file_storage]:
"""
@@ -23,8 +23,7 @@ import sys
import operator
from .. import open, ifcopenshell_wrapper
from ..file import file
from ..entity_instance import entity_instance
from ifcopenshell import file, entity_instance
from . import has_occ
@@ -305,7 +304,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
self.settings = settings
if isinstance(file_or_filename, file):
self.file = file
file_or_filename = file_or_filename.wrapped_data
file_or_filename = file_or_filename
else:
file_or_filename = self.file = open(file_or_filename)
@@ -367,13 +366,13 @@ class tree(ifcopenshell_wrapper.tree):
def __init__(self, file: Optional[file] = None, settings: Optional[settings] = None):
args = [self]
if file is not None:
args.append(file.wrapped_data)
args.append(file)
if settings is not None:
args.append(settings)
ifcopenshell_wrapper.tree.__init__(*args)
def add_file(self, file: file, settings: settings) -> None:
ifcopenshell_wrapper.tree.add_file(self, file.wrapped_data, settings)
ifcopenshell_wrapper.tree.add_file(self, file, settings)
def add_iterator(self, iterator: iterator) -> None:
ifcopenshell_wrapper.tree.add_file(self, iterator)
@@ -387,7 +386,7 @@ class tree(ifcopenshell_wrapper.tree):
) -> list[entity_instance]:
def unwrap(value):
if isinstance(value, entity_instance):
return value.wrapped_data
return value
elif all(map(lambda v: hasattr(value, v), "XYZ")):
return value.X(), value.Y(), value.Z()
return value
@@ -406,12 +405,12 @@ class tree(ifcopenshell_wrapper.tree):
args.append(kwargs.get("completely_within", False))
if "extend" in kwargs:
args.append(kwargs["extend"])
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select(*args)]
return ifcopenshell_wrapper.tree.select(*args)
def select_box(self, value, **kwargs) -> list[entity_instance]:
def unwrap(value):
if isinstance(value, entity_instance):
return value.wrapped_data
return value
elif hasattr(value, "Get"):
return value.Get()[:3], value.Get()[3:]
return value
@@ -421,7 +420,7 @@ class tree(ifcopenshell_wrapper.tree):
args.append(kwargs.get("completely_within", False))
if "extend" in kwargs:
args.append(kwargs.get("extend", -1.0e-5))
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select_box(*args)]
return ifcopenshell_wrapper.tree.select_box(*args)
def clash_intersection_many(
self,
@@ -430,13 +429,13 @@ class tree(ifcopenshell_wrapper.tree):
tolerance: float = 0.002,
check_all: bool = True,
) -> tuple[ifcopenshell_wrapper.clash, ...]:
args = [self, [e.wrapped_data for e in set_a], [e.wrapped_data for e in set_b], tolerance, check_all]
args = [self, set_a, set_b, tolerance, check_all]
return ifcopenshell_wrapper.tree.clash_intersection_many(*args)
def clash_collision_many(
self, set_a: Iterable[entity_instance], set_b: Iterable[entity_instance], allow_touching=False
) -> tuple[ifcopenshell_wrapper.clash, ...]:
args = [self, [e.wrapped_data for e in set_a], [e.wrapped_data for e in set_b], allow_touching]
args = [self, set_a, set_b, allow_touching]
return ifcopenshell_wrapper.tree.clash_collision_many(*args)
def clash_clearance_many(
@@ -446,7 +445,7 @@ class tree(ifcopenshell_wrapper.tree):
clearance: float = 0.05,
check_all: bool = False,
) -> tuple[ifcopenshell_wrapper.clash, ...]:
args = [self, [e.wrapped_data for e in set_a], [e.wrapped_data for e in set_b], clearance, check_all]
args = [self, set_a, set_b, clearance, check_all]
return ifcopenshell_wrapper.tree.clash_clearance_many(*args)
@staticmethod
@@ -509,7 +508,7 @@ def create_shape(
return wrap_shape_creation(
settings,
ifcopenshell_wrapper.create_shape(
settings, inst.wrapped_data, repr.wrapped_data if repr is not None else None, geometry_library
settings, inst, repr if repr is not None else None, geometry_library
),
)
@@ -525,7 +524,7 @@ def map_shape(settings: settings, inst: entity_instance) -> ifcopenshell_wrapper
>>> ifcopenshell.geom.map_shape(ifcopenshell.geom.settings(), point).components
(0.0, 0.0, 0.0)
"""
return ifcopenshell_wrapper.map_shape(settings, inst.wrapped_data)
return ifcopenshell_wrapper.map_shape(settings, inst)
@overload
@@ -618,20 +617,17 @@ def iterate(
def make_shape_function(fn):
def entity_instance_or_none(e):
return None if e is None else entity_instance(e)
if has_occ:
def _(schema, string_or_shape, *args):
if isinstance(string_or_shape, TopoDS.TopoDS_Shape):
string_or_shape = utils.serialize_shape(string_or_shape)
return entity_instance_or_none(fn(schema, string_or_shape, *args))
return fn(schema, string_or_shape, *args)
else:
def _(schema, string, *args):
return entity_instance_or_none(fn(schema, string, *args))
return fn(schema, string, *args)
return _