Move 9 init/seed scripts into scripts/seed/ and 7 validation scripts (+ validators/ subfolder) into scripts/validate/ to reduce clutter in the root scripts/ directory. Update all references across Makefile, CI/CD configs, pre-commit hooks, docs (~40 files), and Python imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
157 lines
5.2 KiB
Python
Executable File
157 lines
5.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Verify Critical Imports
|
|
========================
|
|
Checks that critical imports (re-exports) haven't been removed by linters.
|
|
|
|
This script verifies that essential import statements exist in key files,
|
|
preventing issues where tools like Ruff might remove imports that appear
|
|
unused but are actually critical for the application structure.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Define critical imports that must exist
|
|
# Format: {file_path: [(import_line, description)]}
|
|
CRITICAL_IMPORTS: dict[str, list[tuple[str, str]]] = {
|
|
"models/database/base.py": [
|
|
("from app.core.database import Base", "Re-export Base for all models"),
|
|
],
|
|
"models/__init__.py": [
|
|
("from .database.base import Base", "Export Base for Alembic and models"),
|
|
],
|
|
"models/database/__init__.py": [
|
|
("from .base import Base", "Export Base from database package"),
|
|
],
|
|
"app/core/database.py": [
|
|
(
|
|
"from sqlalchemy.ext.declarative import declarative_base",
|
|
"SQLAlchemy Base declaration",
|
|
),
|
|
# Note: Might also use sqlalchemy.orm declarative_base in newer versions
|
|
],
|
|
}
|
|
|
|
|
|
class ImportVerifier:
|
|
"""Verifies critical imports exist in codebase"""
|
|
|
|
def __init__(self, project_root: Path):
|
|
self.project_root = project_root
|
|
self.issues: list[str] = []
|
|
|
|
def verify_all(self) -> bool:
|
|
"""Verify all critical imports"""
|
|
print("🔍 Verifying critical imports...\n")
|
|
|
|
all_good = True
|
|
for file_path, imports in CRITICAL_IMPORTS.items():
|
|
if not self.verify_file(file_path, imports):
|
|
all_good = False
|
|
|
|
return all_good
|
|
|
|
def verify_file(
|
|
self, file_path: str, required_imports: list[tuple[str, str]]
|
|
) -> bool:
|
|
"""Verify imports in a single file"""
|
|
full_path = self.project_root / file_path
|
|
|
|
if not full_path.exists():
|
|
self.issues.append(f"❌ File not found: {file_path}")
|
|
print(f"❌ {file_path}: File not found")
|
|
return False
|
|
|
|
content = full_path.read_text()
|
|
file_ok = True
|
|
|
|
for import_line, description in required_imports:
|
|
# Check for exact import or variations
|
|
if import_line in content:
|
|
print(f"✅ {file_path}: {import_line}")
|
|
else:
|
|
# Check for alternative import formats
|
|
alternatives = self._get_import_alternatives(import_line)
|
|
found = any(alt in content for alt in alternatives)
|
|
|
|
if found:
|
|
print(f"✅ {file_path}: {import_line} (alternative format)")
|
|
else:
|
|
self.issues.append(
|
|
f"❌ {file_path}: Missing critical import\n"
|
|
f" Expected: {import_line}\n"
|
|
f" Purpose: {description}"
|
|
)
|
|
print(f"❌ {file_path}: Missing {import_line}")
|
|
file_ok = False
|
|
|
|
print()
|
|
return file_ok
|
|
|
|
def _get_import_alternatives(self, import_line: str) -> list[str]:
|
|
"""Get alternative formats for an import"""
|
|
alternatives = [import_line]
|
|
|
|
# Handle 'from x import y' vs 'from x import (y)'
|
|
if "from" in import_line and "import" in import_line:
|
|
parts = import_line.split("import")
|
|
if len(parts) == 2:
|
|
from_part = parts[0].strip()
|
|
import_part = parts[1].strip()
|
|
|
|
# Add parenthesized version
|
|
alternatives.append(f"{from_part} import ({import_part})")
|
|
|
|
# Add version with 'as' clause
|
|
alternatives.append(f"{import_line} as")
|
|
|
|
# Handle declarative_base alternatives (sqlalchemy changes)
|
|
if "declarative_base" in import_line:
|
|
# Old style
|
|
alternatives.append(
|
|
"from sqlalchemy.ext.declarative import declarative_base"
|
|
)
|
|
# New style (SQLAlchemy 1.4+)
|
|
alternatives.append("from sqlalchemy.orm import declarative_base")
|
|
|
|
return alternatives
|
|
|
|
def print_summary(self):
|
|
"""Print summary of verification"""
|
|
print("\n" + "=" * 80)
|
|
print("📊 CRITICAL IMPORTS VERIFICATION SUMMARY")
|
|
print("=" * 80)
|
|
|
|
if not self.issues:
|
|
print("\n✅ All critical imports verified successfully!")
|
|
print("\nAll re-export patterns are intact.")
|
|
else:
|
|
print(f"\n❌ Found {len(self.issues)} issue(s):\n")
|
|
for issue in self.issues:
|
|
print(issue)
|
|
print()
|
|
|
|
print("💡 RESOLUTION:")
|
|
print(" 1. Check if imports were removed by linter (Ruff)")
|
|
print(" 2. Add missing imports back to the files")
|
|
print(" 3. Update pyproject.toml to ignore F401 for these files")
|
|
print(" 4. Run this script again to verify")
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
project_root = Path(__file__).parent.parent
|
|
|
|
verifier = ImportVerifier(project_root)
|
|
success = verifier.verify_all()
|
|
verifier.print_summary()
|
|
|
|
sys.exit(0 if success else 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|