292 Zeilen
11 KiB
Python
292 Zeilen
11 KiB
Python
"""
|
|
Result Enhancement Decorators - Backward-compatible result standardization
|
|
Erweitert bestehende Methoden ohne sie zu ändern
|
|
"""
|
|
|
|
import functools
|
|
import logging
|
|
import time
|
|
import threading
|
|
from typing import Any, Callable, Union
|
|
from domain.value_objects.operation_result import OperationResult, CommonErrorCodes
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def result_enhanced(preserve_original: bool = True,
|
|
error_code_mapping: dict = None,
|
|
capture_metadata: bool = True):
|
|
"""
|
|
Decorator der bestehende Methoden erweitert ohne sie zu ändern.
|
|
|
|
Args:
|
|
preserve_original: Ob die Original-Methode verfügbar bleiben soll
|
|
error_code_mapping: Mapping von Exception-Types zu Error-Codes
|
|
capture_metadata: Ob Metadaten (Timing, Thread-Info) erfasst werden sollen
|
|
"""
|
|
def decorator(func: Callable) -> Callable:
|
|
@functools.wraps(func)
|
|
def wrapper(*args, **kwargs) -> OperationResult:
|
|
start_time = time.time() if capture_metadata else None
|
|
metadata = {}
|
|
|
|
if capture_metadata:
|
|
metadata.update({
|
|
'function_name': func.__name__,
|
|
'thread_id': threading.current_thread().ident,
|
|
'thread_name': threading.current_thread().name
|
|
})
|
|
|
|
try:
|
|
# Original-Methode aufrufen
|
|
result = func(*args, **kwargs)
|
|
|
|
if capture_metadata:
|
|
metadata['execution_time'] = time.time() - start_time
|
|
|
|
# Boolean results zu OperationResult erweitern
|
|
if isinstance(result, bool):
|
|
return OperationResult.from_legacy_boolean(
|
|
result=result,
|
|
success_data=result if result else None,
|
|
error_message="Operation returned False" if not result else None
|
|
)
|
|
|
|
# Dict results erweitern
|
|
elif isinstance(result, dict) and 'success' in result:
|
|
op_result = OperationResult.from_legacy_dict(result)
|
|
if capture_metadata:
|
|
op_result.metadata.update(metadata)
|
|
return op_result
|
|
|
|
# None als Fehler behandeln
|
|
elif result is None:
|
|
return OperationResult.error_result(
|
|
message="Method returned None",
|
|
code=CommonErrorCodes.BROWSER_ERROR,
|
|
metadata=metadata
|
|
)
|
|
|
|
# Alle anderen Results als Erfolg
|
|
else:
|
|
return OperationResult.success_result(
|
|
data=result,
|
|
metadata=metadata,
|
|
legacy_result=result
|
|
)
|
|
|
|
except Exception as e:
|
|
if capture_metadata:
|
|
metadata.update({
|
|
'execution_time': time.time() - start_time,
|
|
'exception_occurred_at': time.time()
|
|
})
|
|
|
|
# Error code mapping anwenden
|
|
error_code = None
|
|
if error_code_mapping:
|
|
error_code = error_code_mapping.get(type(e), type(e).__name__)
|
|
|
|
return OperationResult.from_exception(
|
|
exception=e,
|
|
code=error_code,
|
|
metadata=metadata
|
|
)
|
|
|
|
# Original-Methode verfügbar machen wenn gewünscht
|
|
if preserve_original:
|
|
wrapper.original = func
|
|
wrapper.get_original = lambda: func
|
|
|
|
# Zusätzliche Utility-Methoden
|
|
wrapper.call_original = lambda *args, **kwargs: func(*args, **kwargs)
|
|
wrapper.is_enhanced = True
|
|
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
def instagram_result_enhanced(func: Callable) -> Callable:
|
|
"""
|
|
Spezialisierter Decorator für Instagram-Operationen
|
|
"""
|
|
instagram_error_mapping = {
|
|
TimeoutError: CommonErrorCodes.NETWORK_TIMEOUT,
|
|
ConnectionError: CommonErrorCodes.PROXY_ERROR,
|
|
ValueError: CommonErrorCodes.SELECTOR_NOT_FOUND,
|
|
RuntimeError: CommonErrorCodes.BROWSER_ERROR,
|
|
Exception: CommonErrorCodes.BROWSER_ERROR
|
|
}
|
|
|
|
return result_enhanced(
|
|
preserve_original=True,
|
|
error_code_mapping=instagram_error_mapping,
|
|
capture_metadata=True
|
|
)(func)
|
|
|
|
|
|
def fingerprint_result_enhanced(func: Callable) -> Callable:
|
|
"""
|
|
Spezialisierter Decorator für Fingerprint-Operationen
|
|
"""
|
|
fingerprint_error_mapping = {
|
|
FileNotFoundError: CommonErrorCodes.FINGERPRINT_NOT_FOUND,
|
|
PermissionError: CommonErrorCodes.FINGERPRINT_GENERATION_FAILED,
|
|
ValueError: CommonErrorCodes.FINGERPRINT_GENERATION_FAILED,
|
|
RuntimeError: CommonErrorCodes.FINGERPRINT_RACE_CONDITION,
|
|
Exception: CommonErrorCodes.FINGERPRINT_GENERATION_FAILED
|
|
}
|
|
|
|
return result_enhanced(
|
|
preserve_original=True,
|
|
error_code_mapping=fingerprint_error_mapping,
|
|
capture_metadata=True
|
|
)(func)
|
|
|
|
|
|
def session_result_enhanced(func: Callable) -> Callable:
|
|
"""
|
|
Spezialisierter Decorator für Session-Operationen
|
|
"""
|
|
session_error_mapping = {
|
|
TimeoutError: CommonErrorCodes.SESSION_EXPIRED,
|
|
ValueError: CommonErrorCodes.SESSION_INVALID,
|
|
IOError: CommonErrorCodes.SESSION_SAVE_FAILED,
|
|
Exception: CommonErrorCodes.SESSION_INVALID
|
|
}
|
|
|
|
return result_enhanced(
|
|
preserve_original=True,
|
|
error_code_mapping=session_error_mapping,
|
|
capture_metadata=True
|
|
)(func)
|
|
|
|
|
|
class ResultEnhancer:
|
|
"""
|
|
Utility-Klasse für programmatische Result-Enhancement ohne Decorators
|
|
"""
|
|
|
|
@staticmethod
|
|
def enhance_method(obj: Any, method_name: str, enhancement_type: str = "general") -> None:
|
|
"""
|
|
Erweitert eine bestehende Methode eines Objekts zur Laufzeit
|
|
|
|
Args:
|
|
obj: Das Objekt dessen Methode erweitert werden soll
|
|
method_name: Name der zu erweiternden Methode
|
|
enhancement_type: Art der Erweiterung ("general", "instagram", "fingerprint", "session")
|
|
"""
|
|
if not hasattr(obj, method_name):
|
|
logger.warning(f"Method {method_name} not found on object {obj}")
|
|
return
|
|
|
|
original_method = getattr(obj, method_name)
|
|
|
|
# Bereits erweiterte Methoden überspringen
|
|
if getattr(original_method, 'is_enhanced', False):
|
|
logger.debug(f"Method {method_name} already enhanced")
|
|
return
|
|
|
|
# Entsprechenden Decorator wählen
|
|
if enhancement_type == "instagram":
|
|
enhanced_method = instagram_result_enhanced(original_method)
|
|
elif enhancement_type == "fingerprint":
|
|
enhanced_method = fingerprint_result_enhanced(original_method)
|
|
elif enhancement_type == "session":
|
|
enhanced_method = session_result_enhanced(original_method)
|
|
else:
|
|
enhanced_method = result_enhanced()(original_method)
|
|
|
|
# Methode ersetzen
|
|
setattr(obj, method_name, enhanced_method)
|
|
|
|
# Original unter anderem Namen verfügbar machen
|
|
setattr(obj, f"{method_name}_original", original_method)
|
|
|
|
logger.info(f"Enhanced method {method_name} on {type(obj).__name__}")
|
|
|
|
@staticmethod
|
|
def enhance_class_methods(cls: type, method_names: list, enhancement_type: str = "general") -> None:
|
|
"""
|
|
Erweitert mehrere Methoden einer Klasse
|
|
"""
|
|
for method_name in method_names:
|
|
if hasattr(cls, method_name):
|
|
original_method = getattr(cls, method_name)
|
|
|
|
if enhancement_type == "instagram":
|
|
enhanced_method = instagram_result_enhanced(original_method)
|
|
elif enhancement_type == "fingerprint":
|
|
enhanced_method = fingerprint_result_enhanced(original_method)
|
|
elif enhancement_type == "session":
|
|
enhanced_method = session_result_enhanced(original_method)
|
|
else:
|
|
enhanced_method = result_enhanced()(original_method)
|
|
|
|
setattr(cls, method_name, enhanced_method)
|
|
setattr(cls, f"{method_name}_original", original_method)
|
|
|
|
logger.info(f"Enhanced class method {cls.__name__}.{method_name}")
|
|
|
|
|
|
class BatchResultWrapper:
|
|
"""
|
|
Wrapper für Batch-Operationen mit einheitlicher Result-Struktur
|
|
"""
|
|
|
|
def __init__(self, operation_name: str = "batch_operation"):
|
|
self.operation_name = operation_name
|
|
self.results = []
|
|
self.success_count = 0
|
|
self.error_count = 0
|
|
|
|
def add_result(self, result: Union[OperationResult, bool, dict, Any]) -> None:
|
|
"""Fügt ein Result zur Batch hinzu"""
|
|
if isinstance(result, OperationResult):
|
|
op_result = result
|
|
elif isinstance(result, bool):
|
|
op_result = OperationResult.from_legacy_boolean(result)
|
|
elif isinstance(result, dict) and 'success' in result:
|
|
op_result = OperationResult.from_legacy_dict(result)
|
|
else:
|
|
op_result = OperationResult.success_result(data=result)
|
|
|
|
self.results.append(op_result)
|
|
|
|
if op_result.success:
|
|
self.success_count += 1
|
|
else:
|
|
self.error_count += 1
|
|
|
|
def get_batch_result(self) -> OperationResult:
|
|
"""Gibt das Gesamtergebnis der Batch zurück"""
|
|
total_count = len(self.results)
|
|
success_rate = self.success_count / total_count if total_count > 0 else 0
|
|
|
|
metadata = {
|
|
'total_operations': total_count,
|
|
'successful_operations': self.success_count,
|
|
'failed_operations': self.error_count,
|
|
'success_rate': success_rate,
|
|
'operation_name': self.operation_name
|
|
}
|
|
|
|
# Batch als erfolgreich bewerten wenn > 50% erfolgreich
|
|
batch_success = success_rate > 0.5
|
|
|
|
if batch_success:
|
|
return OperationResult.success_result(
|
|
data={
|
|
'results': [r.to_dict() for r in self.results],
|
|
'summary': metadata
|
|
},
|
|
metadata=metadata
|
|
)
|
|
else:
|
|
error_messages = [r.error_message for r in self.results if not r.success]
|
|
return OperationResult.error_result(
|
|
message=f"Batch operation failed: {'; '.join(error_messages[:3])}...",
|
|
code="BATCH_OPERATION_FAILED",
|
|
metadata=metadata
|
|
) |