From 9ed8f3e244917933e7477ba037c3de85d3cc076e Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 13 Mar 2026 13:30:02 +0500 Subject: [PATCH] Bonsai - fix missing Bonsai Fatal Error UI 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 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 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 from bonsai.tool.attribute import Attribute File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\attribute.py", line 31, in import bonsai.bim.helper as helper File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\__init__.py", line 28, in from . import handler, operator, prop, ui File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\handler.py", line 36, in 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 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 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) ``` --- src/bonsai/bonsai/__init__.py | 39 ++++++++++++++++++++++++---- src/bonsai/bonsai/bim/schema.py | 11 ++------ src/bonsai/test/tool/test_blender.py | 23 ++++++++++++++++ 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/bonsai/bonsai/__init__.py b/src/bonsai/bonsai/__init__.py index e30e2bf699..4a4932316b 100644 --- a/src/bonsai/bonsai/__init__.py +++ b/src/bonsai/bonsai/__init__.py @@ -40,7 +40,7 @@ import webbrowser from collections import deque from collections.abc import Generator from pathlib import Path -from typing import Any, Union +from typing import TYPE_CHECKING, Any, Union last_commit_hash = "8888888" last_commit_date = "9999999" @@ -72,6 +72,21 @@ REINSTALLED_BBIM_VERSION: Union[str, None] = None REGISTERED_BBIM_PACKAGE: str +def is_registering() -> bool: + """ + During addon registration ``bpy.context`` and ``bpy.data`` are restricted + and you can't access their properties. + """ + import bpy + + if TYPE_CHECKING or bpy.app.version >= (5, 0, 0): + import _bpy_restrict_state as bpy_restrict_state + else: + import bpy_restrict_state + + return isinstance(bpy.context, bpy_restrict_state._RestrictContext) + + def initialize_bbim_semver(): """Initialize `bbim_semver` dictionary. @@ -93,9 +108,13 @@ def initialize_bbim_semver(): bbim_semver["version"] = version_str -def get_debug_info(): +def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]: + import bpy + bbim_version = bbim_semver["version"] + # All data here should be gettable even in case of `bpy.context` and `bpy.data` being inaccessible + # and Bonsai completely failed to load. debug_info = { "os": platform.system(), "os_version": platform.version(), @@ -111,6 +130,14 @@ def get_debug_info(): "last_error": last_error, } + # Can't access blend data or context during registration. + # If Bonsai failed to load we cannot safely access any of its properties or its tools + # as they may not be registered yet and acessing them will break Bonsai Fatal Error UI. + if is_registering() or bonsai_failed_to_load: + return debug_info + + import bonsai.tool as tool + # Add .blend file save information if bpy.data.is_saved: debug_info["blend_file_path"] = bpy.data.filepath @@ -131,7 +158,7 @@ def get_debug_info(): return debug_info -def format_debug_info(info: dict): +def format_debug_info(info: dict[str, Any]) -> str: last_actions = "" for action in info["last_actions"]: last_actions += f"\n# {action['type']}: {action['name']}" @@ -205,6 +232,8 @@ def clean_up_dlls_safe_links() -> None: if IN_BLENDER: + import bpy + initialize_bbim_semver() def get_binary_info() -> dict[str, Any]: @@ -333,7 +362,7 @@ if IN_BLENDER: bl_context = "scene" def draw(self, context): - info = get_debug_info() + info = get_debug_info(bonsai_failed_to_load=True) layout = self.layout layout.alert = True @@ -409,7 +438,7 @@ if IN_BLENDER: bl_description = "Copies debugging information to your clipboard for use in bugreports" def execute(self, context): - info = get_debug_info() + info = get_debug_info(bonsai_failed_to_load=True) info.update(get_binary_info()) info = format_debug_info(info) context.window_manager.clipboard = info diff --git a/src/bonsai/bonsai/bim/schema.py b/src/bonsai/bonsai/bim/schema.py index 4ef96e72a7..15dfa87a59 100644 --- a/src/bonsai/bonsai/bim/schema.py +++ b/src/bonsai/bonsai/bim/schema.py @@ -16,19 +16,12 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . -from typing import TYPE_CHECKING - -import bpy import ifcopenshell import ifcopenshell.util.pset +import bonsai import bonsai.tool as tool -if TYPE_CHECKING or bpy.app.version >= (5, 0, 0): - import _bpy_restrict_state as bpy_restrict_state -else: - import bpy_restrict_state - class IfcSchema: data_dir: str @@ -62,7 +55,7 @@ class IfcSchema: self.psetqto.get_by_name.cache_clear() # During register we cannot access the context either way. - if isinstance(bpy.context, bpy_restrict_state._RestrictContext): + if bonsai.is_registering(): return for path in tool.Blender.get_data_dir_paths("pset", "*.ifc"): self.psetqto.templates.append(ifcopenshell.open(path)) diff --git a/src/bonsai/test/tool/test_blender.py b/src/bonsai/test/tool/test_blender.py index cd155b5dee..cb5d06bc71 100644 --- a/src/bonsai/test/tool/test_blender.py +++ b/src/bonsai/test/tool/test_blender.py @@ -24,6 +24,7 @@ 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 @@ -144,3 +145,25 @@ class TestGetSelectedFiles(NewFile): 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