Source code for httk.serve.web.widgets.assets

"""Engine-owned registration for trusted widget assets."""

from threading import RLock

from .core import WidgetAsset


[docs] class WidgetAssetConflictError(ValueError): """Report conflicting declarations for one widget asset path. :param path: Conflicting deployment-relative asset path. """ def __init__(self, path: str) -> None:
[docs] self.path = path
super().__init__(f"conflicting widget asset declaration for {path!r}")
[docs] class WidgetAssetRegistry: """Register immutable widget assets for one engine instance. Registration is atomic across a batch and rejects conflicting content before changing the registry. """ def __init__(self) -> None: self._assets: dict[str, WidgetAsset] = {} self._lock = RLock()
[docs] def register(self, asset: WidgetAsset) -> WidgetAsset: """Register one widget asset. :param asset: Asset declaration to register. :return: The registered asset. :raises WidgetAssetConflictError: If the path has different content. """ return self.register_many((asset,))[0]
[docs] def register_many(self, assets: tuple[WidgetAsset, ...]) -> tuple[WidgetAsset, ...]: """Register a batch after preflighting every conflict. :param assets: Asset declarations to register. :return: Registered assets in input order. :raises WidgetAssetConflictError: If any path has different content. """ with self._lock: declarations: dict[str, WidgetAsset] = {} for asset in assets: local = declarations.get(asset.path) if local is not None and local != asset: raise WidgetAssetConflictError(asset.path) declarations[asset.path] = asset for path, asset in declarations.items(): existing = self._assets.get(path) if existing is not None and existing != asset: raise WidgetAssetConflictError(path) self._assets.update({path: asset for path, asset in declarations.items() if path not in self._assets}) return tuple(self._assets[asset.path] for asset in assets)
[docs] def get(self, path: str) -> WidgetAsset | None: """Look up an asset by its deployment-relative path. :param path: Registered asset path. :return: Registered asset, or ``None`` when absent. """ with self._lock: return self._assets.get(path)