fishertools 0.2.1__py3-none-any.whl → 0.4.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.
Files changed (69) hide show
  1. fishertools/__init__.py +16 -5
  2. fishertools/errors/__init__.py +11 -3
  3. fishertools/errors/exception_types.py +282 -0
  4. fishertools/errors/explainer.py +87 -1
  5. fishertools/errors/models.py +73 -1
  6. fishertools/errors/patterns.py +40 -0
  7. fishertools/examples/cli_example.py +156 -0
  8. fishertools/examples/learn_example.py +65 -0
  9. fishertools/examples/logger_example.py +176 -0
  10. fishertools/examples/menu_example.py +101 -0
  11. fishertools/examples/storage_example.py +175 -0
  12. fishertools/input_utils.py +185 -0
  13. fishertools/learn/__init__.py +19 -2
  14. fishertools/learn/examples.py +88 -1
  15. fishertools/learn/knowledge_engine.py +321 -0
  16. fishertools/learn/repl/__init__.py +19 -0
  17. fishertools/learn/repl/cli.py +31 -0
  18. fishertools/learn/repl/code_sandbox.py +229 -0
  19. fishertools/learn/repl/command_handler.py +544 -0
  20. fishertools/learn/repl/command_parser.py +165 -0
  21. fishertools/learn/repl/engine.py +479 -0
  22. fishertools/learn/repl/models.py +121 -0
  23. fishertools/learn/repl/session_manager.py +284 -0
  24. fishertools/learn/repl/test_code_sandbox.py +261 -0
  25. fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
  26. fishertools/learn/repl/test_command_handler.py +224 -0
  27. fishertools/learn/repl/test_command_handler_pbt.py +189 -0
  28. fishertools/learn/repl/test_command_parser.py +160 -0
  29. fishertools/learn/repl/test_command_parser_pbt.py +100 -0
  30. fishertools/learn/repl/test_engine.py +190 -0
  31. fishertools/learn/repl/test_session_manager.py +310 -0
  32. fishertools/learn/repl/test_session_manager_pbt.py +182 -0
  33. fishertools/learn/test_knowledge_engine.py +241 -0
  34. fishertools/learn/test_knowledge_engine_pbt.py +180 -0
  35. fishertools/patterns/__init__.py +46 -0
  36. fishertools/patterns/cli.py +175 -0
  37. fishertools/patterns/logger.py +140 -0
  38. fishertools/patterns/menu.py +99 -0
  39. fishertools/patterns/storage.py +127 -0
  40. fishertools/readme_transformer.py +631 -0
  41. fishertools/safe/__init__.py +6 -1
  42. fishertools/safe/files.py +329 -1
  43. fishertools/transform_readme.py +105 -0
  44. fishertools-0.4.0.dist-info/METADATA +104 -0
  45. fishertools-0.4.0.dist-info/RECORD +131 -0
  46. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
  47. tests/test_documentation_properties.py +329 -0
  48. tests/test_documentation_structure.py +349 -0
  49. tests/test_errors/test_exception_types.py +446 -0
  50. tests/test_errors/test_exception_types_pbt.py +333 -0
  51. tests/test_errors/test_patterns.py +52 -0
  52. tests/test_input_utils/__init__.py +1 -0
  53. tests/test_input_utils/test_input_utils.py +65 -0
  54. tests/test_learn/test_examples.py +179 -1
  55. tests/test_learn/test_explain_properties.py +307 -0
  56. tests/test_patterns_cli.py +611 -0
  57. tests/test_patterns_docstrings.py +473 -0
  58. tests/test_patterns_logger.py +465 -0
  59. tests/test_patterns_menu.py +440 -0
  60. tests/test_patterns_storage.py +447 -0
  61. tests/test_readme_enhancements_v0_3_1.py +2036 -0
  62. tests/test_readme_transformer/__init__.py +1 -0
  63. tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
  64. tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
  65. tests/test_safe/test_files.py +726 -1
  66. fishertools-0.2.1.dist-info/METADATA +0 -256
  67. fishertools-0.2.1.dist-info/RECORD +0 -81
  68. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
  69. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
