error-translator-cli-v2 2.0.0__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.
Files changed (32) hide show
  1. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/PKG-INFO +2 -23
  2. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/pyproject.toml +6 -5
  3. error_translator_cli_v2-3.0.2/setup.py +16 -0
  4. error_translator_cli_v2-3.0.2/src/error_translator/__init__.py +7 -0
  5. error_translator_cli_v2-3.0.2/src/error_translator/api/server.py +94 -0
  6. error_translator_cli_v2-3.0.2/src/error_translator/ast/ast_engine.py +132 -0
  7. error_translator_cli_v2-3.0.2/src/error_translator/ast/ast_handlers.py +63 -0
  8. error_translator_cli_v2-3.0.2/src/error_translator/auto.py +37 -0
  9. error_translator_cli_v2-3.0.2/src/error_translator/banner.py +53 -0
  10. error_translator_cli_v2-3.0.2/src/error_translator/cli.py +75 -0
  11. error_translator_cli_v2-3.0.2/src/error_translator/core.py +109 -0
  12. error_translator_cli_v2-3.0.2/src/error_translator/ext/fast_matcher.c +95 -0
  13. error_translator_cli_v2-3.0.2/src/error_translator/parser.py +43 -0
  14. error_translator_cli_v2-3.0.2/src/error_translator/rules.py +40 -0
  15. error_translator_cli_v2-3.0.2/src/error_translator/runner.py +55 -0
  16. error_translator_cli_v2-3.0.2/src/error_translator/ui.py +161 -0
  17. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/PKG-INFO +2 -23
  18. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/SOURCES.txt +11 -0
  19. error_translator_cli_v2-3.0.2/tests/test_ast.py +41 -0
  20. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/tests/test_core.py +17 -0
  21. error_translator_cli_v2-2.0.0/src/error_translator/__init__.py +0 -0
  22. error_translator_cli_v2-2.0.0/src/error_translator/auto.py +0 -22
  23. error_translator_cli_v2-2.0.0/src/error_translator/cli.py +0 -265
  24. error_translator_cli_v2-2.0.0/src/error_translator/core.py +0 -113
  25. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/LICENSE +0 -0
  26. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/README.md +0 -0
  27. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/setup.cfg +0 -0
  28. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator/rules.json +0 -0
  29. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/dependency_links.txt +0 -0
  30. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/entry_points.txt +0 -0
  31. {error_translator_cli_v2-2.0.0 → error_translator_cli_v2-3.0.2}/src/error_translator_cli_v2.egg-info/requires.txt +0 -0
  32. {error_translator_cli_v2-2.0.0 → 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: 2.0.0
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 License
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/
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "error-translator-cli-v2"
7
- version = "2.0.0"
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 = { file = "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
- package-dir = {"" = "src"}
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,7 @@
1
+ """
2
+ Error Translator
3
+ Turn cryptic Python tracebacks into clear, actionable advice.
4
+ """
5
+ from .core import translate_error
6
+
7
+ __all__ = ["translate_error"]
@@ -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()
@@ -0,0 +1,109 @@
1
+ """
2
+ Core translation engine for the Error Translator CLI.
3
+
4
+ This module is responsible for:
5
+ 1. Orchestrating error translation.
6
+ 2. Using a fast C extension for matching if available, with a Python fallback.
7
+ 3. Delegating to AST handlers for deep, code-specific insights.
8
+ """
9
+ from .ast.ast_handlers import AST_REGISTRY
10
+ from .rules import load_rules, compiled_rules
11
+ from .parser import extract_location, extract_code_context
12
+
13
+ # Attempt to load the ultra-fast C extension for matching rules,
14
+ # and fallback to the Python implementation if it's unavailable.
15
+ try:
16
+ from .fast_matcher import match_loop # type: ignore
17
+ C_EXTENSION_AVAILABLE = True
18
+ except ImportError:
19
+ C_EXTENSION_AVAILABLE = False
20
+
21
+
22
+ def translate_error(traceback_text: str) -> dict:
23
+ """
24
+ Translate a raw traceback string into a detailed explanation dictionary.
25
+
26
+ Args:
27
+ traceback_text (str): The raw traceback string.
28
+
29
+ Returns:
30
+ dict: A dictionary containing the explanation, suggested fix,
31
+ AST-based insight (if any), file, line number, code context,
32
+ and the matched error line.
33
+ """
34
+ # Load configuration rules and pre-compiled regex patterns
35
+ data = load_rules()
36
+ rules = compiled_rules()
37
+ default_error = data["default"]
38
+
39
+ # Extract non-empty lines from the traceback
40
+ lines = [line.strip() for line in traceback_text.strip().split("\n") if line.strip()]
41
+ if not lines:
42
+ return {"explanation": "No error text provided.", "fix": "Provide a valid Python error."}
43
+
44
+ # The actual error message is typically the last line in a traceback
45
+ actual_error_line = lines[-1]
46
+
47
+ # Extract the origin of the error (file name and line number)
48
+ file_name, line_number = extract_location(traceback_text)
49
+
50
+ # Attempt to read the exact line of code that caused the error
51
+ code_context = extract_code_context(file_name, line_number)
52
+
53
+ # ==========================================
54
+ # FAST MATCHING ENGINE (C Extension + Python Fallback)
55
+ # ==========================================
56
+ match = None
57
+ rule = None
58
+
59
+ if C_EXTENSION_AVAILABLE:
60
+ # Execute the C extension for maximum performance
61
+ result = match_loop(actual_error_line, rules)
62
+ if result:
63
+ match, rule = result
64
+ else:
65
+ # Fallback to standard Python regex loop
66
+ for pattern, r in rules:
67
+ m = pattern.search(actual_error_line)
68
+ if m:
69
+ match, rule = m, r
70
+ break
71
+
72
+ # If we found a matching rule, format the explanation and fix
73
+ if match and rule:
74
+ # Extract variables from the regex groups (e.g., variable names, functions)
75
+ extracted_values = list(match.groups())
76
+
77
+ # Inject the extracted values into the template fix string
78
+ fix_text = rule["fix"].format(*extracted_values)
79
+
80
+ # Parse the error type (e.g., "NameError", "TypeError") to dispatch AST insights
81
+ error_type = actual_error_line.split(":")[0].strip()
82
+
83
+ # Check if there's a specialized AST handler for this specific error type
84
+ handler_function = AST_REGISTRY.get(error_type)
85
+ insight = None
86
+
87
+ # If an AST handler exists and the file is accessible, run deep code analysis
88
+ if handler_function and file_name != "Unknown File":
89
+ insight = handler_function(file_name, line_number, extracted_values)
90
+
91
+ return {
92
+ "explanation": rule["explanation"].format(*extracted_values),
93
+ "fix": fix_text,
94
+ "ast_insight": insight,
95
+ "matched_error": actual_error_line,
96
+ "file": file_name,
97
+ "line": line_number,
98
+ "code": code_context,
99
+ }
100
+
101
+ # Fallback when the error doesn't match any known rules
102
+ return {
103
+ "explanation": default_error["explanation"],
104
+ "fix": default_error["fix"],
105
+ "matched_error": actual_error_line,
106
+ "file": file_name,
107
+ "line": line_number,
108
+ "code": code_context,
109
+ }