unrealon 1.1.6__py3-none-any.whl → 2.0.5__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.
- {unrealon-1.1.6.dist-info/licenses → unrealon-2.0.5.dist-info}/LICENSE +1 -1
- unrealon-2.0.5.dist-info/METADATA +491 -0
- unrealon-2.0.5.dist-info/RECORD +128 -0
- {unrealon-1.1.6.dist-info → unrealon-2.0.5.dist-info}/WHEEL +2 -1
- unrealon-2.0.5.dist-info/entry_points.txt +3 -0
- unrealon-2.0.5.dist-info/top_level.txt +3 -0
- unrealon_browser/__init__.py +5 -6
- unrealon_browser/cli/browser_cli.py +18 -9
- unrealon_browser/cli/interactive_mode.py +13 -4
- unrealon_browser/core/browser_manager.py +29 -16
- unrealon_browser/dto/__init__.py +21 -0
- unrealon_browser/dto/bot_detection.py +175 -0
- unrealon_browser/dto/models/config.py +9 -3
- unrealon_browser/managers/__init__.py +1 -1
- unrealon_browser/managers/logger_bridge.py +1 -4
- unrealon_browser/stealth/__init__.py +27 -0
- unrealon_browser/stealth/bypass_techniques.pyc +0 -0
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- unrealon_core/__init__.py +172 -0
- unrealon_core/config/__init__.py +16 -0
- unrealon_core/config/environment.py +151 -0
- unrealon_core/config/urls.py +94 -0
- unrealon_core/enums/__init__.py +24 -0
- unrealon_core/enums/status.py +216 -0
- unrealon_core/enums/types.py +240 -0
- unrealon_core/error_handling/__init__.py +45 -0
- unrealon_core/error_handling/circuit_breaker.py +292 -0
- unrealon_core/error_handling/error_context.py +324 -0
- unrealon_core/error_handling/recovery.py +371 -0
- unrealon_core/error_handling/retry.py +268 -0
- unrealon_core/exceptions/__init__.py +46 -0
- unrealon_core/exceptions/base.py +292 -0
- unrealon_core/exceptions/communication.py +22 -0
- unrealon_core/exceptions/driver.py +11 -0
- unrealon_core/exceptions/proxy.py +11 -0
- unrealon_core/exceptions/task.py +12 -0
- unrealon_core/exceptions/validation.py +17 -0
- unrealon_core/models/__init__.py +79 -0
- unrealon_core/models/arq_context.py +252 -0
- unrealon_core/models/arq_responses.py +125 -0
- unrealon_core/models/base.py +291 -0
- unrealon_core/models/bridge_stats.py +58 -0
- unrealon_core/models/communication.py +39 -0
- unrealon_core/models/connection_stats.py +47 -0
- unrealon_core/models/driver.py +30 -0
- unrealon_core/models/driver_details.py +98 -0
- unrealon_core/models/logging.py +28 -0
- unrealon_core/models/task.py +21 -0
- unrealon_core/models/typed_responses.py +210 -0
- unrealon_core/models/websocket/__init__.py +91 -0
- unrealon_core/models/websocket/base.py +49 -0
- unrealon_core/models/websocket/config.py +200 -0
- unrealon_core/models/websocket/driver.py +215 -0
- unrealon_core/models/websocket/errors.py +138 -0
- unrealon_core/models/websocket/heartbeat.py +100 -0
- unrealon_core/models/websocket/logging.py +261 -0
- unrealon_core/models/websocket/proxy.py +496 -0
- unrealon_core/models/websocket/tasks.py +275 -0
- unrealon_core/models/websocket/utils.py +153 -0
- unrealon_core/models/websocket_session.py +144 -0
- unrealon_core/monitoring/__init__.py +43 -0
- unrealon_core/monitoring/alerts.py +398 -0
- unrealon_core/monitoring/dashboard.py +307 -0
- unrealon_core/monitoring/health_check.py +354 -0
- unrealon_core/monitoring/metrics.py +352 -0
- unrealon_core/utils/__init__.py +11 -0
- unrealon_core/utils/time.py +61 -0
- unrealon_core/version.py +219 -0
- unrealon_driver/__init__.py +90 -51
- unrealon_driver/core_module/__init__.py +34 -0
- unrealon_driver/core_module/base.py +184 -0
- unrealon_driver/core_module/config.py +30 -0
- unrealon_driver/core_module/event_manager.py +127 -0
- unrealon_driver/core_module/protocols.py +98 -0
- unrealon_driver/core_module/registry.py +146 -0
- unrealon_driver/decorators/__init__.py +15 -0
- unrealon_driver/decorators/retry.py +117 -0
- unrealon_driver/decorators/schedule.py +137 -0
- unrealon_driver/decorators/task.py +61 -0
- unrealon_driver/decorators/timing.py +132 -0
- unrealon_driver/driver/__init__.py +20 -0
- unrealon_driver/driver/communication/__init__.py +10 -0
- unrealon_driver/driver/communication/session.py +203 -0
- unrealon_driver/driver/communication/websocket_client.py +205 -0
- unrealon_driver/driver/core/__init__.py +10 -0
- unrealon_driver/driver/core/config.py +175 -0
- unrealon_driver/driver/core/driver.py +221 -0
- unrealon_driver/driver/factory/__init__.py +9 -0
- unrealon_driver/driver/factory/manager_factory.py +130 -0
- unrealon_driver/driver/lifecycle/__init__.py +11 -0
- unrealon_driver/driver/lifecycle/daemon.py +76 -0
- unrealon_driver/driver/lifecycle/initialization.py +97 -0
- unrealon_driver/driver/lifecycle/shutdown.py +48 -0
- unrealon_driver/driver/monitoring/__init__.py +9 -0
- unrealon_driver/driver/monitoring/health.py +63 -0
- unrealon_driver/driver/utilities/__init__.py +10 -0
- unrealon_driver/driver/utilities/logging.py +51 -0
- unrealon_driver/driver/utilities/serialization.py +61 -0
- unrealon_driver/managers/__init__.py +32 -0
- unrealon_driver/managers/base.py +174 -0
- unrealon_driver/managers/browser.py +98 -0
- unrealon_driver/managers/cache.py +116 -0
- unrealon_driver/managers/http.py +107 -0
- unrealon_driver/managers/logger.py +286 -0
- unrealon_driver/managers/proxy.py +99 -0
- unrealon_driver/managers/registry.py +87 -0
- unrealon_driver/managers/threading.py +54 -0
- unrealon_driver/managers/update.py +107 -0
- unrealon_driver/utils/__init__.py +9 -0
- unrealon_driver/utils/time.py +10 -0
- unrealon-1.1.6.dist-info/METADATA +0 -625
- unrealon-1.1.6.dist-info/RECORD +0 -55
- unrealon-1.1.6.dist-info/entry_points.txt +0 -9
- unrealon_browser/managers/stealth.py +0 -388
- unrealon_driver/README.md +0 -0
- unrealon_driver/exceptions.py +0 -33
- unrealon_driver/html_analyzer/__init__.py +0 -32
- unrealon_driver/html_analyzer/cleaner.py +0 -657
- unrealon_driver/html_analyzer/config.py +0 -64
- unrealon_driver/html_analyzer/manager.py +0 -247
- unrealon_driver/html_analyzer/models.py +0 -115
- unrealon_driver/html_analyzer/websocket_analyzer.py +0 -157
- unrealon_driver/models/__init__.py +0 -31
- unrealon_driver/models/websocket.py +0 -98
- unrealon_driver/parser/__init__.py +0 -36
- unrealon_driver/parser/cli_manager.py +0 -142
- unrealon_driver/parser/daemon_manager.py +0 -403
- unrealon_driver/parser/managers/__init__.py +0 -25
- unrealon_driver/parser/managers/config.py +0 -293
- unrealon_driver/parser/managers/error.py +0 -412
- unrealon_driver/parser/managers/result.py +0 -321
- unrealon_driver/parser/parser_manager.py +0 -458
- unrealon_driver/smart_logging/__init__.py +0 -24
- unrealon_driver/smart_logging/models.py +0 -44
- unrealon_driver/smart_logging/smart_logger.py +0 -406
- unrealon_driver/smart_logging/unified_logger.py +0 -525
- unrealon_driver/websocket/__init__.py +0 -31
- unrealon_driver/websocket/client.py +0 -249
- unrealon_driver/websocket/config.py +0 -188
- unrealon_driver/websocket/manager.py +0 -90
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clean driver configuration without hardcoded values.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from pydantic import BaseModel, Field, computed_field
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from unrealon_core.config.environment import get_environment_config
|
|
12
|
+
UNREALON_CORE_AVAILABLE = True
|
|
13
|
+
except ImportError:
|
|
14
|
+
UNREALON_CORE_AVAILABLE = False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DriverMode(str, Enum):
|
|
18
|
+
"""Driver operation modes."""
|
|
19
|
+
STANDALONE = "standalone"
|
|
20
|
+
DAEMON = "daemon"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DriverConfig(BaseModel):
|
|
24
|
+
"""
|
|
25
|
+
Clean driver configuration.
|
|
26
|
+
No hardcoded presets - user configures everything explicitly.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# Basic settings
|
|
30
|
+
name: str = Field(..., description="Driver name")
|
|
31
|
+
mode: DriverMode = Field(default=DriverMode.STANDALONE, description="Operation mode")
|
|
32
|
+
|
|
33
|
+
# WebSocket connection (auto-detected)
|
|
34
|
+
websocket_url: Optional[str] = Field(default=None, description="Manual WebSocket URL override")
|
|
35
|
+
websocket_timeout: int = Field(default=30, description="WebSocket timeout seconds")
|
|
36
|
+
|
|
37
|
+
@computed_field
|
|
38
|
+
@property
|
|
39
|
+
def effective_websocket_url(self) -> Optional[str]:
|
|
40
|
+
"""
|
|
41
|
+
Auto-detect WebSocket URL from multiple sources.
|
|
42
|
+
|
|
43
|
+
Priority:
|
|
44
|
+
1. Explicit websocket_url field
|
|
45
|
+
2. Environment variables (UNREALON_WEBSOCKET_URL, UNREALON_WS_URL, WS_URL)
|
|
46
|
+
3. UnrealOn core environment config (if available)
|
|
47
|
+
4. No default - return None if nothing configured
|
|
48
|
+
"""
|
|
49
|
+
# 1. Explicit override
|
|
50
|
+
if self.websocket_url:
|
|
51
|
+
return self.websocket_url
|
|
52
|
+
|
|
53
|
+
# 2. Environment variables
|
|
54
|
+
env_url = (
|
|
55
|
+
os.getenv('UNREALON_WEBSOCKET_URL') or
|
|
56
|
+
os.getenv('UNREALON_WS_URL') or
|
|
57
|
+
os.getenv('WS_URL')
|
|
58
|
+
)
|
|
59
|
+
if env_url:
|
|
60
|
+
return env_url
|
|
61
|
+
|
|
62
|
+
# 3. Try unrealon_core environment config
|
|
63
|
+
if UNREALON_CORE_AVAILABLE:
|
|
64
|
+
try:
|
|
65
|
+
env_config = get_environment_config()
|
|
66
|
+
return env_config.websocket_url
|
|
67
|
+
except Exception:
|
|
68
|
+
pass # Fallback gracefully if core config fails
|
|
69
|
+
|
|
70
|
+
# 4. No default - return None if nothing is configured
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
# Logging
|
|
74
|
+
log_level: str = Field(default="INFO", description="Logging level")
|
|
75
|
+
log_file: Optional[str] = Field(default=None, description="Log file path")
|
|
76
|
+
|
|
77
|
+
# HTTP settings
|
|
78
|
+
http_timeout: int = Field(default=30, description="HTTP timeout seconds")
|
|
79
|
+
max_retries: int = Field(default=3, description="Max HTTP retries")
|
|
80
|
+
|
|
81
|
+
# Browser settings
|
|
82
|
+
browser_headless: bool = Field(default=True, description="Run browser headless")
|
|
83
|
+
browser_timeout: int = Field(default=30, description="Browser timeout seconds")
|
|
84
|
+
|
|
85
|
+
# Proxy settings
|
|
86
|
+
proxy_enabled: bool = Field(default=False, description="Enable proxy rotation")
|
|
87
|
+
proxy_rotation_interval: int = Field(default=300, description="Proxy rotation seconds")
|
|
88
|
+
|
|
89
|
+
# Cache settings
|
|
90
|
+
cache_enabled: bool = Field(default=True, description="Enable response caching")
|
|
91
|
+
cache_ttl: int = Field(default=3600, description="Cache TTL seconds")
|
|
92
|
+
|
|
93
|
+
# Threading
|
|
94
|
+
max_workers: int = Field(default=4, description="Max thread workers")
|
|
95
|
+
|
|
96
|
+
# Performance
|
|
97
|
+
batch_size: int = Field(default=10, description="Batch processing size")
|
|
98
|
+
|
|
99
|
+
model_config = {"extra": "forbid"}
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def for_development(cls, name: str, **kwargs) -> "DriverConfig":
|
|
103
|
+
"""
|
|
104
|
+
Create development configuration with sensible defaults.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
name: Driver name
|
|
108
|
+
**kwargs: Additional configuration overrides
|
|
109
|
+
"""
|
|
110
|
+
defaults = {
|
|
111
|
+
"name": name,
|
|
112
|
+
"mode": DriverMode.STANDALONE,
|
|
113
|
+
"log_level": "DEBUG",
|
|
114
|
+
"browser_headless": False,
|
|
115
|
+
"proxy_enabled": False,
|
|
116
|
+
"cache_enabled": True,
|
|
117
|
+
"max_workers": 2,
|
|
118
|
+
}
|
|
119
|
+
defaults.update(kwargs)
|
|
120
|
+
return cls(**defaults)
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def for_production(cls, name: str, **kwargs) -> "DriverConfig":
|
|
124
|
+
"""
|
|
125
|
+
Create production configuration with performance and reliability defaults.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
name: Driver name
|
|
129
|
+
**kwargs: Additional configuration overrides
|
|
130
|
+
"""
|
|
131
|
+
defaults = {
|
|
132
|
+
"name": name,
|
|
133
|
+
"mode": DriverMode.DAEMON,
|
|
134
|
+
"log_level": "INFO",
|
|
135
|
+
"browser_headless": True,
|
|
136
|
+
"proxy_enabled": True,
|
|
137
|
+
"cache_enabled": True,
|
|
138
|
+
"max_workers": 4,
|
|
139
|
+
"max_retries": 5,
|
|
140
|
+
"http_timeout": 60,
|
|
141
|
+
"browser_timeout": 60,
|
|
142
|
+
}
|
|
143
|
+
defaults.update(kwargs)
|
|
144
|
+
return cls(**defaults)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def auto_detect(cls, name: str, **kwargs) -> "DriverConfig":
|
|
148
|
+
"""
|
|
149
|
+
Auto-detect environment and create appropriate configuration.
|
|
150
|
+
|
|
151
|
+
Uses UNREALON_ENV environment variable or unrealon_core config to determine environment.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
name: Driver name
|
|
155
|
+
**kwargs: Additional configuration overrides
|
|
156
|
+
"""
|
|
157
|
+
# Try to detect environment
|
|
158
|
+
env_name = os.getenv("UNREALON_ENV", "development").lower()
|
|
159
|
+
|
|
160
|
+
# Try unrealon_core if available
|
|
161
|
+
if UNREALON_CORE_AVAILABLE:
|
|
162
|
+
try:
|
|
163
|
+
env_config = get_environment_config()
|
|
164
|
+
if env_config.is_production:
|
|
165
|
+
return cls.for_production(name, **kwargs)
|
|
166
|
+
elif env_config.is_development:
|
|
167
|
+
return cls.for_development(name, **kwargs)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass # Fallback to env variable
|
|
170
|
+
|
|
171
|
+
# Fallback to environment variable
|
|
172
|
+
if env_name in ("prod", "production"):
|
|
173
|
+
return cls.for_production(name, **kwargs)
|
|
174
|
+
else:
|
|
175
|
+
return cls.for_development(name, **kwargs)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
UniversalDriver - Clean, modular driver orchestrator.
|
|
3
|
+
|
|
4
|
+
The main driver class that coordinates all components through a clean,
|
|
5
|
+
organized architecture with separated concerns.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional, List, Dict, Any, Callable
|
|
11
|
+
|
|
12
|
+
from .config import DriverConfig
|
|
13
|
+
from ..communication.websocket_client import WebSocketClient
|
|
14
|
+
from ..communication.session import DriverSession
|
|
15
|
+
from ..factory.manager_factory import ManagerFactory
|
|
16
|
+
from ..lifecycle.initialization import DriverInitializer
|
|
17
|
+
from ..lifecycle.shutdown import DriverShutdown
|
|
18
|
+
from ..lifecycle.daemon import DaemonManager
|
|
19
|
+
from ..monitoring.health import HealthMonitor
|
|
20
|
+
from ..utilities.logging import LoggingUtility
|
|
21
|
+
from ..utilities.serialization import SerializationUtility
|
|
22
|
+
|
|
23
|
+
from ...managers import (
|
|
24
|
+
LoggerManager, HttpManager, BrowserManager, CacheManager,
|
|
25
|
+
ProxyManager, ThreadManager, UpdateManager, ManagerRegistry
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from ...core_module import (
|
|
29
|
+
EventManager,
|
|
30
|
+
ModuleRegistry
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
from ...decorators import task, retry, schedule, timing
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UniversalDriver:
|
|
39
|
+
"""
|
|
40
|
+
Universal Driver - Clean orchestrator for all parsing operations.
|
|
41
|
+
|
|
42
|
+
Key Features:
|
|
43
|
+
- Zero-config initialization with sensible defaults
|
|
44
|
+
- Modular manager system (HTTP, Browser, Cache, etc.)
|
|
45
|
+
- WebSocket communication for RPC and logging
|
|
46
|
+
- Graceful shutdown with signal handling
|
|
47
|
+
- Built-in utilities for common operations
|
|
48
|
+
- Decorator-based task registration
|
|
49
|
+
|
|
50
|
+
Usage:
|
|
51
|
+
# Basic usage
|
|
52
|
+
config = DriverConfig.for_development("my_parser")
|
|
53
|
+
driver = UniversalDriver(config)
|
|
54
|
+
await driver.initialize()
|
|
55
|
+
|
|
56
|
+
# Daemon mode with RPC
|
|
57
|
+
await driver.run_daemon_mode()
|
|
58
|
+
|
|
59
|
+
# Standalone mode
|
|
60
|
+
await driver.initialize()
|
|
61
|
+
# ... your parsing logic
|
|
62
|
+
await driver.shutdown()
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, config: DriverConfig):
|
|
66
|
+
"""Initialize UniversalDriver with configuration."""
|
|
67
|
+
self.config = config
|
|
68
|
+
self.driver_id = config.name
|
|
69
|
+
self.is_initialized = False
|
|
70
|
+
self.capabilities: List[str] = []
|
|
71
|
+
|
|
72
|
+
# Core components
|
|
73
|
+
self.websocket_client: Optional[WebSocketClient] = None
|
|
74
|
+
self.session: Optional[DriverSession] = None
|
|
75
|
+
|
|
76
|
+
# Manager system (initialized by factory)
|
|
77
|
+
self.manager_registry: Optional[ManagerRegistry] = None
|
|
78
|
+
self.logger_manager: Optional[LoggerManager] = None
|
|
79
|
+
self.http: Optional[HttpManager] = None
|
|
80
|
+
self.browser: Optional[BrowserManager] = None
|
|
81
|
+
self.cache: Optional[CacheManager] = None
|
|
82
|
+
self.proxy: Optional[ProxyManager] = None
|
|
83
|
+
self.threading: Optional[ThreadManager] = None
|
|
84
|
+
self.update: Optional[UpdateManager] = None
|
|
85
|
+
|
|
86
|
+
# Module system
|
|
87
|
+
self.event_manager = EventManager()
|
|
88
|
+
self.module_registry = ModuleRegistry(self.event_manager)
|
|
89
|
+
|
|
90
|
+
# Utilities
|
|
91
|
+
self._logging_util = LoggingUtility(self.driver_id)
|
|
92
|
+
|
|
93
|
+
# Decorators (exposed as instance methods for convenience)
|
|
94
|
+
self.task = task
|
|
95
|
+
self.retry = retry
|
|
96
|
+
self.schedule = schedule
|
|
97
|
+
self.timing = timing
|
|
98
|
+
|
|
99
|
+
# Setup managers using factory
|
|
100
|
+
self._setup_components()
|
|
101
|
+
|
|
102
|
+
def _setup_components(self):
|
|
103
|
+
"""Setup all driver components."""
|
|
104
|
+
# Initialize managers using factory
|
|
105
|
+
self.manager_registry = ManagerFactory.setup_managers(self)
|
|
106
|
+
|
|
107
|
+
# Update logging utility with manager
|
|
108
|
+
self._logging_util.logger_manager = self.logger_manager
|
|
109
|
+
|
|
110
|
+
logger.debug(f"UniversalDriver components initialized: {self.driver_id}")
|
|
111
|
+
|
|
112
|
+
# === Lifecycle Management ===
|
|
113
|
+
|
|
114
|
+
async def initialize(self, capabilities: List[str] = []) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Initialize driver with all components.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
capabilities: List of driver capabilities
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
True if initialization successful
|
|
123
|
+
"""
|
|
124
|
+
return await DriverInitializer.initialize_driver(self, capabilities)
|
|
125
|
+
|
|
126
|
+
async def shutdown(self):
|
|
127
|
+
"""Shutdown driver cleanly."""
|
|
128
|
+
await DriverShutdown.shutdown_driver(self)
|
|
129
|
+
|
|
130
|
+
async def run_daemon_mode(self, on_start_message: str = "Driver initialized, waiting for RPC tasks..."):
|
|
131
|
+
"""
|
|
132
|
+
Run driver in daemon mode with graceful shutdown handling.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
on_start_message: Message to display when daemon starts
|
|
136
|
+
"""
|
|
137
|
+
await DaemonManager.run_daemon_mode(self, on_start_message)
|
|
138
|
+
|
|
139
|
+
# === Task Registration ===
|
|
140
|
+
|
|
141
|
+
def register_task_handler(self, task_type: str, handler: Callable):
|
|
142
|
+
"""Register task handler for RPC tasks."""
|
|
143
|
+
if self.session:
|
|
144
|
+
self.session.register_task_handler(task_type, handler)
|
|
145
|
+
else:
|
|
146
|
+
logger.warning("No session available for task handler registration")
|
|
147
|
+
|
|
148
|
+
# === Health & Monitoring ===
|
|
149
|
+
|
|
150
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
151
|
+
"""Get comprehensive health status."""
|
|
152
|
+
return await HealthMonitor.get_health_status(self)
|
|
153
|
+
|
|
154
|
+
def get_status(self) -> Dict[str, Any]:
|
|
155
|
+
"""Get basic driver status (synchronous)."""
|
|
156
|
+
return HealthMonitor.get_basic_status(self)
|
|
157
|
+
|
|
158
|
+
# === Utilities ===
|
|
159
|
+
|
|
160
|
+
def save_results_to_file(self, data: dict, filename: str, results_dir: Optional[str] = None) -> Path:
|
|
161
|
+
"""
|
|
162
|
+
Save parsing results to JSON file with automatic serialization.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
data: Data to save (can contain Pydantic models)
|
|
166
|
+
filename: Base filename (without extension)
|
|
167
|
+
results_dir: Directory to save to (default: ./data/results)
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Path to saved file
|
|
171
|
+
"""
|
|
172
|
+
return SerializationUtility.save_results_to_file(data, filename, results_dir)
|
|
173
|
+
|
|
174
|
+
# === Logging Methods ===
|
|
175
|
+
|
|
176
|
+
def log(self, level: str, message: str, context: Optional[Dict[str, Any]] = None):
|
|
177
|
+
"""Log message through logger manager."""
|
|
178
|
+
self._logging_util.log(level, message, context)
|
|
179
|
+
|
|
180
|
+
def debug(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
181
|
+
"""Log debug message."""
|
|
182
|
+
self._logging_util.debug(message, context)
|
|
183
|
+
|
|
184
|
+
def info(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
185
|
+
"""Log info message."""
|
|
186
|
+
self._logging_util.info(message, context)
|
|
187
|
+
|
|
188
|
+
def warning(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
189
|
+
"""Log warning message."""
|
|
190
|
+
self._logging_util.warning(message, context)
|
|
191
|
+
|
|
192
|
+
def error(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
193
|
+
"""Log error message."""
|
|
194
|
+
self._logging_util.error(message, context)
|
|
195
|
+
|
|
196
|
+
def critical(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
197
|
+
"""Log critical message."""
|
|
198
|
+
self._logging_util.critical(message, context)
|
|
199
|
+
|
|
200
|
+
# === Convenience Properties ===
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def is_daemon_mode(self) -> bool:
|
|
204
|
+
"""Check if driver is running in daemon mode."""
|
|
205
|
+
return self.session is not None
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def is_connected(self) -> bool:
|
|
209
|
+
"""Check if WebSocket is connected."""
|
|
210
|
+
return self.websocket_client is not None and self.websocket_client.is_connected
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def manager_count(self) -> int:
|
|
214
|
+
"""Get number of active managers."""
|
|
215
|
+
return len(self.manager_registry.managers) if self.manager_registry else 0
|
|
216
|
+
|
|
217
|
+
def __repr__(self) -> str:
|
|
218
|
+
"""String representation of driver."""
|
|
219
|
+
status = "initialized" if self.is_initialized else "not initialized"
|
|
220
|
+
mode = "daemon" if self.is_daemon_mode else "standalone"
|
|
221
|
+
return f"UniversalDriver(id='{self.driver_id}', status='{status}', mode='{mode}')"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manager Factory - Setup and configuration of all managers.
|
|
3
|
+
|
|
4
|
+
Handles the creation and configuration of manager components.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from ...managers import (
|
|
11
|
+
LoggerManager, HttpManager, BrowserManager, CacheManager,
|
|
12
|
+
ProxyManager, ThreadManager, UpdateManager, ManagerRegistry
|
|
13
|
+
)
|
|
14
|
+
from ...managers.logger import LoggerManagerConfig
|
|
15
|
+
from ...managers.http import HttpManagerConfig
|
|
16
|
+
from ...managers.browser import BrowserManagerConfig
|
|
17
|
+
from ...managers.cache import CacheManagerConfig
|
|
18
|
+
from ...managers.proxy import ProxyManagerConfig
|
|
19
|
+
from ...managers.threading import ThreadManagerConfig
|
|
20
|
+
from ...managers.update import UpdateManagerConfig
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ..core.driver import UniversalDriver
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ManagerFactory:
|
|
29
|
+
"""Factory for creating and configuring manager components."""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def setup_managers(driver: 'UniversalDriver') -> ManagerRegistry:
|
|
33
|
+
"""
|
|
34
|
+
Setup all managers with configurations.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
driver: UniversalDriver instance
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Configured ManagerRegistry
|
|
41
|
+
"""
|
|
42
|
+
# Create registry
|
|
43
|
+
manager_registry = ManagerRegistry()
|
|
44
|
+
|
|
45
|
+
# Setup each manager
|
|
46
|
+
ManagerFactory._setup_logger_manager(driver, manager_registry)
|
|
47
|
+
ManagerFactory._setup_http_manager(driver, manager_registry)
|
|
48
|
+
ManagerFactory._setup_browser_manager(driver, manager_registry)
|
|
49
|
+
ManagerFactory._setup_cache_manager(driver, manager_registry)
|
|
50
|
+
ManagerFactory._setup_proxy_manager(driver, manager_registry)
|
|
51
|
+
ManagerFactory._setup_threading_manager(driver, manager_registry)
|
|
52
|
+
ManagerFactory._setup_update_manager(driver, manager_registry)
|
|
53
|
+
|
|
54
|
+
logger.debug(f"Managers setup complete for driver: {driver.driver_id}")
|
|
55
|
+
return manager_registry
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _setup_logger_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
59
|
+
"""Setup logger manager."""
|
|
60
|
+
logger_config = LoggerManagerConfig(
|
|
61
|
+
enabled=True,
|
|
62
|
+
log_file=driver.config.log_file,
|
|
63
|
+
log_level=driver.config.log_level,
|
|
64
|
+
driver_id=driver.driver_id,
|
|
65
|
+
timeout=driver.config.websocket_timeout
|
|
66
|
+
)
|
|
67
|
+
driver.logger_manager = LoggerManager(logger_config)
|
|
68
|
+
registry.register(driver.logger_manager)
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _setup_http_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
72
|
+
"""Setup HTTP manager."""
|
|
73
|
+
http_config = HttpManagerConfig(
|
|
74
|
+
enabled=True,
|
|
75
|
+
timeout=driver.config.http_timeout,
|
|
76
|
+
max_retries=driver.config.max_retries
|
|
77
|
+
)
|
|
78
|
+
driver.http = HttpManager(http_config)
|
|
79
|
+
registry.register(driver.http)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _setup_browser_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
83
|
+
"""Setup browser manager."""
|
|
84
|
+
browser_config = BrowserManagerConfig(
|
|
85
|
+
enabled=True,
|
|
86
|
+
headless=driver.config.browser_headless,
|
|
87
|
+
timeout=driver.config.browser_timeout,
|
|
88
|
+
parser_name=driver.driver_id
|
|
89
|
+
)
|
|
90
|
+
driver.browser = BrowserManager(browser_config)
|
|
91
|
+
registry.register(driver.browser)
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def _setup_cache_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
95
|
+
"""Setup cache manager."""
|
|
96
|
+
cache_config = CacheManagerConfig(
|
|
97
|
+
enabled=driver.config.cache_enabled,
|
|
98
|
+
default_ttl=driver.config.cache_ttl
|
|
99
|
+
)
|
|
100
|
+
driver.cache = CacheManager(cache_config)
|
|
101
|
+
registry.register(driver.cache)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _setup_proxy_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
105
|
+
"""Setup proxy manager."""
|
|
106
|
+
proxy_config = ProxyManagerConfig(
|
|
107
|
+
enabled=driver.config.proxy_enabled,
|
|
108
|
+
rotation_interval=driver.config.proxy_rotation_interval
|
|
109
|
+
)
|
|
110
|
+
driver.proxy = ProxyManager(proxy_config)
|
|
111
|
+
registry.register(driver.proxy)
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _setup_threading_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
115
|
+
"""Setup threading manager."""
|
|
116
|
+
threading_config = ThreadManagerConfig(
|
|
117
|
+
enabled=True,
|
|
118
|
+
max_workers=driver.config.max_workers
|
|
119
|
+
)
|
|
120
|
+
driver.threading = ThreadManager(threading_config)
|
|
121
|
+
registry.register(driver.threading)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _setup_update_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
125
|
+
"""Setup update manager."""
|
|
126
|
+
update_config = UpdateManagerConfig(
|
|
127
|
+
enabled=True
|
|
128
|
+
)
|
|
129
|
+
driver.update = UpdateManager(update_config)
|
|
130
|
+
registry.register(driver.update)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Driver lifecycle management.
|
|
3
|
+
|
|
4
|
+
Handles initialization, shutdown, and daemon mode operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .initialization import DriverInitializer
|
|
8
|
+
from .shutdown import DriverShutdown
|
|
9
|
+
from .daemon import DaemonManager
|
|
10
|
+
|
|
11
|
+
__all__ = ["DriverInitializer", "DriverShutdown", "DaemonManager"]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Daemon mode management.
|
|
3
|
+
|
|
4
|
+
Handles running the driver in daemon mode with graceful shutdown.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import signal
|
|
10
|
+
import sys
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from .initialization import DriverInitializer
|
|
14
|
+
from .shutdown import DriverShutdown
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ..core.driver import UniversalDriver
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DaemonManager:
|
|
23
|
+
"""Manages daemon mode operations."""
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
async def run_daemon_mode(driver: 'UniversalDriver', on_start_message: str = "Driver initialized, waiting for RPC tasks..."):
|
|
27
|
+
"""
|
|
28
|
+
Run driver in daemon mode with graceful shutdown handling.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
driver: UniversalDriver instance
|
|
32
|
+
on_start_message: Message to display when daemon starts
|
|
33
|
+
"""
|
|
34
|
+
# Flag for graceful shutdown
|
|
35
|
+
shutdown_event = asyncio.Event()
|
|
36
|
+
|
|
37
|
+
def signal_handler():
|
|
38
|
+
"""Handle shutdown signals gracefully."""
|
|
39
|
+
logger.info("Shutdown signal received")
|
|
40
|
+
shutdown_event.set()
|
|
41
|
+
|
|
42
|
+
# Register signal handlers
|
|
43
|
+
if sys.platform != "win32":
|
|
44
|
+
loop = asyncio.get_running_loop()
|
|
45
|
+
for sig in (signal.SIGTERM, signal.SIGINT):
|
|
46
|
+
loop.add_signal_handler(sig, signal_handler)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
# Initialize driver with capabilities
|
|
50
|
+
success = await DriverInitializer.initialize_driver(driver, driver.capabilities)
|
|
51
|
+
if not success:
|
|
52
|
+
raise RuntimeError("Driver initialization failed")
|
|
53
|
+
|
|
54
|
+
# Call on_start hook if available
|
|
55
|
+
if hasattr(driver, 'on_start') and callable(driver.on_start):
|
|
56
|
+
await driver.on_start()
|
|
57
|
+
|
|
58
|
+
logger.info(on_start_message)
|
|
59
|
+
|
|
60
|
+
# Keep running until shutdown signal
|
|
61
|
+
await shutdown_event.wait()
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"Daemon mode error: {e}")
|
|
65
|
+
raise
|
|
66
|
+
finally:
|
|
67
|
+
# Always shutdown cleanly
|
|
68
|
+
try:
|
|
69
|
+
# Call on_shutdown hook if available
|
|
70
|
+
if hasattr(driver, 'on_shutdown') and callable(driver.on_shutdown):
|
|
71
|
+
await driver.on_shutdown()
|
|
72
|
+
|
|
73
|
+
await DriverShutdown.shutdown_driver(driver)
|
|
74
|
+
logger.info("Driver shutdown complete")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"Error during shutdown: {e}")
|