mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Bonsai: inline get_root_context, trim docstrings, confirm get_parent_library unchanged
Per Moult's round 3 review. get_root_context added nothing over
ifc_file.by_type("IfcProject")[0], which is guaranteed by the IFC Project
Context concept template; remove it and inline the call at its three sites
(operator.py's RefreshLibrary and AddProjectLibrary, data.py's
parent_libraries_enum). Trim the get_parent_library docstring to one line;
its logic is untouched by this PR, byte for byte identical to origin/v0.8.0,
and still returns None only when project_library has neither Nests nor
HasContext, never for a library declared directly to IfcProject.
Rework test_project_library_data.py to match: replace the two
get_root_context-specific tests with one that exercises the real call site
(ProjectLibraryData.parent_libraries_enum raising IndexError for a file
without IfcProject), and add an explicit test that get_parent_library
returns None for a genuinely orphaned library. Also drop a long inline
comment that restated what the test body already shows.
Verified live in headless Blender (isolated profile, source-loaded, never
the real profile): all 17 test/bim/module/project tests pass, including the
new get_parent_library None-for-orphan case. Ran the full test/bim suite
before and after on the identical harness: 82 failed/1335 passed both times,
same failing tests (all pre-existing, unrelated to this module).
This change was made with the assistance of an AI tool.
This commit is contained in:
@@ -162,7 +162,7 @@ class ProjectLibraryData:
|
||||
library_file = IfcStore.library_file
|
||||
if library_file is None or library_file.schema == "IFC2X3":
|
||||
return results
|
||||
root = tool.Project.get_root_context(library_file)
|
||||
root = library_file.by_type("IfcProject")[0]
|
||||
results.append((str(root.id()), f"{root.is_a()} {root.Name or 'Unnamed'}", root.Description or ""))
|
||||
for library_id, data in cls.data["project_libraries"].items():
|
||||
results.append((str(library_id), data["Name"] or "Unnamed", data["Description"] or ""))
|
||||
|
||||
@@ -281,7 +281,7 @@ class RefreshLibrary(bpy.types.Operator):
|
||||
elements = {e for e in elements if not tool.Project.is_element_assigned_to_project_library(e, rels)}
|
||||
self.props.add_library_project_library("Unassigned", len(elements), 0, False)
|
||||
|
||||
root_context = tool.Project.get_root_context(library_file)
|
||||
root_context = library_file.by_type("IfcProject")[0]
|
||||
hierarchy = tool.Project.get_project_hierarchy(library_file)
|
||||
tool.Project.load_project_libraries_to_ui(root_context, hierarchy)
|
||||
return {"FINISHED"}
|
||||
@@ -807,7 +807,7 @@ class AddProjectLibrary(bpy.types.Operator):
|
||||
props = tool.Project.get_project_props()
|
||||
library_file = IfcStore.library_file
|
||||
assert library_file
|
||||
root_context = tool.Project.get_root_context(library_file)
|
||||
root_context = library_file.by_type("IfcProject")[0]
|
||||
project_library = ifcopenshell.api.root.create_entity(library_file, "IfcProjectLibrary")
|
||||
ifcopenshell.api.project.assign_declaration(library_file, [project_library], root_context)
|
||||
ProjectLibraryData.load() # Update enum.
|
||||
|
||||
@@ -388,30 +388,14 @@ class Project(bonsai.core.tool.Project):
|
||||
def get_parent_library(
|
||||
cls, project_library: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Return the IfcContext that declares or nests ``project_library``.
|
||||
|
||||
Every IfcProjectLibrary in a supported (spec-valid) file is either nested
|
||||
under another library or declared to the file's IfcProject. Returns ``None``
|
||||
only as a defensive fallback for malformed data with neither relationship.
|
||||
"""
|
||||
"""Return the IfcContext that declares or nests ``project_library``, or ``None``
|
||||
if neither relationship is present."""
|
||||
if nests := project_library.Nests:
|
||||
return nests[0].RelatingObject
|
||||
if has_context := project_library.HasContext:
|
||||
return has_context[0].RelatingContext
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_root_context(cls, ifc_file: ifcopenshell.file) -> ifcopenshell.entity_instance:
|
||||
"""Return the file's IfcProject.
|
||||
|
||||
Per the IFC Project Context concept template, every project data set (this
|
||||
includes library files) shall contain exactly one IfcProject; there is no
|
||||
such thing as a spec-valid file rooted on IfcProjectLibrary alone. A file
|
||||
without an IfcProject is not supported and this raises IndexError. Caller is
|
||||
responsible for the IFC2X3 guard; IfcContext does not exist in that schema.
|
||||
"""
|
||||
return ifc_file.by_type("IfcProject")[0]
|
||||
|
||||
@classmethod
|
||||
def get_project_hierarchy(cls, ifc_file: ifcopenshell.file) -> HiearchyDict:
|
||||
"""Get project hierarchy in the following form:
|
||||
|
||||
@@ -55,23 +55,18 @@ class TestLibraryFile(NewIfc):
|
||||
"""Project-library UI code operating on a spec-valid model (IfcProject root).
|
||||
|
||||
A file containing only IfcProjectLibrary and no IfcProject is not valid IFC and
|
||||
is not supported; see test_get_root_context_raises_for_a_file_without_a_project.
|
||||
is not supported; see test_parent_libraries_enum_raises_for_a_file_without_a_project.
|
||||
"""
|
||||
|
||||
def test_get_root_context_returns_the_project(self):
|
||||
library_file = _make_library_file()
|
||||
project = library_file.by_type("IfcProject")[0]
|
||||
|
||||
root = tool.Project.get_root_context(library_file)
|
||||
|
||||
assert root == project
|
||||
|
||||
def test_get_root_context_raises_for_a_file_without_a_project(self):
|
||||
def test_parent_libraries_enum_raises_for_a_file_without_a_project(self):
|
||||
library_file = ifcopenshell.api.project.create_file(version="IFC4")
|
||||
ifcopenshell.api.root.create_entity(library_file, ifc_class="IfcProjectLibrary", name="RootLib")
|
||||
|
||||
with pytest.raises(IndexError):
|
||||
tool.Project.get_root_context(library_file)
|
||||
IfcStore.library_file = library_file
|
||||
try:
|
||||
with pytest.raises(IndexError):
|
||||
ProjectLibraryData.parent_libraries_enum()
|
||||
finally:
|
||||
IfcStore.library_file = None
|
||||
|
||||
def test_get_parent_library_returns_project_for_declared_root_library(self):
|
||||
library_file = _make_library_file()
|
||||
@@ -80,6 +75,12 @@ class TestLibraryFile(NewIfc):
|
||||
|
||||
assert tool.Project.get_parent_library(root) == project
|
||||
|
||||
def test_get_parent_library_returns_none_for_an_orphaned_library(self):
|
||||
library_file = ifcopenshell.api.project.create_file(version="IFC4")
|
||||
orphan = ifcopenshell.api.root.create_entity(library_file, ifc_class="IfcProjectLibrary", name="Orphan")
|
||||
|
||||
assert tool.Project.get_parent_library(orphan) is None
|
||||
|
||||
def test_get_project_hierarchy_roots_libraries_under_the_project(self):
|
||||
library_file = _make_library_file(with_child=True)
|
||||
project = library_file.by_type("IfcProject")[0]
|
||||
@@ -92,12 +93,6 @@ class TestLibraryFile(NewIfc):
|
||||
assert child in hierarchy[root]
|
||||
|
||||
def test_project_library_data_loads_with_unique_enum_keys(self):
|
||||
# Regression test for the ci-bonsai-daily failure: parent_libraries_enum()
|
||||
# must never emit two entries with the same STEP id (Blender's EnumProperty
|
||||
# requires unique keys). The original failure only occurred because the root
|
||||
# context could incorrectly resolve to an IfcProjectLibrary that was also
|
||||
# collected by project_libraries(); on a spec-valid model root is always the
|
||||
# IfcProject, whose id never collides with a library id.
|
||||
IfcStore.library_file = _make_library_file(with_child=True)
|
||||
try:
|
||||
ProjectLibraryData.is_loaded = False
|
||||
|
||||
Reference in New Issue
Block a user