fishertools/__init__.py CHANGED
@@ -11,7 +11,7 @@ Fishertools - инструменты, которые делают Python удо
11
11
  legacy - функции для обратной совместимости
12
12
  """
13
13
 
14
- __version__ = "0.2.1"
14
+ __version__ = "0.3.4"
15
15
  __author__ = "f1sherFM"
16
16
 
17
17
  # Primary API - main interface for users
@@ -19,7 +19,7 @@ from .errors import explain_error
19
19
 
20
20
  # Exception classes for error handling
21
21
  from .errors import (
22
- FishertoolsError, ExplanationError, FormattingError,
22
+ FishertoolsError, ExceptionExplanation, ExplanationError, FormattingError,
23
23
  ConfigurationError, PatternError, SafeUtilityError
24
24
  )
25
25
 
@@ -27,7 +27,8 @@ from .errors import (
27
27
  from .safe import (
28
28
  safe_get, safe_divide, safe_max, safe_min, safe_sum,
29
29
  safe_read_file, safe_write_file, safe_file_exists,
30
- safe_get_file_size, safe_list_files
30
+ safe_get_file_size, safe_list_files,
31
+ safe_open, find_file, project_root
31
32
  )
32
33
 
33
34
  # Learning tools - educational functions
@@ -36,6 +37,11 @@ from .learn import (
36
37
  list_available_concepts, list_available_topics
37
38
  )
38
39
 
40
+ # Input validation functions
41
+ from .input_utils import (
42
+ ask_int, ask_float, ask_str, ask_choice
43
+ )
44
+
39
45
  # Legacy imports for backward compatibility
40
46
  from . import utils
41
47
  from . import decorators
@@ -46,6 +52,7 @@ from . import errors
46
52
  from . import safe
47
53
  from . import learn
48
54
  from . import legacy
55
+ from . import input_utils
49
56
 
50
57
  # New enhancement modules (fishertools-enhancements)
51
58
  from . import learning
@@ -59,13 +66,17 @@ __all__ = [
59
66
  "explain_error",
60
67
 
61
68
  # Exception classes for error handling
62
- "FishertoolsError", "ExplanationError", "FormattingError",
69
+ "FishertoolsError", "ExceptionExplanation", "ExplanationError", "FormattingError",
63
70
  "ConfigurationError", "PatternError", "SafeUtilityError",
64
71
 
65
72
  # Safe utilities - direct access to commonly used functions
66
73
  "safe_get", "safe_divide", "safe_max", "safe_min", "safe_sum",
67
74
  "safe_read_file", "safe_write_file", "safe_file_exists",
68
75
  "safe_get_file_size", "safe_list_files",
76
+ "safe_open", "find_file", "project_root",
77
+
78
+ # Input validation functions
79
+ "ask_int", "ask_float", "ask_str", "ask_choice",
69
80
 
70
81
  # Learning tools - direct access to educational functions
71
82
  "generate_example", "show_best_practice",
@@ -75,7 +86,7 @@ __all__ = [
75
86
  "utils", "decorators", "helpers",
76
87
 
77
88
  # New modules for advanced usage
78
- "errors", "safe", "learn", "legacy",
89
+ "errors", "safe", "learn", "legacy", "input_utils",
79
90
 
80
91
  # Enhancement modules (fishertools-enhancements)
81
92
  "learning", "documentation", "examples", "config", "integration"
@@ -8,7 +8,7 @@ for beginners learning Python.
8
8
  from .explainer import ErrorExplainer, explain_error
9
9
  from .patterns import ErrorPattern
10
10
  from .formatters import ConsoleFormatter, PlainFormatter, JsonFormatter, get_formatter
11
- from .models import ErrorExplanation, ExplainerConfig
11
+ from .models import ErrorExplanation, ExplainerConfig, ExceptionExplanation
12
12
  from .exceptions import (
13
13
  FishertoolsError, ExplanationError, FormattingError,
14
14
  ConfigurationError, PatternError, SafeUtilityError
@@ -17,13 +17,21 @@ from .recovery import (
17
17
  ErrorRecoveryManager, ErrorSeverity, RecoveryStrategy, ErrorContext, RecoveryAction,
18
18
  get_recovery_manager, handle_error_with_recovery, with_error_recovery
19
19
  )
20
+ from .exception_types import (
21
+ identify_exception_type, get_exception_type_info, is_supported_exception_type,
22
+ get_exception_type_mapping, get_supported_exception_types, ExceptionTypeInfo,
23
+ EXCEPTION_TYPE_MAPPING
24
+ )
20
25
 
21
26
  __all__ = [
22
27
  "ErrorExplainer", "explain_error", "ErrorPattern",
23
28
  "ConsoleFormatter", "PlainFormatter", "JsonFormatter", "get_formatter",
24
- "ErrorExplanation", "ExplainerConfig",
29
+ "ErrorExplanation", "ExceptionExplanation", "ExplainerConfig",
25
30
  "FishertoolsError", "ExplanationError", "FormattingError",
26
31
  "ConfigurationError", "PatternError", "SafeUtilityError",
27
32
  "ErrorRecoveryManager", "ErrorSeverity", "RecoveryStrategy", "ErrorContext", "RecoveryAction",
28
- "get_recovery_manager", "handle_error_with_recovery", "with_error_recovery"
33
+ "get_recovery_manager", "handle_error_with_recovery", "with_error_recovery",
34
+ "identify_exception_type", "get_exception_type_info", "is_supported_exception_type",
35
+ "get_exception_type_mapping", "get_supported_exception_types", "ExceptionTypeInfo",
36
+ "EXCEPTION_TYPE_MAPPING"
29
37
  ]
@@ -0,0 +1,282 @@
1
+ """
2
+ Exception type identification module.
3
+
4
+ This module provides utilities for identifying and mapping Python exception types
5
+ to explanation templates. It supports the following exception types:
6
+ - TypeError
7
+ - ValueError
8
+ - IndexError
9
+ - KeyError
10
+ - AttributeError
11
+ - FileNotFoundError
12
+ - PermissionError
13
+ - ZeroDivisionError
14
+ - NameError
15
+ - And provides fallback for unknown exception types
16
+ """
17
+
18
+ from typing import Dict, Type, Optional, Callable
19
+ from dataclasses import dataclass
20
+
21
+
22
+ @dataclass
23
+ class ExceptionTypeInfo:
24
+ """Information about an exception type."""
25
+ exception_class: Type[Exception]
26
+ name: str
27
+ description: str
28
+ common_causes: list
29
+
30
+
31
+ # Mapping of exception types to their information and explanation templates
32
+ EXCEPTION_TYPE_MAPPING: Dict[Type[Exception], ExceptionTypeInfo] = {
33
+ TypeError: ExceptionTypeInfo(
34
+ exception_class=TypeError,
35
+ name="TypeError",
36
+ description="Type mismatch or incompatible operation",
37
+ common_causes=[
38
+ "Mixing incompatible types (e.g., adding string and number)",
39
+ "Wrong number of function arguments",
40
+ "Calling a non-callable object",
41
+ "Invalid operation for the given type"
42
+ ]
43
+ ),
44
+ ValueError: ExceptionTypeInfo(
45
+ exception_class=ValueError,
46
+ name="ValueError",
47
+ description="Invalid value for the operation",
48
+ common_causes=[
49
+ "Invalid literal for type conversion",
50
+ "Unpacking wrong number of values",
51
+ "Invalid argument value",
52
+ "String format error"
53
+ ]
54
+ ),
55
+ IndexError: ExceptionTypeInfo(
56
+ exception_class=IndexError,
57
+ name="IndexError",
58
+ description="Sequence index out of range",
59
+ common_causes=[
60
+ "Accessing list/string index that doesn't exist",
61
+ "Index too large or negative",
62
+ "Empty sequence access",
63
+ "Off-by-one error in loop"
64
+ ]
65
+ ),
66
+ KeyError: ExceptionTypeInfo(
67
+ exception_class=KeyError,
68
+ name="KeyError",
69
+ description="Dictionary key not found",
70
+ common_causes=[
71
+ "Accessing non-existent dictionary key",
72
+ "Typo in key name",
73
+ "Key not added to dictionary",
74
+ "Wrong key type"
75
+ ]
76
+ ),
77
+ AttributeError: ExceptionTypeInfo(
78
+ exception_class=AttributeError,
79
+ name="AttributeError",
80
+ description="Attribute or method not found on object",
81
+ common_causes=[
82
+ "Typo in attribute/method name",
83
+ "Wrong object type",
84
+ "Object not initialized",
85
+ "Attribute doesn't exist on this type"
86
+ ]
87
+ ),
88
+ FileNotFoundError: ExceptionTypeInfo(
89
+ exception_class=FileNotFoundError,
90
+ name="FileNotFoundError",
91
+ description="File or directory not found",
92
+ common_causes=[
93
+ "File path is incorrect",
94
+ "File doesn't exist",
95
+ "Wrong working directory",
96
+ "Typo in filename"
97
+ ]
98
+ ),
99
+ PermissionError: ExceptionTypeInfo(
100
+ exception_class=PermissionError,
101
+ name="PermissionError",
102
+ description="Permission denied for file operation",
103
+ common_causes=[
104
+ "Insufficient file permissions",
105
+ "File is read-only",
106
+ "Directory is protected",
107
+ "Running without required privileges"
108
+ ]
109
+ ),
110
+ ZeroDivisionError: ExceptionTypeInfo(
111
+ exception_class=ZeroDivisionError,
112
+ name="ZeroDivisionError",
113
+ description="Division by zero",
114
+ common_causes=[
115
+ "Dividing by zero",
116
+ "Modulo by zero",
117
+ "Denominator is zero",
118
+ "Variable contains zero unexpectedly"
119
+ ]
120
+ ),
121
+ NameError: ExceptionTypeInfo(
122
+ exception_class=NameError,
123
+ name="NameError",
124
+ description="Name not defined",
125
+ common_causes=[
126
+ "Variable not defined",
127
+ "Typo in variable name",
128
+ "Variable used before definition",
129
+ "Variable out of scope"
130
+ ]
131
+ ),
132
+ }
133
+
134
+
135
+ def identify_exception_type(exception: Exception) -> str:
136
+ """
137
+ Identify the type of an exception and return its name.
138
+
139
+ Args:
140
+ exception: The exception object to identify
141
+
142
+ Returns:
143
+ String name of the exception type (e.g., "TypeError", "ValueError")
144
+
145
+ Raises:
146
+ TypeError: If the argument is not an Exception instance
147
+
148
+ Example:
149
+ >>> try:
150
+ ... x = 1 / 0
151
+ ... except Exception as e:
152
+ ... exc_type = identify_exception_type(e)
153
+ ... print(exc_type) # "ZeroDivisionError"
154
+ """
155
+ if not isinstance(exception, Exception):
156
+ raise TypeError(f"Expected Exception instance, got {type(exception).__name__}")
157
+
158
+ exception_class = type(exception)
159
+
160
+ # Check if it's a known exception type
161
+ if exception_class in EXCEPTION_TYPE_MAPPING:
162
+ return EXCEPTION_TYPE_MAPPING[exception_class].name
163
+
164
+ # Check if it's a subclass of a known exception type
165
+ for known_type, info in EXCEPTION_TYPE_MAPPING.items():
166
+ if isinstance(exception, known_type):
167
+ return info.name
168
+
169
+ # Fallback for unknown exception types
170
+ return exception_class.__name__
171
+
172
+
173
+ def get_exception_type_info(exception: Exception) -> ExceptionTypeInfo:
174
+ """
175
+ Get detailed information about an exception type.
176
+
177
+ Args:
178
+ exception: The exception object
179
+
180
+ Returns:
181
+ ExceptionTypeInfo object with details about the exception type,
182
+ or a generic info object for unknown types
183
+
184
+ Raises:
185
+ TypeError: If the argument is not an Exception instance
186
+
187
+ Example:
188
+ >>> try:
189
+ ... d = {}
190
+ ... value = d['missing_key']
191
+ ... except Exception as e:
192
+ ... info = get_exception_type_info(e)
193
+ ... print(info.description) # "Dictionary key not found"
194
+ """
195
+ if not isinstance(exception, Exception):
196
+ raise TypeError(f"Expected Exception instance, got {type(exception).__name__}")
197
+
198
+ exception_class = type(exception)
199
+
200
+ # Check if it's a known exception type
201
+ if exception_class in EXCEPTION_TYPE_MAPPING:
202
+ return EXCEPTION_TYPE_MAPPING[exception_class]
203
+
204
+ # Check if it's a subclass of a known exception type
205
+ for known_type, info in EXCEPTION_TYPE_MAPPING.items():
206
+ if isinstance(exception, known_type):
207
+ return info
208
+
209
+ # Fallback for unknown exception types
210
+ return ExceptionTypeInfo(
211
+ exception_class=exception_class,
212
+ name=exception_class.__name__,
213
+ description="An unexpected error occurred",
214
+ common_causes=["Unknown cause - check the error message for details"]
215
+ )
216
+
217
+
218
+ def is_supported_exception_type(exception: Exception) -> bool:
219
+ """
220
+ Check if an exception type is explicitly supported.
221
+
222
+ Args:
223
+ exception: The exception object to check
224
+
225
+ Returns:
226
+ True if the exception type is in the supported list, False otherwise
227
+
228
+ Raises:
229
+ TypeError: If the argument is not an Exception instance
230
+
231
+ Example:
232
+ >>> try:
233
+ ... x = 1 / 0
234
+ ... except Exception as e:
235
+ ... if is_supported_exception_type(e):
236
+ ... print("This exception type is supported")
237
+ """
238
+ if not isinstance(exception, Exception):
239
+ raise TypeError(f"Expected Exception instance, got {type(exception).__name__}")
240
+
241
+ exception_class = type(exception)
242
+
243
+ # Check if it's a known exception type
244
+ if exception_class in EXCEPTION_TYPE_MAPPING:
245
+ return True
246
+
247
+ # Check if it's a subclass of a known exception type
248
+ for known_type in EXCEPTION_TYPE_MAPPING.keys():
249
+ if isinstance(exception, known_type):
250
+ return True
251
+
252
+ return False
253
+
254
+
255
+ def get_exception_type_mapping() -> Dict[Type[Exception], ExceptionTypeInfo]:
256
+ """
257
+ Get the complete exception type mapping.
258
+
259
+ Returns:
260
+ Dictionary mapping exception classes to their information
261
+
262
+ Example:
263
+ >>> mapping = get_exception_type_mapping()
264
+ >>> for exc_class, info in mapping.items():
265
+ ... print(f"{info.name}: {info.description}")
266
+ """
267
+ return EXCEPTION_TYPE_MAPPING.copy()
268
+
269
+
270
+ def get_supported_exception_types() -> list:
271
+ """
272
+ Get a list of all supported exception types.
273
+
274
+ Returns:
275
+ List of supported exception type names
276
+
277
+ Example:
278
+ >>> types = get_supported_exception_types()
279
+ >>> print(types)
280
+ ['TypeError', 'ValueError', 'IndexError', ...]
281
+ """
282
+ return [info.name for info in EXCEPTION_TYPE_MAPPING.values()]
@@ -5,7 +5,7 @@ This module contains the ErrorExplainer class and explain_error function.
5
5
  """
