feat(sdk): addon asset path resolution — addon_resource() (#389) #401

Merged
forbes merged 1 commits from feat/sdk-addon-resource into main 2026-03-04 19:47:50 +00:00
2 changed files with 53 additions and 1 deletions

View File

@@ -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",

View File

@@ -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