File size: 9,091 Bytes
383cb38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/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())