import gradio as gr import ast, traceback, sys, io import subprocess, tempfile, os # ── Python static analysis ────────────────────────────────────────────────── def analyze_python(code: str): errors = [] warnings = [] # 1. Syntax check via ast try: tree = ast.parse(code) except SyntaxError as e: errors.append(f"SyntaxError (line {e.lineno}): {e.msg}") return errors, warnings, tree if 'tree' in dir() else None # 2. Walk AST for common issues for node in ast.walk(tree): # Bare except if isinstance(node, ast.ExceptHandler) and node.type is None: warnings.append(f"Line {node.lineno}: Bare `except:` — catch specific exceptions instead.") # == None instead of is None if isinstance(node, ast.Compare): for op, comp in zip(node.ops, node.comparators): if isinstance(op, (ast.Eq, ast.NotEq)) and isinstance(comp, ast.Constant) and comp.value is None: warnings.append(f"Line {node.lineno}: Use `is None` / `is not None` instead of `== None`.") # Mutable default argument if isinstance(node, ast.FunctionDef): for default in node.args.defaults: if isinstance(default, (ast.List, ast.Dict, ast.Set)): warnings.append(f"Line {node.lineno}: Mutable default argument in `{node.name}()` — use None instead.") # Undefined names (basic) if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load): pass # would need scope tracking — skipped for simplicity return errors, warnings, tree def run_python(code: str): """Safely run code in a subprocess with timeout.""" try: with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f: f.write(code) fname = f.name result = subprocess.run( [sys.executable, fname], capture_output=True, text=True, timeout=5 ) os.unlink(fname) stdout = result.stdout.strip() stderr = result.stderr.strip() return stdout, stderr except subprocess.TimeoutExpired: return "", "⏱️ Execution timed out (5s limit)." except Exception as e: return "", str(e) def analyze_javascript(code: str): """Basic JS pattern checks.""" errors, warnings = [], [] lines = code.split("\n") for i, line in enumerate(lines, 1): s = line.strip() if s.startswith("var "): warnings.append(f"Line {i}: Use `let` or `const` instead of `var`.") if "==" in s and "===" not in s and "!==" not in s: warnings.append(f"Line {i}: Use `===` instead of `==` for strict equality.") if "eval(" in s: errors.append(f"Line {i}: Avoid `eval()` — security risk.") if s.endswith("{") is False and s and not s.startswith("//") and not s.endswith(";") and not s.endswith("{") and not s.endswith("}") and not s.endswith(","): pass # too noisy to flag missing semicolons return errors, warnings def check_code(code, language): if not code.strip(): return "❌ Please paste some code first." result_parts = [] if language == "Python": errors, warnings, _ = analyze_python(code) stdout, stderr = run_python(code) if errors: result_parts.append("### 🔴 Errors\n" + "\n".join(f"- {e}" for e in errors)) if stderr: result_parts.append("### 🔴 Runtime Error\n```\n" + stderr + "\n```") if warnings: result_parts.append("### 🟡 Warnings\n" + "\n".join(f"- {w}" for w in warnings)) if stdout: result_parts.append("### ✅ Output\n```\n" + stdout + "\n```") if not errors and not warnings and not stderr: result_parts.append("### ✅ No issues found!\nCode looks clean.") elif language == "JavaScript": errors, warnings = analyze_javascript(code) if errors: result_parts.append("### 🔴 Errors\n" + "\n".join(f"- {e}" for e in errors)) if warnings: result_parts.append("### 🟡 Warnings\n" + "\n".join(f"- {w}" for w in warnings)) if not errors and not warnings: result_parts.append("### ✅ No issues found!\nCode looks clean.") else: result_parts.append("ℹ️ Basic analysis only for this language.\nPaste your code and check manually for syntax issues.") return "\n\n".join(result_parts) def suggest_fix(code, language, analysis_result): """Rule-based fix suggestions.""" if not code.strip(): return "Paste code first." fixes = [] if language == "Python": fixed = code if "except:" in fixed: fixed = fixed.replace("except:", "except Exception as e:") fixes.append("- Replaced bare `except:` with `except Exception as e:`") if "== None" in fixed: fixed = fixed.replace("== None", "is None") fixes.append("- Replaced `== None` with `is None`") if "!= None" in fixed: fixed = fixed.replace("!= None", "is not None") fixes.append("- Replaced `!= None` with `is not None`") if fixes: return "### 🔧 Auto-Fixed Code\n```python\n" + fixed + "\n```\n\n**Changes made:**\n" + "\n".join(fixes) return "### ✅ No automatic fixes needed.\nReview warnings manually." elif language == "JavaScript": fixed = code if "var " in fixed: fixed = fixed.replace("var ", "let ") fixes.append("- Replaced `var` with `let`") if fixes: return "### 🔧 Auto-Fixed Code\n```javascript\n" + fixed + "\n```\n\n**Changes made:**\n" + "\n".join(fixes) return "### ✅ No automatic fixes needed." return "Auto-fix not available for this language." with gr.Blocks(title="Code Error Finder", theme=gr.themes.Soft()) as app: gr.Markdown("# 🐛 Code Error Finder & Fixer\nPaste code → detect errors → get fix suggestions.") with gr.Row(): with gr.Column(scale=2): language = gr.Dropdown( choices=["Python", "JavaScript", "Other"], value="Python", label="Language" ) code_input = gr.Code(label="Paste Your Code", language="python", lines=20) with gr.Column(scale=2): analysis_out = gr.Markdown(label="Analysis Result") fix_out = gr.Markdown(label="Fix Suggestions") with gr.Row(): check_btn = gr.Button("🔍 Analyze Code", variant="primary") fix_btn = gr.Button("🔧 Suggest Fix", variant="secondary") check_btn.click(check_code, inputs=[code_input, language], outputs=analysis_out) fix_btn.click(suggest_fix, inputs=[code_input, language, analysis_out], outputs=fix_out) gr.Examples( examples=[ ["x = None\nif x == None:\n print('is none')\n\ntry:\n y = 1/0\nexcept:\n pass", "Python"], ["var x = 5;\nif (x == '5') {\n console.log('equal');\n}", "JavaScript"], ], inputs=[code_input, language] ) app.launch()