error-translator-cli-v2 1.1.5__tar.gz → 3.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/PKG-INFO +8 -23
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/README.md +6 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/pyproject.toml +6 -5
- error_translator_cli_v2-3.0.2/setup.py +16 -0
- error_translator_cli_v2-3.0.2/src/error_translator/__init__.py +7 -0
- error_translator_cli_v2-3.0.2/src/error_translator/api/server.py +94 -0
- error_translator_cli_v2-3.0.2/src/error_translator/ast/ast_engine.py +132 -0
- error_translator_cli_v2-3.0.2/src/error_translator/ast/ast_handlers.py +63 -0
- error_translator_cli_v2-3.0.2/src/error_translator/auto.py +37 -0
- error_translator_cli_v2-3.0.2/src/error_translator/banner.py +53 -0
- error_translator_cli_v2-3.0.2/src/error_translator/cli.py +75 -0
- error_translator_cli_v2-3.0.2/src/error_translator/core.py +109 -0
- error_translator_cli_v2-3.0.2/src/error_translator/ext/fast_matcher.c +95 -0
- error_translator_cli_v2-3.0.2/src/error_translator/parser.py +43 -0
- error_translator_cli_v2-3.0.2/src/error_translator/rules.py +40 -0
- error_translator_cli_v2-3.0.2/src/error_translator/runner.py +55 -0
- error_translator_cli_v2-3.0.2/src/error_translator/ui.py +161 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/PKG-INFO +8 -23
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/SOURCES.txt +11 -0
- error_translator_cli_v2-3.0.2/tests/test_ast.py +41 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/tests/test_core.py +17 -0
- error_translator_cli_v2-1.1.5/src/error_translator/__init__.py +0 -0
- error_translator_cli_v2-1.1.5/src/error_translator/auto.py +0 -22
- error_translator_cli_v2-1.1.5/src/error_translator/cli.py +0 -207
- error_translator_cli_v2-1.1.5/src/error_translator/core.py +0 -113
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/LICENSE +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/setup.cfg +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator/rules.json +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/dependency_links.txt +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/entry_points.txt +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/requires.txt +0 -0
- {error_translator_cli_v2-1.1.5 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/top_level.txt +0 -0
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: error-translator-cli-v2
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Offline Python traceback translator and error explainer CLI that converts exceptions into clear explanations and actionable fixes.
|
|
5
5
|
Author-email: Gourabananda Datta <gourabanandadatta@zohomail.in>
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2026 Gourabananda Datta
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
6
|
+
License: MIT
|
|
28
7
|
Project-URL: Homepage, https://github.com/gourabanandad/error-translator-cli-v2
|
|
29
8
|
Project-URL: Source, https://github.com/gourabanandad/error-translator-cli-v2
|
|
30
9
|
Project-URL: Documentation, https://gourabanandad.github.io/error-translator-cli-v2/
|
|
@@ -90,6 +69,7 @@ If this project saves you debugging time, please consider starring it on GitHub:
|
|
|
90
69
|
## Key Features
|
|
91
70
|
|
|
92
71
|
* **CLI-First Architecture**: Seamlessly process scripts, direct error strings, or piped logs via the `explain-error` command.
|
|
72
|
+
* **Professional Rich Terminal UI**: Clean panels, syntax-highlighted code context, structured sections, and improved readability for day-to-day debugging.
|
|
93
73
|
* **Automatic Integration Mode**: Inject the module via `import error_translator.auto` to automatically override `sys.excepthook` for graceful, translated crash reporting.
|
|
94
74
|
* **Extensible API Surfaces**: Integrate natively within Python or expose the core engine over HTTP via the included FastAPI server.
|
|
95
75
|
* **Deterministic Rules Engine**: High-performance, regex-based matching powered by `rules.json` guarantees offline and privacy-first translations.
|
|
@@ -137,6 +117,11 @@ explain-error --json "NameError: name 'x' is not defined"
|
|
|
137
117
|
# {"explanation": "...", "fix": "...", "matched_error": "...", "file": "...", "line": "...", "code": "...", "ast_insight": null}
|
|
138
118
|
```
|
|
139
119
|
|
|
120
|
+
Show an about screen with project metadata and quick usage examples:
|
|
121
|
+
```bash
|
|
122
|
+
explain-error --about
|
|
123
|
+
```
|
|
124
|
+
|
|
140
125
|
The `--json` flag works with every input mode (`run <script>`, raw string, piped log).
|
|
141
126
|
|
|
142
127
|
### 2. Automatic Import Hook
|
|
@@ -25,6 +25,7 @@ If this project saves you debugging time, please consider starring it on GitHub:
|
|
|
25
25
|
## Key Features
|
|
26
26
|
|
|
27
27
|
* **CLI-First Architecture**: Seamlessly process scripts, direct error strings, or piped logs via the `explain-error` command.
|
|
28
|
+
* **Professional Rich Terminal UI**: Clean panels, syntax-highlighted code context, structured sections, and improved readability for day-to-day debugging.
|
|
28
29
|
* **Automatic Integration Mode**: Inject the module via `import error_translator.auto` to automatically override `sys.excepthook` for graceful, translated crash reporting.
|
|
29
30
|
* **Extensible API Surfaces**: Integrate natively within Python or expose the core engine over HTTP via the included FastAPI server.
|
|
30
31
|
* **Deterministic Rules Engine**: High-performance, regex-based matching powered by `rules.json` guarantees offline and privacy-first translations.
|
|
@@ -72,6 +73,11 @@ explain-error --json "NameError: name 'x' is not defined"
|
|
|
72
73
|
# {"explanation": "...", "fix": "...", "matched_error": "...", "file": "...", "line": "...", "code": "...", "ast_insight": null}
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
Show an about screen with project metadata and quick usage examples:
|
|
77
|
+
```bash
|
|
78
|
+
explain-error --about
|
|
79
|
+
```
|
|
80
|
+
|
|
75
81
|
The `--json` flag works with every input mode (`run <script>`, raw string, piped log).
|
|
76
82
|
|
|
77
83
|
### 2. Automatic Import Hook
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "error-translator-cli-v2"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.2"
|
|
8
8
|
description = "Offline Python traceback translator and error explainer CLI that converts exceptions into clear explanations and actionable fixes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
|
-
license = {
|
|
11
|
+
license = { text = "MIT" }
|
|
12
12
|
authors = [
|
|
13
13
|
{ name = "Gourabananda Datta", email = "gourabanandadatta@zohomail.in" }
|
|
14
14
|
]
|
|
@@ -62,12 +62,13 @@ server = ["fastapi>=0.110", "pydantic>=2.6", "uvicorn>=0.29"]
|
|
|
62
62
|
docs = ["mkdocs>=1.6", "mkdocs-material>=9.7", "pymdown-extensions>=10.0"]
|
|
63
63
|
dev = ["pytest>=8.0", "build>=1.2", "twine>=5.0"]
|
|
64
64
|
|
|
65
|
-
[tool.setuptools]
|
|
66
|
-
|
|
67
|
-
packages = ["error_translator"]
|
|
65
|
+
[tool.setuptools.packages.find]
|
|
66
|
+
where = ["src"]
|
|
68
67
|
|
|
69
68
|
[tool.pytest.ini_options]
|
|
69
|
+
testpaths = ["tests"]
|
|
70
70
|
pythonpath = ["src"]
|
|
71
|
+
addopts = "--cov=error_translator --cov-report=term-missing"
|
|
71
72
|
|
|
72
73
|
[tool.setuptools.package-data]
|
|
73
74
|
error_translator = ["rules.json", "static/*.html", "static/*.css"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from setuptools import setup, Extension
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Windows (MSVC) uses /O2, Linux/Mac (GCC/Clang) uses -O3
|
|
5
|
+
compile_args = ['/O2'] if sys.platform == 'win32' else ['-O3']
|
|
6
|
+
|
|
7
|
+
fast_matcher_module = Extension(
|
|
8
|
+
'error_translator.fast_matcher',
|
|
9
|
+
sources=['src/error_translator/ext/fast_matcher.c'],
|
|
10
|
+
extra_compile_args=compile_args
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
setup(
|
|
15
|
+
ext_modules=[fast_matcher_module]
|
|
16
|
+
)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Server for Error Translation.
|
|
3
|
+
|
|
4
|
+
This module exposes the error translation functionality as a RESTful web API.
|
|
5
|
+
It includes endpoints for single and batch translations, and serves a static
|
|
6
|
+
web interface. Useful for integrating the translator into web apps or distributed systems.
|
|
7
|
+
"""
|
|
8
|
+
from src.error_translator.core import translate_error
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from fastapi import FastAPI
|
|
11
|
+
from fastapi.staticfiles import StaticFiles
|
|
12
|
+
from fastapi.responses import FileResponse
|
|
13
|
+
import os
|
|
14
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
15
|
+
import asyncio
|
|
16
|
+
from typing import List
|
|
17
|
+
|
|
18
|
+
# Try to determine the package version to expose in the API metadata.
|
|
19
|
+
try:
|
|
20
|
+
VERSION = version("error-translator-cli-v2")
|
|
21
|
+
except PackageNotFoundError:
|
|
22
|
+
VERSION = "unknown (not installed via pip)"
|
|
23
|
+
|
|
24
|
+
# Initialize the FastAPI application with basic metadata
|
|
25
|
+
app = FastAPI(
|
|
26
|
+
title="Error translator API",
|
|
27
|
+
description="An API that translates Python errors into human-readable English.",
|
|
28
|
+
version=VERSION
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# --- Pydantic Models for Request Validation ---
|
|
32
|
+
|
|
33
|
+
class ErrorRequest(BaseModel):
|
|
34
|
+
"""Schema for a single error translation request."""
|
|
35
|
+
traceback_setting: str
|
|
36
|
+
|
|
37
|
+
class BatchErrorRequest(BaseModel):
|
|
38
|
+
"""Schema for translating multiple errors in a single request."""
|
|
39
|
+
tracebacks: List[str]
|
|
40
|
+
|
|
41
|
+
# --- API Endpoints ---
|
|
42
|
+
|
|
43
|
+
@app.post("/translate")
|
|
44
|
+
def translation_endpoint(request: ErrorRequest):
|
|
45
|
+
"""
|
|
46
|
+
Translates a single Python traceback.
|
|
47
|
+
Accepts a JSON payload with a `traceback_setting` field and returns the translation.
|
|
48
|
+
"""
|
|
49
|
+
translation_result = translate_error(request.traceback_setting)
|
|
50
|
+
return translation_result
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.post("/translate/batch")
|
|
54
|
+
async def batch_translation_endoint(request: BatchErrorRequest):
|
|
55
|
+
"""
|
|
56
|
+
Translates an array of tracebacks concurrently using asyncio.
|
|
57
|
+
Ideal for processing bulk logs from message queues or distributed systems.
|
|
58
|
+
"""
|
|
59
|
+
# Create an async task for each traceback to run them in parallel
|
|
60
|
+
tasks = [
|
|
61
|
+
asyncio.to_thread(translate_error, tb) for tb in request.tracebacks
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# Gather results from all tasks
|
|
65
|
+
results = await asyncio.gather(*tasks)
|
|
66
|
+
return {"translate_errors": results}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.get("/")
|
|
70
|
+
def read_root():
|
|
71
|
+
"""
|
|
72
|
+
Serves the web UI index.html file at the root URL.
|
|
73
|
+
Provides a simple browser-based interface for the translator.
|
|
74
|
+
"""
|
|
75
|
+
index_path = os.path.join(os.path.dirname(__file__), "static", "index.html")
|
|
76
|
+
if os.path.exists(index_path):
|
|
77
|
+
return FileResponse(index_path)
|
|
78
|
+
return {
|
|
79
|
+
"message": "Error translation API is running. Web UI not found. Please ensure static/index.html exists."
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@app.get("/health")
|
|
84
|
+
def health_check():
|
|
85
|
+
"""
|
|
86
|
+
Health check endpoint useful for monitoring systems (e.g., Kubernetes, Docker).
|
|
87
|
+
"""
|
|
88
|
+
return {"status": "ok"}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Mount static files directory (CSS, JS, images, etc.) so they can be loaded by the web UI
|
|
92
|
+
static_path = os.path.join(os.path.dirname(__file__), "static")
|
|
93
|
+
if os.path.exists(static_path):
|
|
94
|
+
app.mount("/static", StaticFiles(directory=static_path), name="static")
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import difflib
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
class ScopedSymbolCollector(ast.NodeVisitor):
|
|
6
|
+
"""
|
|
7
|
+
Walks the Abstract Syntax Tree (AST) of the target Python file.
|
|
8
|
+
It collects variables, functions, classes, and attributes defined in the code.
|
|
9
|
+
|
|
10
|
+
Crucially, it respects lexical scope by using the crash line number.
|
|
11
|
+
It will only descend into function or class bodies if the crash actually
|
|
12
|
+
happened inside them, preventing it from suggesting variables from unrelated scopes.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, target_line: int):
|
|
15
|
+
self.target_line = target_line
|
|
16
|
+
self.names = set() # Variables (e.g., local/global variable assignments)
|
|
17
|
+
self.attributes = set() # Object attributes/methods (e.g., obj.method_name)
|
|
18
|
+
self.classes = set() # Class names defined in the scope
|
|
19
|
+
self.functions = set() # Function names defined in the scope
|
|
20
|
+
self.imports = set() # Imported modules or aliases
|
|
21
|
+
|
|
22
|
+
def visit_Name(self, node):
|
|
23
|
+
"""Collects variable names when they are assigned (Store context)."""
|
|
24
|
+
if isinstance(node.ctx, ast.Store):
|
|
25
|
+
self.names.add(node.id)
|
|
26
|
+
self.generic_visit(node)
|
|
27
|
+
|
|
28
|
+
def visit_FunctionDef(self, node):
|
|
29
|
+
"""
|
|
30
|
+
Collects the function name into the current scope.
|
|
31
|
+
Only visits the function's internal body if the error line is within it.
|
|
32
|
+
"""
|
|
33
|
+
# The function's name is always added to the enclosing scope
|
|
34
|
+
self.names.add(node.name)
|
|
35
|
+
self.functions.add(node.name)
|
|
36
|
+
|
|
37
|
+
# SCOPING LOGIC: Only visit the body if the crash happened INSIDE this function
|
|
38
|
+
if hasattr(node, 'lineno') and hasattr(node, 'end_lineno'):
|
|
39
|
+
if node.lineno <= self.target_line <= node.end_lineno:
|
|
40
|
+
self.generic_visit(node)
|
|
41
|
+
else:
|
|
42
|
+
self.generic_visit(node)
|
|
43
|
+
|
|
44
|
+
def visit_ClassDef(self, node):
|
|
45
|
+
"""
|
|
46
|
+
Collects the class name into the current scope.
|
|
47
|
+
Only visits the class's internal body if the error line is within it.
|
|
48
|
+
"""
|
|
49
|
+
# The class name is always added to the enclosing scope
|
|
50
|
+
self.names.add(node.name)
|
|
51
|
+
self.classes.add(node.name)
|
|
52
|
+
|
|
53
|
+
# SCOPING LOGIC: Only visit the body if the crash happened INSIDE this class
|
|
54
|
+
if hasattr(node, 'lineno') and hasattr(node, 'end_lineno'):
|
|
55
|
+
if node.lineno <= self.target_line <= node.end_lineno:
|
|
56
|
+
self.generic_visit(node)
|
|
57
|
+
else:
|
|
58
|
+
self.generic_visit(node)
|
|
59
|
+
|
|
60
|
+
def visit_Attribute(self, node):
|
|
61
|
+
"""Collects attribute accesses (e.g., node.attr)."""
|
|
62
|
+
self.attributes.add(node.attr)
|
|
63
|
+
self.generic_visit(node)
|
|
64
|
+
|
|
65
|
+
def visit_Import(self, node):
|
|
66
|
+
"""Collects basic import names or aliases."""
|
|
67
|
+
for alias in node.names:
|
|
68
|
+
name = alias.asname if alias.asname else alias.name
|
|
69
|
+
self.imports.add(name)
|
|
70
|
+
self.names.add(name)
|
|
71
|
+
self.generic_visit(node)
|
|
72
|
+
|
|
73
|
+
def visit_ImportFrom(self, node):
|
|
74
|
+
"""Collects specific names imported from a module."""
|
|
75
|
+
for alias in node.names:
|
|
76
|
+
name = alias.asname if alias.asname else alias.name
|
|
77
|
+
self.imports.add(name)
|
|
78
|
+
self.names.add(name)
|
|
79
|
+
self.generic_visit(node)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_ast_suggestions(filepath: str, line_number: str, target_word: str, error_type: str) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Parses a Python file into an AST, extracts available symbols for the crashing scope,
|
|
85
|
+
and uses string matching (difflib) to find the closest suggestion to a misspelled word.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
filepath: Absolute path to the Python file that crashed.
|
|
89
|
+
line_number: The line number where the error was thrown.
|
|
90
|
+
target_word: The misspelled variable, attribute, or import.
|
|
91
|
+
error_type: The type of error (NameError, AttributeError, etc.) to determine the pool of words.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
str: The suggested correct spelling, or None if no close match is found.
|
|
95
|
+
"""
|
|
96
|
+
if not os.path.exists(filepath) or filepath == "Unknown File":
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
target_line = int(line_number) if line_number.isdigit() else 0
|
|
101
|
+
|
|
102
|
+
# Read the crashing source file
|
|
103
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
104
|
+
source_code = f.read()
|
|
105
|
+
|
|
106
|
+
# Parse it into an Abstract Syntax Tree
|
|
107
|
+
tree = ast.parse(source_code)
|
|
108
|
+
|
|
109
|
+
# Walk the tree and collect symbols visible at the target line
|
|
110
|
+
collector = ScopedSymbolCollector(target_line)
|
|
111
|
+
collector.visit(tree)
|
|
112
|
+
|
|
113
|
+
# Determine the search pool based on the type of error
|
|
114
|
+
pool = set()
|
|
115
|
+
if error_type == "NameError":
|
|
116
|
+
pool = collector.names | collector.functions | collector.classes
|
|
117
|
+
elif error_type == "AttributeError":
|
|
118
|
+
pool = collector.attributes | collector.functions
|
|
119
|
+
elif error_type in ("ImportError", "ModuleNotFoundError"):
|
|
120
|
+
pool = collector.classes | collector.functions | collector.names | collector.imports
|
|
121
|
+
|
|
122
|
+
# Use difflib to find the most similar symbol in the pool
|
|
123
|
+
# Cutoff=0.6 means the suggestion must be at least 60% similar to the target word
|
|
124
|
+
matches = difflib.get_close_matches(target_word, pool, n=1, cutoff=0.6)
|
|
125
|
+
|
|
126
|
+
if matches:
|
|
127
|
+
return matches[0]
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
except Exception:
|
|
131
|
+
# If parsing or processing fails (e.g., SyntaxError in the file), fail gracefully
|
|
132
|
+
return None
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST Handlers for error translation.
|
|
3
|
+
|
|
4
|
+
This module contains strategy functions (handlers) that provide deeper insights into
|
|
5
|
+
specific error types (like NameError, AttributeError, ImportError). It uses Abstract
|
|
6
|
+
Syntax Tree (AST) analysis to look at the exact code context and suggest valid
|
|
7
|
+
alternative variable names, attributes, or imports based on what is actually available
|
|
8
|
+
in the scope.
|
|
9
|
+
"""
|
|
10
|
+
from .ast_engine import get_ast_suggestions
|
|
11
|
+
|
|
12
|
+
# --- 1. THE INDIVIDUAL STRATEGIES (PLUGINS) ---
|
|
13
|
+
# Each handler is responsible for analyzing the AST for a specific type of error
|
|
14
|
+
# and returning actionable insights to the user.
|
|
15
|
+
|
|
16
|
+
def handle_name_error(file_path: str, line_number: str, extracted_values: list) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Handles NameError: name 'X' is not defined.
|
|
19
|
+
Uses AST analysis to look for typos in variable names within the scope.
|
|
20
|
+
"""
|
|
21
|
+
bad_name = extracted_values[0] if extracted_values else ""
|
|
22
|
+
|
|
23
|
+
suggestion = get_ast_suggestions(file_path, line_number, bad_name, "NameError")
|
|
24
|
+
|
|
25
|
+
if suggestion:
|
|
26
|
+
return f"Did you mean '{suggestion}'? There appears to be a typo."
|
|
27
|
+
return f"The variable '{bad_name}' was not found in the file's scope. Ensure it is defined before this line."
|
|
28
|
+
|
|
29
|
+
def handle_attribute_error(file_path: str, line_number: str, extracted_values: list) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Handles AttributeError: 'X' object has no attribute 'Y'.
|
|
32
|
+
Uses AST analysis to suggest similarly-named attributes or methods on the object.
|
|
33
|
+
"""
|
|
34
|
+
# Assuming the last extracted value is the missing attribute (e.g., 'apend')
|
|
35
|
+
bad_attr = extracted_values[-1] if extracted_values else ""
|
|
36
|
+
suggestion = get_ast_suggestions(file_path, line_number, bad_attr, "AttributeError")
|
|
37
|
+
|
|
38
|
+
if suggestion:
|
|
39
|
+
return f"Did you mean the attribute or method '{suggestion}'?"
|
|
40
|
+
return f"The attribute or method '{bad_attr}' was not found in the file's scope. Ensure it is defined before this line."
|
|
41
|
+
|
|
42
|
+
def handle_import_error(file_path: str, line_number: str, extracted_values: list) -> str:
|
|
43
|
+
"""
|
|
44
|
+
Handles ImportError / ModuleNotFoundError.
|
|
45
|
+
Uses AST analysis to verify if the class/function being imported exists or suggests fixes.
|
|
46
|
+
"""
|
|
47
|
+
bad_name = extracted_values[0] if extracted_values else ""
|
|
48
|
+
|
|
49
|
+
suggestion = get_ast_suggestions(file_path, line_number, bad_name, "ImportError")
|
|
50
|
+
if suggestion:
|
|
51
|
+
return f"Did you mean to import '{suggestion}' instead?"
|
|
52
|
+
return f"Verify that the class/function exists in the target module and is spelled correctly."
|
|
53
|
+
|
|
54
|
+
# --- 2. THE REGISTRY (THE MAGIC ROUTER) ---
|
|
55
|
+
# We map the string name of the error to the function that handles it.
|
|
56
|
+
# The core engine uses this registry to dynamically dispatch AST insights based on the error type.
|
|
57
|
+
AST_REGISTRY = {
|
|
58
|
+
"NameError": handle_name_error,
|
|
59
|
+
"AttributeError": handle_attribute_error,
|
|
60
|
+
"ImportError": handle_import_error,
|
|
61
|
+
# Additional error handlers can be registered here as new strategies are developed.
|
|
62
|
+
# "SyntaxError": handle_syntax_error,
|
|
63
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-translator hook module.
|
|
3
|
+
|
|
4
|
+
When imported, this module automatically overrides the default Python exception handler
|
|
5
|
+
(sys.excepthook). Instead of a standard traceback, it intercepts any unhandled exception,
|
|
6
|
+
translates it, and prints a user-friendly explanation using the CLI output formatting.
|
|
7
|
+
"""
|
|
8
|
+
import sys
|
|
9
|
+
import traceback
|
|
10
|
+
from .core import translate_error
|
|
11
|
+
from .ui import print_result
|
|
12
|
+
|
|
13
|
+
def magic_hook(exc_type, exc_value, tb):
|
|
14
|
+
"""
|
|
15
|
+
Custom exception hook that intercepts unhandled Python exceptions before
|
|
16
|
+
they are printed to the terminal. It formats the standard traceback,
|
|
17
|
+
translates the error into human-readable advice, and prints it using
|
|
18
|
+
the rich UI components.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
exc_type: The type of the exception (e.g., ValueError, NameError).
|
|
22
|
+
exc_value: The exception instance itself.
|
|
23
|
+
tb: The traceback object containing the call stack.
|
|
24
|
+
"""
|
|
25
|
+
# 1. Convert the raw crash data into a standard traceback string
|
|
26
|
+
tb_lines = traceback.format_exception(exc_type, exc_value, tb)
|
|
27
|
+
tb_string = "".join(tb_lines)
|
|
28
|
+
|
|
29
|
+
# 2. Pass the standard traceback through our translation engine
|
|
30
|
+
result = translate_error(tb_string)
|
|
31
|
+
|
|
32
|
+
# 3. Print our beautiful colorized output instead of the default Python crash
|
|
33
|
+
print_result(result)
|
|
34
|
+
|
|
35
|
+
# Replace Python's default sys.excepthook with our custom translation hook.
|
|
36
|
+
# Any unhandled exception will now automatically be intercepted and translated.
|
|
37
|
+
sys.excepthook = magic_hook
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Installation banner module for Error Translator CLI.
|
|
3
|
+
This module prints a stylish banner to the terminal during installation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
def print_install_banner():
|
|
7
|
+
"""Prints a stylish banner using ANSI escape codes for colors."""
|
|
8
|
+
RED = '\033[91m'
|
|
9
|
+
BLUE = '\033[94m'
|
|
10
|
+
CYAN = '\033[96m'
|
|
11
|
+
MAGENTA = '\033[95m'
|
|
12
|
+
YELLOW = '\033[93m'
|
|
13
|
+
WHITE = '\033[97m'
|
|
14
|
+
RESET = '\033[0m'
|
|
15
|
+
BOLD = '\033[1m'
|
|
16
|
+
|
|
17
|
+
error_art = r"""
|
|
18
|
+
███████╗██████╗ ██████╗ ██████╗ ██████╗
|
|
19
|
+
██╔════╝██╔══██╗██╔══██╗██╔═══██╗██╔══██╗
|
|
20
|
+
█████╗ ██████╔╝██████╔╝██║ ██║██████╔╝
|
|
21
|
+
██╔══╝ ██╔══██╗██╔══██╗██║ ██║██╔══██╗
|
|
22
|
+
███████╗██║ ██║██║ ██║╚██████╔╝██║ ██║
|
|
23
|
+
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
translator_art = r"""
|
|
27
|
+
████████╗██████╗ █████╗ ███╗ ██╗███████╗██╗ █████╗ ████████╗ ██████╗ ██████╗
|
|
28
|
+
╚══██╔══╝██╔══██╗██╔══██╗████╗ ██║██╔════╝██║ ██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗
|
|
29
|
+
██║ ██████╔╝███████║██╔██╗ ██║███████╗██║ ███████║ ██║ ██║ ██║██████╔╝
|
|
30
|
+
██║ ██╔══██╗██╔══██║██║╚██╗██║╚════██║██║ ██╔══██║ ██║ ██║ ██║██╔══██╗
|
|
31
|
+
██║ ██║ ██║██║ ██║██║ ╚████║███████║███████╗██║ ██║ ██║ ╚██████╔╝██║ ██║
|
|
32
|
+
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
cli_v2_art = r"""
|
|
36
|
+
██████╗██╗ ██╗ ██╗ ██╗██████╗
|
|
37
|
+
██╔════╝██║ ██║ ██║ ██║╚════██╗
|
|
38
|
+
██║ ██║ ██║ ██║ ██║ █████╔╝
|
|
39
|
+
██║ ██║ ██║ ╚██╗ ██╔╝██╔═══╝
|
|
40
|
+
╚██████╗███████╗██║ ╚████╔╝ ███████╗
|
|
41
|
+
╚═════╝╚══════╝╚═╝ ╚═══╝ ╚══════╝
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
print(f"\n{BOLD}{RED}{error_art.strip()}{RESET}")
|
|
45
|
+
print(f"{BOLD}{BLUE}{translator_art.strip()}{RESET}")
|
|
46
|
+
print(f"{BOLD}{CYAN}{cli_v2_art.strip()}{RESET}")
|
|
47
|
+
print(f"\n{BOLD}{MAGENTA}================================================================================{RESET}")
|
|
48
|
+
print(f"{BOLD}{YELLOW}⚡ Fatal to Fabulous ⚡{RESET}")
|
|
49
|
+
print(f"{BOLD}{WHITE} ⇆ Offline Python Traceback Explainer V2 ⇆ {RESET}")
|
|
50
|
+
print(f"{BOLD}{MAGENTA}================================================================================{RESET}\n")
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
print_install_banner()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command Line Interface (CLI) module for the Error Translator.
|
|
3
|
+
|
|
4
|
+
This module provides the terminal entry point (`explain-error`). It parses arguments,
|
|
5
|
+
handles standard input streams, and orchestrates the translation process.
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
from .core import translate_error
|
|
10
|
+
from .ui import print_about, print_help, print_result, print_result_json, VERSION, console
|
|
11
|
+
from .runner import run_script
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
"""Main CLI entry point."""
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="explain-error",
|
|
17
|
+
description="Error Translator — Turn cryptic Python tracebacks into clear, actionable advice.",
|
|
18
|
+
epilog="""
|
|
19
|
+
Examples:
|
|
20
|
+
explain-error run my_script.py
|
|
21
|
+
explain-error "NameError: name 'usr_count' is not defined"
|
|
22
|
+
cat error.log | explain-error
|
|
23
|
+
""",
|
|
24
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
25
|
+
add_help=False
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
parser.add_argument("-a", "--about", action="store_true", help="Display information about the tool.")
|
|
29
|
+
parser.add_argument("-v", "--version", action="store_true", help="Show the current version of the tool.")
|
|
30
|
+
parser.add_argument("--json", action="store_true", dest="as_json", help="Output the translated error as a JSON object.")
|
|
31
|
+
parser.add_argument("-h", "--help", action="store_true", help="Help user through documentation.")
|
|
32
|
+
parser.add_argument("args", nargs="*", help="Positional arguments.")
|
|
33
|
+
|
|
34
|
+
parsed_args = parser.parse_args()
|
|
35
|
+
|
|
36
|
+
# Handle meta-flags
|
|
37
|
+
if parsed_args.about:
|
|
38
|
+
print_about()
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
if parsed_args.version:
|
|
42
|
+
console.print(f"Error Translator CLI Version: [bold green]{VERSION}[/]")
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
if parsed_args.help:
|
|
46
|
+
console.print(f"Error Translator CLI Version: [bold green]{VERSION}[/]")
|
|
47
|
+
print_help()
|
|
48
|
+
sys.exit(0)
|
|
49
|
+
|
|
50
|
+
# Choose output strategy
|
|
51
|
+
emit = print_result_json if parsed_args.as_json else print_result
|
|
52
|
+
|
|
53
|
+
# Handle piped input (e.g. `cat error.log | explain-error`)
|
|
54
|
+
if not sys.stdin.isatty():
|
|
55
|
+
error_input = sys.stdin.read()
|
|
56
|
+
if error_input.strip():
|
|
57
|
+
emit(translate_error(error_input))
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# Provide help if no arguments are passed
|
|
61
|
+
if not parsed_args.args:
|
|
62
|
+
print_help()
|
|
63
|
+
sys.exit(1)
|
|
64
|
+
|
|
65
|
+
# Detect the "run <script.py>" sub-command
|
|
66
|
+
if parsed_args.args[0] == "run" and len(parsed_args.args) > 1:
|
|
67
|
+
script_name = parsed_args.args[1]
|
|
68
|
+
run_script(script_name, as_json=parsed_args.as_json)
|
|
69
|
+
else:
|
|
70
|
+
# Otherwise, treat the entire string of arguments as a raw traceback text
|
|
71
|
+
error_input = " ".join(parsed_args.args)
|
|
72
|
+
emit(translate_error(error_input))
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
main()
|