6
6
 
7
7
  from typing import Optional, List
8
- from .models import ErrorExplanation, ExplainerConfig, ErrorPattern
8
+ from .models import ErrorExplanation, ExplainerConfig, ErrorPattern, ExceptionExplanation
9
9
  from .patterns import load_default_patterns
10
10
  from .exceptions import ExplanationError, FormattingError, ConfigurationError, FishertoolsError
11
11
 
@@ -83,6 +83,92 @@ class ErrorExplainer:
83
83
  # Graceful degradation - create a minimal explanation if all else fails
84
84
  return self._create_emergency_explanation(exception, e)
85
85
 
86
+ def explain_structured(self, exception: Exception) -> ExceptionExplanation:
87
+ """
88
+ Create a structured explanation for the given exception.
89
+
90
+ This method generates an ExceptionExplanation object with all required fields:
91
+ - exception_type: The type of the exception
92
+ - simple_explanation: Plain-language explanation of what went wrong
93
+ - fix_suggestions: List of ways to fix the problem
94
+ - code_example: Minimal code example showing correct usage
95
+ - traceback_context: Optional traceback information
96
+
97
+ Args:
98
+ exception: The exception to explain
99
+
100
+ Returns:
101
+ ExceptionExplanation object with structured explanation
102
+
103
+ Raises:
104
+ ExceptionError: If explanation creation fails
105
+
106
+ Example:
107
+ >>> try:
108
+ ... x = 1 / 0
109
+ ... except Exception as e:
110
+ ... explanation = explainer.explain_structured(e)
111
+ ... print(explanation.simple_explanation)
112
+ """
113
+ if not isinstance(exception, Exception):
114
+ raise ExplanationError(f"Параметр должен быть экземпляром Exception, получен {type(exception).__name__}")
115
+
116
+ try:
117
+ # Get the basic explanation
118
+ error_explanation = self.explain(exception)
119
+
120
+ # Convert to structured format
121
+ return ExceptionExplanation(
122
+ exception_type=error_explanation.error_type,
123
+ simple_explanation=error_explanation.simple_explanation,
124
+ fix_suggestions=[error_explanation.fix_tip], # Convert single tip to list
125
+ code_example=error_explanation.code_example,
126
+ traceback_context=error_explanation.additional_info
127
+ )
128
+ except Exception as e:
129
+ if isinstance(e, ExplanationError):
130
+ raise
131
+ # Graceful degradation
132
+ return self._create_emergency_structured_explanation(exception, e)
133
+
134
+ def _create_emergency_structured_explanation(self, exception: Exception,
135
+ original_error: Exception) -> ExceptionExplanation:
136
+ """
137
+ Create a minimal structured explanation when all other methods fail.
138
+
139
+ Args:
140
+ exception: The original exception to explain
141
+ original_error: The error that prevented normal explanation
142
+
143
+ Returns:
144
+ Minimal ExceptionExplanation that should always work
145
+ """
146
+ try:
147
+ error_type = getattr(type(exception), '__name__', 'Unknown')
148
+ error_message = str(exception) if exception else 'Unknown error'
149
+
150
+ return ExceptionExplanation(
151
+ exception_type=error_type,
152
+ simple_explanation="An error occurred in your code. Unfortunately, a detailed explanation could not be generated.",
153
+ fix_suggestions=[
154
+ "Check the error message above and try to find the problem in your code.",
155
+ "Search for information about this error type in Python documentation.",
156
+ "Ask for help if you cannot solve the problem yourself."
157
+ ],
158
+ code_example=f"# General error handling:\ntry:\n # your code\n pass\nexcept {error_type} as e:\n print(f'Error: {{e}}')",
159
+ traceback_context=f"Internal fishertools error: {original_error}"
160
+ )
161
+ except Exception:
162
+ # Absolute last resort
163
+ return ExceptionExplanation(
164
+ exception_type="Critical",
165
+ simple_explanation="A critical error occurred in the error explanation system.",
166
+ fix_suggestions=["Contact fishertools developers with a description of the problem."],
167
+ code_example="# Please contact support",
168
+ traceback_context="Critical system error"
169
+ )
170
+
171
+
86
172
  def _match_pattern(self, exception: Exception) -> Optional[ErrorPattern]:
