fishertools 0.2.1__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.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error patterns database.
|
|
3
|
+
|
|
4
|
+
This module contains predefined patterns for common Python exceptions
|
|
5
|
+
with Russian explanations for beginners.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .models import ErrorPattern
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Error patterns for common Python exceptions
|
|
12
|
+
DEFAULT_PATTERNS = [
|
|
13
|
+
# TypeError patterns
|
|
14
|
+
ErrorPattern(
|
|
15
|
+
error_type=TypeError,
|
|
16
|
+
error_keywords=["unsupported operand type", "can't multiply", "can't add", "not supported between"],
|
|
17
|
+
explanation="Вы пытаетесь выполнить операцию между несовместимыми типами данных. Например, нельзя сложить число и строку.",
|
|
18
|
+
tip="Убедитесь, что все операнды имеют совместимые типы. Используйте функции преобразования типов (int(), str(), float()) если нужно.",
|
|
19
|
+
example="# Неправильно:\n# result = 5 + '3' # TypeError\n\n# Правильно:\nresult = 5 + int('3') # 8\n# или\nresult = str(5) + '3' # '53'",
|
|
20
|
+
common_causes=["смешивание чисел и строк", "неправильные типы аргументов функции", "операции с None"]
|
|
21
|
+
),
|
|
22
|
+
|
|
23
|
+
ErrorPattern(
|
|
24
|
+
error_type=TypeError,
|
|
25
|
+
error_keywords=["takes", "positional argument", "got", "missing", "required positional argument"],
|
|
26
|
+
explanation="Функция вызвана с неправильным количеством аргументов. Либо передано слишком много, либо слишком мало параметров.",
|
|
27
|
+
tip="Проверьте определение функции и убедитесь, что передаете правильное количество аргументов в правильном порядке.",
|
|
28
|
+
example="# Неправильно:\n# def greet(name, age):\n# return f'Привет, {name}! Тебе {age} лет.'\n# greet('Анна') # TypeError - не хватает аргумента\n\n# Правильно:\ndef greet(name, age):\n return f'Привет, {name}! Тебе {age} лет.'\nresult = greet('Анна', 25)",
|
|
29
|
+
common_causes=["забыли передать аргумент", "передали лишний аргумент", "неправильный порядок аргументов"]
|
|
30
|
+
),
|
|
31
|
+
|
|
32
|
+
ErrorPattern(
|
|
33
|
+
error_type=TypeError,
|
|
34
|
+
error_keywords=["not callable", "object is not callable"],
|
|
35
|
+
explanation="Вы пытаетесь вызвать как функцию объект, который функцией не является. Возможно, забыли скобки или перепутали переменную с функцией.",
|
|
36
|
+
tip="Проверьте, что вызываете именно функцию. Убедитесь, что не перезаписали имя функции переменной.",
|
|
37
|
+
example="# Неправильно:\n# my_list = [1, 2, 3]\n# result = my_list() # TypeError - список не функция\n\n# Правильно:\nmy_list = [1, 2, 3]\nresult = len(my_list) # вызываем функцию len()",
|
|
38
|
+
common_causes=["вызов переменной как функции", "перезапись имени функции", "опечатка в имени функции"]
|
|
39
|
+
),
|
|
40
|
+
|
|
41
|
+
# ValueError patterns
|
|
42
|
+
ErrorPattern(
|
|
43
|
+
error_type=ValueError,
|
|
44
|
+
error_keywords=["invalid literal", "could not convert", "base"],
|
|
45
|
+
explanation="Не удалось преобразовать строку в число, потому что строка содержит недопустимые символы для числа.",
|
|
46
|
+
tip="Убедитесь, что строка содержит только цифры (и точку для float). Проверьте входные данные перед преобразованием.",
|
|
47
|
+
example="# Неправильно:\n# number = int('abc') # ValueError\n\n# Правильно:\nuser_input = '123'\nif user_input.isdigit():\n number = int(user_input)\nelse:\n print('Введите корректное число')",
|
|
48
|
+
common_causes=["ввод текста вместо числа", "лишние пробелы в строке", "неправильный формат числа"]
|
|
49
|
+
),
|
|
50
|
+
|
|
51
|
+
ErrorPattern(
|
|
52
|
+
error_type=ValueError,
|
|
53
|
+
error_keywords=["not enough values to unpack", "too many values to unpack"],
|
|
54
|
+
explanation="Количество переменных не соответствует количеству значений при распаковке. Либо переменных больше чем значений, либо наоборот.",
|
|
55
|
+
tip="Убедитесь, что количество переменных слева от знака = равно количеству элементов в последовательности справа.",
|
|
56
|
+
example="# Неправильно:\n# a, b = [1, 2, 3] # слишком много значений\n# x, y, z = [1, 2] # не хватает значений\n\n# Правильно:\na, b, c = [1, 2, 3] # количество совпадает\n# или используйте индексы:\ndata = [1, 2, 3]\na = data[0]\nb = data[1]",
|
|
57
|
+
common_causes=["неправильное количество переменных", "изменился размер списка", "ошибка в логике программы"]
|
|
58
|
+
),
|
|
59
|
+
|
|
60
|
+
# AttributeError patterns
|
|
61
|
+
ErrorPattern(
|
|
62
|
+
error_type=AttributeError,
|
|
63
|
+
error_keywords=["has no attribute", "object has no attribute"],
|
|
64
|
+
explanation="Вы пытаетесь обратиться к атрибуту или методу, которого не существует у данного объекта.",
|
|
65
|
+
tip="Проверьте правильность написания имени атрибута или метода. Убедитесь, что объект имеет нужный атрибут. Используйте dir() для просмотра доступных атрибутов.",
|
|
66
|
+
example="# Неправильно:\n# my_string = 'привет'\n# my_string.append('!') # у строк нет метода append\n\n# Правильно:\nmy_string = 'привет'\nmy_string = my_string + '!' # конкатенация строк\n# или для списков:\nmy_list = ['привет']\nmy_list.append('!')",
|
|
67
|
+
common_causes=["опечатка в имени метода", "неправильный тип объекта", "объект не инициализирован"]
|
|
68
|
+
),
|
|
69
|
+
|
|
70
|
+
# IndexError patterns
|
|
71
|
+
ErrorPattern(
|
|
72
|
+
error_type=IndexError,
|
|
73
|
+
error_keywords=["list index out of range", "string index out of range"],
|
|
74
|
+
explanation="Вы пытаетесь обратиться к элементу списка или строки по индексу, который не существует. Индекс слишком большой или отрицательный.",
|
|
75
|
+
tip="Проверьте длину списка/строки с помощью len(). Помните, что индексы начинаются с 0 и заканчиваются на len()-1.",
|
|
76
|
+
example="# Неправильно:\n# my_list = [1, 2, 3]\n# print(my_list[5]) # IndexError - индекс 5 не существует\n\n# Правильно:\nmy_list = [1, 2, 3]\nif len(my_list) > 2:\n print(my_list[2]) # проверяем длину перед обращением\n# или используйте безопасный доступ:\nindex = 2\nif 0 <= index < len(my_list):\n print(my_list[index])",
|
|
77
|
+
common_causes=["неправильный расчет индекса", "пустой список", "цикл выходит за границы"]
|
|
78
|
+
),
|
|
79
|
+
|
|
80
|
+
# KeyError patterns
|
|
81
|
+
ErrorPattern(
|
|
82
|
+
error_type=KeyError,
|
|
83
|
+
error_keywords=[""], # KeyError messages are often just the key name, so match any KeyError
|
|
84
|
+
explanation="Вы пытаетесь получить значение из словаря по ключу, которого в словаре не существует.",
|
|
85
|
+
tip="Проверьте, существует ли ключ в словаре с помощью 'in' или используйте метод get() с значением по умолчанию.",
|
|
86
|
+
example="# Неправильно:\n# my_dict = {'имя': 'Анна', 'возраст': 25}\n# print(my_dict['город']) # KeyError - ключа 'город' нет\n\n# Правильно:\nmy_dict = {'имя': 'Анна', 'возраст': 25}\n# Способ 1: проверка существования ключа\nif 'город' in my_dict:\n print(my_dict['город'])\nelse:\n print('Город не указан')\n# Способ 2: использование get()\nprint(my_dict.get('город', 'Не указан'))",
|
|
87
|
+
common_causes=["опечатка в имени ключа", "ключ не был добавлен в словарь", "неправильный тип ключа"]
|
|
88
|
+
),
|
|
89
|
+
|
|
90
|
+
# ImportError patterns
|
|
91
|
+
ErrorPattern(
|
|
92
|
+
error_type=ImportError,
|
|
93
|
+
error_keywords=["No module named", "cannot import name"],
|
|
94
|
+
explanation="Python не может найти модуль или функцию, которую вы пытаетесь импортировать. Возможно, модуль не установлен или имя написано неправильно.",
|
|
95
|
+
tip="Убедитесь, что модуль установлен (pip install имя_модуля) и имя написано правильно. Проверьте, что файл находится в правильной папке.",
|
|
96
|
+
example="# Если модуль не установлен:\n# pip install requests\n\n# Правильный импорт:\nimport os # встроенный модуль\nfrom datetime import datetime # импорт конкретной функции\n\n# Для собственных модулей:\n# убедитесь, что файл my_module.py находится в той же папке\n# import my_module",
|
|
97
|
+
common_causes=["модуль не установлен", "опечатка в имени", "неправильный путь к файлу", "проблемы с виртуальным окружением"]
|
|
98
|
+
),
|
|
99
|
+
|
|
100
|
+
# SyntaxError patterns
|
|
101
|
+
ErrorPattern(
|
|
102
|
+
error_type=SyntaxError,
|
|
103
|
+
error_keywords=["invalid syntax", "unexpected EOF", "unmatched", "expected"],
|
|
104
|
+
explanation="В коде есть синтаксическая ошибка - нарушены правила написания кода Python. Это может быть незакрытая скобка, неправильный отступ или опечатка.",
|
|
105
|
+
tip="Внимательно проверьте строку, указанную в ошибке. Убедитесь, что все скобки закрыты, отступы правильные, и нет опечаток в ключевых словах.",
|
|
106
|
+
example="# Неправильно:\n# if x > 5 # забыли двоеточие\n# print('больше 5')\n# print('привет' # незакрытая скобка\n\n# Правильно:\nif x > 5: # двоеточие обязательно\n print('больше 5') # правильный отступ\nprint('привет') # закрытая скобка",
|
|
107
|
+
common_causes=["забыли двоеточие после if/for/def", "незакрытые скобки", "неправильные отступы", "опечатки в ключевых словах"]
|
|
108
|
+
)
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def load_default_patterns():
|
|
113
|
+
"""
|
|
114
|
+
Load default error patterns for common Python exceptions.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
List of ErrorPattern objects for common Python exceptions
|
|
118
|
+
"""
|
|
119
|
+
return DEFAULT_PATTERNS.copy()
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error recovery manager for graceful degradation and error handling.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Optional, Dict, Any, List, Callable
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from .exceptions import FishertoolsError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ErrorSeverity(Enum):
|
|
13
|
+
"""Severity levels for errors."""
|
|
14
|
+
LOW = "low"
|
|
15
|
+
MEDIUM = "medium"
|
|
16
|
+
HIGH = "high"
|
|
17
|
+
CRITICAL = "critical"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RecoveryStrategy(Enum):
|
|
21
|
+
"""Recovery strategies for different error types."""
|
|
22
|
+
RETRY = "retry"
|
|
23
|
+
FALLBACK = "fallback"
|
|
24
|
+
GRACEFUL_DEGRADATION = "graceful_degradation"
|
|
25
|
+
FAIL_FAST = "fail_fast"
|
|
26
|
+
IGNORE = "ignore"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ErrorContext:
|
|
31
|
+
"""Context information for an error."""
|
|
32
|
+
component: str
|
|
33
|
+
operation: str
|
|
34
|
+
error_type: str
|
|
35
|
+
error_message: str
|
|
36
|
+
severity: ErrorSeverity
|
|
37
|
+
metadata: Dict[str, Any]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class RecoveryAction:
|
|
42
|
+
"""Action to take for error recovery."""
|
|
43
|
+
strategy: RecoveryStrategy
|
|
44
|
+
fallback_function: Optional[Callable] = None
|
|
45
|
+
retry_count: int = 0
|
|
46
|
+
max_retries: int = 3
|
|
47
|
+
message: str = ""
|
|
48
|
+
should_log: bool = True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ErrorRecoveryManager:
|
|
52
|
+
"""
|
|
53
|
+
Manages error recovery strategies and graceful degradation.
|
|
54
|
+
|
|
55
|
+
Provides centralized error handling with configurable recovery
|
|
56
|
+
strategies for different types of errors and components.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self):
|
|
60
|
+
"""Initialize the error recovery manager."""
|
|
61
|
+
self._recovery_strategies: Dict[str, RecoveryAction] = {}
|
|
62
|
+
self._error_handlers: Dict[str, Callable] = {}
|
|
63
|
+
self._fallback_functions: Dict[str, Callable] = {}
|
|
64
|
+
self._error_counts: Dict[str, int] = {}
|
|
65
|
+
|
|
66
|
+
# Set up default recovery strategies
|
|
67
|
+
self._setup_default_strategies()
|
|
68
|
+
|
|
69
|
+
# Set up logging
|
|
70
|
+
self.logger = logging.getLogger(__name__)
|
|
71
|
+
|
|
72
|
+
def _setup_default_strategies(self) -> None:
|
|
73
|
+
"""Set up default recovery strategies for common error types."""
|
|
74
|
+
|
|
75
|
+
# Configuration errors - use defaults and continue
|
|
76
|
+
self._recovery_strategies['config_error'] = RecoveryAction(
|
|
77
|
+
strategy=RecoveryStrategy.FALLBACK,
|
|
78
|
+
message="Configuration error detected, using default settings",
|
|
79
|
+
should_log=True
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Learning content errors - graceful degradation
|
|
83
|
+
self._recovery_strategies['content_error'] = RecoveryAction(
|
|
84
|
+
strategy=RecoveryStrategy.GRACEFUL_DEGRADATION,
|
|
85
|
+
message="Learning content unavailable, providing basic functionality",
|
|
86
|
+
should_log=True
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Documentation generation errors - retry then fallback
|
|
90
|
+
self._recovery_strategies['documentation_error'] = RecoveryAction(
|
|
91
|
+
strategy=RecoveryStrategy.RETRY,
|
|
92
|
+
max_retries=2,
|
|
93
|
+
message="Documentation generation failed, retrying",
|
|
94
|
+
should_log=True
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Interactive session errors - graceful degradation
|
|
98
|
+
self._recovery_strategies['session_error'] = RecoveryAction(
|
|
99
|
+
strategy=RecoveryStrategy.GRACEFUL_DEGRADATION,
|
|
100
|
+
message="Interactive session error, continuing with limited functionality",
|
|
101
|
+
should_log=True
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Visual documentation errors - ignore and continue
|
|
105
|
+
self._recovery_strategies['visual_error'] = RecoveryAction(
|
|
106
|
+
strategy=RecoveryStrategy.IGNORE,
|
|
107
|
+
message="Visual documentation unavailable, continuing without visuals",
|
|
108
|
+
should_log=True
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Critical system errors - fail fast
|
|
112
|
+
self._recovery_strategies['system_error'] = RecoveryAction(
|
|
113
|
+
strategy=RecoveryStrategy.FAIL_FAST,
|
|
114
|
+
message="Critical system error detected",
|
|
115
|
+
should_log=True
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def handle_error(self, error_context: ErrorContext) -> RecoveryAction:
|
|
119
|
+
"""
|
|
120
|
+
Handle an error using the appropriate recovery strategy.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
error_context: Context information about the error
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
RecoveryAction: Action taken for recovery
|
|
127
|
+
"""
|
|
128
|
+
error_key = f"{error_context.component}_{error_context.error_type}"
|
|
129
|
+
|
|
130
|
+
# Track error frequency
|
|
131
|
+
self._error_counts[error_key] = self._error_counts.get(error_key, 0) + 1
|
|
132
|
+
|
|
133
|
+
# Get recovery strategy
|
|
134
|
+
strategy = self._get_recovery_strategy(error_context)
|
|
135
|
+
|
|
136
|
+
# Log the error if required
|
|
137
|
+
if strategy.should_log:
|
|
138
|
+
self._log_error(error_context, strategy)
|
|
139
|
+
|
|
140
|
+
# Execute recovery action
|
|
141
|
+
return self._execute_recovery(error_context, strategy)
|
|
142
|
+
|
|
143
|
+
def register_fallback(self, component: str, operation: str, fallback_function: Callable) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Register a fallback function for a specific component and operation.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
component: Component name
|
|
149
|
+
operation: Operation name
|
|
150
|
+
fallback_function: Function to call as fallback
|
|
151
|
+
"""
|
|
152
|
+
key = f"{component}_{operation}"
|
|
153
|
+
self._fallback_functions[key] = fallback_function
|
|
154
|
+
|
|
155
|
+
def register_error_handler(self, error_type: str, handler: Callable) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Register a custom error handler for a specific error type.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
error_type: Type of error to handle
|
|
161
|
+
handler: Handler function
|
|
162
|
+
"""
|
|
163
|
+
self._error_handlers[error_type] = handler
|
|
164
|
+
|
|
165
|
+
def set_recovery_strategy(self, error_type: str, strategy: RecoveryAction) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Set a custom recovery strategy for an error type.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
error_type: Type of error
|
|
171
|
+
strategy: Recovery strategy to use
|
|
172
|
+
"""
|
|
173
|
+
self._recovery_strategies[error_type] = strategy
|
|
174
|
+
|
|
175
|
+
def handle_config_error(self, error: Exception, component: str = "config") -> RecoveryAction:
|
|
176
|
+
"""
|
|
177
|
+
Handle configuration-related errors.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
error: Configuration error
|
|
181
|
+
component: Component that had the error
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
RecoveryAction: Recovery action taken
|
|
185
|
+
"""
|
|
186
|
+
error_context = ErrorContext(
|
|
187
|
+
component=component,
|
|
188
|
+
operation="load_config",
|
|
189
|
+
error_type="config_error",
|
|
190
|
+
error_message=str(error),
|
|
191
|
+
severity=ErrorSeverity.MEDIUM,
|
|
192
|
+
metadata={"error_class": error.__class__.__name__}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return self.handle_error(error_context)
|
|
196
|
+
|
|
197
|
+
def handle_content_error(self, error: Exception, component: str = "learning") -> RecoveryAction:
|
|
198
|
+
"""
|
|
199
|
+
Handle learning content-related errors.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
error: Content error
|
|
203
|
+
component: Component that had the error
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
RecoveryAction: Recovery action taken
|
|
207
|
+
"""
|
|
208
|
+
error_context = ErrorContext(
|
|
209
|
+
component=component,
|
|
210
|
+
operation="load_content",
|
|
211
|
+
error_type="content_error",
|
|
212
|
+
error_message=str(error),
|
|
213
|
+
severity=ErrorSeverity.LOW,
|
|
214
|
+
metadata={"error_class": error.__class__.__name__}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return self.handle_error(error_context)
|
|
218
|
+
|
|
219
|
+
def handle_documentation_error(self, error: Exception, component: str = "documentation") -> RecoveryAction:
|
|
220
|
+
"""
|
|
221
|
+
Handle documentation generation errors.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
error: Documentation error
|
|
225
|
+
component: Component that had the error
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
RecoveryAction: Recovery action taken
|
|
229
|
+
"""
|
|
230
|
+
error_context = ErrorContext(
|
|
231
|
+
component=component,
|
|
232
|
+
operation="generate_docs",
|
|
233
|
+
error_type="documentation_error",
|
|
234
|
+
error_message=str(error),
|
|
235
|
+
severity=ErrorSeverity.MEDIUM,
|
|
236
|
+
metadata={"error_class": error.__class__.__name__}
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return self.handle_error(error_context)
|
|
240
|
+
|
|
241
|
+
def handle_session_error(self, error: Exception, component: str = "session") -> RecoveryAction:
|
|
242
|
+
"""
|
|
243
|
+
Handle interactive session errors.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
error: Session error
|
|
247
|
+
component: Component that had the error
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
RecoveryAction: Recovery action taken
|
|
251
|
+
"""
|
|
252
|
+
error_context = ErrorContext(
|
|
253
|
+
component=component,
|
|
254
|
+
operation="manage_session",
|
|
255
|
+
error_type="session_error",
|
|
256
|
+
error_message=str(error),
|
|
257
|
+
severity=ErrorSeverity.LOW,
|
|
258
|
+
metadata={"error_class": error.__class__.__name__}
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return self.handle_error(error_context)
|
|
262
|
+
|
|
263
|
+
def handle_visual_error(self, error: Exception, component: str = "visual") -> RecoveryAction:
|
|
264
|
+
"""
|
|
265
|
+
Handle visual documentation errors.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
error: Visual documentation error
|
|
269
|
+
component: Component that had the error
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
RecoveryAction: Recovery action taken
|
|
273
|
+
"""
|
|
274
|
+
error_context = ErrorContext(
|
|
275
|
+
component=component,
|
|
276
|
+
operation="create_visual",
|
|
277
|
+
error_type="visual_error",
|
|
278
|
+
error_message=str(error),
|
|
279
|
+
severity=ErrorSeverity.LOW,
|
|
280
|
+
metadata={"error_class": error.__class__.__name__}
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return self.handle_error(error_context)
|
|
284
|
+
|
|
285
|
+
def _get_recovery_strategy(self, error_context: ErrorContext) -> RecoveryAction:
|
|
286
|
+
"""Get the appropriate recovery strategy for an error."""
|
|
287
|
+
# Check for specific component + error type strategy
|
|
288
|
+
specific_key = f"{error_context.component}_{error_context.error_type}"
|
|
289
|
+
if specific_key in self._recovery_strategies:
|
|
290
|
+
return self._recovery_strategies[specific_key]
|
|
291
|
+
|
|
292
|
+
# Check for general error type strategy
|
|
293
|
+
if error_context.error_type in self._recovery_strategies:
|
|
294
|
+
return self._recovery_strategies[error_context.error_type]
|
|
295
|
+
|
|
296
|
+
# Default strategy based on severity
|
|
297
|
+
if error_context.severity == ErrorSeverity.CRITICAL:
|
|
298
|
+
return RecoveryAction(
|
|
299
|
+
strategy=RecoveryStrategy.FAIL_FAST,
|
|
300
|
+
message="Critical error - stopping execution"
|
|
301
|
+
)
|
|
302
|
+
elif error_context.severity == ErrorSeverity.HIGH:
|
|
303
|
+
return RecoveryAction(
|
|
304
|
+
strategy=RecoveryStrategy.RETRY,
|
|
305
|
+
max_retries=1,
|
|
306
|
+
message="High severity error - attempting recovery"
|
|
307
|
+
)
|
|
308
|
+
else:
|
|
309
|
+
return RecoveryAction(
|
|
310
|
+
strategy=RecoveryStrategy.GRACEFUL_DEGRADATION,
|
|
311
|
+
message="Error detected - continuing with reduced functionality"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def _execute_recovery(self, error_context: ErrorContext, strategy: RecoveryAction) -> RecoveryAction:
|
|
315
|
+
"""Execute the recovery strategy."""
|
|
316
|
+
error_key = f"{error_context.component}_{error_context.error_type}"
|
|
317
|
+
|
|
318
|
+
if strategy.strategy == RecoveryStrategy.RETRY:
|
|
319
|
+
# Check if we've exceeded retry limit
|
|
320
|
+
if self._error_counts.get(error_key, 0) > strategy.max_retries:
|
|
321
|
+
# Switch to fallback strategy
|
|
322
|
+
strategy.strategy = RecoveryStrategy.GRACEFUL_DEGRADATION
|
|
323
|
+
strategy.message = f"Max retries exceeded, switching to graceful degradation"
|
|
324
|
+
|
|
325
|
+
elif strategy.strategy == RecoveryStrategy.FALLBACK:
|
|
326
|
+
# Try to find and execute fallback function
|
|
327
|
+
fallback_key = f"{error_context.component}_{error_context.operation}"
|
|
328
|
+
if fallback_key in self._fallback_functions:
|
|
329
|
+
try:
|
|
330
|
+
strategy.fallback_function = self._fallback_functions[fallback_key]
|
|
331
|
+
except Exception as e:
|
|
332
|
+
self.logger.warning(f"Fallback function failed: {e}")
|
|
333
|
+
strategy.strategy = RecoveryStrategy.GRACEFUL_DEGRADATION
|
|
334
|
+
|
|
335
|
+
elif strategy.strategy == RecoveryStrategy.FAIL_FAST:
|
|
336
|
+
# For critical errors, raise the original error
|
|
337
|
+
raise FishertoolsError(f"Critical error in {error_context.component}: {error_context.error_message}")
|
|
338
|
+
|
|
339
|
+
return strategy
|
|
340
|
+
|
|
341
|
+
def _log_error(self, error_context: ErrorContext, strategy: RecoveryAction) -> None:
|
|
342
|
+
"""Log error information."""
|
|
343
|
+
log_level = self._get_log_level(error_context.severity)
|
|
344
|
+
|
|
345
|
+
message = (
|
|
346
|
+
f"Error in {error_context.component}.{error_context.operation}: "
|
|
347
|
+
f"{error_context.error_message}. "
|
|
348
|
+
f"Recovery: {strategy.strategy.value}. "
|
|
349
|
+
f"{strategy.message}"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
self.logger.log(log_level, message, extra={
|
|
353
|
+
'component': error_context.component,
|
|
354
|
+
'operation': error_context.operation,
|
|
355
|
+
'error_type': error_context.error_type,
|
|
356
|
+
'severity': error_context.severity.value,
|
|
357
|
+
'recovery_strategy': strategy.strategy.value
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
def _get_log_level(self, severity: ErrorSeverity) -> int:
|
|
361
|
+
"""Get appropriate log level for error severity."""
|
|
362
|
+
severity_to_level = {
|
|
363
|
+
ErrorSeverity.LOW: logging.INFO,
|
|
364
|
+
ErrorSeverity.MEDIUM: logging.WARNING,
|
|
365
|
+
ErrorSeverity.HIGH: logging.ERROR,
|
|
366
|
+
ErrorSeverity.CRITICAL: logging.CRITICAL
|
|
367
|
+
}
|
|
368
|
+
return severity_to_level.get(severity, logging.WARNING)
|
|
369
|
+
|
|
370
|
+
def get_error_statistics(self) -> Dict[str, Any]:
|
|
371
|
+
"""
|
|
372
|
+
Get statistics about errors and recovery actions.
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Dict[str, Any]: Error statistics
|
|
376
|
+
"""
|
|
377
|
+
total_errors = sum(self._error_counts.values())
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
'total_errors': total_errors,
|
|
381
|
+
'error_counts_by_type': dict(self._error_counts),
|
|
382
|
+
'registered_fallbacks': len(self._fallback_functions),
|
|
383
|
+
'registered_handlers': len(self._error_handlers),
|
|
384
|
+
'recovery_strategies': len(self._recovery_strategies)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
def reset_error_counts(self) -> None:
|
|
388
|
+
"""Reset error count statistics."""
|
|
389
|
+
self._error_counts.clear()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# Global error recovery manager instance
|
|
393
|
+
_recovery_manager: Optional[ErrorRecoveryManager] = None
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def get_recovery_manager() -> ErrorRecoveryManager:
|
|
397
|
+
"""
|
|
398
|
+
Get or create the global error recovery manager.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
ErrorRecoveryManager: The global recovery manager instance
|
|
402
|
+
"""
|
|
403
|
+
global _recovery_manager
|
|
404
|
+
|
|
405
|
+
if _recovery_manager is None:
|
|
406
|
+
_recovery_manager = ErrorRecoveryManager()
|
|
407
|
+
|
|
408
|
+
return _recovery_manager
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def handle_error_with_recovery(error: Exception, component: str, operation: str, error_type: str = "general") -> RecoveryAction:
|
|
412
|
+
"""
|
|
413
|
+
Convenience function to handle errors with recovery.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
error: The exception that occurred
|
|
417
|
+
component: Component where error occurred
|
|
418
|
+
operation: Operation that failed
|
|
419
|
+
error_type: Type of error for recovery strategy selection
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
RecoveryAction: Recovery action taken
|
|
423
|
+
"""
|
|
424
|
+
recovery_manager = get_recovery_manager()
|
|
425
|
+
|
|
426
|
+
error_context = ErrorContext(
|
|
427
|
+
component=component,
|
|
428
|
+
operation=operation,
|
|
429
|
+
error_type=error_type,
|
|
430
|
+
error_message=str(error),
|
|
431
|
+
severity=ErrorSeverity.MEDIUM,
|
|
432
|
+
metadata={"error_class": error.__class__.__name__}
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
return recovery_manager.handle_error(error_context)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def with_error_recovery(component: str, operation: str, error_type: str = "general"):
|
|
439
|
+
"""
|
|
440
|
+
Decorator to add error recovery to functions.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
component: Component name
|
|
444
|
+
operation: Operation name
|
|
445
|
+
error_type: Error type for recovery strategy
|
|
446
|
+
"""
|
|
447
|
+
def decorator(func):
|
|
448
|
+
def wrapper(*args, **kwargs):
|
|
449
|
+
try:
|
|
450
|
+
return func(*args, **kwargs)
|
|
451
|
+
except Exception as e:
|
|
452
|
+
recovery_action = handle_error_with_recovery(e, component, operation, error_type)
|
|
453
|
+
|
|
454
|
+
if recovery_action.fallback_function:
|
|
455
|
+
try:
|
|
456
|
+
return recovery_action.fallback_function(*args, **kwargs)
|
|
457
|
+
except Exception as fallback_error:
|
|
458
|
+
logging.warning(f"Fallback function failed: {fallback_error}")
|
|
459
|
+
|
|
460
|
+
if recovery_action.strategy == RecoveryStrategy.FAIL_FAST:
|
|
461
|
+
raise
|
|
462
|
+
|
|
463
|
+
# For other strategies, return None or appropriate default
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
return wrapper
|
|
467
|
+
return decorator
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example Repository Module
|
|
3
|
+
|
|
4
|
+
Manages collections of examples and scenarios for Python beginners.
|
|
5
|
+
Provides categorized examples with step-by-step explanations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .repository import ExampleRepository
|
|
9
|
+
from .models import (
|
|
10
|
+
CodeExample,
|
|
11
|
+
Scenario,
|
|
12
|
+
ProjectTemplate,
|
|
13
|
+
LineByLineExplanation
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ExampleRepository",
|
|
18
|
+
"CodeExample",
|
|
19
|
+
"Scenario",
|
|
20
|
+
"ProjectTemplate",
|
|
21
|
+
"LineByLineExplanation"
|
|
22
|
+
]
|