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