Files
rays 8537b1b082 refactor: remove unused frontend modules, helpers and a stray script
Remove code that nothing imports or calls:

- web/doctor_chat.js (the ChatPanel class, superseded by the chat
  island; only its import in doctor_ui.js remained), web/doctor_chat.css
  and web/doctor_telemetry.js. ComfyUI still loaded these files as
  extension scripts, but they had no module-level side effects.
- DoctorUI.triggerAIAnalysis and DoctorUI.updatePollInterval;
  DoctorAPI.getHistory, DoctorAPI.verifyApiKey and DoctorAPI.chat;
  renderContentPipeline.
- The first, shadowed PatternLoader.get_pattern_info definition (the
  later one was always the one in effect).
- verify_routes.py, which could not run under package imports and
  replaced sys.modules entries with mocks when imported.

The architecture guide lists only existing frontend modules, enforced by
a new test, and the import-scan skip lists no longer name the script.
2026-10-13 14:45:04 +08:00

302 lines
10 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import ast
import importlib
import importlib.abc
import importlib.util
import io
import os
import sys
import tempfile
import traceback
import types
import uuid
from contextlib import contextmanager
from pathlib import Path
from typing import Iterable
PROJECT_ROOT = Path(__file__).resolve().parent.parent
PACKAGE_MODULES = [
"terminal_output",
"config",
"history_store",
"logger",
"outbound",
"session_manager",
"statistics",
"system_info",
"telemetry",
"analyzer",
"api_routes",
"pipeline.orchestrator",
"pipeline.plugins",
"pipeline.stages.pattern_matcher",
"pipeline.stages.llm_builder",
"services.job_manager",
"services.routes",
"services.intent",
"services.diagnostics",
"services.diagnostics.store",
"services.diagnostics.runner",
"services.policy",
"services.providers.base",
"services.providers.registry",
"services.prompt_composer",
"services.prompt_helpers",
"services.log_ring_buffer",
]
SKIP_STATIC_IMPORT_SCAN = {"prestartup_script.py"}
SKIP_SCAN_DIR_NAMES = {"__pycache__", ".venv", ".venv-wsl", "tests", "scripts", "reference", "docs", "web", "node_modules"}
ALLOWED_BARE_INTERNAL_IMPORTS = {"import_compat"}
class _BlockedTopLevelFinder(importlib.abc.MetaPathFinder):
def __init__(self, blocked_top_levels: set[str]):
self.blocked_top_levels = blocked_top_levels
def find_spec(self, fullname, path=None, target=None):
if fullname.split(".")[0] in self.blocked_top_levels:
raise ModuleNotFoundError(f"Blocked host-like internal import: {fullname}")
return None
class AsciiOnlyStream(io.StringIO):
encoding = "cp1252"
def write(self, message):
text = str(message)
text.encode("ascii")
return super().write(text)
def isatty(self):
return False
def fileno(self):
raise OSError("No file descriptor available")
def _project_init_path(project_root: Path) -> Path:
init_path = project_root / "__init__.py"
if not init_path.is_file():
raise FileNotFoundError(f"Missing canonical package entrypoint: {init_path}")
return init_path
def _internal_top_levels(project_root: Path) -> set[str]:
names = {"services", "pipeline"}
for path in project_root.glob("*.py"):
if path.name.startswith("test_"):
continue
names.add(path.stem)
return names
def _should_detach(module_name: str, internal_top_levels: set[str]) -> bool:
return any(
module_name == prefix or module_name.startswith(f"{prefix}.")
for prefix in internal_top_levels
)
@contextmanager
def synthetic_package_import_context(project_root: Path):
internal_top_levels = _internal_top_levels(project_root)
package_name = f"doctor_hostload_{uuid.uuid4().hex}"
original_sys_path = list(sys.path)
original_meta_path = list(sys.meta_path)
detached = {
name: sys.modules.pop(name)
for name in list(sys.modules)
if _should_detach(name, internal_top_levels)
}
blocked_finder = _BlockedTopLevelFinder(set(internal_top_levels))
filtered_path = []
for entry in original_sys_path:
try:
if Path(entry or ".").resolve() == project_root.resolve():
continue
except Exception:
pass
filtered_path.append(entry)
try:
sys.path[:] = filtered_path
sys.meta_path.insert(0, blocked_finder)
package = types.ModuleType(package_name)
package.__file__ = str(_project_init_path(project_root))
package.__package__ = package_name
package.__path__ = [str(project_root)]
sys.modules[package_name] = package
yield package_name
finally:
for name in list(sys.modules):
if name == package_name or name.startswith(f"{package_name}."):
sys.modules.pop(name, None)
sys.modules.update(detached)
sys.path[:] = original_sys_path
sys.meta_path[:] = original_meta_path
# IMPORTANT: keep this check host-like. The point is to validate package imports
# without assuming the extension root is a top-level import root.
def check_package_imports(project_root: Path = PROJECT_ROOT, modules: Iterable[str] = PACKAGE_MODULES) -> list[str]:
issues: list[str] = []
with synthetic_package_import_context(project_root) as package_name:
for module_name in modules:
try:
module = importlib.import_module(f"{package_name}.{module_name}")
if module.__name__ != f"{package_name}.{module_name}":
issues.append(f"{module_name}: imported as unexpected module name {module.__name__}")
except Exception as exc:
issues.append(
f"{module_name}: {exc.__class__.__name__}: {exc}"
)
return issues
def check_prestartup_bootstrap(project_root: Path = PROJECT_ROOT) -> list[str]:
issues: list[str] = []
module_name = f"doctor_prestartup_{uuid.uuid4().hex}"
script_path = project_root / "prestartup_script.py"
if not script_path.exists():
return [f"Missing prestartup script: {script_path}"]
original_stdout = sys.stdout
original_stderr = sys.stderr
original_env = {key: os.environ.get(key) for key in ("COMFYUI_DOCTOR_LOG_PATH", "COMFYUI_DOCTOR_LOG_DIR")}
ascii_stream = AsciiOnlyStream()
module = None
with synthetic_package_import_context(project_root):
try:
sys.stdout = ascii_stream
sys.stderr = ascii_stream
with tempfile.TemporaryDirectory(prefix="doctor-hostload-") as tmpdir:
os.environ["COMFYUI_DOCTOR_LOG_DIR"] = tmpdir
os.environ.pop("COMFYUI_DOCTOR_LOG_PATH", None)
spec = importlib.util.spec_from_file_location(module_name, script_path)
if spec is None or spec.loader is None:
return [f"Unable to build import spec for {script_path}"]
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
except Exception as exc:
issues.append(f"prestartup_script.py: {exc.__class__.__name__}: {exc}")
issues.append(traceback.format_exc(limit=4).strip())
finally:
if module is not None and hasattr(module, "PrestartupLogger"):
try:
module.PrestartupLogger.uninstall()
except Exception:
pass
sys.modules.pop(module_name, None)
sys.stdout = original_stdout
sys.stderr = original_stderr
for key, value in original_env.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
return issues
def _iter_product_python_files(project_root: Path) -> Iterable[Path]:
for path in sorted(project_root.glob("*.py")):
if path.name not in SKIP_STATIC_IMPORT_SCAN:
yield path
for folder_name in ("services", "pipeline"):
base = project_root / folder_name
if not base.exists():
continue
for path in sorted(base.rglob("*.py")):
if any(part in SKIP_SCAN_DIR_NAMES for part in path.parts):
continue
yield path
def check_import_policy(project_root: Path = PROJECT_ROOT) -> list[str]:
issues: list[str] = []
internal_top_levels = _internal_top_levels(project_root)
for path in _iter_product_python_files(project_root):
try:
source = path.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(path))
except Exception as exc:
issues.append(f"{path.relative_to(project_root)}: failed to parse source: {exc}")
continue
relative_modules: set[str] = set()
absolute_modules: list[tuple[int, str]] = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
if not node.module:
continue
first = node.module.split(".")[0]
if first not in internal_top_levels:
continue
if node.level > 0:
relative_modules.add(node.module)
else:
absolute_modules.append((node.lineno, node.module))
elif isinstance(node, ast.Import):
for alias in node.names:
first = alias.name.split(".")[0]
if first in internal_top_levels:
absolute_modules.append((node.lineno, alias.name))
for lineno, module_name in absolute_modules:
if module_name in ALLOWED_BARE_INTERNAL_IMPORTS:
continue
if module_name not in relative_modules:
issues.append(
f"{path.relative_to(project_root)}:{lineno}: bare internal import '{module_name}' without relative-first fallback"
)
return issues
def run_all_checks(project_root: Path = PROJECT_ROOT) -> dict[str, list[str]]:
return {
"package_imports": check_package_imports(project_root),
"prestartup_bootstrap": check_prestartup_bootstrap(project_root),
"import_policy": check_import_policy(project_root),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Validate host-like ComfyUI package/startup loading behavior.")
parser.add_argument("--project-root", default=str(PROJECT_ROOT), help="Project root to validate")
args = parser.parse_args(argv)
project_root = Path(args.project_root).resolve()
results = run_all_checks(project_root)
print(f"[host-load] Validating project root: {project_root}")
had_errors = False
for check_name, issues in results.items():
if issues:
had_errors = True
print(f"[host-load] FAIL {check_name}: {len(issues)} issue(s)")
for issue in issues:
print(f"[host-load] - {issue}")
else:
print(f"[host-load] PASS {check_name}")
if had_errors:
print("[host-load] Host-like validation failed.")
return 1
print("[host-load] All host-like package/startup checks passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())