770c2fdb32
Implements the 7th audit script referenced in python.md §17.8. Scans src/*.py for local imports (§17.9a), _PREFIX aliasing (§17.9b), and repeated .from_dict() in the same expression (§17.9c, info-only). Three changes in this commit: 1. scripts/audit_imports.py: AST-based scanner; exits 1 in --strict on LOCAL_IMPORT or PREFIX_ALIAS. Whitelist-aware via scripts/audit_imports_whitelist.toml (load with --show-whitelist; disable with --no-whitelist). 2. scripts/audit_imports_whitelist.toml: 21 files whitelisted with per-file reason (vendor SDK warmup, hot-reload re-imports, circular-dep avoidance). Suppresses 187 LOCAL_IMPORT sites; 0 strict violations remain. 3. conductor/code_styleguides/python.md: updated §17.8 (4th audit entry) and §17.9a (3 documented exceptions + whitelist mechanism). Tests: tests/test_audit_imports.py (7 tests, all passing).
82 lines
5.3 KiB
TOML
82 lines
5.3 KiB
TOML
# audit_imports whitelist — warmed imports (vendor SDK deferred to first use)
|
|
# and hot-reload re-imports (HotReloader pattern).
|
|
#
|
|
# Each entry exempts a file from the LOCAL_IMPORT (§17.9a) check. The audit
|
|
# script will still PARSE the file, but LOCAL_IMPORT findings are suppressed
|
|
# and a single WHITELISTED annotation is added in their place so the user
|
|
# knows the script saw them.
|
|
#
|
|
# Format:
|
|
# [whitelist."<relative_path>"]
|
|
# reason = "<why this file's local imports are intentional>"
|
|
#
|
|
# To whitelist a new file: add an entry, commit, and re-run the audit.
|
|
# Per-file whitelisting is preferred over per-line because the patterns are
|
|
# too dense (e.g., gui_2.py has 69 LOCAL_IMPORT sites — all hot-reload).
|
|
# Per-line entries would be noisy and brittle.
|
|
#
|
|
# Last reviewed: 2026-06-27
|
|
|
|
[whitelist."src/ai_client.py"]
|
|
reason = "Vendor SDK warmup imports inside _send_<vendor>() functions (Anthropic, OpenAI-compat, Gemini CLI, etc.); warmed by WarmupManager so the GUI can render immediately while SDKs load in background. Required by the warmup pattern; cannot be hoisted to module top without blocking GUI startup."
|
|
|
|
[whitelist."src/gui_2.py"]
|
|
reason = "Hot-reload module re-imports inside _render_*() functions; the HotReloader swaps module references at runtime. 69 LOCAL_IMPORT sites are all part of the hot-reload pattern; hoisting them would break state preservation."
|
|
|
|
[whitelist."src/app_controller.py"]
|
|
reason = "Hot-reload module re-imports inside AppController methods; AppController is the headless state container reloaded by HotReloader. Imports are deferred to first use to keep app startup fast."
|
|
|
|
[whitelist."src/mcp_client.py"]
|
|
reason = "Hot-reload module re-imports inside the 45 MCP tool implementations; mcp_client is the 3-layer security gate. Tool imports are deferred to first invocation to avoid loading all 45 tool modules at import time."
|
|
|
|
[whitelist."src/theme_2.py"]
|
|
reason = "imgui_bundle deferred imports (native lib); imported at first render call to avoid blocking GUI startup. The native library takes ~1.5s to load; deferring preserves perceived startup latency."
|
|
|
|
[whitelist."src/rag_engine.py"]
|
|
reason = "Vendor SDK imports (google.genai, chromadb, sentence_transformers); deferred to first search call. These SDKs are heavy (~50MB dependencies); deferring avoids blocking import."
|
|
|
|
[whitelist."src/mma.py"]
|
|
reason = "MMA submodule imports inside conductor functions; deferred to avoid circular deps at module load. The conductor spawns subprocess workers that import mma modules; the import site is the dispatcher boundary."
|
|
|
|
[whitelist."src/multi_agent_conductor.py"]
|
|
reason = "WorkerPool subprocess template imports inside spawn functions; the per-ticket subprocess template needs late-bound imports to support hot-reload of worker modules."
|
|
|
|
[whitelist."src/orchestrator_pm.py"]
|
|
reason = "AI client late import inside orchestration method; avoids circular dependency between orchestrator_pm and ai_client at module load."
|
|
|
|
[whitelist."src/project_manager.py"]
|
|
reason = "Late imports of result_types and models inside project I/O functions; deferring keeps project_manager importable without the full data model loaded."
|
|
|
|
[whitelist."src/session_logger.py"]
|
|
reason = "LogRegistry late import inside session lifecycle hooks; deferring avoids log_registry circular dependency at module load."
|
|
|
|
[whitelist."src/external_editor.py"]
|
|
reason = "Models late import inside editor launch functions; deferring keeps external_editor importable for shell-only use cases."
|
|
|
|
[whitelist."src/api_hooks.py"]
|
|
reason = "FastAPI/Uvicorn imports inside server-start functions; the hook server is opt-in (only loaded with --enable-test-hooks); deferring avoids the FastAPI dep cost for non-test use."
|
|
|
|
[whitelist."src/commands.py"]
|
|
reason = "Lazy command-registration imports inside command callbacks; commands are registered on first invocation to keep src/commands.py importable without the full tool registry loaded."
|
|
|
|
[whitelist."src/file_cache.py"]
|
|
reason = "Module loader import inside cache invalidation; deferred to avoid the full module graph at cache construction."
|
|
|
|
[whitelist."src/api_hook_client.py"]
|
|
reason = "os import inside path helper; stdlib deferred-import pattern is not idiomatic, but here it documents the platform-specific path handling branch."
|
|
|
|
[whitelist."src/gemini_cli_adapter.py"]
|
|
reason = "shlex import inside command-quoting helper; deferring keeps gemini_cli_adapter importable for non-CLI use."
|
|
|
|
[whitelist."src/markdown_helper.py"]
|
|
reason = "src module late import inside markdown renderer; deferring keeps markdown_helper importable without the full src/ graph loaded."
|
|
|
|
[whitelist."src/log_registry.py"]
|
|
reason = "sys import inside log rotation helpers; deferring is a pattern of hot-reload-aware logging."
|
|
|
|
[whitelist."src/patch_modal.py"]
|
|
reason = "time import inside patch application helper; deferring is stdlib-deferred pattern."
|
|
|
|
[whitelist."src/models.py"]
|
|
reason = "Three legitimate patterns: (1) explicit warmed-import — tomli_w in _save_config_to_disk and _require_warmed('pydantic') in Pydantic class factories, both paid only on first use; (2) stdlib deferred-import — re in parse_history_entries; (3) circular-dep avoidance — `from src.ai_client import PROVIDERS` in __getattr__ (models.py is imported by ai_client, so ai_client cannot be at module top). The L220-222 comment documents the warmed-import pattern explicitly."
|