error-translator-cli-v2 0.1.2__tar.gz → 1.0.0__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-0.1.2 → error_translator_cli_v2-1.0.0}/PKG-INFO +1 -1
- error_translator_cli_v2-1.0.0/error_translator/auto.py +22 -0
- error_translator_cli_v2-1.0.0/error_translator/cli.py +93 -0
- error_translator_cli_v2-1.0.0/error_translator/core.py +53 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/PKG-INFO +1 -1
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/SOURCES.txt +1 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/pyproject.toml +1 -1
- error_translator_cli_v2-1.0.0/tests/test_core.py +35 -0
- error_translator_cli_v2-0.1.2/error_translator/cli.py +0 -61
- error_translator_cli_v2-0.1.2/error_translator/core.py +0 -45
- error_translator_cli_v2-0.1.2/tests/test_core.py +0 -13
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/README.md +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator/__init__.py +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator/rules.py +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/dependency_links.txt +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/entry_points.txt +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/top_level.txt +0 -0
- {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
from .core import translate_error
|
|
4
|
+
from .cli import print_result
|
|
5
|
+
|
|
6
|
+
def magic_hook(exc_type, exc_value, tb):
|
|
7
|
+
"""
|
|
8
|
+
This function intercepts a Python crash right before it prints
|
|
9
|
+
to the terminal, formats the error, and translates it.
|
|
10
|
+
"""
|
|
11
|
+
# 1. Convert the raw crash data into a standard traceback string
|
|
12
|
+
tb_lines = traceback.format_exception(exc_type, exc_value, tb)
|
|
13
|
+
tb_string = "".join(tb_lines)
|
|
14
|
+
|
|
15
|
+
# 2. Pass it through our translation engine
|
|
16
|
+
result = translate_error(tb_string)
|
|
17
|
+
|
|
18
|
+
# 3. Print our beautiful colorized output instead of the ugly default
|
|
19
|
+
print_result(result)
|
|
20
|
+
|
|
21
|
+
# The Magic Trick: Replace Python's default crash handler with ours
|
|
22
|
+
sys.excepthook = magic_hook
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
from .core import translate_error
|
|
4
|
+
|
|
5
|
+
# ANSI Color Codes
|
|
6
|
+
class Colors:
|
|
7
|
+
RED = '\033[91m'
|
|
8
|
+
GREEN = '\033[92m'
|
|
9
|
+
YELLOW = '\033[93m'
|
|
10
|
+
CYAN = '\033[96m'
|
|
11
|
+
BOLD = '\033[1m'
|
|
12
|
+
RESET = '\033[0m'
|
|
13
|
+
|
|
14
|
+
def print_result(result: dict):
|
|
15
|
+
"""Prints the translated error to the terminal with colors."""
|
|
16
|
+
print(f"\n{Colors.RED}{Colors.BOLD} Error Detected:{Colors.RESET}")
|
|
17
|
+
print(f"{result.get('matched_error', 'N/A')}")
|
|
18
|
+
|
|
19
|
+
if "file" in result:
|
|
20
|
+
print(f"{Colors.YELLOW} Location: {result['file']} (Line {result['line']}){Colors.RESET}\n")
|
|
21
|
+
else:
|
|
22
|
+
print()
|
|
23
|
+
|
|
24
|
+
print(f"{Colors.CYAN}{Colors.BOLD} Explanation:{Colors.RESET}")
|
|
25
|
+
print(f"{result['explanation']}\n")
|
|
26
|
+
|
|
27
|
+
print(f"{Colors.GREEN}{Colors.BOLD} Suggested Fix:{Colors.RESET}")
|
|
28
|
+
print(f"{result['fix']}\n")
|
|
29
|
+
|
|
30
|
+
def run_script(script_name: str):
|
|
31
|
+
"""Runs a python script in the background and catches its errors."""
|
|
32
|
+
import subprocess
|
|
33
|
+
try:
|
|
34
|
+
# Run the script using the current Python environment
|
|
35
|
+
result = subprocess.run(
|
|
36
|
+
[sys.executable, script_name],
|
|
37
|
+
capture_output=True,
|
|
38
|
+
text=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# If the script ran perfectly (Return Code 0), print standard output
|
|
42
|
+
if result.returncode == 0:
|
|
43
|
+
print(result.stdout, end="")
|
|
44
|
+
|
|
45
|
+
# If the script crashed, print whatever succeeded, THEN translate the error
|
|
46
|
+
else:
|
|
47
|
+
if result.stdout:
|
|
48
|
+
print(result.stdout, end="")
|
|
49
|
+
|
|
50
|
+
translation = translate_error(result.stderr)
|
|
51
|
+
print_result(translation)
|
|
52
|
+
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
print(f"{Colors.RED}Error: Could not find script '{script_name}'{Colors.RESET}")
|
|
55
|
+
|
|
56
|
+
def main():
|
|
57
|
+
parser = argparse.ArgumentParser(
|
|
58
|
+
description="Translate Python error messages into simple English."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Allow multiple arguments so we can accept "run script.py" or a long error string
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"args",
|
|
64
|
+
nargs="*",
|
|
65
|
+
help="Use 'run <script.py>' to execute a file, or pass an error string."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
parsed_args = parser.parse_args()
|
|
69
|
+
|
|
70
|
+
# 1. Handle Piped Input (e.g., cat error.log | explain-error)
|
|
71
|
+
if not sys.stdin.isatty():
|
|
72
|
+
error_input = sys.stdin.read()
|
|
73
|
+
if error_input.strip():
|
|
74
|
+
print_result(translate_error(error_input))
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
# 2. Print help if no arguments
|
|
78
|
+
if not parsed_args.args:
|
|
79
|
+
parser.print_help()
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
# 3. Check if the user used the "run" command
|
|
83
|
+
if parsed_args.args[0] == "run" and len(parsed_args.args) > 1:
|
|
84
|
+
script_name = parsed_args.args[1]
|
|
85
|
+
run_script(script_name)
|
|
86
|
+
|
|
87
|
+
# 4. Fallback: Treat the input as a raw error string
|
|
88
|
+
else:
|
|
89
|
+
error_input = " ".join(parsed_args.args)
|
|
90
|
+
print_result(translate_error(error_input))
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def load_rules():
|
|
5
|
+
"""Loads the error rules from the JSON file."""
|
|
6
|
+
# Find the absolute path to the json file next to this script
|
|
7
|
+
# current_dir = os.path.dirname(data\rules.json)
|
|
8
|
+
current_dir = 'data/'
|
|
9
|
+
json_path = os.path.join(current_dir, 'rules.json')
|
|
10
|
+
|
|
11
|
+
with open(json_path, 'r') as file:
|
|
12
|
+
return json.load(file)
|
|
13
|
+
|
|
14
|
+
def translate_error(traceback_text: str) -> dict:
|
|
15
|
+
import re # Lazy import
|
|
16
|
+
|
|
17
|
+
data = load_rules()
|
|
18
|
+
rules = data["rules"]
|
|
19
|
+
default_error = data["default"]
|
|
20
|
+
|
|
21
|
+
lines = [line.strip() for line in traceback_text.strip().split('\n') if line.strip()]
|
|
22
|
+
if not lines:
|
|
23
|
+
return {"explanation": "No error text provided.", "fix": "Provide a valid Python error."}
|
|
24
|
+
|
|
25
|
+
actual_error_line = lines[-1]
|
|
26
|
+
|
|
27
|
+
# Flexible regex to catch single or double quotes, and handle missing ones
|
|
28
|
+
location_match = re.search(r'File\s+[\'"]?(.*?)[\'"]?,\s+line\s+(\d+)', traceback_text)
|
|
29
|
+
file_name = location_match.group(1) if location_match else "Unknown File"
|
|
30
|
+
line_number = location_match.group(2) if location_match else "Unknown Line"
|
|
31
|
+
|
|
32
|
+
for rule in rules:
|
|
33
|
+
# Compile the regex pattern on the fly
|
|
34
|
+
pattern = re.compile(rule["pattern"])
|
|
35
|
+
match = pattern.search(actual_error_line)
|
|
36
|
+
|
|
37
|
+
if match:
|
|
38
|
+
extracted_values = match.groups()
|
|
39
|
+
return {
|
|
40
|
+
"explanation": rule["explanation"].format(*extracted_values),
|
|
41
|
+
"fix": rule["fix"].format(*extracted_values),
|
|
42
|
+
"matched_error": actual_error_line,
|
|
43
|
+
"file": file_name,
|
|
44
|
+
"line": line_number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
"explanation": default_error["explanation"],
|
|
49
|
+
"fix": default_error["fix"],
|
|
50
|
+
"matched_error": actual_error_line,
|
|
51
|
+
"file": file_name,
|
|
52
|
+
"line": line_number
|
|
53
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import error_translator.auto
|
|
2
|
+
|
|
3
|
+
def test_name_error_translation_double_quotes():
|
|
4
|
+
"""Test standard traceback with double quotes around the filename."""
|
|
5
|
+
mock_traceback = """Traceback (most recent call last):
|
|
6
|
+
File "script.py", line 2, in <module>
|
|
7
|
+
print(my_variable)
|
|
8
|
+
NameError: name 'my_variable' is not defined"""
|
|
9
|
+
|
|
10
|
+
result = translate_error(mock_traceback)
|
|
11
|
+
|
|
12
|
+
assert "my_variable" in result["explanation"]
|
|
13
|
+
assert result["file"] == "script.py"
|
|
14
|
+
assert result["line"] == "2"
|
|
15
|
+
|
|
16
|
+
def test_name_error_translation_single_quotes():
|
|
17
|
+
"""Test PowerShell-style traceback with single quotes."""
|
|
18
|
+
mock_traceback = """Traceback (most recent call last):
|
|
19
|
+
File 'script.py', line 2, in <module>
|
|
20
|
+
print(my_variable)
|
|
21
|
+
NameError: name 'my_variable' is not defined"""
|
|
22
|
+
|
|
23
|
+
result = translate_error(mock_traceback)
|
|
24
|
+
|
|
25
|
+
assert result["file"] == "script.py"
|
|
26
|
+
assert result["line"] == "2"
|
|
27
|
+
|
|
28
|
+
def test_unknown_error_fallback():
|
|
29
|
+
"""Test that garbage input returns the default safe message."""
|
|
30
|
+
mock_traceback = "Something completely random went wrong here."
|
|
31
|
+
|
|
32
|
+
result = translate_error(mock_traceback)
|
|
33
|
+
|
|
34
|
+
assert "unknown error" in result["explanation"]
|
|
35
|
+
assert result["matched_error"] == "Something completely random went wrong here."
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import argparse
|
|
3
|
-
|
|
4
|
-
from cv2 import line
|
|
5
|
-
from .core import translate_error
|
|
6
|
-
|
|
7
|
-
# ANSI Color Codes for terminal formatting
|
|
8
|
-
class Colors:
|
|
9
|
-
RED = '\033[91m'
|
|
10
|
-
GREEN = '\033[92m'
|
|
11
|
-
YELLOW = '\033[93m'
|
|
12
|
-
CYAN = '\033[96m'
|
|
13
|
-
BOLD = '\033[1m'
|
|
14
|
-
RESET = '\033[0m'
|
|
15
|
-
|
|
16
|
-
def print_result(result: dict):
|
|
17
|
-
"""Prints the translated error to the terminal with colors."""
|
|
18
|
-
print(f"\n{Colors.RED}{Colors.BOLD}🚨 Error Detected:{Colors.RESET}")
|
|
19
|
-
print(f"{result.get('matched_error', 'N/A')}\n")
|
|
20
|
-
|
|
21
|
-
# If file and line info is available, print it
|
|
22
|
-
if "file" in result:
|
|
23
|
-
print(f"{Colors.YELLOW}📍 Location: {result['file']} (Line {result['line']}){Colors.RESET}\n")
|
|
24
|
-
else:
|
|
25
|
-
print() # Just a newline
|
|
26
|
-
|
|
27
|
-
print(f"{Colors.CYAN}{Colors.BOLD}🧠 Explanation:{Colors.RESET}")
|
|
28
|
-
print(f"{result['explanation']}\n")
|
|
29
|
-
|
|
30
|
-
print(f"{Colors.GREEN}{Colors.BOLD}🛠️ Suggested Fix:{Colors.RESET}")
|
|
31
|
-
print(f"{result['fix']}\n")
|
|
32
|
-
|
|
33
|
-
def main():
|
|
34
|
-
parser = argparse.ArgumentParser(
|
|
35
|
-
description="Translate Python error messages into simple English."
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
parser.add_argument(
|
|
39
|
-
"error_text",
|
|
40
|
-
nargs="?",
|
|
41
|
-
help="The error message or traceback string to translate."
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
args = parser.parse_args()
|
|
45
|
-
|
|
46
|
-
# Read from positional argument OR standard input (piping)
|
|
47
|
-
error_input = args.error_text
|
|
48
|
-
|
|
49
|
-
if not error_input and not sys.stdin.isatty():
|
|
50
|
-
# Allows usage like: cat error.log | explain-error
|
|
51
|
-
error_input = sys.stdin.read()
|
|
52
|
-
|
|
53
|
-
if not error_input:
|
|
54
|
-
parser.print_help()
|
|
55
|
-
sys.exit(1)
|
|
56
|
-
|
|
57
|
-
result = translate_error(error_input)
|
|
58
|
-
print_result(result)
|
|
59
|
-
|
|
60
|
-
if __name__ == "__main__":
|
|
61
|
-
main()
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from .rules import ERROR_RULES, DEFAULT_ERROR
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
def translate_error(traceback_text: str) -> dict:
|
|
5
|
-
"""
|
|
6
|
-
Parses traceback text and returns a human-readable explanation and fix.
|
|
7
|
-
"""
|
|
8
|
-
# Update this line inside your translate_error function:
|
|
9
|
-
location_match = re.search(r'File\s+[\'"]?(.*?)[\'"]?,\s+line\s+(\d+)', traceback_text)
|
|
10
|
-
file_name = location_match.group(1) if location_match else "unknown file"
|
|
11
|
-
line_number = location_match.group(2) if location_match else "unknown line"
|
|
12
|
-
# Grab the last non-empty line of the traceback, which usually contains the actual error
|
|
13
|
-
print(f"\n--- DEBUG RAW INPUT ---\n{repr(traceback_text)}\n-----------------------\n")
|
|
14
|
-
lines = [line.strip() for line in traceback_text.strip().split('\n') if line.strip()]
|
|
15
|
-
if not lines:
|
|
16
|
-
return {"explanation": "No error text provided.", "fix": "Provide a valid Python error."}
|
|
17
|
-
|
|
18
|
-
actual_error_line = lines[-1]
|
|
19
|
-
|
|
20
|
-
for rule in ERROR_RULES:
|
|
21
|
-
match = rule["pattern"].search(actual_error_line)
|
|
22
|
-
if match:
|
|
23
|
-
# Extract the captured regex groups (e.g., the variable name)
|
|
24
|
-
extracted_values = match.groups()
|
|
25
|
-
|
|
26
|
-
# Format the explanation and fix using the extracted values
|
|
27
|
-
explanation = rule["explanation"].format(*extracted_values)
|
|
28
|
-
fix = rule["fix"].format(*extracted_values)
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
"explanation": explanation,
|
|
32
|
-
"fix": fix,
|
|
33
|
-
"matched_error": actual_error_line,
|
|
34
|
-
"file": file_name,
|
|
35
|
-
"line": line_number,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# If no rules match, return the default payload
|
|
39
|
-
return {
|
|
40
|
-
"explanation": DEFAULT_ERROR["explanation"],
|
|
41
|
-
"fix": DEFAULT_ERROR["fix"],
|
|
42
|
-
"matched_error": actual_error_line,
|
|
43
|
-
"file": file_name,
|
|
44
|
-
"line": line_number,
|
|
45
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from error_translator.core import translate_error
|
|
2
|
-
|
|
3
|
-
def test_name_error_translation():
|
|
4
|
-
mock_traceback = """Traceback (most recent call last):
|
|
5
|
-
File "main.py", line 3, in <module>
|
|
6
|
-
print(x)
|
|
7
|
-
NameError: name 'x' is not defined"""
|
|
8
|
-
|
|
9
|
-
result = translate_error(mock_traceback)
|
|
10
|
-
|
|
11
|
-
assert "x" in result["explanation"]
|
|
12
|
-
assert result["file"] == "main.py"
|
|
13
|
-
assert result["line"] == "3"
|
|
File without changes
|
{error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|