mrradix commited on
Commit
47ab117
·
verified ·
1 Parent(s): 5819cb0

Update utils/error_handling.py

Browse files
Files changed (1) hide show
  1. utils/error_handling.py +59 -231
utils/error_handling.py CHANGED
@@ -1,256 +1,84 @@
1
- """
2
- Error handling utilities for the MONA application
3
- Fixed version with proper exception handling and logging
4
- """
5
-
6
  import functools
7
  import traceback
8
- from typing import Any, Callable, Dict, Optional, Union
9
-
10
- from utils.logging import get_logger, log_error, log_warning
11
-
12
 
13
  class DataError(Exception):
14
  """Custom exception for data-related errors"""
15
- def __init__(self, message: str, error_code: str = None, details: Dict = None):
16
- self.message = message
17
- self.error_code = error_code or "DATA_ERROR"
18
- self.details = details or {}
19
- super().__init__(self.message)
20
-
21
 
22
  class ValidationError(Exception):
23
  """Custom exception for validation errors"""
24
- def __init__(self, message: str, field: str = None, value: Any = None):
25
- self.message = message
26
- self.field = field
27
- self.value = value
28
- super().__init__(self.message)
29
 
 
 
 
30
 
31
- class ProcessingError(Exception):
32
- """Custom exception for processing errors"""
33
- def __init__(self, message: str, step: str = None, context: Dict = None):
34
- self.message = message
35
- self.step = step
36
- self.context = context or {}
37
- super().__init__(self.message)
38
-
39
-
40
- def handle_data_exceptions(func: Callable) -> Callable:
41
- """
42
- Decorator to handle data-related exceptions
43
-
44
- Args:
45
- func: Function to wrap with exception handling
46
-
47
- Returns:
48
- Wrapped function with exception handling
49
- """
50
  @functools.wraps(func)
51
  def wrapper(*args, **kwargs):
52
  try:
53
  return func(*args, **kwargs)
54
- except DataError as e:
55
- log_error(f"Data error in {func.__name__}: {e.message}",
56
- error=e,
57
- extra_data={"error_code": e.error_code, "details": e.details})
58
- raise
59
- except ValidationError as e:
60
- log_error(f"Validation error in {func.__name__}: {e.message}",
61
- error=e,
62
- extra_data={"field": e.field, "value": e.value})
63
- raise
64
- except ProcessingError as e:
65
- log_error(f"Processing error in {func.__name__}: {e.message}",
66
- error=e,
67
- extra_data={"step": e.step, "context": e.context})
68
- raise
69
  except Exception as e:
70
- log_error(f"Unexpected error in {func.__name__}: {str(e)}",
71
- error=e)
72
- raise ProcessingError(f"Unexpected error in {func.__name__}",
73
- context={"original_error": str(e)})
74
-
75
  return wrapper
76
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- def safe_execute(func: Callable, *args, default_return=None, **kwargs) -> tuple:
79
- """
80
- Safely execute a function and return success status with result
81
-
82
- Args:
83
- func: Function to execute
84
- *args: Positional arguments for the function
85
- default_return: Default value to return on error
86
- **kwargs: Keyword arguments for the function
87
-
88
- Returns:
89
- tuple: (success: bool, result: Any, error: Optional[Exception])
90
- """
91
  try:
92
- result = func(*args, **kwargs)
93
- return True, result, None
94
  except Exception as e:
95
- log_error(f"Error executing {func.__name__}: {str(e)}", error=e)
96
- return False, default_return, e
97
 
98
-
99
- def validate_data(data: Any, validation_rules: Dict) -> bool:
100
- """
101
- Validate data against a set of rules
102
-
103
- Args:
104
- data: Data to validate
105
- validation_rules: Dictionary of validation rules
106
-
107
- Returns:
108
- bool: True if validation passes
109
-
110
- Raises:
111
- ValidationError: If validation fails
112
- """
113
- if not isinstance(validation_rules, dict):
114
- raise ValidationError("Validation rules must be a dictionary")
115
 
