pydefine 1.0.0__py3-none-any.whl
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.
- pydefine/__init__.py +84 -0
- pydefine/cli.py +284 -0
- pydefine/core.py +456 -0
- pydefine/i18n.py +344 -0
- pydefine/mapping.py +1037 -0
- pydefine/utils.py +442 -0
- pydefine/version.py +20 -0
- pydefine-1.0.0.dist-info/METADATA +395 -0
- pydefine-1.0.0.dist-info/RECORD +13 -0
- pydefine-1.0.0.dist-info/WHEEL +5 -0
- pydefine-1.0.0.dist-info/entry_points.txt +3 -0
- pydefine-1.0.0.dist-info/licenses/LICENSE +21 -0
- pydefine-1.0.0.dist-info/top_level.txt +1 -0
pydefine/core.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pydefine.core
|
|
3
|
+
~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
Core functionality for pyDefine library.
|
|
6
|
+
|
|
7
|
+
This module provides the main API functions:
|
|
8
|
+
- decode_traceback(traceback_text): Parse and decode a traceback string
|
|
9
|
+
- decode_exception(e): Decode an exception object directly
|
|
10
|
+
- safe_run(code, filename): Execute code safely with error decoding
|
|
11
|
+
|
|
12
|
+
All functions return structured dictionaries with error information,
|
|
13
|
+
simple explanations, and fix suggestions.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
import traceback
|
|
18
|
+
import re
|
|
19
|
+
from typing import Dict, Any, Optional, Union
|
|
20
|
+
|
|
21
|
+
from .mapping import get_exception_info
|
|
22
|
+
from .utils import extract_error_info, format_output, tokenize_output
|
|
23
|
+
from .i18n import translate_explanation, get_language
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def decode_traceback(traceback_text: str) -> Dict[str, Any]:
|
|
27
|
+
"""
|
|
28
|
+
Decode a raw traceback string into beginner-friendly explanation.
|
|
29
|
+
|
|
30
|
+
This is the main function for parsing complete traceback text from
|
|
31
|
+
terminal output, log files, or string captures.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
traceback_text: Raw traceback string (multi-line)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Dictionary with keys:
|
|
38
|
+
- error_type: Exception class name (e.g., 'ValueError')
|
|
39
|
+
- original_message: Original error message
|
|
40
|
+
- simple_explanation: 2-3 line beginner explanation
|
|
41
|
+
- fix_suggestion: One-line actionable fix
|
|
42
|
+
- line_number: Line number where error occurred (if found)
|
|
43
|
+
- file_name: File name where error occurred (if found)
|
|
44
|
+
- tags: List of classification keywords
|
|
45
|
+
- emoji: Visual identifier emoji
|
|
46
|
+
- formatted_output: Pretty formatted string (optional)
|
|
47
|
+
- success: Always False for errors
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> tb_text = '''Traceback (most recent call last):
|
|
51
|
+
... File "test.py", line 5, in <module>
|
|
52
|
+
... print(x)
|
|
53
|
+
... NameError: name 'x' is not defined'''
|
|
54
|
+
>>> result = decode_traceback(tb_text)
|
|
55
|
+
>>> print(result['simple_explanation'])
|
|
56
|
+
You tried to use a variable or function name that doesn't exist yet...
|
|
57
|
+
"""
|
|
58
|
+
if not traceback_text or not isinstance(traceback_text, str):
|
|
59
|
+
return {
|
|
60
|
+
"error_type": "InvalidInput",
|
|
61
|
+
"original_message": "No traceback provided",
|
|
62
|
+
"simple_explanation": "No error information was provided to decode.",
|
|
63
|
+
"fix_suggestion": "Pass a valid traceback string to decode_traceback()",
|
|
64
|
+
"line_number": None,
|
|
65
|
+
"file_name": None,
|
|
66
|
+
"tags": ["invalid-input"],
|
|
67
|
+
"emoji": "❓",
|
|
68
|
+
"success": False,
|
|
69
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Extract error information from traceback text
|
|
73
|
+
error_info = extract_error_info(traceback_text)
|
|
74
|
+
|
|
75
|
+
# Get exception mapping info
|
|
76
|
+
exception_data = get_exception_info(error_info["error_type"])
|
|
77
|
+
|
|
78
|
+
# Build result dictionary
|
|
79
|
+
result = {
|
|
80
|
+
"error_type": error_info["error_type"],
|
|
81
|
+
"original_message": error_info["original_message"],
|
|
82
|
+
"simple_explanation": exception_data["simple_explanation"],
|
|
83
|
+
"fix_suggestion": exception_data["fix_suggestion"],
|
|
84
|
+
"line_number": error_info.get("line_number"),
|
|
85
|
+
"file_name": error_info.get("file_name"),
|
|
86
|
+
"tags": exception_data.get("tags", []),
|
|
87
|
+
"emoji": exception_data.get("emoji", "❓"),
|
|
88
|
+
"success": False
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Add translation if language is not English
|
|
92
|
+
current_lang = get_language()
|
|
93
|
+
if current_lang != "en":
|
|
94
|
+
result["translated_explanation"] = translate_explanation(
|
|
95
|
+
result["simple_explanation"],
|
|
96
|
+
current_lang
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Add formatted output
|
|
100
|
+
result["formatted_output"] = format_output(result)
|
|
101
|
+
|
|
102
|
+
# Add tokenized output (for rich rendering with images/audio)
|
|
103
|
+
result["tokens"] = tokenize_output(result)
|
|
104
|
+
|
|
105
|
+
# Add branding footer
|
|
106
|
+
result["branding"] = "Powered by pyDefine ● Created by Yahya"
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def decode_exception(e: Exception) -> Dict[str, Any]:
|
|
112
|
+
"""
|
|
113
|
+
Decode an exception object directly into beginner-friendly explanation.
|
|
114
|
+
|
|
115
|
+
This is useful when you catch an exception in a try/except block and
|
|
116
|
+
want to decode it immediately without converting to string.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
e: Exception object (any subclass of BaseException)
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Same dictionary format as decode_traceback()
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
>>> try:
|
|
126
|
+
... result = 10 / 0
|
|
127
|
+
... except Exception as e:
|
|
128
|
+
... decoded = decode_exception(e)
|
|
129
|
+
... print(decoded['simple_explanation'])
|
|
130
|
+
You tried to divide a number by zero, which is impossible in math...
|
|
131
|
+
"""
|
|
132
|
+
if not isinstance(e, BaseException):
|
|
133
|
+
return {
|
|
134
|
+
"error_type": "InvalidInput",
|
|
135
|
+
"original_message": "Not a valid exception object",
|
|
136
|
+
"simple_explanation": "The input is not a valid Python exception.",
|
|
137
|
+
"fix_suggestion": "Pass an exception object caught in try/except block",
|
|
138
|
+
"line_number": None,
|
|
139
|
+
"file_name": None,
|
|
140
|
+
"tags": ["invalid-input"],
|
|
141
|
+
"emoji": "❓",
|
|
142
|
+
"success": False,
|
|
143
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Extract exception details
|
|
147
|
+
error_type = type(e).__name__
|
|
148
|
+
original_message = str(e)
|
|
149
|
+
|
|
150
|
+
# Try to get traceback information
|
|
151
|
+
line_number = None
|
|
152
|
+
file_name = None
|
|
153
|
+
|
|
154
|
+
if hasattr(e, '__traceback__') and e.__traceback__ is not None:
|
|
155
|
+
tb = e.__traceback__
|
|
156
|
+
# Walk to the last frame (where error occurred)
|
|
157
|
+
while tb.tb_next is not None:
|
|
158
|
+
tb = tb.tb_next
|
|
159
|
+
line_number = tb.tb_lineno
|
|
160
|
+
file_name = tb.tb_frame.f_code.co_filename
|
|
161
|
+
|
|
162
|
+
# Get exception mapping info
|
|
163
|
+
exception_data = get_exception_info(error_type)
|
|
164
|
+
|
|
165
|
+
# Build result dictionary
|
|
166
|
+
result = {
|
|
167
|
+
"error_type": error_type,
|
|
168
|
+
"original_message": original_message,
|
|
169
|
+
"simple_explanation": exception_data["simple_explanation"],
|
|
170
|
+
"fix_suggestion": exception_data["fix_suggestion"],
|
|
171
|
+
"line_number": line_number,
|
|
172
|
+
"file_name": file_name,
|
|
173
|
+
"tags": exception_data.get("tags", []),
|
|
174
|
+
"emoji": exception_data.get("emoji", "❓"),
|
|
175
|
+
"success": False
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Add translation if language is not English
|
|
179
|
+
current_lang = get_language()
|
|
180
|
+
if current_lang != "en":
|
|
181
|
+
result["translated_explanation"] = translate_explanation(
|
|
182
|
+
result["simple_explanation"],
|
|
183
|
+
current_lang
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Add formatted output
|
|
187
|
+
result["formatted_output"] = format_output(result)
|
|
188
|
+
|
|
189
|
+
# Add tokenized output
|
|
190
|
+
result["tokens"] = tokenize_output(result)
|
|
191
|
+
|
|
192
|
+
# Add branding footer
|
|
193
|
+
result["branding"] = "Powered by pyDefine ● Created by Yahya"
|
|
194
|
+
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def safe_run(code: str, filename: str = "<input>", globals_dict: Optional[Dict] = None, locals_dict: Optional[Dict] = None) -> Dict[str, Any]:
|
|
199
|
+
"""
|
|
200
|
+
Execute Python code safely and decode any exceptions that occur.
|
|
201
|
+
|
|
202
|
+
This function runs arbitrary Python code in a controlled environment,
|
|
203
|
+
catches any exceptions, and returns either success or decoded error info.
|
|
204
|
+
|
|
205
|
+
SECURITY WARNING: This uses exec() which can be dangerous with untrusted
|
|
206
|
+
code. Only use with code from trusted sources or in sandboxed environments.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
code: Python code string to execute (can be multi-line)
|
|
210
|
+
filename: Name to use in traceback (default: "<input>")
|
|
211
|
+
globals_dict: Global namespace for execution (optional, uses safe defaults)
|
|
212
|
+
locals_dict: Local namespace for execution (optional)
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Dictionary with keys:
|
|
216
|
+
- success: True if code ran without errors, False otherwise
|
|
217
|
+
- result: Return value if success, None otherwise
|
|
218
|
+
- output: Any printed output (captured from stdout)
|
|
219
|
+
|
|
220
|
+
If error occurred, also includes all decode_traceback() fields:
|
|
221
|
+
- error_type, simple_explanation, fix_suggestion, etc.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> result = safe_run("print(10 / 2)")
|
|
225
|
+
>>> print(result['success']) # True
|
|
226
|
+
>>> result = safe_run("print(10 / 0)")
|
|
227
|
+
>>> print(result['success']) # False
|
|
228
|
+
>>> print(result['simple_explanation'])
|
|
229
|
+
You tried to divide a number by zero...
|
|
230
|
+
"""
|
|
231
|
+
if not isinstance(code, str):
|
|
232
|
+
return {
|
|
233
|
+
"success": False,
|
|
234
|
+
"error_type": "InvalidInput",
|
|
235
|
+
"original_message": "Code must be a string",
|
|
236
|
+
"simple_explanation": "The code input is not a valid string.",
|
|
237
|
+
"fix_suggestion": "Pass a string containing Python code",
|
|
238
|
+
"tags": ["invalid-input"],
|
|
239
|
+
"emoji": "❓",
|
|
240
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if not code.strip():
|
|
244
|
+
return {
|
|
245
|
+
"success": True,
|
|
246
|
+
"result": None,
|
|
247
|
+
"output": "",
|
|
248
|
+
"message": "No code to execute",
|
|
249
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Prepare safe execution environment
|
|
253
|
+
if globals_dict is None:
|
|
254
|
+
# Provide a minimal safe globals dict
|
|
255
|
+
globals_dict = {
|
|
256
|
+
"__builtins__": {
|
|
257
|
+
# Safe built-ins only
|
|
258
|
+
"print": print,
|
|
259
|
+
"len": len,
|
|
260
|
+
"range": range,
|
|
261
|
+
"int": int,
|
|
262
|
+
"float": float,
|
|
263
|
+
"str": str,
|
|
264
|
+
"bool": bool,
|
|
265
|
+
"list": list,
|
|
266
|
+
"dict": dict,
|
|
267
|
+
"tuple": tuple,
|
|
268
|
+
"set": set,
|
|
269
|
+
"abs": abs,
|
|
270
|
+
"min": min,
|
|
271
|
+
"max": max,
|
|
272
|
+
"sum": sum,
|
|
273
|
+
"sorted": sorted,
|
|
274
|
+
"reversed": reversed,
|
|
275
|
+
"enumerate": enumerate,
|
|
276
|
+
"zip": zip,
|
|
277
|
+
"map": map,
|
|
278
|
+
"filter": filter,
|
|
279
|
+
"round": round,
|
|
280
|
+
"pow": pow,
|
|
281
|
+
"isinstance": isinstance,
|
|
282
|
+
"type": type,
|
|
283
|
+
"dir": dir,
|
|
284
|
+
"help": help,
|
|
285
|
+
"ValueError": ValueError,
|
|
286
|
+
"TypeError": TypeError,
|
|
287
|
+
"KeyError": KeyError,
|
|
288
|
+
"IndexError": IndexError,
|
|
289
|
+
"ZeroDivisionError": ZeroDivisionError,
|
|
290
|
+
"NameError": NameError,
|
|
291
|
+
"AttributeError": AttributeError,
|
|
292
|
+
# Add more as needed, but avoid dangerous ones like open, eval, exec, __import__
|
|
293
|
+
},
|
|
294
|
+
"__name__": "__main__",
|
|
295
|
+
"__file__": filename,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if locals_dict is None:
|
|
299
|
+
locals_dict = {}
|
|
300
|
+
|
|
301
|
+
# Capture stdout to get print output
|
|
302
|
+
import io
|
|
303
|
+
old_stdout = sys.stdout
|
|
304
|
+
sys.stdout = captured_output = io.StringIO()
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
# Compile code first to get better error messages
|
|
308
|
+
compiled_code = compile(code, filename, 'exec')
|
|
309
|
+
|
|
310
|
+
# Execute the compiled code
|
|
311
|
+
exec(compiled_code, globals_dict, locals_dict)
|
|
312
|
+
|
|
313
|
+
# Get captured output
|
|
314
|
+
output = captured_output.getvalue()
|
|
315
|
+
|
|
316
|
+
# Restore stdout
|
|
317
|
+
sys.stdout = old_stdout
|
|
318
|
+
|
|
319
|
+
# Return success result
|
|
320
|
+
result = {
|
|
321
|
+
"success": True,
|
|
322
|
+
"result": locals_dict.get("result", None), # If code sets a 'result' variable
|
|
323
|
+
"output": output,
|
|
324
|
+
"message": "Code executed successfully",
|
|
325
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return result
|
|
329
|
+
|
|
330
|
+
except Exception as e:
|
|
331
|
+
# Restore stdout
|
|
332
|
+
sys.stdout = old_stdout
|
|
333
|
+
|
|
334
|
+
# Get the full traceback
|
|
335
|
+
tb_lines = traceback.format_exception(type(e), e, e.__traceback__)
|
|
336
|
+
tb_text = ''.join(tb_lines)
|
|
337
|
+
|
|
338
|
+
# Decode the exception using our decoder
|
|
339
|
+
decoded = decode_traceback(tb_text)
|
|
340
|
+
|
|
341
|
+
# Add success flag and captured output
|
|
342
|
+
decoded["success"] = False
|
|
343
|
+
decoded["output"] = captured_output.getvalue()
|
|
344
|
+
|
|
345
|
+
return decoded
|
|
346
|
+
|
|
347
|
+
except BaseException as e:
|
|
348
|
+
# Catch system exits, keyboard interrupts, etc.
|
|
349
|
+
sys.stdout = old_stdout
|
|
350
|
+
|
|
351
|
+
# Still try to decode
|
|
352
|
+
decoded = decode_exception(e)
|
|
353
|
+
decoded["success"] = False
|
|
354
|
+
decoded["output"] = captured_output.getvalue()
|
|
355
|
+
|
|
356
|
+
return decoded
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def decode_traceback_file(filepath: str) -> Dict[str, Any]:
|
|
360
|
+
"""
|
|
361
|
+
Read a traceback from a file and decode it.
|
|
362
|
+
|
|
363
|
+
Useful for analyzing error logs or saved traceback output.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
filepath: Path to file containing traceback text
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Same as decode_traceback()
|
|
370
|
+
|
|
371
|
+
Example:
|
|
372
|
+
>>> result = decode_traceback_file("error.log")
|
|
373
|
+
>>> print(result['simple_explanation'])
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
377
|
+
traceback_text = f.read()
|
|
378
|
+
return decode_traceback(traceback_text)
|
|
379
|
+
except FileNotFoundError:
|
|
380
|
+
return {
|
|
381
|
+
"success": False,
|
|
382
|
+
"error_type": "FileNotFoundError",
|
|
383
|
+
"original_message": f"File not found: {filepath}",
|
|
384
|
+
"simple_explanation": "The traceback file you specified doesn't exist.",
|
|
385
|
+
"fix_suggestion": f"Check if the file path '{filepath}' is correct",
|
|
386
|
+
"tags": ["file", "not-found"],
|
|
387
|
+
"emoji": "📁",
|
|
388
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
389
|
+
}
|
|
390
|
+
except Exception as e:
|
|
391
|
+
return {
|
|
392
|
+
"success": False,
|
|
393
|
+
"error_type": type(e).__name__,
|
|
394
|
+
"original_message": str(e),
|
|
395
|
+
"simple_explanation": f"Could not read the file: {str(e)}",
|
|
396
|
+
"fix_suggestion": "Check file permissions and format",
|
|
397
|
+
"tags": ["file", "read-error"],
|
|
398
|
+
"emoji": "📁",
|
|
399
|
+
"branding": "Powered by pyDefine ● Created by Yahya"
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# Convenience function for quick debugging
|
|
404
|
+
def explain(exception_or_traceback: Union[Exception, str]) -> str:
|
|
405
|
+
"""
|
|
406
|
+
Quick one-line function to get simple explanation from exception or traceback.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
exception_or_traceback: Either an Exception object or traceback string
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Simple explanation string (formatted with emoji)
|
|
413
|
+
|
|
414
|
+
Example:
|
|
415
|
+
>>> try:
|
|
416
|
+
... x = 10 / 0
|
|
417
|
+
... except Exception as e:
|
|
418
|
+
... print(explain(e))
|
|
419
|
+
➗ You tried to divide a number by zero, which is impossible in math...
|
|
420
|
+
"""
|
|
421
|
+
if isinstance(exception_or_traceback, BaseException):
|
|
422
|
+
decoded = decode_exception(exception_or_traceback)
|
|
423
|
+
elif isinstance(exception_or_traceback, str):
|
|
424
|
+
decoded = decode_traceback(exception_or_traceback)
|
|
425
|
+
else:
|
|
426
|
+
return "❓ Invalid input - pass an exception object or traceback string"
|
|
427
|
+
|
|
428
|
+
emoji = decoded.get("emoji", "")
|
|
429
|
+
explanation = decoded.get("simple_explanation", "No explanation available")
|
|
430
|
+
|
|
431
|
+
return f"{emoji} {explanation}"
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
# Module-level convenience for interactive use
|
|
435
|
+
def quick_decode(e: Exception) -> None:
|
|
436
|
+
"""
|
|
437
|
+
Print a quick decoded explanation of an exception (interactive use).
|
|
438
|
+
|
|
439
|
+
Example:
|
|
440
|
+
>>> try:
|
|
441
|
+
... bad_code()
|
|
442
|
+
... except Exception as e:
|
|
443
|
+
... quick_decode(e)
|
|
444
|
+
"""
|
|
445
|
+
decoded = decode_exception(e)
|
|
446
|
+
print("\n" + "="*70)
|
|
447
|
+
print(f"🔍 {decoded['error_type']}: {decoded['original_message']}")
|
|
448
|
+
print("="*70)
|
|
449
|
+
print(f"\n{decoded['emoji']} {decoded['simple_explanation']}\n")
|
|
450
|
+
print(f"💡 Fix: {decoded['fix_suggestion']}\n")
|
|
451
|
+
if decoded.get('line_number'):
|
|
452
|
+
print(f"📍 Line {decoded['line_number']}")
|
|
453
|
+
if decoded.get('file_name'):
|
|
454
|
+
print(f"📁 File: {decoded['file_name']}")
|
|
455
|
+
print("\n" + decoded['branding'])
|
|
456
|
+
print("="*70 + "\n")
|