From 00bee0011c1b407ab2cd90193bdfdf9546b79348 Mon Sep 17 00:00:00 2001 From: Nose Date: Fri, 16 Jan 2026 03:25:36 -0800 Subject: [PATCH] f --- API/folder.py | 9 ++++++++- CLI.py | 4 +++- cmdlet/_shared.py | 44 +++++++++++++++++++++++++++++++------------- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/API/folder.py b/API/folder.py index 7ec1f02..2c2b509 100644 --- a/API/folder.py +++ b/API/folder.py @@ -16,6 +16,7 @@ import logging import subprocess import shutil import time +import os from contextlib import contextmanager from datetime import datetime from pathlib import Path, PurePosixPath @@ -23,12 +24,18 @@ from threading import RLock from typing import Optional, Dict, Any, List, Tuple, Set from SYS.utils import sha256_file, expand_path -from SYS.logger import debug as mm_debug +from SYS.logger import debug as _debug logger = logging.getLogger(__name__) WORKER_LOG_MAX_ENTRIES = 50 # Reduced from 99 to keep log size down MAX_FINISHED_WORKERS = 100 # Only keep 100 finished workers globally +# Wrapper: only emit folder DB diagnostics when MM_DEBUG=1 +def mm_debug(*args, **kwargs): + if not os.environ.get("MM_DEBUG"): + return + _debug(*args, **kwargs) + # Helper: decorate DB write methods to retry transient SQLITE 'database is locked' errors def _db_retry(max_attempts: int = 6, base_sleep: float = 0.1): def _decorator(func): diff --git a/CLI.py b/CLI.py index 147f750..6861ed6 100644 --- a/CLI.py +++ b/CLI.py @@ -823,7 +823,9 @@ class CmdletIntrospection: normalized_arg = (arg_name or "").lstrip("-").strip().lower() if normalized_arg in ("storage", "store"): - backends = cls.store_choices(config, force=force) + # Use cached/lightweight names for completions to avoid instantiating backends + # (instantiating backends may perform initialization such as opening folder DBs). + backends = cls.store_choices(config, force=False) if backends: return backends diff --git a/cmdlet/_shared.py b/cmdlet/_shared.py index c09d9d9..efb4417 100644 --- a/cmdlet/_shared.py +++ b/cmdlet/_shared.py @@ -222,19 +222,27 @@ class SharedArgs: if not force and hasattr(SharedArgs, "_cached_available_stores"): return SharedArgs._cached_available_stores or [] - # Refresh the cache - SharedArgs._refresh_store_choices_cache(config) + # Refresh the cache. When not forcing, prefer a lightweight configured-name + # pass to avoid instantiating backends (which may perform work such as opening DBs). + if not force: + SharedArgs._refresh_store_choices_cache(config, skip_instantiation=True) + else: + SharedArgs._refresh_store_choices_cache(config, skip_instantiation=False) return SharedArgs._cached_available_stores or [] @staticmethod - def _refresh_store_choices_cache(config: Optional[Dict[str, Any]] = None) -> None: + def _refresh_store_choices_cache(config: Optional[Dict[str, Any]] = None, skip_instantiation: bool = False) -> None: """Refresh the cached store choices list. Should be called once at startup. - This performs the actual StoreRegistry initialization check and caches the result. - Subsequent calls to get_store_choices() will use this cache. + This performs a lightweight pass first (reads configured names only, without + instantiating backend classes) to avoid side-effects during autocompletion or + other quick lookups. When `skip_instantiation` is False, the function will + attempt a full StoreRegistry initialization to filter out backends that failed + to initialize properly. Args: config: Config dict. If not provided, will try to load from config module. + skip_instantiation: When True, do not instantiate backend classes; use a lightweight list only. """ try: if config is None: @@ -245,17 +253,27 @@ class SharedArgs: SharedArgs._cached_available_stores = [] return - # Initialize registry once to filter disabled stores - from Store.registry import Store as StoreRegistry - + # Lightweight pass: return configured names without instantiating backends try: - registry = StoreRegistry(config=config, suppress_debug=True) - available = registry.list_backends() - SharedArgs._cached_available_stores = available or [] - except Exception: - # If registry creation fails, fallback to configured names from Store.registry import list_configured_backend_names SharedArgs._cached_available_stores = list_configured_backend_names(config) or [] + except Exception: + SharedArgs._cached_available_stores = [] + + # If caller explicitly requested a full scan, instantiate registry to get + # only backends that actually initialized successfully. + if skip_instantiation: + return + + try: + from Store.registry import Store as StoreRegistry + registry = StoreRegistry(config=config, suppress_debug=True) + available = registry.list_backends() + if available: + SharedArgs._cached_available_stores = available + except Exception: + # Keep the lightweight list if full initialization fails + pass except Exception: SharedArgs._cached_available_stores = []