116
- for field, rules in validation_rules.items():
117
- if isinstance(data, dict):
118
- value = data.get(field)
119
- else:
120
- value = getattr(data, field, None)
121
-
122
- # Check required fields
123
- if rules.get('required', False) and value is None:
124
- raise ValidationError(f"Field '{field}' is required", field=field, value=value)
125
-
126
- # Check data type
127
- if 'type' in rules and value is not None:
128
- expected_type = rules['type']
129
- if not isinstance(value, expected_type):
130
- raise ValidationError(
131
- f"Field '{field}' must be of type {expected_type.__name__}, got {type(value).__name__}",
132
- field=field,
133
- value=value
134
- )
135
-
136
- # Check value range for numbers
137
- if 'min_value' in rules and isinstance(value, (int, float)):
138
- if value < rules['min_value']:
139
- raise ValidationError(
140
- f"Field '{field}' must be >= {rules['min_value']}, got {value}",
141
- field=field,
142
- value=value
143
- )
144
-
145
- if 'max_value' in rules and isinstance(value, (int, float)):
146
- if value > rules['max_value']:
147
- raise ValidationError(
148
- f"Field '{field}' must be <= {rules['max_value']}, got {value}",
149
- field=field,
150
- value=value
151
- )
152
-
153
- # Check string length
154
- if 'min_length' in rules and isinstance(value, str):
155
- if len(value) < rules['min_length']:
156
- raise ValidationError(
157
- f"Field '{field}' must be at least {rules['min_length']} characters",
158
- field=field,
159
- value=value
160
- )
161
-
162
- if 'max_length' in rules and isinstance(value, str):
163
- if len(value) > rules['max_length']:
164
- raise ValidationError(
165
- f"Field '{field}' must be at most {rules['max_length']} characters",
166
- field=field,
167
- value=value
168
- )
169
 
170
  return True
171
 
172
-
173
- def format_error_response(error: Exception) -> Dict:
174
- """
175
- Format an error into a standardized response
176
-
177
- Args:
178
- error: Exception to format
179
-
180
- Returns:
181
- Dict: Formatted error response
182
- """
183
- if isinstance(error, (DataError, ValidationError, ProcessingError)):
184
- return {
185
- "success": False,
186
- "error_type": error.__class__.__name__,
187
- "message": error.message,
188
- "details": getattr(error, 'details', {}),
189
- "field": getattr(error, 'field', None),
190
- "error_code": getattr(error, 'error_code', None)
191
- }
192
- else:
193
- return {
194
- "success": False,
195
- "error_type": "UnexpectedError",
196
- "message": str(error),
197
- "details": {}
198
- }
199
-
200
-
201
- def retry_on_failure(max_retries: int = 3, delay: float = 1.0, exceptions: tuple = (Exception,)):
202
- """
203
- Decorator to retry function execution on failure
204
-
205
- Args:
206
- max_retries: Maximum number of retry attempts
207
- delay: Delay between retries in seconds
208
- exceptions: Tuple of exceptions to catch and retry on
209
-
210
- Returns:
211
- Decorator function
212
- """
213
- def decorator(func: Callable) -> Callable:
214
- @functools.wraps(func)
215
- def wrapper(*args, **kwargs):
216
- import time
217
-
218
- last_exception = None
219
- for attempt in range(max_retries + 1):
220
- try:
221
- return func(*args, **kwargs)
222
- except exceptions as e:
223
- last_exception = e
224
- if attempt < max_retries:
225
- log_warning(f"Attempt {attempt + 1} failed for {func.__name__}: {str(e)}. Retrying in {delay}s...")
226
- time.sleep(delay)
227
- else:
228
- log_error(f"All {max_retries + 1} attempts failed for {func.__name__}", error=e)
229
-
230
- raise last_exception
231
-
232
- return wrapper
233
- return decorator
234
-
235
-
236
- # Context manager for error handling
237
- class ErrorHandler:
238
- """Context manager for comprehensive error handling"""
239
-
240
- def __init__(self, operation_name: str, raise_on_error: bool = True):
241
- self.operation_name = operation_name
242
- self.raise_on_error = raise_on_error
243
- self.error = None
244
-
245
- def __enter__(self):
246
- return self
247
-
248
- def __exit__(self, exc_type, exc_val, exc_tb):
249
- if exc_type is not None:
250
- self.error = exc_val
251
- log_error(f"Error in {self.operation_name}: {str(exc_val)}", error=exc_val)
252
-
253
- if not self.raise_on_error:
254
- return True # Suppress the exception
255
-
256
- return False # Let the exception propagate
 
 
 
 
 
 
1
  import functools
