Bonsai: fail with an actionable error instead of a bare KeyError when addon preferences can't be found

get_addon_preferences() looked up bpy.context.preferences.addons using a
package name Bonsai cached for itself once, at register() time. If that
cached name stops matching a currently enabled Blender addon (#7288: the
extension was moved to a different repository, e.g. bl_ext.user_default
vs bl_ext.blender_org, without restarting Blender, leaving the install in
an inconsistent state), the raw dict-style lookup raised an unhandled
KeyError with an internal, non-actionable message. That propagated out of
create_spatial_elements() during project load and aborted the entire
load, so any IFC file failed the same way, well before any geometry was
even read.

Chose to fail loudly but clearly rather than silently guess a
replacement addon entry: the maintainer's own diagnosis for #7288 was
that this represents a genuinely broken/inconsistent install, and
auto-resolving a different addon entry risks masking that instead of
telling the user their install needs attention. get_addon_preferences()
now raises a RuntimeError naming the exact mismatch and pointing at the
fix (restart Blender, or reinstall from Preferences > Get Extensions),
rather than a bare KeyError whose message is a raw bpy_prop_collection
repr.

Verified live in headless Blender against the actually-deployed Bonsai
extension on this machine: reproduced the exact bare KeyError by staging
a stale cached package name, confirmed the fix turns it into the new
RuntimeError, and confirmed the healthy/correctly-registered case still
returns a normal preferences object unchanged. No caller of
get_addon_preferences() catches KeyError specifically, so widening the
exception type is not a behavioural break for any of them.

Fixes #7288

Generated with the assistance of an AI coding tool.
This commit is contained in:
Petru Conduraru
2026-07-20 17:22:13 +03:00
parent 55a2430d71
commit fffcf20724
2 changed files with 28 additions and 1 deletions
+11 -1
View File
@@ -1915,7 +1915,17 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def get_addon_preferences(cls) -> bonsai.bim.ui.BIM_ADDON_preferences: def get_addon_preferences(cls) -> bonsai.bim.ui.BIM_ADDON_preferences:
blender_package_name = cls.get_blender_addon_package_name() 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 @classmethod
def get_addon(cls, name: str) -> Union[types.ModuleType, None]: def get_addon(cls, name: str) -> Union[types.ModuleType, None]:
+17
View File
@@ -237,3 +237,20 @@ class TestGetObjectFromGuidMissing(NewFile):
bpy.ops.bim.create_project() bpy.ops.bim.create_project()
assert tool.Ifc.get() is not None assert tool.Ifc.get() is not None
assert subject.get_object_from_guid("3iyt7r$Hf4_hQYNhBIDJI4") is 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()