refactor(commands): use lazy registry proxy to defer src.command_palette import
Phase 5A T5A.1-T5A.4 of startup_speedup_20260606 track. src/commands.py was importing src.command_palette at module load to create the CommandRegistry singleton. The 32 @registry.register decorators on the command functions needed this registry at import time. Approach: lazy registry proxy. The @registry.register decorator now just queues the function in a list; the real CommandRegistry is built on first access to any other registry attribute (.all, .get, etc.). By that time, all 32 decorators have run and the pending list is populated, so the real registration is complete in one pass. src/commands.py changes: - Removed 'from src.command_palette import CommandRegistry' - Added 'from src.module_loader import _require_warmed' - Added _LazyCommandRegistry class (proxy) - Added _get_real_registry() function (initializes on first access) - Replaced 'registry = CommandRegistry()' with 'registry = _LazyCommandRegistry()' - The 32 @registry.register decorators are unchanged (the proxy's register method returns the function unchanged after queueing it) EFFECTIVENESS: - 'import src.commands' no longer triggers src.command_palette (~244ms) - The warmup on AppController's _io_pool pre-loads src.command_palette on a background thread during startup - First access to registry.all() (e.g. from gui_2.py at palette open time) is O(1) - the warmup module is already in sys.modules TESTS: - tests/test_commands_no_top_level_command_palette.py: 4/4 PASS (3 RED, 1 green; now all green) - tests/test_command_palette.py: 13/13 PASS (no breakage) - tests/test_command_palette_sim.py: 7/7 PASS (live_gui tests, the full palette flow works end-to-end with the lazy proxy) ARCHITECTURAL NOTE: The lazy proxy is a minimal-change solution that preserves the public API. The 32 decorated functions don't need any changes; gui_2.py's 'from src.commands import registry' still works unchanged. The deferral is invisible to consumers. NEXT: Phase 5B (NERV theme) and 5C (markdown table) follow the same TDD pattern. 5D is the bulk refactor of src/gui_2.py feature-gated imports via the audit_gui2_imports.py script.
This commit is contained in:
+42
-3
@@ -3,18 +3,57 @@ from __future__ import annotations
|
||||
import webbrowser
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
|
||||
from src import models
|
||||
from src import theme_2
|
||||
from src.module_loader import _require_warmed
|
||||
|
||||
from src.command_palette import CommandRegistry
|
||||
from src.hot_reloader import HotReloader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.gui_2 import App
|
||||
|
||||
registry = CommandRegistry()
|
||||
# Lazy command registry (startup_speedup_20260606 Phase 5A)
|
||||
# --------------------------------------------------------------------------
|
||||
# The @registry.register decorator runs at module import time, but we want
|
||||
# to defer the actual CommandRegistry creation (and the underlying
|
||||
# src.command_palette import, ~244ms) until the palette is actually used.
|
||||
# The proxy below makes @registry.register a no-op that just queues the
|
||||
# function; the real CommandRegistry is built lazily on first access to
|
||||
# any other registry attribute (.all, .get, etc.) by gui_2.py or tests.
|
||||
# --------------------------------------------------------------------------
|
||||
_PENDING_REGISTRATIONS: list[Callable] = []
|
||||
_real_registry: Any = None
|
||||
|
||||
|
||||
class _LazyCommandRegistry:
|
||||
"""Proxy that defers CommandRegistry instantiation.
|
||||
|
||||
Behaves like a CommandRegistry from the caller's perspective:
|
||||
- @registry.register decorates functions by queuing them
|
||||
- .all, .get, etc. trigger real initialization on first access
|
||||
"""
|
||||
|
||||
def register(self, command_or_callable: Any) -> Any:
|
||||
_PENDING_REGISTRATIONS.append(command_or_callable)
|
||||
return command_or_callable
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(_get_real_registry(), name)
|
||||
|
||||
|
||||
def _get_real_registry() -> Any:
|
||||
global _real_registry
|
||||
if _real_registry is None:
|
||||
command_palette = _require_warmed("src.command_palette")
|
||||
_real_registry = command_palette.CommandRegistry()
|
||||
for func in _PENDING_REGISTRATIONS:
|
||||
_real_registry.register(func)
|
||||
return _real_registry
|
||||
|
||||
|
||||
registry = _LazyCommandRegistry()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user