Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Auto-fix script for indentation errors in Atom backend. | |
| This script fixes the common pattern where `with get_db_session() as db:` | |
| is followed by an incorrectly indented `try:` block. | |
| Also: | |
| - Removes redundant `finally: db.close()` blocks (context manager handles cleanup) | |
| - Adds proper exception handlers where missing | |
| - Replaces bare `except:` with proper logging | |
| """ | |
| import ast | |
| import os | |
| from pathlib import Path | |
| import re | |
| import sys | |
| from typing import List, Tuple | |
| # Backend directory | |
| BACKEND_DIR = Path("/Users/rushiparikh/projects/atom/backend") | |
| # Patterns to fix | |
| PATTERN_WITH_TRY = re.compile( | |
| r'(\s*)with get_db_session\(\) as db:\s*\n\s*try:', | |
| re.MULTILINE | |
| ) | |
| PATTERN_FINALLY_CLOSE = re.compile( | |
| r'\s*finally:\s*\n\s*db\.close\(\)\s*\n', | |
| re.MULTILINE | |
| ) | |
| PATTERN_BARE_EXCEPT = re.compile( | |
| r'except:\s*(?:pass|continue)\s*\n', | |
| re.MULTILINE | |
| ) | |
| def fix_indentation_and_try_blocks(content: str, filepath: str) -> Tuple[str, int]: | |
| """ | |
| Fix indentation issues with `with get_db_session()` followed by `try:`. | |
| Returns fixed content and number of fixes made. | |
| """ | |
| original_content = content | |
| fixes = 0 | |
| lines = content.split('\n') | |
| new_lines = [] | |
| i = 0 | |
| while i < len(lines): | |
| line = lines[i] | |
| # Pattern 1: `with get_db_session() as db:` followed by `try:` at same indentation | |
| if 'with get_db_session() as db:' in line: | |
| # Get the indentation of the with statement | |
| with_indent = len(line) - len(line.lstrip()) | |
| new_lines.append(line) | |
| i += 1 | |
| # Check if next line is `try:` with same or less indentation | |
| if i < len(lines): | |
| next_line = lines[i] | |
| try_indent = len(next_line) - len(next_line.lstrip()) if next_line.strip() else with_indent + 4 | |
| # If `try:` is at same indentation or less than `with`, fix it | |
| if 'try:' in next_line and try_indent <= with_indent + 1: | |
| # Add proper indentation (4 spaces more than with) | |
| fixed_try = ' ' * (with_indent + 4) + 'try:' | |
| new_lines.append(fixed_try) | |
| fixes += 1 | |
| i += 1 | |
| # Continue processing subsequent lines with adjusted indentation | |
| # until we exit the try block | |
| base_indent = with_indent + 4 | |
| while i < len(lines): | |
| current_line = lines[i] | |
| if not current_line.strip(): | |
| new_lines.append(current_line) | |
| i += 1 | |
| continue | |
| current_indent = len(current_line) - len(current_line.lstrip()) | |
| # Check if we've exited the try/except/finally block | |
| # (dedented back to or past the base with indentation) | |
| if current_line.strip() and current_indent <= with_indent and not current_line.strip().startswith(('except', 'finally', 'except Exception', 'except ValueError', 'except TypeError', 'except json.JSONDecodeError')): | |
| # We've exited the block, add the line as-is and break | |
| new_lines.append(current_line) | |
| i += 1 | |
| break | |
| # Check for redundant `finally: db.close()` | |
| if 'finally:' in current_line and 'db.close()' in lines[i+1] if i+1 < len(lines) else False: | |
| # Skip the finally and db.close() lines | |
| i += 2 | |
| # Continue to add remaining lines at original indentation | |
| continue | |
| new_lines.append(current_line) | |
| i += 1 | |
| continue | |
| # Pattern 2: Replace bare `except: pass` or `except: continue` | |
| if 'except:' in line and ('pass' in line or 'continue' in line): | |
| # Extract indentation | |
| indent = len(line) - len(line.lstrip()) | |
| indent_str = ' ' * indent | |
| # Get context for better error message | |
| context = filepath.name | |
| if 'continue' in line: | |
| # Common in loops - add debug log | |
| new_lines.append(indent_str + 'except Exception as e:') | |
| new_lines.append(indent_str + ' logger.debug(f"Operation failed in {context}: {{e}}")') | |
| new_lines.append(indent_str + ' continue') | |
| fixes += 1 | |
| else: | |
| # Replace with proper logging | |
| new_lines.append(indent_str + 'except Exception as e:') | |
| new_lines.append(indent_str + f' logger.warning(f"Operation failed in {context}: {{e}}")') | |
| fixes += 1 | |
| i += 1 | |
| continue | |
| # Pattern 3: Standalone bare `except:` followed by nothing | |
| if line.strip() == 'except:': | |
| indent = len(line) - len(line.lstrip()) | |
| indent_str = ' ' * indent | |
| # Look ahead to see what's after | |
| if i + 1 < len(lines) and ('pass' in lines[i+1] or 'continue' in lines[i+1]): | |
| # Skip the except line, the next pattern handler will catch it | |
| new_lines.append(line) | |
| else: | |
| # Replace with proper exception handler | |
| new_lines.append(indent_str + 'except Exception as e:') | |
| new_lines.append(indent_str + f' logger.warning(f"Unexpected error in {filepath.name}: {{e}}")') | |
| fixes += 1 | |
| i += 1 | |
| continue | |
| # Default: add line as-is | |
| new_lines.append(line) | |
| i += 1 | |
| return '\n'.join(new_lines), fixes | |
| def fix_file(filepath: Path) -> bool: | |
| """Fix a single file and return True if successful.""" | |
| try: | |
| with open(filepath, 'r') as f: | |
| content = f.read() | |
| fixed_content, fixes = fix_indentation_and_try_blocks(content, filepath) | |
| if fixes > 0: | |
| # Verify syntax is valid after fix | |
| try: | |
| ast.parse(fixed_content) | |
| except SyntaxError as e: | |
| print(f" ⚠️ Syntax error after fix: {e}") | |
| return False | |
| # Write back | |
| with open(filepath, 'w') as f: | |
| f.write(fixed_content) | |
| print(f" ✓ Fixed {fixes} issue(s)") | |
| return True | |
| else: | |
| return False | |
| except Exception as e: | |
| print(f" ✗ Error: {e}") | |
| return False | |
| def scan_and_fix(): | |
| """Scan all Python files and fix indentation errors.""" | |
| print("=" * 70) | |
| print("Auto-fixing indentation errors in Atom backend") | |
| print("=" * 70) | |
| print() | |
| # Find all Python files (excluding venv, __pycache__, etc.) | |
| python_files = [] | |
| for root, dirs, files in os.walk(BACKEND_DIR): | |
| # Skip certain directories | |
| dirs[:] = [d for d in dirs if d not in [ | |
| 'venv', '__pycache__', 'node_modules', '.git', | |
| 'dist', 'build', '.pytest_cache', 'scripts' | |
| ]] | |
| for file in files: | |
| if file.endswith('.py'): | |
| filepath = Path(root) / file | |
| python_files.append(filepath) | |
| print(f"Found {len(python_files)} Python files") | |
| print() | |
| # Check each file for syntax errors | |
| files_with_errors = [] | |
| for filepath in python_files: | |
| try: | |
| with open(filepath, 'r') as f: | |
| content = f.read() | |
| ast.parse(content) | |
| except (SyntaxError, IndentationError) as e: | |
| files_with_errors.append((filepath, e)) | |
| if not files_with_errors: | |
| print("✅ No syntax errors found!") | |
| return 0 | |
| print(f"Found {len(files_with_errors)} files with syntax errors:") | |
| print() | |
| fixed_count = 0 | |
| for filepath, error in files_with_errors: | |
| print(f"🔧 {filepath.relative_to(BACKEND_DIR)}:{error.lineno}") | |
| print(f" {error.msg}") | |
| # Attempt to fix | |
| if fix_file(filepath): | |
| fixed_count += 1 | |
| print() | |
| print("=" * 70) | |
| print(f"Fixed {fixed_count} / {len(files_with_errors)} files") | |
| print("=" * 70) | |
| print() | |
| # Verify all fixes | |
| print("Verifying fixes...") | |
| remaining_errors = [] | |
| for filepath, _ in files_with_errors: | |
| try: | |
| with open(filepath, 'r') as f: | |
| content = f.read() | |
| ast.parse(content) | |
| except (SyntaxError, IndentationError) as e: | |
| remaining_errors.append((filepath, e)) | |
| if remaining_errors: | |
| print(f"⚠️ {len(remaining_errors)} files still have errors:") | |
| for filepath, error in remaining_errors: | |
| print(f" {filepath.relative_to(BACKEND_DIR)}:{error.lineno} - {error.msg}") | |
| return 1 | |
| else: | |
| print("✅ All files fixed successfully!") | |
| return 0 | |
| if __name__ == '__main__': | |
| sys.exit(scan_and_fix()) | |