diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index aa9656c76e..e5f7824836 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -1915,7 +1915,17 @@ class Blender(bonsai.core.tool.Blender): @classmethod def get_addon_preferences(cls) -> bonsai.bim.ui.BIM_ADDON_preferences: blender_package_name = cls.get_blender_addon_package_name() - return bpy.context.preferences.addons[blender_package_name].preferences + addon = bpy.context.preferences.addons.get(blender_package_name) + if addon is None: + raise RuntimeError( + f"Bonsai's addon preferences could not be found: Bonsai registered itself as " + f"'{blender_package_name}', but that is not a currently enabled Blender addon. " + "This usually means the Bonsai install is broken or in an inconsistent state, for " + "example if it was moved to a different extension repository without restarting " + "Blender. Please restart Blender, or reinstall the Bonsai extension from " + "Preferences > Get Extensions." + ) + return addon.preferences @classmethod def get_addon(cls, name: str) -> Union[types.ModuleType, None]: diff --git a/src/bonsai/test/tool/test_blender.py b/src/bonsai/test/tool/test_blender.py index 5f6aa72430..31c8f0ea67 100644 --- a/src/bonsai/test/tool/test_blender.py +++ b/src/bonsai/test/tool/test_blender.py @@ -237,3 +237,20 @@ class TestGetObjectFromGuidMissing(NewFile): bpy.ops.bim.create_project() assert tool.Ifc.get() is not None assert subject.get_object_from_guid("3iyt7r$Hf4_hQYNhBIDJI4") is None + + +class TestGetAddonPreferences(NewFile): + """``get_addon_preferences`` looks up Bonsai's own cached registration name in + ``bpy.context.preferences.addons``. If Bonsai's install ends up in an + inconsistent state (e.g. moved to a different extension repository without + restarting Blender), that cached name stops matching any enabled addon. This + must fail with an actionable ``RuntimeError``, not the bare ``KeyError`` a raw + dict-style lookup would raise.""" + + def test_returns_preferences_for_the_currently_registered_addon(self): + assert subject.get_addon_preferences() is not None + + def test_raises_actionable_error_when_registered_name_is_stale(self, monkeypatch): + monkeypatch.setattr(bonsai, "REGISTERED_BBIM_PACKAGE", "bl_ext.some_other_repo.bonsai") + with pytest.raises(RuntimeError, match="bl_ext.some_other_repo.bonsai"): + subject.get_addon_preferences()