mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-09 05:46:51 +00:00
9ed8f3e244
Since we added more data to debug info in fcf5614 Fatal Error itself started to fail and was never displayed due some props being inaccessible during load, should be fixed now.
Possible error that were fixed:
```
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 328, in <module>
print(format_debug_info(get_debug_info()))
~~~~~~~~~~~~~~^^
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 117, in get_debug_info
if bpy.data.is_saved:
^^^^^^^^^^^^^^^^^
AttributeError: '_RestrictData' object has no attribute 'is_saved'
Traceback (most recent call last):
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 366, in draw
info = get_debug_info()
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 152, in get_debug_info
bim_props = tool.Blender.get_bim_props()
^^^^
NameError: name 'tool' is not defined. Did you mean: 'bool'?
Traceback (most recent call last):
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 366, in draw
info = get_debug_info()
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 141, in get_debug_info
import bonsai.tool as tool
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\__init__.py", line 355, in <module>
print(format_debug_info(get_debug_info()))
~~~~~~~~~~~~~~^^
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\__init__.py", line 141, in get_debug_info
import bonsai.tool as tool
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\__init__.py", line 23, in <module>
from bonsai.tool.attribute import Attribute
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\attribute.py", line 31, in <module>
import bonsai.bim.helper as helper
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\__init__.py", line 28, in <module>
from . import handler, operator, prop, ui
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\handler.py", line 36, in <module>
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\module\aggregate\__init__.py", line 21, in <module>
from . import operator, prop, ui
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\module\aggregate\operator.py", line 32, in <module>
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
^^^^^^^^
AttributeError: partially initialized module 'bonsai.tool' from '\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\__init__.py' has no attribute 'Ifc' (most likely due to a circular import)
```
170 lines
6.0 KiB
Python
170 lines
6.0 KiB
Python
# Bonsai - OpenBIM Blender Add-on
|
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
|
#
|
|
# This file is part of Bonsai.
|
|
#
|
|
# Bonsai is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Bonsai is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
import bpy
|
|
import ifcopenshell
|
|
import pytest
|
|
|
|
import bonsai
|
|
import bonsai.core.tool
|
|
import bonsai.tool as tool
|
|
from bonsai.tool.blender import Blender as subject
|
|
from test.bim.bootstrap import NewFile
|
|
|
|
if TYPE_CHECKING:
|
|
import bpy.stub_internal.rna_enums as rna_enums
|
|
|
|
|
|
class TestImplementsTool(NewFile):
|
|
def test_run(self):
|
|
assert isinstance(subject(), bonsai.core.tool.Blender)
|
|
|
|
|
|
class TestCopyNodeGraph(NewFile):
|
|
def test_run(self):
|
|
material_to = bpy.data.materials.new("material_to")
|
|
tool.Style.set_use_nodes(material_to, True)
|
|
assert material_to.node_tree
|
|
material_to_nodes = material_to.node_tree.nodes
|
|
assert len(material_to_nodes) == 2
|
|
for node in material_to_nodes:
|
|
material_to_nodes.remove(node)
|
|
assert len(material_to_nodes) == 0
|
|
|
|
material_from = bpy.data.materials.new("material_from")
|
|
tool.Style.set_use_nodes(material_from, True)
|
|
|
|
subject.copy_node_graph(material_to, material_from)
|
|
assert len(material_to_nodes) == 2
|
|
|
|
|
|
class TestSortPanelsForRegister(NewFile):
|
|
def test_run(self):
|
|
items = ["A", "B", "C", "D"]
|
|
items_to_parents = {"A": "D", "D": "C", "C": "B"}
|
|
sorted_items = subject.sort_panels_for_register(items, items_to_parents)
|
|
assert tuple(sorted_items) == ("B", "C", "D", "A")
|
|
|
|
with pytest.raises(AssertionError):
|
|
subject.sort_panels_for_register(items, {"A": "K"})
|
|
|
|
with pytest.raises(AssertionError):
|
|
subject.sort_panels_for_register(items, {"J": "A"})
|
|
|
|
|
|
class TestBlenderErrorMessageExtraction(NewFile):
|
|
def test_extract_operator_reports(self) -> None:
|
|
|
|
ERROR_REPORTS = ["ERROR!!!\nERROR", "ERROR"]
|
|
|
|
class OBJECT_OT_test_fail_operator(bpy.types.Operator):
|
|
bl_idname = "object.test_fail_operator"
|
|
bl_label = "Test Fail Operator"
|
|
|
|
def execute(self, context) -> "set[rna_enums.OperatorReturnItems]":
|
|
self.report({"INFO"}, "Info message.")
|
|
subject.report_operator_errors(self, ERROR_REPORTS)
|
|
return {"FINISHED"}
|
|
|
|
bpy.utils.register_class(OBJECT_OT_test_fail_operator)
|
|
|
|
try:
|
|
bpy.ops.object.test_fail_operator()
|
|
except RuntimeError as e:
|
|
error_reports = subject.extract_error_reports(e)
|
|
assert error_reports == ERROR_REPORTS
|
|
|
|
bpy.utils.unregister_class(OBJECT_OT_test_fail_operator)
|
|
|
|
def test_ignore_actual_runtime_errors_from_operators(self) -> None:
|
|
|
|
class OBJECT_OT_test_fail_operator(bpy.types.Operator):
|
|
bl_idname = "object.test_fail_operator"
|
|
bl_label = "Test Fail Operator"
|
|
|
|
def execute(self, context):
|
|
raise RuntimeError("Intentional runtime error.")
|
|
|
|
bpy.utils.register_class(OBJECT_OT_test_fail_operator)
|
|
|
|
try:
|
|
bpy.ops.object.test_fail_operator()
|
|
except RuntimeError as e:
|
|
error_reports = subject.extract_error_reports(e)
|
|
assert error_reports == []
|
|
|
|
bpy.utils.unregister_class(OBJECT_OT_test_fail_operator)
|
|
|
|
|
|
class TestGetSelectedFiles(NewFile):
|
|
def test_get_a_single_file(self) -> None:
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
file = type("", (object,), {"name": f.name})()
|
|
assert subject.get_selected_files(Path(f.name).parent, [file]) == [f.name]
|
|
|
|
def test_get_multiple_files(self) -> None:
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
with tempfile.NamedTemporaryFile() as g:
|
|
file = type("", (object,), {"name": f.name})()
|
|
file2 = type("", (object,), {"name": g.name})()
|
|
assert subject.get_selected_files(Path(f.name).parent, [file, file2]) == [f.name, g.name]
|
|
|
|
def test_exclude_directories(self) -> None:
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
with tempfile.TemporaryDirectory() as d:
|
|
file = type("", (object,), {"name": f.name})()
|
|
directory = type("", (object,), {"name": d})()
|
|
assert subject.get_selected_files(Path(f.name).parent, [file, directory]) == [f.name]
|
|
|
|
def test_get_relative_paths(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
base_path = Path(tmp_dir)
|
|
with tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".ifc") as f:
|
|
tool.Ifc.set_path(str(f.name))
|
|
with tempfile.NamedTemporaryFile(dir=tmp_dir) as g:
|
|
file = type("", (object,), {"name": g.name})
|
|
assert subject.get_selected_files(Path(g.name).parent, [file], use_relative_path=True) == [
|
|
Path(g.name).name
|
|
]
|
|
|
|
|
|
class TestGetDebugInfo(NewFile):
|
|
# Only keys that are safe to set if Bonsai fails to load.
|
|
EXPECTED_KEYS = {
|
|
"os",
|
|
"os_version",
|
|
"python_version",
|
|
"architecture",
|
|
"machine",
|
|
"processor",
|
|
"blender_version",
|
|
"bonsai_version",
|
|
"bonsai_commit_hash",
|
|
"bonsai_commit_date",
|
|
"last_actions",
|
|
"last_error",
|
|
}
|
|
|
|
def test_failed_to_load_returns_only_base_keys(self):
|
|
info = bonsai.get_debug_info(bonsai_failed_to_load=True)
|
|
assert set(info.keys()) == self.EXPECTED_KEYS
|