Download bug_checker.py from ashritaa-chinnari123/code_explainer_bug_checker_NLP_project: direct link, hf CLI and curl.
- Browser
- Download file 5.24 kB
-
https://huggingface.co/spaces/ashritaa-chinnari123/code_explainer_bug_checker_NLP_project/resolve/main/bug_checker.py
- Command line
-
hf download hf://spaces/ashritaa-chinnari123/code_explainer_bug_checker_NLP_project/bug_checker.py
-
curl -L -o bug_checker.py https://huggingface.co/spaces/ashritaa-chinnari123/code_explainer_bug_checker_NLP_project/resolve/main/bug_checker.py
5.24 kB
| """ | |
| Bug checker — v2, using real static analysis instead of a heuristic. | |
| Why the old approach was wrong: | |
| The previous version fed code into a base (non-fine-tuned) CodeT5 model and | |
| flagged "bug" whenever its generated output differed from the input. That's | |
| not bug detection -- an untrained generative model will almost always | |
| produce *some* different text, so it was flagging correct code constantly. | |
| What this version does instead: | |
| 1. Syntax check via Python's own `ast.parse` -- catches things like missing | |
| colons, unmatched brackets, bad indentation. 100% accurate, since it's | |
| Python's real parser, not a guess. | |
| 2. Static analysis via `pyflakes` -- catches real bugs: undefined names, | |
| unused imports/variables, redefined functions, unreachable code, etc. | |
| This is the same engine that powers linters in VS Code / PyCharm. | |
| 3. (Optional) The fine-tuned seq2seq model from train.py, if you've trained | |
| one, is used ONLY to suggest a fix once a real issue has already been | |
| found by steps 1-2 -- not to decide whether a bug exists in the first | |
| place. | |
| Install: pip install pyflakes | |
| """ | |
| import ast | |
| import io | |
| from pyflakes.api import check as pyflakes_check | |
| from pyflakes.reporter import Reporter | |
| import pycodestyle | |
| class _ListReport(pycodestyle.BaseReport): | |
| """Collects pycodestyle warnings into a list instead of printing them.""" | |
| def __init__(self, options): | |
| super().__init__(options) | |
| self.results = [] | |
| def error(self, line_number, offset, text, check): | |
| code = super().error(line_number, offset, text, check) | |
| if code: | |
| self.results.append(f"line {line_number}, col {offset + 1}: {text}") | |
| return code | |
| def check_style(code: str): | |
| """ | |
| Runs pycodestyle (PEP8) checks. | |
| Returns a list of style warning strings, e.g.: | |
| ["line 2, col 80: E501 line too long (85 > 79 characters)"] | |
| Empty list means no style issues found. | |
| This is SEPARATE from real bugs -- style issues don't break the code, | |
| they just deviate from PEP8 conventions (naming, spacing, line length). | |
| """ | |
| lines = code.splitlines(keepends=True) | |
| guide = pycodestyle.StyleGuide(reporter=_ListReport) | |
| report = guide.init_report() | |
| checker = pycodestyle.Checker(lines=lines, options=guide.options, report=report) | |
| try: | |
| checker.check_all() | |
| except Exception: | |
| return ["Style check could not process this code."] | |
| return report.results | |
| def check_syntax(code: str): | |
| """Returns (is_valid: bool, error_message: str or None)""" | |
| try: | |
| ast.parse(code) | |
| return True, None | |
| except SyntaxError as e: | |
| return False, f"Syntax error at line {e.lineno}: {e.msg}" | |
| def check_static_issues(code: str): | |
| """ | |
| Runs pyflakes static analysis. | |
| Returns a list of issue strings, e.g.: | |
| ["line 3: undefined name 'x'", "line 1: 'os' imported but unused"] | |
| Empty list means no issues found. | |
| """ | |
| out_stream = io.StringIO() | |
| err_stream = io.StringIO() | |
| reporter = Reporter(out_stream, err_stream) | |
| try: | |
| pyflakes_check(code, filename="<input>", reporter=reporter) | |
| except Exception: | |
| # pyflakes itself can't handle the code (e.g. severe syntax issues) -- | |
| # the syntax check above should catch these first, so this is a safety net. | |
| return ["Static analysis could not process this code."] | |
| issues = [] | |
| for stream in (out_stream, err_stream): | |
| text = stream.getvalue().strip() | |
| if text: | |
| issues.extend(line.replace("<input>:", "line ") for line in text.split("\n")) | |
| return issues | |
| def detect_bugs(code: str): | |
| """ | |
| Main entry point. Returns: | |
| { | |
| "has_bug": bool, | |
| "syntax_valid": bool, | |
| "syntax_error": str or None, | |
| "static_issues": list[str], | |
| "summary": str -- human-readable summary | |
| } | |
| """ | |
| syntax_valid, syntax_error = check_syntax(code) | |
| if not syntax_valid: | |
| return { | |
| "has_bug": True, | |
| "syntax_valid": False, | |
| "syntax_error": syntax_error, | |
| "static_issues": [], | |
| "summary": f"Syntax error found: {syntax_error}", | |
| } | |
| static_issues = check_static_issues(code) | |
| has_bug = len(static_issues) > 0 | |
| if has_bug: | |
| summary = f"{len(static_issues)} issue(s) found via static analysis." | |
| else: | |
| summary = "No issues found by static analysis (syntax is valid, no undefined names, no unused imports/variables detected)." | |
| return { | |
| "has_bug": has_bug, | |
| "syntax_valid": True, | |
| "syntax_error": None, | |
| "static_issues": static_issues, | |
| "summary": summary, | |
| } | |
| if __name__ == "__main__": | |
| # Test cases | |
| examples = [ | |
| "def add(a, b)\n return a + b", # syntax error (missing colon) | |
| "def add(a, b):\n return a + c", # undefined name 'c' | |
| "import os\ndef add(a, b):\n return a + b", # unused import | |
| "def add(a, b):\n return a + b", # actually fine | |
| ] | |
| for code in examples: | |
| print("CODE:\n", code) | |
| print("RESULT:", detect_bugs(code)) | |
| print("-" * 60) |