Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Check import organization in Python files and identify files that need manual standardization. | |
| """ | |
| import ast | |
| import os | |
| from pathlib import Path | |
| from typing import List, Tuple | |
| def analyze_imports(file_path: str) -> Tuple[bool, List[str]]: | |
| """Analyze imports in a file and return (is_standardized, issues)""" | |
| try: | |
| with open(file_path, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| tree = ast.parse(content) | |
| except (SyntaxError, UnicodeDecodeError) as e: | |
| return False, [f"Parse error: {e}"] | |
| issues = [] | |
| stdlib_imports = [] | |
| third_party_imports = [] | |
| local_imports = [] | |
| # Standard library modules (common ones) | |
| stdlib_modules = { | |
| 'os', 'sys', 'json', 'logging', 'datetime', 'time', 'asyncio', | |
| 'typing', 'functools', 'collections', 'itertools', 'pathlib', | |
| 'unittest', 'argparse', 'subprocess', 're', 'uuid', 'ssl', | |
| 'threading', 'ast', 'shutil', 'tempfile' | |
| } | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.Import): | |
| for alias in node.names: | |
| module_root = alias.name.split('.')[0] | |
| if module_root in stdlib_modules: | |
| stdlib_imports.append((node.lineno, f"import {alias.name}")) | |
| elif module_root in ['analytics', 'tests', 'scripts']: | |
| local_imports.append((node.lineno, f"import {alias.name}")) | |
| else: | |
| third_party_imports.append((node.lineno, f"import {alias.name}")) | |
| elif isinstance(node, ast.ImportFrom): | |
| if node.module: | |
| module_root = node.module.split('.')[0] | |
| names = [alias.name for alias in node.names] | |
| import_str = f"from {node.module} import {', '.join(names)}" | |
| if module_root in stdlib_modules: | |
| stdlib_imports.append((node.lineno, import_str)) | |
| elif module_root in ['analytics', 'tests', 'scripts'] or node.module.startswith('.'): | |
| local_imports.append((node.lineno, import_str)) | |
| else: | |
| third_party_imports.append((node.lineno, import_str)) | |
| # Check if imports are properly ordered | |
| all_imports = stdlib_imports + third_party_imports + local_imports | |
| all_imports.sort(key=lambda x: x[0]) # Sort by line number | |
| # Check ordering | |
| current_section = 'stdlib' | |
| for line_no, import_str in all_imports: | |
| if import_str.split()[1].split('.')[0] in stdlib_modules: | |
| if current_section not in ['stdlib']: | |
| issues.append(f"Line {line_no}: stdlib import after non-stdlib imports") | |
| current_section = 'stdlib' | |
| elif any(import_str.startswith(f"from {mod}") or import_str.startswith(f"import {mod}") | |
| for mod in ['analytics', 'tests', 'scripts']) or '.database' in import_str: | |
| if current_section not in ['stdlib', 'third_party', 'local']: | |
| issues.append(f"Line {line_no}: local import in wrong position") | |
| current_section = 'local' | |
| else: | |
| if current_section not in ['stdlib', 'third_party']: | |
| issues.append(f"Line {line_no}: third-party import after local imports") | |
| current_section = 'third_party' | |
| return len(issues) == 0, issues | |
| def main(): | |
| """Check import organization across the codebase""" | |
| project_root = Path(__file__).parent.parent.parent | |
| files_to_check = [] | |
| # Collect Python files | |
| for pattern in ['*.py', 'analytics/*.py', 'tests/*.py', 'scripts/**/*.py', 'archive/*.py']: | |
| files_to_check.extend(project_root.glob(pattern)) | |
| # Also check subdirectories | |
| for subdir in ['tests/unit', 'tests/integration', 'tests/utilities', 'tests/performance']: | |
| subdir_path = project_root / subdir | |
| if subdir_path.exists(): | |
| files_to_check.extend(subdir_path.glob('*.py')) | |
| print("🔍 Checking import organization...") | |
| needs_attention = [] | |
| well_organized = [] | |
| for file_path in files_to_check: | |
| if 'backup' in str(file_path): | |
| continue # Skip backup files | |
| is_standardized, issues = analyze_imports(str(file_path)) | |
| if is_standardized: | |
| well_organized.append(str(file_path)) | |
| else: | |
| needs_attention.append((str(file_path), issues)) | |
| print(f"\n✅ Well-organized files: {len(well_organized)}") | |
| for file_path in well_organized: | |
| rel_path = os.path.relpath(file_path, project_root) | |
| print(f" ✓ {rel_path}") | |
| print(f"\n⚠️ Files needing attention: {len(needs_attention)}") | |
| for file_path, issues in needs_attention: | |
| rel_path = os.path.relpath(file_path, project_root) | |
| print(f" 📝 {rel_path}") | |
| for issue in issues[:3]: # Show first 3 issues | |
| print(f" - {issue}") | |
| if len(issues) > 3: | |
| print(f" - ... and {len(issues) - 3} more issues") | |
| print(f"\n📊 Summary:") | |
| print(f" Total files checked: {len(well_organized) + len(needs_attention)}") | |
| print(f" Well-organized: {len(well_organized)}") | |
| print(f" Need attention: {len(needs_attention)}") | |
| if __name__ == "__main__": | |
| main() |