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.
Files changed (18) hide show
  1. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/PKG-INFO +1 -1
  2. error_translator_cli_v2-1.0.0/error_translator/auto.py +22 -0
  3. error_translator_cli_v2-1.0.0/error_translator/cli.py +93 -0
  4. error_translator_cli_v2-1.0.0/error_translator/core.py +53 -0
  5. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/PKG-INFO +1 -1
  6. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator_cli_v2.egg-info/SOURCES.txt +1 -0
  7. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/pyproject.toml +1 -1
  8. error_translator_cli_v2-1.0.0/tests/test_core.py +35 -0
  9. error_translator_cli_v2-0.1.2/error_translator/cli.py +0 -61
  10. error_translator_cli_v2-0.1.2/error_translator/core.py +0 -45
  11. error_translator_cli_v2-0.1.2/tests/test_core.py +0 -13
  12. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/README.md +0 -0
  13. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator/__init__.py +0 -0
  14. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/error_translator/rules.py +0 -0
  15. {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
  16. {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
  17. {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
  18. {error_translator_cli_v2-0.1.2 → error_translator_cli_v2-1.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: error-translator-cli-v2
3
- Version: 0.1.2
3
+ Version: 1.0.0
4
4
  Summary: A CLI tool that explains Python errors in simple human language.
5
5
  Author-email: Gourabananda Datta <gourabanandadatta@zohomail.com>
6
6
  Requires-Python: >=3.7
@@ -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
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: error-translator-cli-v2
3
- Version: 0.1.2
3
+ Version: 1.0.0
4
4
  Summary: A CLI tool that explains Python errors in simple human language.
5
5
  Author-email: Gourabananda Datta <gourabanandadatta@zohomail.com>
6
6
  Requires-Python: >=3.7
@@ -1,6 +1,7 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  error_translator/__init__.py
4
+ error_translator/auto.py
4
5
  error_translator/cli.py
5
6
  error_translator/core.py
6
7
  error_translator/rules.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "error-translator-cli-v2"
7
- version = "0.1.2"
7
+ version = "1.0.0"
8
8
  description = "A CLI tool that explains Python errors in simple human language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -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"