2
  import traceback
3
+ import streamlit as st
4
+ from utils.logging import log_error, get_logger
 
 
5
 
6
  class DataError(Exception):
7
  """Custom exception for data-related errors"""
8
+ pass
 
 
 
 
 
9
 
10
  class ValidationError(Exception):
11
  """Custom exception for validation errors"""
12
+ pass
 
 
 
 
13
 
14
+ class ConfigError(Exception):
15
+ """Custom exception for configuration errors"""
16
+ pass
17
 
18
+ def handle_data_exceptions(func):
19
+ """Decorator to handle data-related exceptions"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  @functools.wraps(func)
21
  def wrapper(*args, **kwargs):
22
  try:
23
  return func(*args, **kwargs)
24
+ except (DataError, ValidationError) as e:
25
+ log_error(f"Data error in {func.__name__}", error=e)
26
+ st.error(f"Data processing error: {str(e)}")
27
+ return None
 
 
 
 
 
 
 
 
 
 
 
28
  except Exception as e:
29
+ log_error(f"Unexpected error in {func.__name__}", error=e)
30
+ st.error(f"An unexpected error occurred: {str(e)}")
31
+ return None
 
 
32
  return wrapper
33
 
34
+ def handle_ui_exceptions(func):
35
+ """Decorator to handle UI-related exceptions"""
36
+ @functools.wraps(func)
37
+ def wrapper(*args, **kwargs):
38
+ try:
39
+ return func(*args, **kwargs)
40
+ except Exception as e:
41
+ log_error(f"UI error in {func.__name__}", error=e)
42
+ st.error(f"Interface error: {str(e)}")
43
+ return None
44
+ return wrapper
45
 
46
+ def safe_execute(func, *args, **kwargs):
47
+ """Safely execute a function with error handling"""
 
 
 
 
 
 
 
 
 
 
 
48
  try:
49
+ return func(*args, **kwargs)
 
50
  except Exception as e:
51
+ log_error(f"Error executing {func.__name__}", error=e)
52
+ return None
53
 
54
+ def validate_input(data, required_fields=None):
55
+ """Validate input data"""
56
+ if not data:
57
+ raise ValidationError("Input data is empty or None")
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ if required_fields:
60
+ missing_fields = [field for field in required_fields if field not in data]
61
+ if missing_fields:
62
+ raise ValidationError(f"Missing required fields: {', '.join(missing_fields)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  return True
65
 
66
+ def handle_file_operations(func):
67
+ """Decorator to handle file operation exceptions"""
68
+ @functools.wraps(func)
69
+ def wrapper(*args, **kwargs):
70
+ try:
71
+ return func(*args, **kwargs)
72
+ except FileNotFoundError as e:
73
+ log_error(f"File not found in {func.__name__}", error=e)
74
+ st.error("File not found. Please check the file path.")
75
+ return None
76
+ except PermissionError as e:
77
+ log_error(f"Permission error in {func.__name__}", error=e)
78
+ st.error("Permission denied. Please check file permissions.")
79
+ return None
80
+ except Exception as e:
81
+ log_error(f"File operation error in {func.__name__}", error=e)
82
+ st.error(f"File operation failed: {str(e)}")
83
+ return None
84
+ return wrapper