87
173
  """
88
174
  Find the best matching pattern for the given exception.
@@ -225,4 +225,76 @@ class ExplainerConfig:
225
225
  return cls(**data)
226
226
  except Exception as e:
227
227
  from .exceptions import ConfigurationError
228
- raise ConfigurationError(f"Не удалось создать ExplainerConfig из словаря: {e}", original_error=e)
228
+ raise ConfigurationError(f"Не удалось создать ExplainerConfig из словаря: {e}", original_error=e)
229
+
230
+
231
+
232
+ @dataclass
233
+ class ExceptionExplanation:
234
+ """
235
+ Structured explanation of an exception.
236
+
237
+ This dataclass provides a comprehensive explanation of a Python exception,
238
+ including the exception type, a simple explanation, fix suggestions,
239
+ a code example, and optional traceback context.
240
+ """
241
+ exception_type: str
242
+ simple_explanation: str
243
+ fix_suggestions: List[str]
244
+ code_example: str
245
+ traceback_context: Optional[str] = None
246
+
247
+ def __post_init__(self):
248
+ """Validate the explanation after initialization."""
249
+ from .exceptions import ExplanationError
250
+
251
+ if not self.exception_type.strip():
252
+ raise ExplanationError("exception_type cannot be empty")
253
+ if not self.simple_explanation.strip():
254
+ raise ExplanationError("simple_explanation cannot be empty")
255
+ if not self.fix_suggestions:
256
+ raise ExplanationError("fix_suggestions cannot be empty")
257
+ if not all(isinstance(s, str) and s.strip() for s in self.fix_suggestions):
258
+ raise ExplanationError("All fix_suggestions must be non-empty strings")
259
+ if not self.code_example.strip():
260
+ raise ExplanationError("code_example cannot be empty")
261
+
262
+ def to_dict(self) -> Dict[str, Any]:
263
+ """
264
+ Convert explanation to dictionary for serialization.
265
+
266
+ Returns:
267
+ Dictionary representation of the explanation
268
+ """
269
+ return asdict(self)
270
+
271
+ def to_json(self) -> str:
272
+ """
273
+ Convert explanation to JSON string.
274
+
275
+ Returns:
276
+ JSON representation of the explanation
277
+ """
278
+ try:
279
+ return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
280
+ except Exception as e:
281
+ from .exceptions import FormattingError
282
+ raise FormattingError(f"Could not convert explanation to JSON: {e}",
283
+ formatter_type="json", original_error=e)
284
+
285
+ @classmethod
286
+ def from_dict(cls, data: Dict[str, Any]) -> 'ExceptionExplanation':
287
+ """
288
+ Create ExceptionExplanation from dictionary.
289
+
290
+ Args:
291
+ data: Dictionary containing explanation data
292
+
293
+ Returns:
294
+ ExceptionExplanation instance
295
+ """
296
+ try:
297
+ return cls(**data)
298
+ except Exception as e:
299
+ from .exceptions import ExplanationError
300
+ raise ExplanationError(f"Could not create ExceptionExplanation from dictionary: {e}", original_error=e)
@@ -105,6 +105,46 @@ DEFAULT_PATTERNS = [
105
105
  tip="Внимательно проверьте строку, указанную в ошибке. Убедитесь, что все скобки закрыты, отступы правильные, и нет опечаток в ключевых словах.",
106
106
  example="# Неправильно:\n# if x > 5 # забыли двоеточие\n# print('больше 5')\n# print('привет' # незакрытая скобка\n\n# Правильно:\nif x > 5: # двоеточие обязательно\n print('больше 5') # правильный отступ\nprint('привет') # закрытая скобка",
107
107
  common_causes=["забыли двоеточие после if/for/def", "незакрытые скобки", "неправильные отступы", "опечатки в ключевых словах"]
108
+ ),
109
+
110
+ # FileNotFoundError patterns
111
+ ErrorPattern(
112
+ error_type=FileNotFoundError,
113
+ error_keywords=["No such file or directory", "cannot find the file"],
114
+ explanation="Файл, который вы пытаетесь открыть, не существует. Возможно, неправильно указан путь к файлу или файл находится в другой папке.",
115
+ tip="Проверьте правильность пути к файлу. Убедитесь, что файл существует в указанной папке. Используйте абсолютный путь или проверьте текущую рабочую папку.",
116
+ example="# Неправильно:\n# with open('data.txt', 'r') as f: # файл может не существовать\n# data = f.read()\n\n# Правильно:\nimport os\nfilepath = 'data.txt'\nif os.path.exists(filepath):\n with open(filepath, 'r') as f:\n data = f.read()\nelse:\n print(f'Файл {filepath} не найден')",
117
+ common_causes=["неправильный путь к файлу", "файл не существует", "неправильная рабочая папка", "опечатка в имени файла"]
118
+ ),
119
+
120
+ # PermissionError patterns
121
+ ErrorPattern(
122
+ error_type=PermissionError,
123
+ error_keywords=["Permission denied", "access denied"],
124
+ explanation="У вас нет прав доступа для выполнения этой операции с файлом. Это может быть файл, защищенный от записи, или папка, к которой нет доступа.",
125
+ tip="Проверьте права доступа к файлу. Попробуйте запустить программу с правами администратора. Убедитесь, что файл не открыт в другой программе.",
126
+ example="# Неправильно:\n# with open('protected_file.txt', 'w') as f: # может быть защищен\n# f.write('data')\n\n# Правильно:\nimport os\nfilepath = 'protected_file.txt'\nif os.access(filepath, os.W_OK):\n with open(filepath, 'w') as f:\n f.write('data')\nelse:\n print(f'Нет прав доступа к файлу {filepath}')",
127
+ common_causes=["файл защищен от записи", "недостаточно прав доступа", "файл открыт в другой программе", "папка защищена"]
128
+ ),
129
+
130
+ # ZeroDivisionError patterns
131
+ ErrorPattern(
132
+ error_type=ZeroDivisionError,
133
+ error_keywords=["division by zero", "integer division or modulo by zero"],
134
+ explanation="Вы пытаетесь разделить число на ноль, что математически невозможно. Это может быть деление или операция модуля (%).",
135
+ tip="Проверьте, что делитель не равен нулю перед выполнением операции деления. Используйте условие if для проверки.",
136
+ example="# Неправильно:\n# result = 10 / 0 # ZeroDivisionError\n\n# Правильно:\ndivisor = 0\nif divisor != 0:\n result = 10 / divisor\nelse:\n print('Нельзя делить на ноль')\n result = None",
137
+ common_causes=["деление на ноль", "модуль по нулю", "переменная содержит ноль неожиданно", "ошибка в расчетах"]
138
+ ),
139
+
140
+ # NameError patterns
141
+ ErrorPattern(
142
+ error_type=NameError,
143
+ error_keywords=["is not defined", "name"],
144
+ explanation="Вы используете переменную или функцию, которая не была определена. Возможно, опечатка в имени или переменная определена в другой области видимости.",
145
+ tip="Проверьте правильность написания имени переменной. Убедитесь, что переменная определена перед использованием. Проверьте область видимости переменной.",
146
+ example="# Неправильно:\n# print(my_variable) # NameError - переменная не определена\n\n# Правильно:\nmy_variable = 'Hello' # определяем переменную\nprint(my_variable) # теперь можно использовать\n\n# Или проверяем область видимости:\nif True:\n local_var = 'local'\nprint(local_var) # NameError - переменная локальная",
147
+ common_causes=["опечатка в имени переменной", "переменная не определена", "переменная в другой области видимости", "забыли импортировать модуль"]
108
148
  )
109
149
  ]
110
150