Files
orion/scripts/verify_critical_imports.py
Samir Boulahtit b8a46e1746 fix: protect critical re-export imports from linter removal
Problem:
- Ruff removed 'from app.core.database import Base' from models/database/base.py
- Import appeared "unused" (F401) but was actually a critical re-export
- Caused ImportError: cannot import name 'Base' at runtime
- Re-export pattern: import in one file to export from package

Solution:
1. Added F401 ignore for models/database/base.py in pyproject.toml
2. Created scripts/verify_critical_imports.py verification script
3. Integrated verification into make check and CI pipeline
4. Updated documentation with explanation

New Verification Script:
- Checks all critical re-export imports exist
- Detects import variations (parentheses, 'as' clauses)
- Handles SQLAlchemy declarative_base alternatives
- Runs as part of make check automatically

Protected Files:
- models/database/base.py - Re-exports Base for all models
- models/__init__.py - Exports Base for Alembic
- models/database/__init__.py - Exports Base from package
- All __init__.py files (already protected)

Makefile Changes:
- make verify-imports - Run import verification
- make check - Now includes verify-imports
- make ci - Includes verify-imports in pipeline

Documentation Updated:
- Code quality guide explains re-export protection
- Pre-commit workflow includes verification
- Examples of why re-exports matter

This prevents future issues where linters remove seemingly
"unused" imports that are actually critical for application structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 20:10:22 +01:00

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()