crawlo 1.4.2__py3-none-any.whl → 1.4.3__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.
Potentially problematic release.
This version of crawlo might be problematic. Click here for more details.
- crawlo/__init__.py +93 -93
- crawlo/__version__.py +1 -1
- crawlo/cli.py +75 -75
- crawlo/commands/__init__.py +14 -14
- crawlo/commands/check.py +594 -594
- crawlo/commands/genspider.py +151 -151
- crawlo/commands/help.py +138 -138
- crawlo/commands/list.py +155 -155
- crawlo/commands/run.py +341 -341
- crawlo/commands/startproject.py +436 -436
- crawlo/commands/stats.py +187 -187
- crawlo/commands/utils.py +196 -196
- crawlo/config.py +312 -312
- crawlo/config_validator.py +277 -277
- crawlo/core/__init__.py +52 -52
- crawlo/core/engine.py +438 -439
- crawlo/core/processor.py +47 -47
- crawlo/core/scheduler.py +291 -257
- crawlo/crawler.py +650 -650
- crawlo/data/__init__.py +5 -5
- crawlo/data/user_agents.py +194 -194
- crawlo/downloader/__init__.py +273 -273
- crawlo/downloader/aiohttp_downloader.py +233 -233
- crawlo/downloader/cffi_downloader.py +245 -245
- crawlo/downloader/httpx_downloader.py +259 -259
- crawlo/downloader/hybrid_downloader.py +212 -212
- crawlo/downloader/playwright_downloader.py +402 -402
- crawlo/downloader/selenium_downloader.py +472 -472
- crawlo/event.py +11 -11
- crawlo/exceptions.py +81 -81
- crawlo/extension/__init__.py +63 -63
- crawlo/extension/health_check.py +141 -141
- crawlo/extension/log_interval.py +94 -94
- crawlo/extension/log_stats.py +70 -70
- crawlo/extension/logging_extension.py +61 -61
- crawlo/extension/memory_monitor.py +104 -104
- crawlo/extension/performance_profiler.py +133 -133
- crawlo/extension/request_recorder.py +107 -107
- crawlo/factories/__init__.py +27 -27
- crawlo/factories/base.py +68 -68
- crawlo/factories/crawler.py +103 -103
- crawlo/factories/registry.py +84 -84
- crawlo/filters/__init__.py +154 -154
- crawlo/filters/aioredis_filter.py +257 -257
- crawlo/filters/memory_filter.py +269 -269
- crawlo/framework.py +292 -292
- crawlo/initialization/__init__.py +44 -44
- crawlo/initialization/built_in.py +425 -425
- crawlo/initialization/context.py +141 -141
- crawlo/initialization/core.py +193 -193
- crawlo/initialization/phases.py +148 -148
- crawlo/initialization/registry.py +145 -145
- crawlo/items/__init__.py +23 -23
- crawlo/items/base.py +23 -23
- crawlo/items/fields.py +52 -52
- crawlo/items/items.py +104 -104
- crawlo/logging/__init__.py +45 -37
- crawlo/logging/async_handler.py +181 -0
- crawlo/logging/config.py +196 -96
- crawlo/logging/factory.py +171 -128
- crawlo/logging/manager.py +111 -111
- crawlo/logging/monitor.py +153 -0
- crawlo/logging/sampler.py +167 -0
- crawlo/middleware/__init__.py +21 -21
- crawlo/middleware/default_header.py +132 -132
- crawlo/middleware/download_delay.py +104 -104
- crawlo/middleware/middleware_manager.py +135 -135
- crawlo/middleware/offsite.py +123 -123
- crawlo/middleware/proxy.py +386 -386
- crawlo/middleware/request_ignore.py +86 -86
- crawlo/middleware/response_code.py +150 -150
- crawlo/middleware/response_filter.py +136 -136
- crawlo/middleware/retry.py +124 -124
- crawlo/middleware/simple_proxy.py +65 -65
- crawlo/mode_manager.py +219 -219
- crawlo/network/__init__.py +21 -21
- crawlo/network/request.py +379 -379
- crawlo/network/response.py +359 -359
- crawlo/pipelines/__init__.py +21 -21
- crawlo/pipelines/bloom_dedup_pipeline.py +146 -146
- crawlo/pipelines/console_pipeline.py +39 -39
- crawlo/pipelines/csv_pipeline.py +316 -316
- crawlo/pipelines/database_dedup_pipeline.py +197 -197
- crawlo/pipelines/json_pipeline.py +218 -218
- crawlo/pipelines/memory_dedup_pipeline.py +105 -105
- crawlo/pipelines/mongo_pipeline.py +131 -131
- crawlo/pipelines/mysql_pipeline.py +325 -325
- crawlo/pipelines/pipeline_manager.py +100 -84
- crawlo/pipelines/redis_dedup_pipeline.py +156 -156
- crawlo/project.py +349 -338
- crawlo/queue/pqueue.py +42 -42
- crawlo/queue/queue_manager.py +526 -522
- crawlo/queue/redis_priority_queue.py +370 -367
- crawlo/settings/__init__.py +7 -7
- crawlo/settings/default_settings.py +284 -284
- crawlo/settings/setting_manager.py +219 -219
- crawlo/spider/__init__.py +657 -657
- crawlo/stats_collector.py +73 -73
- crawlo/subscriber.py +129 -129
- crawlo/task_manager.py +138 -138
- crawlo/templates/crawlo.cfg.tmpl +10 -10
- crawlo/templates/project/__init__.py.tmpl +3 -3
- crawlo/templates/project/items.py.tmpl +17 -17
- crawlo/templates/project/middlewares.py.tmpl +118 -118
- crawlo/templates/project/pipelines.py.tmpl +96 -96
- crawlo/templates/project/settings.py.tmpl +170 -170
- crawlo/templates/project/settings_distributed.py.tmpl +169 -169
- crawlo/templates/project/settings_gentle.py.tmpl +166 -166
- crawlo/templates/project/settings_high_performance.py.tmpl +167 -167
- crawlo/templates/project/settings_minimal.py.tmpl +65 -65
- crawlo/templates/project/settings_simple.py.tmpl +164 -164
- crawlo/templates/project/spiders/__init__.py.tmpl +9 -9
- crawlo/templates/run.py.tmpl +34 -34
- crawlo/templates/spider/spider.py.tmpl +143 -143
- crawlo/templates/spiders_init.py.tmpl +9 -9
- crawlo/tools/__init__.py +200 -200
- crawlo/tools/anti_crawler.py +268 -268
- crawlo/tools/authenticated_proxy.py +240 -240
- crawlo/tools/data_formatter.py +225 -225
- crawlo/tools/data_validator.py +180 -180
- crawlo/tools/date_tools.py +289 -289
- crawlo/tools/distributed_coordinator.py +384 -384
- crawlo/tools/encoding_converter.py +127 -127
- crawlo/tools/network_diagnostic.py +364 -364
- crawlo/tools/request_tools.py +82 -82
- crawlo/tools/retry_mechanism.py +224 -224
- crawlo/tools/scenario_adapter.py +262 -262
- crawlo/tools/text_cleaner.py +232 -232
- crawlo/utils/__init__.py +34 -34
- crawlo/utils/batch_processor.py +259 -259
- crawlo/utils/class_loader.py +25 -25
- crawlo/utils/controlled_spider_mixin.py +439 -439
- crawlo/utils/db_helper.py +343 -343
- crawlo/utils/enhanced_error_handler.py +356 -356
- crawlo/utils/env_config.py +142 -142
- crawlo/utils/error_handler.py +165 -165
- crawlo/utils/fingerprint.py +122 -122
- crawlo/utils/func_tools.py +82 -82
- crawlo/utils/large_scale_config.py +286 -286
- crawlo/utils/large_scale_helper.py +344 -344
- crawlo/utils/log.py +79 -79
- crawlo/utils/performance_monitor.py +285 -285
- crawlo/utils/queue_helper.py +175 -175
- crawlo/utils/redis_connection_pool.py +388 -388
- crawlo/utils/redis_key_validator.py +198 -198
- crawlo/utils/request.py +267 -267
- crawlo/utils/request_serializer.py +225 -225
- crawlo/utils/spider_loader.py +61 -61
- crawlo/utils/system.py +11 -11
- crawlo/utils/tools.py +4 -4
- crawlo/utils/url.py +39 -39
- crawlo-1.4.3.dist-info/METADATA +190 -0
- crawlo-1.4.3.dist-info/RECORD +326 -0
- examples/__init__.py +7 -7
- examples/test_project/__init__.py +7 -7
- examples/test_project/run.py +34 -34
- examples/test_project/test_project/__init__.py +3 -3
- examples/test_project/test_project/items.py +17 -17
- examples/test_project/test_project/middlewares.py +118 -118
- examples/test_project/test_project/pipelines.py +96 -96
- examples/test_project/test_project/settings.py +169 -169
- examples/test_project/test_project/spiders/__init__.py +9 -9
- examples/test_project/test_project/spiders/of_week_dis.py +143 -143
- tests/__init__.py +7 -7
- tests/advanced_tools_example.py +275 -275
- tests/authenticated_proxy_example.py +106 -106
- tests/baidu_performance_test.py +108 -108
- tests/baidu_test.py +59 -59
- tests/cleaners_example.py +160 -160
- tests/comprehensive_framework_test.py +212 -212
- tests/comprehensive_test.py +81 -81
- tests/comprehensive_testing_summary.md +186 -186
- tests/config_validation_demo.py +142 -142
- tests/controlled_spider_example.py +205 -205
- tests/date_tools_example.py +180 -180
- tests/debug_configure.py +69 -69
- tests/debug_framework_logger.py +84 -84
- tests/debug_log_config.py +126 -126
- tests/debug_log_levels.py +63 -63
- tests/debug_pipelines.py +66 -66
- tests/detailed_log_test.py +233 -233
- tests/distributed_test.py +66 -66
- tests/distributed_test_debug.py +76 -76
- tests/dynamic_loading_example.py +523 -523
- tests/dynamic_loading_test.py +104 -104
- tests/env_config_example.py +133 -133
- tests/error_handling_example.py +171 -171
- tests/final_comprehensive_test.py +151 -151
- tests/final_log_test.py +260 -260
- tests/final_validation_test.py +182 -182
- tests/fix_log_test.py +142 -142
- tests/framework_performance_test.py +202 -202
- tests/log_buffering_test.py +111 -111
- tests/log_generation_timing_test.py +153 -153
- tests/optimized_performance_test.py +211 -211
- tests/performance_comparison.py +245 -245
- tests/queue_blocking_test.py +113 -113
- tests/queue_test.py +89 -89
- tests/redis_key_validation_demo.py +130 -130
- tests/request_params_example.py +150 -150
- tests/response_improvements_example.py +144 -144
- tests/scrapy_comparison/ofweek_scrapy.py +138 -138
- tests/scrapy_comparison/scrapy_test.py +133 -133
- tests/simple_command_test.py +119 -119
- tests/simple_crawlo_test.py +127 -127
- tests/simple_log_test.py +57 -57
- tests/simple_log_test2.py +137 -137
- tests/simple_optimization_test.py +128 -128
- tests/simple_queue_type_test.py +41 -41
- tests/simple_spider_test.py +49 -49
- tests/simple_test.py +47 -47
- tests/spider_log_timing_test.py +177 -177
- tests/test_advanced_tools.py +148 -148
- tests/test_all_commands.py +230 -230
- tests/test_all_pipeline_fingerprints.py +133 -133
- tests/test_all_redis_key_configs.py +145 -145
- tests/test_authenticated_proxy.py +141 -141
- tests/test_batch_processor.py +178 -178
- tests/test_cleaners.py +54 -54
- tests/test_component_factory.py +174 -174
- tests/test_comprehensive.py +146 -146
- tests/test_config_consistency.py +80 -80
- tests/test_config_merge.py +152 -152
- tests/test_config_validator.py +182 -182
- tests/test_controlled_spider_mixin.py +79 -79
- tests/test_crawlo_proxy_integration.py +108 -108
- tests/test_date_tools.py +123 -123
- tests/test_dedup_fix.py +220 -220
- tests/test_dedup_pipeline_consistency.py +125 -0
- tests/test_default_header_middleware.py +313 -313
- tests/test_distributed.py +65 -65
- tests/test_double_crawlo_fix.py +204 -204
- tests/test_double_crawlo_fix_simple.py +124 -124
- tests/test_download_delay_middleware.py +221 -221
- tests/test_downloader_proxy_compatibility.py +268 -268
- tests/test_dynamic_downloaders_proxy.py +124 -124
- tests/test_dynamic_proxy.py +92 -92
- tests/test_dynamic_proxy_config.py +146 -146
- tests/test_dynamic_proxy_real.py +109 -109
- tests/test_edge_cases.py +303 -303
- tests/test_enhanced_error_handler.py +270 -270
- tests/test_enhanced_error_handler_comprehensive.py +245 -245
- tests/test_env_config.py +121 -121
- tests/test_error_handler_compatibility.py +112 -112
- tests/test_factories.py +252 -252
- tests/test_final_validation.py +153 -153
- tests/test_fingerprint_consistency.py +135 -135
- tests/test_fingerprint_simple.py +51 -51
- tests/test_framework_env_usage.py +103 -103
- tests/test_framework_logger.py +66 -66
- tests/test_framework_startup.py +64 -64
- tests/test_get_component_logger.py +83 -83
- tests/test_hash_performance.py +99 -99
- tests/test_integration.py +169 -169
- tests/test_item_dedup_redis_key.py +122 -122
- tests/test_large_scale_config.py +112 -112
- tests/test_large_scale_helper.py +235 -235
- tests/test_logging_enhancements.py +375 -0
- tests/test_logging_final.py +185 -0
- tests/test_logging_integration.py +313 -0
- tests/test_logging_system.py +282 -282
- tests/test_middleware_debug.py +142 -0
- tests/test_mode_change.py +72 -72
- tests/test_mode_consistency.py +51 -51
- tests/test_offsite_middleware.py +244 -244
- tests/test_offsite_middleware_simple.py +203 -203
- tests/test_parsel.py +29 -29
- tests/test_performance.py +327 -327
- tests/test_performance_monitor.py +115 -115
- tests/test_pipeline_fingerprint_consistency.py +86 -86
- tests/test_priority_behavior.py +212 -0
- tests/test_priority_consistency.py +152 -0
- tests/test_priority_consistency_fixed.py +250 -0
- tests/test_proxy_api.py +264 -264
- tests/test_proxy_health_check.py +32 -32
- tests/test_proxy_middleware.py +121 -121
- tests/test_proxy_middleware_enhanced.py +216 -216
- tests/test_proxy_middleware_integration.py +136 -136
- tests/test_proxy_middleware_refactored.py +184 -184
- tests/test_proxy_providers.py +56 -56
- tests/test_proxy_stats.py +19 -19
- tests/test_proxy_strategies.py +59 -59
- tests/test_queue_empty_check.py +41 -41
- tests/test_queue_manager_double_crawlo.py +173 -173
- tests/test_queue_manager_redis_key.py +179 -179
- tests/test_queue_naming.py +154 -154
- tests/test_queue_type.py +106 -106
- tests/test_queue_type_redis_config_consistency.py +131 -0
- tests/test_random_headers_default.py +323 -0
- tests/test_random_headers_necessity.py +309 -0
- tests/test_random_user_agent.py +72 -72
- tests/test_real_scenario_proxy.py +195 -195
- tests/test_redis_config.py +28 -28
- tests/test_redis_connection_pool.py +294 -294
- tests/test_redis_key_naming.py +181 -181
- tests/test_redis_key_validator.py +123 -123
- tests/test_redis_queue.py +224 -224
- tests/test_redis_queue_name_fix.py +175 -175
- tests/test_redis_queue_type_fallback.py +130 -0
- tests/test_request_ignore_middleware.py +182 -182
- tests/test_request_params.py +111 -111
- tests/test_request_serialization.py +70 -70
- tests/test_response_code_middleware.py +349 -349
- tests/test_response_filter_middleware.py +427 -427
- tests/test_response_improvements.py +152 -152
- tests/test_retry_middleware.py +334 -242
- tests/test_retry_middleware_realistic.py +274 -0
- tests/test_scheduler.py +252 -252
- tests/test_scheduler_config_update.py +133 -133
- tests/test_simple_response.py +61 -61
- tests/test_telecom_spider_redis_key.py +205 -205
- tests/test_template_content.py +87 -87
- tests/test_template_redis_key.py +134 -134
- tests/test_tools.py +159 -159
- tests/test_user_agent_randomness.py +177 -0
- tests/test_user_agents.py +96 -96
- tests/tools_example.py +260 -260
- tests/untested_features_report.md +138 -138
- tests/verify_debug.py +51 -51
- tests/verify_distributed.py +117 -117
- tests/verify_log_fix.py +111 -111
- crawlo-1.4.2.dist-info/METADATA +0 -1199
- crawlo-1.4.2.dist-info/RECORD +0 -309
- {crawlo-1.4.2.dist-info → crawlo-1.4.3.dist-info}/WHEEL +0 -0
- {crawlo-1.4.2.dist-info → crawlo-1.4.3.dist-info}/entry_points.txt +0 -0
- {crawlo-1.4.2.dist-info → crawlo-1.4.3.dist-info}/top_level.txt +0 -0
crawlo/initialization/context.py
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
# -*- coding: UTF-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
初始化上下文 - 保存初始化过程中的状态和数据
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import time
|
|
8
|
-
import threading
|
|
9
|
-
from typing import Dict, Any, List, Optional
|
|
10
|
-
from dataclasses import dataclass, field
|
|
11
|
-
|
|
12
|
-
from .phases import InitializationPhase, PhaseResult
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class InitializationContext:
|
|
17
|
-
"""
|
|
18
|
-
初始化上下文
|
|
19
|
-
|
|
20
|
-
保存初始化过程中的状态、数据和结果
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
# 基本信息
|
|
24
|
-
start_time: float = field(default_factory=time.time)
|
|
25
|
-
end_time: Optional[float] = None
|
|
26
|
-
|
|
27
|
-
# 当前状态
|
|
28
|
-
current_phase: InitializationPhase = InitializationPhase.PREPARING
|
|
29
|
-
completed_phases: List[InitializationPhase] = field(default_factory=list)
|
|
30
|
-
failed_phases: List[InitializationPhase] = field(default_factory=list)
|
|
31
|
-
|
|
32
|
-
# 阶段结果
|
|
33
|
-
phase_results: Dict[InitializationPhase, PhaseResult] = field(default_factory=dict)
|
|
34
|
-
|
|
35
|
-
# 共享数据
|
|
36
|
-
shared_data: Dict[str, Any] = field(default_factory=dict)
|
|
37
|
-
|
|
38
|
-
# 配置信息
|
|
39
|
-
settings: Optional[Any] = None
|
|
40
|
-
custom_settings: Optional[Dict[str, Any]] = None
|
|
41
|
-
|
|
42
|
-
# 错误信息
|
|
43
|
-
errors: List[str] = field(default_factory=list)
|
|
44
|
-
warnings: List[str] = field(default_factory=list)
|
|
45
|
-
|
|
46
|
-
# 线程安全
|
|
47
|
-
_lock: threading.RLock = field(default_factory=threading.RLock, init=False)
|
|
48
|
-
|
|
49
|
-
def set_current_phase(self, phase: InitializationPhase):
|
|
50
|
-
"""设置当前阶段"""
|
|
51
|
-
with self._lock:
|
|
52
|
-
self.current_phase = phase
|
|
53
|
-
|
|
54
|
-
def mark_phase_completed(self, phase: InitializationPhase, result: PhaseResult):
|
|
55
|
-
"""标记阶段完成"""
|
|
56
|
-
with self._lock:
|
|
57
|
-
if result.success:
|
|
58
|
-
self.completed_phases.append(phase)
|
|
59
|
-
else:
|
|
60
|
-
self.failed_phases.append(phase)
|
|
61
|
-
|
|
62
|
-
self.phase_results[phase] = result
|
|
63
|
-
|
|
64
|
-
def add_shared_data(self, key: str, value: Any):
|
|
65
|
-
"""添加共享数据"""
|
|
66
|
-
with self._lock:
|
|
67
|
-
self.shared_data[key] = value
|
|
68
|
-
|
|
69
|
-
def get_shared_data(self, key: str, default=None):
|
|
70
|
-
"""获取共享数据"""
|
|
71
|
-
with self._lock:
|
|
72
|
-
return self.shared_data.get(key, default)
|
|
73
|
-
|
|
74
|
-
def add_error(self, message: str):
|
|
75
|
-
"""添加错误信息"""
|
|
76
|
-
with self._lock:
|
|
77
|
-
self.errors.append(message)
|
|
78
|
-
|
|
79
|
-
def add_warning(self, message: str):
|
|
80
|
-
"""添加警告信息"""
|
|
81
|
-
with self._lock:
|
|
82
|
-
self.warnings.append(message)
|
|
83
|
-
|
|
84
|
-
def is_phase_completed(self, phase: InitializationPhase) -> bool:
|
|
85
|
-
"""检查阶段是否完成"""
|
|
86
|
-
with self._lock:
|
|
87
|
-
return phase in self.completed_phases
|
|
88
|
-
|
|
89
|
-
def is_phase_failed(self, phase: InitializationPhase) -> bool:
|
|
90
|
-
"""检查阶段是否失败"""
|
|
91
|
-
with self._lock:
|
|
92
|
-
return phase in self.failed_phases
|
|
93
|
-
|
|
94
|
-
def get_phase_result(self, phase: InitializationPhase) -> Optional[PhaseResult]:
|
|
95
|
-
"""获取阶段结果"""
|
|
96
|
-
with self._lock:
|
|
97
|
-
return self.phase_results.get(phase)
|
|
98
|
-
|
|
99
|
-
def get_total_duration(self) -> float:
|
|
100
|
-
"""获取总耗时"""
|
|
101
|
-
end = self.end_time or time.time()
|
|
102
|
-
return end - self.start_time
|
|
103
|
-
|
|
104
|
-
def get_phase_durations(self) -> Dict[InitializationPhase, float]:
|
|
105
|
-
"""获取各阶段耗时"""
|
|
106
|
-
with self._lock:
|
|
107
|
-
return {
|
|
108
|
-
phase: result.duration
|
|
109
|
-
for phase, result in self.phase_results.items()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
def get_success_rate(self) -> float:
|
|
113
|
-
"""获取成功率"""
|
|
114
|
-
with self._lock:
|
|
115
|
-
total = len(self.completed_phases) + len(self.failed_phases)
|
|
116
|
-
if total == 0:
|
|
117
|
-
return 0.0
|
|
118
|
-
return len(self.completed_phases) / total * 100
|
|
119
|
-
|
|
120
|
-
def finish(self):
|
|
121
|
-
"""标记初始化完成"""
|
|
122
|
-
with self._lock:
|
|
123
|
-
self.end_time = time.time()
|
|
124
|
-
|
|
125
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
126
|
-
"""转换为字典格式"""
|
|
127
|
-
with self._lock:
|
|
128
|
-
return {
|
|
129
|
-
'start_time': self.start_time,
|
|
130
|
-
'end_time': self.end_time,
|
|
131
|
-
'total_duration': self.get_total_duration(),
|
|
132
|
-
'current_phase': self.current_phase.value,
|
|
133
|
-
'completed_phases': [p.value for p in self.completed_phases],
|
|
134
|
-
'failed_phases': [p.value for p in self.failed_phases],
|
|
135
|
-
'success_rate': self.get_success_rate(),
|
|
136
|
-
'error_count': len(self.errors),
|
|
137
|
-
'warning_count': len(self.warnings),
|
|
138
|
-
'phase_durations': {
|
|
139
|
-
p.value: duration
|
|
140
|
-
for p, duration in self.get_phase_durations().items()
|
|
141
|
-
}
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
初始化上下文 - 保存初始化过程中的状态和数据
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from typing import Dict, Any, List, Optional
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
from .phases import InitializationPhase, PhaseResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class InitializationContext:
|
|
17
|
+
"""
|
|
18
|
+
初始化上下文
|
|
19
|
+
|
|
20
|
+
保存初始化过程中的状态、数据和结果
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# 基本信息
|
|
24
|
+
start_time: float = field(default_factory=time.time)
|
|
25
|
+
end_time: Optional[float] = None
|
|
26
|
+
|
|
27
|
+
# 当前状态
|
|
28
|
+
current_phase: InitializationPhase = InitializationPhase.PREPARING
|
|
29
|
+
completed_phases: List[InitializationPhase] = field(default_factory=list)
|
|
30
|
+
failed_phases: List[InitializationPhase] = field(default_factory=list)
|
|
31
|
+
|
|
32
|
+
# 阶段结果
|
|
33
|
+
phase_results: Dict[InitializationPhase, PhaseResult] = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
# 共享数据
|
|
36
|
+
shared_data: Dict[str, Any] = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
# 配置信息
|
|
39
|
+
settings: Optional[Any] = None
|
|
40
|
+
custom_settings: Optional[Dict[str, Any]] = None
|
|
41
|
+
|
|
42
|
+
# 错误信息
|
|
43
|
+
errors: List[str] = field(default_factory=list)
|
|
44
|
+
warnings: List[str] = field(default_factory=list)
|
|
45
|
+
|
|
46
|
+
# 线程安全
|
|
47
|
+
_lock: threading.RLock = field(default_factory=threading.RLock, init=False)
|
|
48
|
+
|
|
49
|
+
def set_current_phase(self, phase: InitializationPhase):
|
|
50
|
+
"""设置当前阶段"""
|
|
51
|
+
with self._lock:
|
|
52
|
+
self.current_phase = phase
|
|
53
|
+
|
|
54
|
+
def mark_phase_completed(self, phase: InitializationPhase, result: PhaseResult):
|
|
55
|
+
"""标记阶段完成"""
|
|
56
|
+
with self._lock:
|
|
57
|
+
if result.success:
|
|
58
|
+
self.completed_phases.append(phase)
|
|
59
|
+
else:
|
|
60
|
+
self.failed_phases.append(phase)
|
|
61
|
+
|
|
62
|
+
self.phase_results[phase] = result
|
|
63
|
+
|
|
64
|
+
def add_shared_data(self, key: str, value: Any):
|
|
65
|
+
"""添加共享数据"""
|
|
66
|
+
with self._lock:
|
|
67
|
+
self.shared_data[key] = value
|
|
68
|
+
|
|
69
|
+
def get_shared_data(self, key: str, default=None):
|
|
70
|
+
"""获取共享数据"""
|
|
71
|
+
with self._lock:
|
|
72
|
+
return self.shared_data.get(key, default)
|
|
73
|
+
|
|
74
|
+
def add_error(self, message: str):
|
|
75
|
+
"""添加错误信息"""
|
|
76
|
+
with self._lock:
|
|
77
|
+
self.errors.append(message)
|
|
78
|
+
|
|
79
|
+
def add_warning(self, message: str):
|
|
80
|
+
"""添加警告信息"""
|
|
81
|
+
with self._lock:
|
|
82
|
+
self.warnings.append(message)
|
|
83
|
+
|
|
84
|
+
def is_phase_completed(self, phase: InitializationPhase) -> bool:
|
|
85
|
+
"""检查阶段是否完成"""
|
|
86
|
+
with self._lock:
|
|
87
|
+
return phase in self.completed_phases
|
|
88
|
+
|
|
89
|
+
def is_phase_failed(self, phase: InitializationPhase) -> bool:
|
|
90
|
+
"""检查阶段是否失败"""
|
|
91
|
+
with self._lock:
|
|
92
|
+
return phase in self.failed_phases
|
|
93
|
+
|
|
94
|
+
def get_phase_result(self, phase: InitializationPhase) -> Optional[PhaseResult]:
|
|
95
|
+
"""获取阶段结果"""
|
|
96
|
+
with self._lock:
|
|
97
|
+
return self.phase_results.get(phase)
|
|
98
|
+
|
|
99
|
+
def get_total_duration(self) -> float:
|
|
100
|
+
"""获取总耗时"""
|
|
101
|
+
end = self.end_time or time.time()
|
|
102
|
+
return end - self.start_time
|
|
103
|
+
|
|
104
|
+
def get_phase_durations(self) -> Dict[InitializationPhase, float]:
|
|
105
|
+
"""获取各阶段耗时"""
|
|
106
|
+
with self._lock:
|
|
107
|
+
return {
|
|
108
|
+
phase: result.duration
|
|
109
|
+
for phase, result in self.phase_results.items()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def get_success_rate(self) -> float:
|
|
113
|
+
"""获取成功率"""
|
|
114
|
+
with self._lock:
|
|
115
|
+
total = len(self.completed_phases) + len(self.failed_phases)
|
|
116
|
+
if total == 0:
|
|
117
|
+
return 0.0
|
|
118
|
+
return len(self.completed_phases) / total * 100
|
|
119
|
+
|
|
120
|
+
def finish(self):
|
|
121
|
+
"""标记初始化完成"""
|
|
122
|
+
with self._lock:
|
|
123
|
+
self.end_time = time.time()
|
|
124
|
+
|
|
125
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
126
|
+
"""转换为字典格式"""
|
|
127
|
+
with self._lock:
|
|
128
|
+
return {
|
|
129
|
+
'start_time': self.start_time,
|
|
130
|
+
'end_time': self.end_time,
|
|
131
|
+
'total_duration': self.get_total_duration(),
|
|
132
|
+
'current_phase': self.current_phase.value,
|
|
133
|
+
'completed_phases': [p.value for p in self.completed_phases],
|
|
134
|
+
'failed_phases': [p.value for p in self.failed_phases],
|
|
135
|
+
'success_rate': self.get_success_rate(),
|
|
136
|
+
'error_count': len(self.errors),
|
|
137
|
+
'warning_count': len(self.warnings),
|
|
138
|
+
'phase_durations': {
|
|
139
|
+
p.value: duration
|
|
140
|
+
for p, duration in self.get_phase_durations().items()
|
|
141
|
+
}
|
|
142
142
|
}
|
crawlo/initialization/core.py
CHANGED
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
# -*- coding: UTF-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
核心初始化器 - 协调整个初始化过程
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import time
|
|
8
|
-
import threading
|
|
9
|
-
from typing import Optional, Any
|
|
10
|
-
|
|
11
|
-
from .context import InitializationContext
|
|
12
|
-
from .phases import InitializationPhase, PhaseResult, get_execution_order, get_phase_definition
|
|
13
|
-
from .registry import get_global_registry, BaseInitializer, register_initializer
|
|
14
|
-
from .built_in import register_built_in_initializers
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class CoreInitializer:
|
|
18
|
-
"""
|
|
19
|
-
核心初始化器 - 协调整个框架的初始化过程
|
|
20
|
-
|
|
21
|
-
职责:
|
|
22
|
-
1. 管理初始化阶段的执行顺序
|
|
23
|
-
2. 处理阶段间的依赖关系
|
|
24
|
-
3. 提供统一的初始化入口
|
|
25
|
-
4. 错误处理和降级策略
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
_instance: Optional['CoreInitializer'] = None
|
|
29
|
-
_lock = threading.Lock()
|
|
30
|
-
|
|
31
|
-
def __new__(cls) -> 'CoreInitializer':
|
|
32
|
-
if cls._instance is None:
|
|
33
|
-
with cls._lock:
|
|
34
|
-
if cls._instance is None:
|
|
35
|
-
cls._instance = super(CoreInitializer, cls).__new__(cls)
|
|
36
|
-
cls._instance._initialized = False
|
|
37
|
-
return cls._instance
|
|
38
|
-
|
|
39
|
-
def __init__(self):
|
|
40
|
-
if hasattr(self, '_initialized') and self._initialized:
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
self._context: Optional[InitializationContext] = None
|
|
44
|
-
self._is_ready = False
|
|
45
|
-
self._init_lock = threading.RLock()
|
|
46
|
-
|
|
47
|
-
# 注册内置初始化器
|
|
48
|
-
register_built_in_initializers()
|
|
49
|
-
|
|
50
|
-
self._initialized = True
|
|
51
|
-
|
|
52
|
-
@property
|
|
53
|
-
def context(self) -> Optional[InitializationContext]:
|
|
54
|
-
"""获取初始化上下文"""
|
|
55
|
-
return self._context
|
|
56
|
-
|
|
57
|
-
@property
|
|
58
|
-
def is_ready(self) -> bool:
|
|
59
|
-
"""检查框架是否已准备就绪"""
|
|
60
|
-
return self._is_ready
|
|
61
|
-
|
|
62
|
-
def initialize(self, settings=None, **kwargs) -> Any:
|
|
63
|
-
"""
|
|
64
|
-
执行框架初始化
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
settings: 配置对象
|
|
68
|
-
**kwargs: 额外的配置参数
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
初始化后的配置管理器
|
|
72
|
-
"""
|
|
73
|
-
with self._init_lock:
|
|
74
|
-
# 如果已经初始化完成,直接返回
|
|
75
|
-
if self._is_ready and self._context and self._context.settings:
|
|
76
|
-
return self._context.settings
|
|
77
|
-
|
|
78
|
-
# 创建初始化上下文
|
|
79
|
-
context = InitializationContext()
|
|
80
|
-
context.custom_settings = kwargs
|
|
81
|
-
self._context = context
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
# 执行初始化阶段
|
|
85
|
-
self._execute_initialization_phases(context)
|
|
86
|
-
|
|
87
|
-
# 检查关键阶段是否完成
|
|
88
|
-
if not context.is_phase_completed(InitializationPhase.SETTINGS):
|
|
89
|
-
raise RuntimeError("Settings initialization failed")
|
|
90
|
-
|
|
91
|
-
self._is_ready = True
|
|
92
|
-
context.finish()
|
|
93
|
-
|
|
94
|
-
return context.settings
|
|
95
|
-
|
|
96
|
-
except Exception as e:
|
|
97
|
-
context.add_error(f"Framework initialization failed: {e}")
|
|
98
|
-
context.finish()
|
|
99
|
-
|
|
100
|
-
# 降级策略
|
|
101
|
-
return self._fallback_initialization(settings, **kwargs)
|
|
102
|
-
|
|
103
|
-
def _execute_initialization_phases(self, context: InitializationContext):
|
|
104
|
-
"""执行初始化阶段"""
|
|
105
|
-
registry = get_global_registry()
|
|
106
|
-
execution_order = get_execution_order()
|
|
107
|
-
|
|
108
|
-
# 只执行已注册的阶段
|
|
109
|
-
registered_phases = set(registry.get_all_phases())
|
|
110
|
-
|
|
111
|
-
for phase in execution_order:
|
|
112
|
-
if phase == InitializationPhase.ERROR:
|
|
113
|
-
continue
|
|
114
|
-
|
|
115
|
-
# 只执行已注册的阶段
|
|
116
|
-
if phase not in registered_phases:
|
|
117
|
-
continue
|
|
118
|
-
|
|
119
|
-
context.set_current_phase(phase)
|
|
120
|
-
|
|
121
|
-
# 检查依赖关系
|
|
122
|
-
if not self._check_dependencies(phase, context):
|
|
123
|
-
phase_def = get_phase_definition(phase)
|
|
124
|
-
if not (phase_def and phase_def.optional):
|
|
125
|
-
raise RuntimeError(f"Dependencies not satisfied for phase {phase}")
|
|
126
|
-
else:
|
|
127
|
-
# 可选阶段,跳过
|
|
128
|
-
continue
|
|
129
|
-
|
|
130
|
-
# 执行阶段
|
|
131
|
-
start_time = time.time()
|
|
132
|
-
try:
|
|
133
|
-
result = registry.execute_phase(phase, context)
|
|
134
|
-
result.duration = time.time() - start_time
|
|
135
|
-
|
|
136
|
-
context.mark_phase_completed(phase, result)
|
|
137
|
-
|
|
138
|
-
if not result.success and not self._is_phase_optional(phase):
|
|
139
|
-
raise RuntimeError(f"Phase {phase} failed: {result.error}")
|
|
140
|
-
|
|
141
|
-
except Exception as e:
|
|
142
|
-
duration = time.time() - start_time
|
|
143
|
-
result = PhaseResult(
|
|
144
|
-
phase=phase,
|
|
145
|
-
success=False,
|
|
146
|
-
duration=duration,
|
|
147
|
-
error=e
|
|
148
|
-
)
|
|
149
|
-
context.mark_phase_completed(phase, result)
|
|
150
|
-
|
|
151
|
-
if not self._is_phase_optional(phase):
|
|
152
|
-
raise
|
|
153
|
-
|
|
154
|
-
def _check_dependencies(self, phase: InitializationPhase,
|
|
155
|
-
context: InitializationContext) -> bool:
|
|
156
|
-
"""检查阶段依赖关系"""
|
|
157
|
-
phase_def = get_phase_definition(phase)
|
|
158
|
-
if not phase_def:
|
|
159
|
-
return True
|
|
160
|
-
|
|
161
|
-
for dependency in phase_def.dependencies:
|
|
162
|
-
if not context.is_phase_completed(dependency):
|
|
163
|
-
return False
|
|
164
|
-
|
|
165
|
-
return True
|
|
166
|
-
|
|
167
|
-
def _is_phase_optional(self, phase: InitializationPhase) -> bool:
|
|
168
|
-
"""检查阶段是否可选"""
|
|
169
|
-
phase_def = get_phase_definition(phase)
|
|
170
|
-
return phase_def.optional if phase_def else False
|
|
171
|
-
|
|
172
|
-
def _fallback_initialization(self, settings=None, **kwargs):
|
|
173
|
-
"""降级初始化策略"""
|
|
174
|
-
try:
|
|
175
|
-
# 尝试创建基本的配置管理器
|
|
176
|
-
from crawlo.settings.setting_manager import SettingManager
|
|
177
|
-
|
|
178
|
-
if settings:
|
|
179
|
-
return settings
|
|
180
|
-
else:
|
|
181
|
-
fallback_settings = SettingManager()
|
|
182
|
-
if kwargs:
|
|
183
|
-
fallback_settings.update_attributes(kwargs)
|
|
184
|
-
return fallback_settings
|
|
185
|
-
|
|
186
|
-
except Exception:
|
|
187
|
-
# 如果连降级都失败,返回None
|
|
188
|
-
return None
|
|
189
|
-
|
|
190
|
-
def reset(self):
|
|
191
|
-
"""重置初始化状态(主要用于测试)"""
|
|
192
|
-
with self._init_lock:
|
|
193
|
-
self._context = None
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
核心初始化器 - 协调整个初始化过程
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from typing import Optional, Any
|
|
10
|
+
|
|
11
|
+
from .context import InitializationContext
|
|
12
|
+
from .phases import InitializationPhase, PhaseResult, get_execution_order, get_phase_definition
|
|
13
|
+
from .registry import get_global_registry, BaseInitializer, register_initializer
|
|
14
|
+
from .built_in import register_built_in_initializers
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CoreInitializer:
|
|
18
|
+
"""
|
|
19
|
+
核心初始化器 - 协调整个框架的初始化过程
|
|
20
|
+
|
|
21
|
+
职责:
|
|
22
|
+
1. 管理初始化阶段的执行顺序
|
|
23
|
+
2. 处理阶段间的依赖关系
|
|
24
|
+
3. 提供统一的初始化入口
|
|
25
|
+
4. 错误处理和降级策略
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_instance: Optional['CoreInitializer'] = None
|
|
29
|
+
_lock = threading.Lock()
|
|
30
|
+
|
|
31
|
+
def __new__(cls) -> 'CoreInitializer':
|
|
32
|
+
if cls._instance is None:
|
|
33
|
+
with cls._lock:
|
|
34
|
+
if cls._instance is None:
|
|
35
|
+
cls._instance = super(CoreInitializer, cls).__new__(cls)
|
|
36
|
+
cls._instance._initialized = False
|
|
37
|
+
return cls._instance
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
if hasattr(self, '_initialized') and self._initialized:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
self._context: Optional[InitializationContext] = None
|
|
44
|
+
self._is_ready = False
|
|
45
|
+
self._init_lock = threading.RLock()
|
|
46
|
+
|
|
47
|
+
# 注册内置初始化器
|
|
48
|
+
register_built_in_initializers()
|
|
49
|
+
|
|
50
|
+
self._initialized = True
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def context(self) -> Optional[InitializationContext]:
|
|
54
|
+
"""获取初始化上下文"""
|
|
55
|
+
return self._context
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def is_ready(self) -> bool:
|
|
59
|
+
"""检查框架是否已准备就绪"""
|
|
60
|
+
return self._is_ready
|
|
61
|
+
|
|
62
|
+
def initialize(self, settings=None, **kwargs) -> Any:
|
|
63
|
+
"""
|
|
64
|
+
执行框架初始化
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
settings: 配置对象
|
|
68
|
+
**kwargs: 额外的配置参数
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
初始化后的配置管理器
|
|
72
|
+
"""
|
|
73
|
+
with self._init_lock:
|
|
74
|
+
# 如果已经初始化完成,直接返回
|
|
75
|
+
if self._is_ready and self._context and self._context.settings:
|
|
76
|
+
return self._context.settings
|
|
77
|
+
|
|
78
|
+
# 创建初始化上下文
|
|
79
|
+
context = InitializationContext()
|
|
80
|
+
context.custom_settings = kwargs
|
|
81
|
+
self._context = context
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# 执行初始化阶段
|
|
85
|
+
self._execute_initialization_phases(context)
|
|
86
|
+
|
|
87
|
+
# 检查关键阶段是否完成
|
|
88
|
+
if not context.is_phase_completed(InitializationPhase.SETTINGS):
|
|
89
|
+
raise RuntimeError("Settings initialization failed")
|
|
90
|
+
|
|
91
|
+
self._is_ready = True
|
|
92
|
+
context.finish()
|
|
93
|
+
|
|
94
|
+
return context.settings
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
context.add_error(f"Framework initialization failed: {e}")
|
|
98
|
+
context.finish()
|
|
99
|
+
|
|
100
|
+
# 降级策略
|
|
101
|
+
return self._fallback_initialization(settings, **kwargs)
|
|
102
|
+
|
|
103
|
+
def _execute_initialization_phases(self, context: InitializationContext):
|
|
104
|
+
"""执行初始化阶段"""
|
|
105
|
+
registry = get_global_registry()
|
|
106
|
+
execution_order = get_execution_order()
|
|
107
|
+
|
|
108
|
+
# 只执行已注册的阶段
|
|
109
|
+
registered_phases = set(registry.get_all_phases())
|
|
110
|
+
|
|
111
|
+
for phase in execution_order:
|
|
112
|
+
if phase == InitializationPhase.ERROR:
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
# 只执行已注册的阶段
|
|
116
|
+
if phase not in registered_phases:
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
context.set_current_phase(phase)
|
|
120
|
+
|
|
121
|
+
# 检查依赖关系
|
|
122
|
+
if not self._check_dependencies(phase, context):
|
|
123
|
+
phase_def = get_phase_definition(phase)
|
|
124
|
+
if not (phase_def and phase_def.optional):
|
|
125
|
+
raise RuntimeError(f"Dependencies not satisfied for phase {phase}")
|
|
126
|
+
else:
|
|
127
|
+
# 可选阶段,跳过
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
# 执行阶段
|
|
131
|
+
start_time = time.time()
|
|
132
|
+
try:
|
|
133
|
+
result = registry.execute_phase(phase, context)
|
|
134
|
+
result.duration = time.time() - start_time
|
|
135
|
+
|
|
136
|
+
context.mark_phase_completed(phase, result)
|
|
137
|
+
|
|
138
|
+
if not result.success and not self._is_phase_optional(phase):
|
|
139
|
+
raise RuntimeError(f"Phase {phase} failed: {result.error}")
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
duration = time.time() - start_time
|
|
143
|
+
result = PhaseResult(
|
|
144
|
+
phase=phase,
|
|
145
|
+
success=False,
|
|
146
|
+
duration=duration,
|
|
147
|
+
error=e
|
|
148
|
+
)
|
|
149
|
+
context.mark_phase_completed(phase, result)
|
|
150
|
+
|
|
151
|
+
if not self._is_phase_optional(phase):
|
|
152
|
+
raise
|
|
153
|
+
|
|
154
|
+
def _check_dependencies(self, phase: InitializationPhase,
|
|
155
|
+
context: InitializationContext) -> bool:
|
|
156
|
+
"""检查阶段依赖关系"""
|
|
157
|
+
phase_def = get_phase_definition(phase)
|
|
158
|
+
if not phase_def:
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
for dependency in phase_def.dependencies:
|
|
162
|
+
if not context.is_phase_completed(dependency):
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
def _is_phase_optional(self, phase: InitializationPhase) -> bool:
|
|
168
|
+
"""检查阶段是否可选"""
|
|
169
|
+
phase_def = get_phase_definition(phase)
|
|
170
|
+
return phase_def.optional if phase_def else False
|
|
171
|
+
|
|
172
|
+
def _fallback_initialization(self, settings=None, **kwargs):
|
|
173
|
+
"""降级初始化策略"""
|
|
174
|
+
try:
|
|
175
|
+
# 尝试创建基本的配置管理器
|
|
176
|
+
from crawlo.settings.setting_manager import SettingManager
|
|
177
|
+
|
|
178
|
+
if settings:
|
|
179
|
+
return settings
|
|
180
|
+
else:
|
|
181
|
+
fallback_settings = SettingManager()
|
|
182
|
+
if kwargs:
|
|
183
|
+
fallback_settings.update_attributes(kwargs)
|
|
184
|
+
return fallback_settings
|
|
185
|
+
|
|
186
|
+
except Exception:
|
|
187
|
+
# 如果连降级都失败,返回None
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
def reset(self):
|
|
191
|
+
"""重置初始化状态(主要用于测试)"""
|
|
192
|
+
with self._init_lock:
|
|
193
|
+
self._context = None
|
|
194
194
|
self._is_ready = False
|