huge refactor of plugin system

This commit is contained in:
2026-04-30 18:56:22 -07:00
parent ea3ead248b
commit be5a11da97
99 changed files with 7603 additions and 11320 deletions
+99 -3
View File
@@ -25,6 +25,7 @@ from Store._base import Store as BaseStore
_SHA256_HEX_RE = re.compile(r"^[0-9a-fA-F]{64}$")
_DISCOVERED_CLASSES_CACHE: Optional[Dict[str, Type[BaseStore]]] = None
_PLUGIN_DISCOVERED_CLASSES_CACHE: Dict[str, Optional[Type[BaseStore]]] = {}
# Backends that failed to initialize earlier in the current process.
# Keyed by (store_type, instance_key) where instance_key is the name used under config.store.<type>.<instance_key>.
@@ -85,6 +86,101 @@ def _discover_store_classes() -> Dict[str, Type[BaseStore]]:
return discovered
def _extract_store_classes(owner: Any) -> Dict[str, Type[BaseStore]]:
discovered: Dict[str, Type[BaseStore]] = {}
def _add_candidate(key: Any, candidate: Any) -> None:
if not inspect.isclass(candidate):
return
if candidate is BaseStore:
return
if not issubclass(candidate, BaseStore):
return
normalized = _normalize_store_type(str(key or candidate.__name__))
if normalized:
discovered[normalized] = candidate
if owner is None:
return discovered
if inspect.isclass(owner):
_add_candidate(None, owner)
return discovered
if isinstance(owner, dict):
for key, candidate in owner.items():
_add_candidate(key, candidate)
return discovered
if isinstance(owner, (list, tuple, set, frozenset)):
for candidate in owner:
_add_candidate(None, candidate)
return discovered
try:
for key, candidate in vars(owner).items():
_add_candidate(key, candidate)
except Exception:
pass
return discovered
def _discover_plugin_store_class(store_type: str) -> Optional[Type[BaseStore]]:
normalized = _normalize_store_type(store_type)
if not normalized:
return None
cached = _PLUGIN_DISCOVERED_CLASSES_CACHE.get(normalized, None)
if normalized in _PLUGIN_DISCOVERED_CLASSES_CACHE:
return cached
try:
plugin_module = importlib.import_module(f"plugins.{normalized}")
except Exception:
_PLUGIN_DISCOVERED_CLASSES_CACHE[normalized] = None
return None
discovered: Dict[str, Type[BaseStore]] = {}
backend_hook = getattr(plugin_module, "get_store_backend_classes", None)
if callable(backend_hook):
try:
discovered.update(_extract_store_classes(backend_hook()))
except Exception as exc:
debug(f"[Store] Failed to load plugin store backends for '{normalized}': {exc}")
discovered.update(_extract_store_classes(getattr(plugin_module, "STORE_BACKENDS", None)))
if normalized not in discovered:
discovered.update(_extract_store_classes(plugin_module))
resolved = discovered.get(normalized)
if resolved is None and len(discovered) == 1:
resolved = next(iter(discovered.values()))
_PLUGIN_DISCOVERED_CLASSES_CACHE[normalized] = resolved
return resolved
def _resolve_store_class(
store_type: str,
classes_by_type: Optional[Dict[str, Type[BaseStore]]] = None,
) -> Optional[Type[BaseStore]]:
normalized = _normalize_store_type(store_type)
if not normalized:
return None
plugin_resolved = _discover_plugin_store_class(normalized)
if plugin_resolved is not None:
return plugin_resolved
discovered = classes_by_type if classes_by_type is not None else _discover_store_classes()
resolved = discovered.get(normalized)
if resolved is not None:
return resolved
return None
def _required_keys_for(store_cls: Type[BaseStore]) -> list[str]:
# Support new config_schema() schema
if hasattr(store_cls, "config_schema") and callable(store_cls.config_schema):
@@ -170,7 +266,7 @@ class Store:
store_type = _normalize_store_type(str(raw_store_type))
if store_type == "folder":
continue
store_cls = classes_by_type.get(store_type)
store_cls = _resolve_store_class(store_type, classes_by_type)
if store_cls is None:
# Skip provider-only names without debug warning
if store_type not in _PROVIDER_ONLY_STORE_NAMES and not self._suppress_debug:
@@ -373,7 +469,7 @@ def list_configured_backend_names(config: Optional[Dict[str, Any]]) -> list[str]
if store_type == "folder" or store_type in _PROVIDER_ONLY_STORE_NAMES:
continue
store_cls = classes_by_type.get(store_type)
store_cls = _resolve_store_class(store_type, classes_by_type)
if store_cls is None:
continue
@@ -417,7 +513,7 @@ def get_backend_instance(config: Optional[Dict[str, Any]], backend_name: str, *,
if not isinstance(instances, dict):
continue
store_type = _normalize_store_type(str(raw_store_type))
store_cls = classes_by_type.get(store_type)
store_cls = _resolve_store_class(store_type, classes_by_type)
if store_cls is None:
continue