"""Share one background model scan between workspaces without racing job writes.""" from __future__ import annotations from copy import deepcopy from dataclasses import asdict from PySide6.QtCore import QObject, Signal from PySide6.QtWidgets import QApplication from adam.assets import AssetRegistry from adam.ui.background import BackgroundRead class AssetRefresh(QObject): changed = Signal() def __init__(self, assets: AssetRegistry, config) -> None: super().__init__(QApplication.instance()) self.assets = assets self.config = config self._folders = None self.reader = BackgroundRead(self) self.reader.ready.connect(self._merge) def request(self, *, force: bool = False) -> None: folders = deepcopy(self.config.get("tool_folders", {})) if self._folders is not None and folders != self._folders: force = True self._folders = folders # Copy only data, not the attached Qt service. Discovery never writes # the registry from a worker or mutates objects used by active jobs. snapshot = object.__new__(AssetRegistry) snapshot.path = self.assets.path snapshot.assets = deepcopy(self.assets.assets) baseline = {item.id: asdict(item) for item in snapshot.assets} settings = deepcopy(self.config.settings) def read(): snapshot.discover(settings, persist=False) return baseline, snapshot.assets self.reader.request(read, force=force) def _merge(self, result) -> None: baseline, discovered = result current = {item.id: item for item in self.assets.assets} paths = {(item.kind, item.path) for item in current.values()} merged = [] for item in discovered: existing = current.get(item.id) if item.id in baseline: if existing is None: continue # The user removed it while discovery was running. merged.append(existing if asdict(existing) != baseline[item.id] else item) elif (item.kind, item.path) not in paths: merged.append(item) retained = {item.id for item in merged} for item in current.values(): if item.id not in retained and (item.id not in baseline or asdict(item) != baseline[item.id]): merged.append(item) if [asdict(item) for item in merged] != [asdict(item) for item in self.assets.assets]: self.assets.assets = merged self.assets.save() self.changed.emit() def asset_refresh(assets: AssetRegistry, config) -> AssetRefresh: service = getattr(assets, "_ui_refresh", None) if service is None: service = AssetRefresh(assets, config) assets._ui_refresh = service return service