annator-atom / backend /scripts /fix_indentation_errors.py
techprotrade's picture
Full stack ATOM backend + AIMONEYFLOW clients (port 7860) (part 5)
f84a02d verified
Raw
History Blame Contribute Delete
9.09 kB
#!/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())