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/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")