WhyCrash 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.
WhyCrash/__init__.py ADDED
@@ -0,0 +1,243 @@
1
+ import sys
2
+ import traceback
3
+ import os
4
+ import re
5
+
6
+ def debug():
7
+ """Эту функцию нужно вызвать в начале кода: WhyCrash.debug() (ГЛОБАЛЬНЫЙ ПЕРЕХВАТ)"""
8
+ start_debug()
9
+
10
+ def start_debug():
11
+ """Включает AI-анализ ошибок для всего последующего кода"""
12
+ if not hasattr(sys, 'ps1'):
13
+ sys.excepthook = _ai_excepthook
14
+
15
+ def end_debug():
16
+ """Выключает AI-анализ ошибок (возвращает стандартное поведение Python)"""
17
+ if sys.excepthook == _ai_excepthook:
18
+ sys.excepthook = sys.__excepthook__
19
+
20
+ import contextlib
21
+ import functools
22
+
23
+ @contextlib.contextmanager
24
+ def catch_block():
25
+ """Контекстный менеджер для перехвата ошибок только в определенном блоке кода"""
26
+ try:
27
+ yield
28
+ except Exception:
29
+ _ai_excepthook(*sys.exc_info())
30
+ sys.exit(1)
31
+
32
+ def catch_errors(func):
33
+ """Декоратор для перехвата ошибок только в конкретной функции"""
34
+ @functools.wraps(func)
35
+ def wrapper(*args, **kwargs):
36
+ try:
37
+ return func(*args, **kwargs)
38
+ except Exception:
39
+ _ai_excepthook(*sys.exc_info())
40
+ sys.exit(1)
41
+ return wrapper
42
+
43
+ def _ai_excepthook(exc_type, exc_value, exc_traceback):
44
+ try:
45
+ import requests
46
+ import json
47
+ except ImportError:
48
+ print("Для работы WhyCrash необходимо установить пакет 'requests': pip install requests")
49
+ sys.__excepthook__(exc_type, exc_value, exc_traceback)
50
+ return
51
+
52
+ try:
53
+ from rich.console import Console
54
+ from rich.markdown import Markdown
55
+ from rich.panel import Panel
56
+ RICH = True
57
+ console = Console()
58
+ except ImportError:
59
+ RICH = False
60
+
61
+ RED = '\033[91m'
62
+ GREEN = '\033[92m'
63
+ YELLOW = '\033[93m'
64
+ CYAN = '\033[96m'
65
+ RESET = '\033[0m'
66
+
67
+ if RICH:
68
+ console.print(Panel("Oops! Произошла ошибка. WhyCrash собирает контекст и анализирует проблему...", border_style="bold red", expand=False))
69
+ else:
70
+ print(f"\n{RED}Oops! Произошла ошибка. WhyCrash собирает контекст и анализирует проблему...{RESET}\n")
71
+
72
+ tb_lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
73
+ tb_text = "".join(tb_lines)
74
+
75
+ local_files = {}
76
+ deepest_file = None
77
+ deepest_line = 0
78
+
79
+ tb = exc_traceback
80
+ while tb:
81
+ filename = tb.tb_frame.f_code.co_filename
82
+ lineno = tb.tb_lineno
83
+ deepest_file = filename
84
+ deepest_line = lineno
85
+
86
+ if isinstance(filename, str) and os.path.exists(filename):
87
+ if 'site-packages' not in filename and 'lib\\python' not in filename.lower() and 'lib/python' not in filename.lower():
88
+ if filename not in local_files:
89
+ try:
90
+ with open(filename, 'r', encoding='utf-8') as f:
91
+ local_files[filename] = f.read()
92
+ except Exception:
93
+ pass
94
+ tb = tb.tb_next
95
+
96
+ context_str = ""
97
+ for fpath, code in local_files.items():
98
+ context_str += f"### Файл: {fpath} ###\n```python\n{code}\n```\n\n"
99
+
100
+ first_prompt = f"""Произошла ошибка в Python приложении.
101
+ Исключение вызвано в файле '{deepest_file}' на строке {deepest_line}.
102
+
103
+ Traceback:
104
+ {tb_text}
105
+
106
+ Исходный код контекстных файлов (только те, что относятся к проекту):
107
+ {context_str}
108
+
109
+ Пожалуйста, проанализируй эту ошибку и детально объясни на русском языке, почему она произошла.
110
+ Пока что НЕ пиши исправленный код, только проанализируй и объясни причину проблемы."""
111
+
112
+ API_KEY = "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"
113
+
114
+ # ========= ПЕРВЫЙ ЗАПРОС К OPENROUTER =========
115
+ messages = [{"role": "user", "content": first_prompt}]
116
+
117
+ try:
118
+ response = requests.post(
119
+ url="https://openrouter.ai/api/v1/chat/completions",
120
+ headers={
121
+ "Authorization": f"Bearer {API_KEY}",
122
+ "Content-Type": "application/json",
123
+ },
124
+ data=json.dumps({
125
+ "model": "minimax/minimax-m2.5",
126
+ "messages": messages,
127
+ "reasoning": {"enabled": True}
128
+ })
129
+ )
130
+ response.raise_for_status()
131
+ resp_json = response.json()
132
+ assistant_message = resp_json['choices'][0]['message']
133
+
134
+ reasoning = assistant_message.get('reasoning_details') or ""
135
+ content = assistant_message.get('content') or ""
136
+
137
+ if RICH:
138
+ if reasoning:
139
+ console.print(Panel(Markdown(f"**Мысли (Reasoning):**\n\n{reasoning}"), title="AI Обдумывает", border_style="cyan"))
140
+ console.print(Panel(Markdown(content), title="Анализ ошибки", border_style="yellow"))
141
+ else:
142
+ print(f"{CYAN}============== Знания и Анализ (Minimax) =============={RESET}\n")
143
+ if reasoning:
144
+ print(f"{CYAN}--- Размышления ---{RESET}\n{reasoning}\n")
145
+ print(f"{YELLOW}--- Объяснение ---{RESET}\n{content}\n")
146
+ print(f"{CYAN}======================================================={RESET}\n")
147
+
148
+ # Просим у пользователя подтверждение
149
+ try:
150
+ import questionary
151
+ answer = questionary.select(
152
+ "Хотите, чтобы WhyCrash исправил эту ошибку?",
153
+ choices=["Да", "Нет"]
154
+ ).ask()
155
+ if answer != "Да":
156
+ print(f"{YELLOW}Отменено. Выходим...{RESET}")
157
+ sys.exit(1)
158
+ except ImportError:
159
+ answer = input(f"{GREEN}Хотите, чтобы WhyCrash исправил эту ошибку? (y/n, enter=yes): {RESET}")
160
+ if answer.strip().lower() not in ('', 'y', 'yes', 'да', 'д'):
161
+ print(f"{YELLOW}Отменено. Выходим...{RESET}")
162
+ sys.exit(1)
163
+
164
+ # ========= ВТОРОЙ ЗАПРОС К OPENROUTER (ПРОДОЛЖАЕМ РАЗМЫШЛЕНИЯ) =========
165
+ if RICH:
166
+ console.print(f"[bold green]Генерируем исправление...[/bold green]")
167
+ else:
168
+ print(f"\n{GREEN}Генерируем исправление...{RESET}")
169
+
170
+ messages.append({
171
+ "role": "assistant",
172
+ "content": content,
173
+ "reasoning_details": reasoning
174
+ })
175
+
176
+ second_prompt = """Напиши ПОЛНЫЙ исправленный код для файла, в котором нужно сделать изменения.
177
+ ВАЖНО: Выведи исправленный код внутри одного блока ```python ... ```.
178
+ Непосредственно перед блоком кода напиши пустой комментарий или строку, указывающую какой файл ты исправляешь, в таком точном формате:
179
+ FILE_TO_FIX: <полный_путь_к_файлу>
180
+ Выводи весь файл целиком, чтобы я мог полностью заменить старый файл."""
181
+
182
+ messages.append({"role": "user", "content": second_prompt})
183
+
184
+ response2 = requests.post(
185
+ url="https://openrouter.ai/api/v1/chat/completions",
186
+ headers={
187
+ "Authorization": f"Bearer {API_KEY}",
188
+ "Content-Type": "application/json",
189
+ },
190
+ data=json.dumps({
191
+ "model": "minimax/minimax-m2.5",
192
+ "messages": messages,
193
+ "reasoning": {"enabled": True}
194
+ })
195
+ )
196
+ response2.raise_for_status()
197
+ resp_json2 = response2.json()
198
+ assistant_message2 = resp_json2['choices'][0]['message']
199
+
200
+ content2 = assistant_message2.get('content') or ""
201
+
202
+ # Парсим ответ
203
+ file_to_fix = deepest_file
204
+ match_file = re.search(r"FILE_TO_FIX:\s*(.*)", content2)
205
+ if match_file:
206
+ file_to_fix = match_file.group(1).strip()
207
+
208
+ parts = content2.split("```python")
209
+ if len(parts) > 1:
210
+ last_block = parts[-1].split("```")[0]
211
+ fixed_code = last_block.strip()
212
+
213
+ if os.path.exists(file_to_fix):
214
+ try:
215
+ with open(file_to_fix, 'w', encoding='utf-8') as f:
216
+ f.write(fixed_code + '\n')
217
+ if RICH:
218
+ console.print(f"[bold green][+] Файл '{file_to_fix}' успешно исправлен! Запустите скрипт заново.[/bold green]")
219
+ else:
220
+ print(f"{GREEN}\n[+] Файл '{file_to_fix}' успешно исправлен! Запустите скрипт заново.{RESET}")
221
+ except Exception as e:
222
+ if RICH:
223
+ console.print(f"[bold red][-] Не удалось записать файл: {e}[/bold red]")
224
+ else:
225
+ print(f"{RED}\n[-] Не удалось записать файл: {e}{RESET}")
226
+ else:
227
+ if RICH:
228
+ console.print(f"[bold red]Не найден файл для исправления: {file_to_fix}[/bold red]")
229
+ else:
230
+ print(f"\n{RED}Не найден файл для исправления: {file_to_fix}{RESET}")
231
+ else:
232
+ if RICH:
233
+ console.print(f"[bold yellow]Код для исправления не найден в ответе.[/bold yellow]")
234
+ else:
235
+ print(f"\n{YELLOW}Код для исправления не найден в ответе.{RESET}")
236
+
237
+ except Exception as e:
238
+ if RICH:
239
+ console.print(f"[bold red]ОШИБКА WhyCrash API: {e}[/bold red]")
240
+ else:
241
+ print(f"{RED}ОШИБКА WhyCrash API: {e}{RESET}")
242
+ print("\nОригинальный Traceback:")
243
+ print(tb_text)
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: WhyCrash
3
+ Version: 1.0.0
4
+ Summary: A highly automatic AI error handler and code fixer using OpenRouter and Minimax.
5
+ Home-page: https://github.com/yourusername/WhyCrash
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: requests
12
+ Requires-Dist: rich
13
+ Requires-Dist: questionary
14
+ Dynamic: classifier
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: requires-dist
19
+ Dynamic: requires-python
20
+ Dynamic: summary
21
+
22
+ # 🚀 WhyCrash
23
+ **WhyCrash** is a fully automatic AI assistant for error handling in Python. When your code crashes, WhyCrash intercepts the error, analyzes it using neural networks (OpenRouter + Minimax), gathers context from your local project files, and provides the cause along with an **AUTOMATIC CODE FIX**.
24
+
25
+ Did your code crash? The AI will explain why and automatically replace the broken file with the fixed one (if you allow it).
26
+
27
+ ![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)
28
+ ![Python Versions](https://img.shields.io/badge/python-3.8%2B-blue)
29
+
30
+ ## ✨ Main Features
31
+ - 🧠 **Smart Traceback Analysis**: Understands not just the line with the error but also gathers imported local project files.
32
+ - 🛠️ **Auto-Fixing**: Proposes a ready-made fix and can rewrite the target Python files itself.
33
+ - 🎯 **Precise Control**: You decide where to catch errors: in the entire project, in a single function, or in a specific block of code.
34
+ - 🎨 **Beautiful Interface**: Uses the `rich` library for nice windows and terminal formatting.
35
+
36
+ ---
37
+
38
+ ## 📦 Installation
39
+
40
+ ```bash
41
+ pip install WhyCrash
42
+ ```
43
+ > *(Requires `requests`, `rich`, and `questionary` — they will install automatically)*
44
+
45
+ ---
46
+
47
+ ## 🛠️ How to Use
48
+
49
+ You have 4 ways to control which errors WhyCrash should catch. Choose the one that fits best!
50
+
51
+ ### 1. Global Intercept (Easiest)
52
+ If you want **any** unhandled error in your program to be analyzed by the AI:
53
+
54
+ ```python
55
+ import WhyCrash
56
+
57
+ # Enable error catching for the whole script
58
+ WhyCrash.debug()
59
+
60
+ # If the code crashes below, WhyCrash comes to the rescue!
61
+ print(1 / 0)
62
+ ```
63
+
64
+ ### 2. Dynamic Toggle (start & end)
65
+ If you have a large block of code and want to turn on smart analysis right before it, and turn it off right after:
66
+
67
+ ```python
68
+ import WhyCrash
69
+
70
+ # ... normal code without WhyCrash ...
71
+
72
+ WhyCrash.start_debug() # Turn on the interceptor
73
+
74
+ a = "text"
75
+ b = int(a) # <-- This error will go to the AI!
76
+
77
+ WhyCrash.end_debug() # Turn off the interceptor (returns to standard behavior)
78
+ ```
79
+
80
+ ### 3. Decorator for Specific Functions `@catch_errors`
81
+ If you are only concerned about the reliability of a specific function, you can wrap it in a decorator. If the function crashes, WhyCrash will trigger, while system errors outside of it remain untouched.
82
+
83
+ ```python
84
+ from WhyCrash import catch_errors
85
+
86
+ @catch_errors
87
+ def my_danger_function():
88
+ # If it breaks here — WhyCrash will trigger
89
+ file = open("no_exist.txt", "r")
90
+
91
+ def normal_function():
92
+ # And if it breaks here — standard Python traceback
93
+ pass
94
+
95
+ my_danger_function()
96
+ ```
97
+
98
+ ### 4. Context Manager `with catch_block()`
99
+ For the most precise control, if you expect a failure in literally 2 specific lines of code:
100
+
101
+ ```python
102
+ from WhyCrash import catch_block
103
+
104
+ print("Starting work...")
105
+ text = "100"
106
+
107
+ with catch_block():
108
+ # Only code inside this block is monitored
109
+ number = int(text)
110
+ result = number / 0 # This will trigger an error sent to WhyCrash!
111
+
112
+ print("This code will not execute if there was an error above.")
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 🛑 How to Ignore Error Catching?
118
+ WhyCrash only analyzes **unhandled** exceptions. If you want an error in your code **not** to reach WhyCrash and the script to keep running, simply use a standard `try...except` block:
119
+
120
+ ```python
121
+ import WhyCrash
122
+ WhyCrash.debug()
123
+
124
+ try:
125
+ int("letter")
126
+ except ValueError:
127
+ print("Error caught, it won't reach WhyCrash. Moving on!")
128
+ ```
129
+
130
+ ## ⚙️ Under the Hood
131
+ - **OpenRouter & Minimax** — Responsible for code analysis, "Reasoning," and generating fix files.
132
+ - **Traceback Walking** — The script automatically follows the error chain, finds all your `.py` files involved, reads them, and sends them to the AI as context.
133
+ - **Rich** — Beautiful console UI (colors, panels, Markdown formatting).
134
+
135
+ ---
136
+
137
+ Made with ❤️ to save developers' nerves!
138
+
139
+ ---
140
+ 🌍 **Languages:** [Русский](docs/README_ru.md) | [Deutsch](docs/README_de.md)
@@ -0,0 +1,5 @@
1
+ WhyCrash/__init__.py,sha256=WkCN9yNCFHlyNfcaY-CBJaXcpPZO7xCSz_sd9PF4WR0,10895
2
+ whycrash-1.0.0.dist-info/METADATA,sha256=q8BqaPT71Cwg3K24gI17mFHQxCuChWLA42oR5-XAEf0,4709
3
+ whycrash-1.0.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
4
+ whycrash-1.0.0.dist-info/top_level.txt,sha256=okvyfU74gtCu42DIh6YyGTqz-vJroxd2XX6JHKf9rUs,9
5
+ whycrash-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ WhyCrash