From 85ae0effc974c4f7be6eb8447ace87ee6a290492 Mon Sep 17 00:00:00 2001 From: forbes Date: Wed, 4 Mar 2026 13:46:25 -0600 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20addon=20asset=20path=20resolution?= =?UTF-8?q?=20=E2=80=94=20addon=5Fresource()=20(#389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add kindred_sdk.addon_resource(name, relative_path) that resolves bundled asset paths relative to an addon's root directory. Looks up the addon manifest from AddonRegistry for the install root, joins the relative path, and validates existence on disk. Raises LookupError if the addon is not registered, FileNotFoundError if the resolved path does not exist. Closes #389 --- mods/sdk/kindred_sdk/__init__.py | 8 +++++- mods/sdk/kindred_sdk/registry.py | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/mods/sdk/kindred_sdk/__init__.py b/mods/sdk/kindred_sdk/__init__.py index 0100622b9d..a0b881c3cf 100644 --- a/mods/sdk/kindred_sdk/__init__.py +++ b/mods/sdk/kindred_sdk/__init__.py @@ -24,7 +24,12 @@ from kindred_sdk.origin import ( set_active_origin, unregister_origin, ) -from kindred_sdk.registry import addon_version, is_addon_loaded, loaded_addons +from kindred_sdk.registry import ( + addon_resource, + addon_version, + is_addon_loaded, + loaded_addons, +) from kindred_sdk.statusbar import register_status_widget from kindred_sdk.theme import get_theme_tokens, load_palette from kindred_sdk.toolbar import register_toolbar @@ -33,6 +38,7 @@ from kindred_sdk.version import SDK_VERSION __all__ = [ "SDK_VERSION", "active_origin", + "addon_resource", "addon_version", "available_contexts", "context_history", diff --git a/mods/sdk/kindred_sdk/registry.py b/mods/sdk/kindred_sdk/registry.py index 6f8f7fc879..e1e22f65ef 100644 --- a/mods/sdk/kindred_sdk/registry.py +++ b/mods/sdk/kindred_sdk/registry.py @@ -3,6 +3,8 @@ # Thin wrappers around FreeCAD.KindredAddons (AddonRegistry) so addons # use a stable SDK import instead of reaching into FreeCAD internals. +import os + import FreeCAD @@ -51,3 +53,47 @@ def loaded_addons() -> list[str]: if registry is None: return [] return [m.name for m in registry.loaded()] + + +def addon_resource(name: str, relative_path: str) -> str: + """Resolve a bundled asset path for an addon. + + Parameters + ---------- + name : str + Addon name as declared in its ``package.xml`` (e.g. ``"silo"``). + relative_path : str + Path relative to the addon's root directory + (e.g. ``"icons/silo_commit.svg"``). + + Returns + ------- + str + Absolute path to the resolved file. + + Raises + ------ + LookupError + If the addon is not registered. + FileNotFoundError + If the resolved path does not exist on disk. + + >>> import kindred_sdk as sdk + >>> sdk.addon_resource("silo", "icons/silo_commit.svg") + '/home/.../mods/silo/freecad/icons/silo_commit.svg' + """ + registry = _get_registry() + if registry is None: + raise LookupError( + f"Addon registry not initialized; cannot resolve resource for '{name}'" + ) + manifest = registry.get(name) + if manifest is None: + raise LookupError(f"Addon '{name}' is not registered") + + resolved = os.path.join(manifest.addon_root, relative_path) + if not os.path.exists(resolved): + raise FileNotFoundError( + f"Resource not found: {resolved} (addon '{name}', path '{relative_path}')" + ) + return resolved -- 2.49.1