sycommon-python-lib 0.1.46__py3-none-any.whl → 0.1.57b1__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.
Files changed (51) hide show
  1. sycommon/config/Config.py +29 -4
  2. sycommon/config/LangfuseConfig.py +15 -0
  3. sycommon/config/RerankerConfig.py +1 -0
  4. sycommon/config/SentryConfig.py +13 -0
  5. sycommon/database/async_base_db_service.py +36 -0
  6. sycommon/database/async_database_service.py +96 -0
  7. sycommon/llm/__init__.py +0 -0
  8. sycommon/llm/embedding.py +204 -0
  9. sycommon/llm/get_llm.py +37 -0
  10. sycommon/llm/llm_logger.py +126 -0
  11. sycommon/llm/llm_tokens.py +119 -0
  12. sycommon/llm/struct_token.py +192 -0
  13. sycommon/llm/sy_langfuse.py +103 -0
  14. sycommon/llm/usage_token.py +117 -0
  15. sycommon/logging/async_sql_logger.py +65 -0
  16. sycommon/logging/kafka_log.py +200 -434
  17. sycommon/logging/logger_levels.py +23 -0
  18. sycommon/middleware/context.py +2 -0
  19. sycommon/middleware/exception.py +10 -16
  20. sycommon/middleware/timeout.py +2 -1
  21. sycommon/middleware/traceid.py +179 -51
  22. sycommon/notice/__init__.py +0 -0
  23. sycommon/notice/uvicorn_monitor.py +200 -0
  24. sycommon/rabbitmq/rabbitmq_client.py +267 -290
  25. sycommon/rabbitmq/rabbitmq_pool.py +277 -465
  26. sycommon/rabbitmq/rabbitmq_service.py +23 -891
  27. sycommon/rabbitmq/rabbitmq_service_client_manager.py +211 -0
  28. sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +73 -0
  29. sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +285 -0
  30. sycommon/rabbitmq/rabbitmq_service_core.py +117 -0
  31. sycommon/rabbitmq/rabbitmq_service_producer_manager.py +238 -0
  32. sycommon/sentry/__init__.py +0 -0
  33. sycommon/sentry/sy_sentry.py +35 -0
  34. sycommon/services.py +144 -115
  35. sycommon/synacos/feign.py +18 -7
  36. sycommon/synacos/feign_client.py +26 -8
  37. sycommon/synacos/nacos_client_base.py +119 -0
  38. sycommon/synacos/nacos_config_manager.py +107 -0
  39. sycommon/synacos/nacos_heartbeat_manager.py +144 -0
  40. sycommon/synacos/nacos_service.py +65 -769
  41. sycommon/synacos/nacos_service_discovery.py +157 -0
  42. sycommon/synacos/nacos_service_registration.py +270 -0
  43. sycommon/tools/env.py +62 -0
  44. sycommon/tools/merge_headers.py +117 -0
  45. sycommon/tools/snowflake.py +238 -23
  46. {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/METADATA +18 -11
  47. sycommon_python_lib-0.1.57b1.dist-info/RECORD +89 -0
  48. sycommon_python_lib-0.1.46.dist-info/RECORD +0 -59
  49. {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/WHEEL +0 -0
  50. {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/entry_points.txt +0 -0
  51. {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,157 @@
1
+ import threading
2
+ from typing import List, Dict
3
+ from sycommon.logging.kafka_log import SYLogger
4
+ from sycommon.synacos.nacos_client_base import NacosClientBase
5
+ from sycommon.synacos.nacos_service_registration import NacosServiceRegistration
6
+
7
+
8
+ class NacosServiceDiscovery:
9
+ """Nacos服务发现类 - 负责服务实例发现和轮询"""
10
+
11
+ def __init__(self, client_base: NacosClientBase):
12
+ self.client_base = client_base
13
+
14
+ # 轮询管理
15
+ self._round_robin_index = 0
16
+ self._round_robin_lock = threading.Lock()
17
+
18
+ # 连接监控配置
19
+ self.connection_check_interval = self.client_base.nacos_config.get(
20
+ 'connectionCheckInterval', 30)
21
+ self._monitor_thread_started = False
22
+ self._monitor_thread_lock = threading.Lock()
23
+
24
+ def discover_services(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
25
+ """发现服务实例列表 (与Java格式兼容)"""
26
+ if not self.client_base.ensure_client_connected():
27
+ return []
28
+
29
+ return self.get_service_instances(service_name, group, version)
30
+
31
+ def get_service_instances(self, service_name: str, group: str = "DEFAULT_GROUP", target_version: str = None) -> List[Dict]:
32
+ """
33
+ 获取服务实例列表,并按照以下优先级规则筛选:
34
+ 1. 相同版本号的实例
35
+ 2. 无版本号的实例
36
+ 3. 所有实例中轮询
37
+ """
38
+ try:
39
+ namespace_id = self.client_base.nacos_config['namespaceId']
40
+ instances = self.client_base.nacos_client.list_naming_instance(
41
+ service_name,
42
+ namespace_id=namespace_id,
43
+ group_name=group,
44
+ healthy_only=True,
45
+ )
46
+
47
+ if not instances or 'hosts' not in instances:
48
+ SYLogger.info(f"nacos:未发现 {service_name} 的服务实例")
49
+ return []
50
+
51
+ all_instances = instances.get('hosts', [])
52
+ all_instances = [
53
+ instance for instance in all_instances
54
+ if instance.get('enabled', True)
55
+ ]
56
+ SYLogger.info(
57
+ f"nacos:共发现 {len(all_instances)} 个 {service_name} 服务实例")
58
+
59
+ version_to_use = target_version
60
+
61
+ if version_to_use:
62
+ same_version_instances = [
63
+ instance for instance in all_instances
64
+ if instance.get('metadata', {}).get('version') == version_to_use
65
+ ]
66
+
67
+ if same_version_instances:
68
+ SYLogger.info(
69
+ f"nacos:筛选出 {len(same_version_instances)} 个与当前版本({version_to_use})匹配的实例")
70
+ return same_version_instances
71
+
72
+ no_version_instances = [
73
+ instance for instance in all_instances
74
+ if 'version' not in instance.get('metadata', {})
75
+ ]
76
+
77
+ if no_version_instances:
78
+ SYLogger.info(
79
+ f"nacos:未找到相同版本({version_to_use})的实例,筛选出 {len(no_version_instances)} 个无版本号的实例")
80
+ return no_version_instances
81
+ else:
82
+ no_version_instances = [
83
+ instance for instance in all_instances
84
+ if 'version' not in instance.get('metadata', {})
85
+ ]
86
+
87
+ if no_version_instances:
88
+ # 从通用实例中轮询
89
+ with self._round_robin_lock:
90
+ selected_index = self._round_robin_index % len(
91
+ no_version_instances)
92
+ self._round_robin_index = (
93
+ selected_index + 1) % len(no_version_instances)
94
+
95
+ SYLogger.info(
96
+ f"nacos:无版本请求,从 {len(no_version_instances)} 个通用实例中选择")
97
+ return [no_version_instances[selected_index]]
98
+
99
+ SYLogger.info(
100
+ f"nacos:使用轮询方式从 {len(all_instances)} 个实例中选择")
101
+
102
+ with self._round_robin_lock:
103
+ selected_index = self._round_robin_index % len(all_instances)
104
+ self._round_robin_index = (
105
+ selected_index + 1) % len(all_instances)
106
+
107
+ return [all_instances[selected_index]]
108
+
109
+ except Exception as e:
110
+ SYLogger.error(f"nacos:服务发现失败: {service_name}: {str(e)}")
111
+ return []
112
+
113
+ def monitor_connection(self, registration: NacosServiceRegistration):
114
+ """连接监控线程"""
115
+ with self._monitor_thread_lock:
116
+ if self.client_base._shutdown_event.is_set() or self._monitor_thread_started:
117
+ SYLogger.warning("nacos:监控线程已启动/已关闭,拒绝重复启动")
118
+ return
119
+ self._monitor_thread_started = True
120
+
121
+ check_interval = self.connection_check_interval
122
+ SYLogger.info(
123
+ f"nacos:连接监控线程启动 - 线程ID: {threading.current_thread().ident}")
124
+
125
+ while not self.client_base._shutdown_event.is_set():
126
+ try:
127
+ if not self.client_base.is_connected:
128
+ SYLogger.warning("nacos:客户端未连接,尝试重新初始化")
129
+ self.client_base.ensure_client_connected()
130
+ else:
131
+ current_registered = registration.check_service_registered()
132
+
133
+ if current_registered != registration.registered:
134
+ registration.registered = current_registered
135
+ if not current_registered:
136
+ SYLogger.warning("nacos:服务实例未注册,触发单次重新注册")
137
+ retry_thread = threading.Thread(
138
+ target=registration.register,
139
+ args=(True,),
140
+ daemon=True,
141
+ name="NacosSingleRetryThread"
142
+ )
143
+ retry_thread.start()
144
+ else:
145
+ SYLogger.info("nacos:服务实例已注册,触发单次验证")
146
+ registration.verify_registration()
147
+
148
+ self.client_base._shutdown_event.wait(check_interval)
149
+
150
+ except Exception as e:
151
+ SYLogger.error(f"nacos:连接监控异常: {str(e)}")
152
+ self.client_base._shutdown_event.wait(5)
153
+
154
+ with self._monitor_thread_lock:
155
+ self._monitor_thread_started = False
156
+ SYLogger.info(
157
+ f"nacos:连接监控线程退出 - 线程ID: {threading.current_thread().ident}")
@@ -0,0 +1,270 @@
1
+ import threading
2
+ import time
3
+ import atexit
4
+ from sycommon.logging.kafka_log import SYLogger
5
+ from sycommon.synacos.nacos_client_base import NacosClientBase
6
+
7
+
8
+ class NacosServiceRegistration:
9
+ """Nacos服务注册类 - 负责服务注册、注销和状态验证"""
10
+
11
+ def __init__(self, client_base: NacosClientBase, service_name: str, real_ip: str, port: int, version: str):
12
+ self.client_base = client_base
13
+ self.service_name = service_name
14
+ self.real_ip = real_ip
15
+ self.port = port
16
+ self.version = version
17
+
18
+ # 注册配置
19
+ self.register_retry_interval = self.client_base.nacos_config.get(
20
+ 'registerRetryInterval', 15)
21
+ self.long_term_retry_delay = self.client_base.nacos_config.get(
22
+ 'longTermRetryDelay', 30)
23
+ self.max_long_term_retries = self.client_base.nacos_config.get(
24
+ 'maxLongTermRetries', -1)
25
+
26
+ # 验证配置
27
+ self.registration_verify_count = self.client_base.nacos_config.get(
28
+ 'registrationVerifyCount', 1)
29
+ self.registration_verify_interval = self.client_base.nacos_config.get(
30
+ 'registrationVerifyInterval', 1)
31
+ self.registration_post_delay = self.client_base.nacos_config.get(
32
+ 'registrationPostDelay', 3)
33
+
34
+ # 状态管理
35
+ self.registered = False
36
+ self._long_term_retry_count = 0
37
+ self._verify_lock = threading.Lock()
38
+ self._last_verify_time = 0
39
+ self._atexit_registered = False
40
+
41
+ def _cleanup_stale_instance(self):
42
+ """清理可能存在的残留实例"""
43
+ if not self.client_base.is_connected:
44
+ return
45
+
46
+ try:
47
+ self.client_base.nacos_client.remove_naming_instance(
48
+ service_name=self.service_name,
49
+ ip=self.real_ip,
50
+ port=int(self.port),
51
+ cluster_name="DEFAULT"
52
+ )
53
+ SYLogger.warning(f"nacos:清理残留实例: {self.real_ip}:{self.port}")
54
+ except Exception as e:
55
+ SYLogger.error(f"nacos:清理残留实例异常: {e}")
56
+
57
+ def check_service_registered(self) -> bool:
58
+ """检查服务是否已注册(基于实例列表)"""
59
+ if not self.client_base.enable_register_nacos:
60
+ return True
61
+
62
+ if not self.client_base.ensure_client_connected():
63
+ return False
64
+
65
+ try:
66
+ namespace_id = self.client_base.nacos_config['namespaceId']
67
+ instances = self.client_base.nacos_client.list_naming_instance(
68
+ service_name=self.service_name,
69
+ namespace_id=namespace_id,
70
+ group_name="DEFAULT_GROUP",
71
+ healthy_only=True,
72
+ )
73
+
74
+ found = False
75
+ for instance in instances.get('hosts', []):
76
+ if (instance.get('ip') == self.real_ip and
77
+ instance.get('port') == int(self.port)):
78
+ SYLogger.info(f"nacos:找到已注册实例: {self.real_ip}:{self.port}")
79
+ found = True
80
+ break
81
+
82
+ if not found:
83
+ SYLogger.warning(f"nacos:未找到注册实例: {self.real_ip}:{self.port}")
84
+
85
+ self.registered = found
86
+ return found
87
+ except Exception as e:
88
+ SYLogger.error(f"nacos:检查服务注册状态失败: {e}")
89
+ return False
90
+
91
+ def verify_registration(self) -> bool:
92
+ """多次验证服务是否成功注册(加锁防止重复执行)"""
93
+ if self._verify_lock.locked():
94
+ SYLogger.warning("nacos:注册验证已在执行中,跳过重复调用")
95
+ return self.registered
96
+
97
+ with self._verify_lock:
98
+ current_time = time.time()
99
+ if current_time - self._last_verify_time < 15: # 15秒冷却
100
+ return True
101
+
102
+ success_count = 0
103
+ verify_count = self.registration_verify_count
104
+ SYLogger.info(
105
+ f"nacos:开始验证服务注册状态,共验证 {verify_count} 次,间隔 {self.registration_verify_interval} 秒")
106
+
107
+ self._last_verify_time = current_time
108
+
109
+ for i in range(verify_count):
110
+ if self.check_service_registered():
111
+ success_count += 1
112
+
113
+ if i < verify_count - 1:
114
+ self.client_base._shutdown_event.wait(
115
+ self.registration_verify_interval)
116
+ if self.client_base._shutdown_event.is_set():
117
+ SYLogger.warning("nacos:应用正在关闭,终止注册验证")
118
+ break
119
+
120
+ pass_threshold = verify_count / 2
121
+ result = success_count >= pass_threshold
122
+
123
+ if result:
124
+ SYLogger.info(
125
+ f"nacos:服务注册验证成功,{success_count}/{verify_count} 次验证通过")
126
+ else:
127
+ SYLogger.error(
128
+ f"nacos:服务注册验证失败,仅 {success_count}/{verify_count} 次验证通过")
129
+
130
+ return result
131
+
132
+ def register(self, force: bool = False) -> bool:
133
+ """注册服务到Nacos"""
134
+ if self.registered and not force and self.check_service_registered():
135
+ return True
136
+
137
+ if self.registered and not force:
138
+ self.registered = False
139
+ SYLogger.warning("nacos:本地状态显示已注册,但Nacos中未找到服务实例,准备重新注册")
140
+
141
+ metadata = {
142
+ "ignore-metrics": "true",
143
+ }
144
+ if self.version:
145
+ metadata["version"] = self.version
146
+
147
+ for attempt in range(self.client_base.max_retries):
148
+ if not self.client_base.ensure_client_connected():
149
+ return False
150
+
151
+ try:
152
+ self.client_base.nacos_client.add_naming_instance(
153
+ service_name=self.service_name,
154
+ ip=self.real_ip,
155
+ port=int(self.port),
156
+ metadata=metadata,
157
+ cluster_name="DEFAULT",
158
+ healthy=True,
159
+ ephemeral=True,
160
+ heartbeat_interval=15 # 心跳间隔默认15秒
161
+ )
162
+ SYLogger.info(
163
+ f"nacos:服务 {self.service_name} 注册请求已发送: {self.real_ip}:{self.port}")
164
+
165
+ if not self._atexit_registered:
166
+ atexit.register(self.deregister_service)
167
+ self._atexit_registered = True
168
+
169
+ return True
170
+ except Exception as e:
171
+ if "signal only works in main thread" in str(e):
172
+ return True
173
+ elif attempt < self.client_base.max_retries - 1:
174
+ SYLogger.warning(
175
+ f"nacos:服务注册失败 (尝试 {attempt+1}/{self.client_base.max_retries}): {e}")
176
+ time.sleep(self.client_base.retry_delay)
177
+ else:
178
+ SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {e}")
179
+ return False
180
+
181
+ def register_with_retry(self) -> bool:
182
+ """带重试机制的服务注册(基于实例列表检查)"""
183
+ retry_count = 0
184
+ last_error = None
185
+ self.registered = False
186
+ # 首次注册尝试标记
187
+ first_attempt = True
188
+
189
+ while (not self.registered) and (self.max_long_term_retries < 0 or retry_count < self.max_long_term_retries):
190
+ if self.registered:
191
+ return True
192
+
193
+ try:
194
+ register_success = self.register(force=True)
195
+ if not register_success:
196
+ raise RuntimeError("nacos:服务注册请求失败")
197
+
198
+ SYLogger.info(
199
+ f"nacos:服务注册请求已发送,{'首次启动信任注册,跳过阻塞验证' if first_attempt else f'延迟 {self.registration_post_delay} 秒后开始验证'}")
200
+
201
+ # 核心逻辑:首次注册跳过阻塞验证,非首次按原逻辑
202
+ if first_attempt:
203
+ # 首次注册:直接标记成功,不阻塞
204
+ self.registered = True
205
+ self.client_base._client_initialized = True
206
+ self.client_base._shutdown_event.set()
207
+ self.client_base._shutdown_event.clear()
208
+ self._long_term_retry_count = 0
209
+
210
+ SYLogger.info(f"nacos:首次启动信任注册成功: {self.service_name}")
211
+ first_attempt = False # 标记为非首次
212
+ return True
213
+ else:
214
+ # 非首次/重试:保留原有阻塞验证逻辑
215
+ time.sleep(self.registration_post_delay)
216
+ registered = self.verify_registration()
217
+ self.registered = registered
218
+
219
+ if self.registered:
220
+ self.client_base._client_initialized = True
221
+ self.client_base._shutdown_event.set()
222
+ self.client_base._shutdown_event.clear()
223
+ self._long_term_retry_count = 0
224
+
225
+ SYLogger.info(
226
+ f"nacos:服务注册成功并通过验证: {self.service_name}")
227
+ return True
228
+ else:
229
+ raise RuntimeError("nacos:服务注册验证失败")
230
+
231
+ except Exception as e:
232
+ last_error = str(e)
233
+ retry_count += 1
234
+ first_attempt = False # 失败后标记为非首次
235
+ delay = min(self.register_retry_interval,
236
+ self.client_base.max_retry_delay)
237
+
238
+ SYLogger.warning(
239
+ f"nacos:服务注册尝试 {retry_count} 失败: {last_error},{delay}秒后重试")
240
+ time.sleep(delay)
241
+
242
+ if self.registered:
243
+ return True
244
+
245
+ if last_error:
246
+ SYLogger.error(f"nacos:服务注册失败,最终错误: {last_error}")
247
+ else:
248
+ SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {self.service_name}")
249
+
250
+ return False
251
+
252
+ def deregister_service(self):
253
+ """从Nacos注销服务"""
254
+ if not self.registered or not self.client_base.is_connected:
255
+ return
256
+
257
+ SYLogger.info("nacos:正在注销服务...")
258
+ try:
259
+ self.client_base.nacos_client.remove_naming_instance(
260
+ service_name=self.service_name,
261
+ ip=self.real_ip,
262
+ port=int(self.port),
263
+ cluster_name="DEFAULT"
264
+ )
265
+ self.registered = False
266
+ SYLogger.info(f"nacos:服务 {self.service_name} 已注销")
267
+ except Exception as e:
268
+ SYLogger.error(f"nacos:注销服务时发生错误: {e}")
269
+ finally:
270
+ self.client_base._shutdown_event.set()
sycommon/tools/env.py ADDED
@@ -0,0 +1,62 @@
1
+ import os
2
+
3
+
4
+ def _normalize_env_key(key: str) -> str:
5
+ """
6
+ 环境变量名标准化:
7
+ 1. 转小写
8
+ 2. 中划线(-)和下划线(_)统一替换为下划线(_)
9
+ :param key: 原始环境变量名
10
+ :return: 标准化后的key
11
+ """
12
+ return key.lower().replace('-', '_')
13
+
14
+
15
+ def check_env_flag(target_keys: list, default: str = 'false') -> bool:
16
+ """
17
+ 检查环境变量是否为"true"(自动兼容:大小写、中划线/下划线)
18
+ :param target_keys: 目标变量名列表(如 ['REGISTER-NACOS'],无需传双key)
19
+ :param default: 默认值(未找到变量时使用)
20
+ :return: 布尔值
21
+ """
22
+ # 1. 标准化目标key(小写+统一下划线)
23
+ target_keys_normalized = [_normalize_env_key(k) for k in target_keys]
24
+
25
+ # 2. 遍历所有环境变量,标准化后匹配
26
+ for env_key, env_val in os.environ.items():
27
+ env_key_normalized = _normalize_env_key(env_key)
28
+ if env_key_normalized in target_keys_normalized:
29
+ # 3. 值去空格 + 转小写 判断
30
+ return env_val.strip().lower() == 'true'
31
+
32
+ # 4. 未找到变量时,判断默认值
33
+ return default.strip().lower() == 'true'
34
+
35
+
36
+ def get_env_var(key: str, default='', case_insensitive: bool = True) -> str:
37
+ """
38
+ 获取环境变量值(自动兼容:大小写、中划线/下划线)
39
+ :param key: 目标环境变量名(如 'REGISTER-NACOS'/'version')
40
+ :param default: 无匹配时的默认值,默认空字符串
41
+ :param case_insensitive: 是否忽略变量名大小写(默认True,建议保持)
42
+ :return: 匹配到的环境变量值 / 默认值
43
+ """
44
+ if case_insensitive:
45
+ # 标准化目标key(小写+统一下划线)
46
+ target_key_normalized = _normalize_env_key(key)
47
+
48
+ # 遍历环境变量,标准化后匹配
49
+ for env_key, env_val in os.environ.items():
50
+ env_key_normalized = _normalize_env_key(env_key)
51
+ if env_key_normalized == target_key_normalized:
52
+ return env_val
53
+ return default
54
+ else:
55
+ # 不忽略大小写时,仅自动兼容中划线/下划线
56
+ target_key_1 = key # 原始key
57
+ target_key_2 = key.replace(
58
+ '-', '_') if '-' in key else key.replace('_', '-') # 替换格式的key
59
+ val = os.getenv(target_key_1)
60
+ if val is not None:
61
+ return val
62
+ return os.getenv(target_key_2, default)
@@ -0,0 +1,117 @@
1
+ def merge_headers(
2
+ source_headers, # 来源headers(支持多种格式:字典/MutableHeaders/键值对列表/元组)
3
+ target_headers, # 目标headers(原有值需保留,同名覆盖source)
4
+ keep_keys=None, # 需保留的key集合(None表示保留所有)
5
+ delete_keys={'content-length', 'accept',
6
+ 'content-type', 'sec-fetch-mode',
7
+ 'sec-fetch-dest', 'sec-fetch-site',
8
+ 'pragma', 'cache-control',
9
+ 'accept-encoding', 'priority'}, # 需删除的source key集合
10
+ encoding='utf-8' # 字符编码(处理bytes转换)
11
+ ) -> dict:
12
+ """
13
+ 合并headers,最终规则:
14
+ 1. 所有key统一转为小写进行比较判断(完全大小写无关)
15
+ 2. target_headers 同名key 完全覆盖 source_headers(source同名key不生效)
16
+ 3. delete_keys 作用于source_headers:source中所有该列表内的key一律不添加(无论是否新增)
17
+ 4. target_headers 中的key即使在delete_keys也始终保留,不受删除规则影响
18
+ 5. 自动处理bytes/其他类型的键值转换为字符串
19
+ 6. 最终输出的key全部为小写
20
+ """
21
+ # 初始化并统一转为小写集合
22
+ keep_keys = {k.lower() for k in keep_keys} if keep_keys else set()
23
+ delete_keys = {k.lower() for k in delete_keys} if delete_keys else set()
24
+
25
+ # 修复1:兼容 MutableHeaders/普通字典/None 等 target_headers 类型
26
+ if target_headers is None:
27
+ target_dict = {}
28
+ elif hasattr(target_headers, 'items'):
29
+ # 支持 MutableHeaders/Headers/普通字典(都有items()方法)
30
+ target_dict = dict(target_headers.items())
31
+ else:
32
+ # 兜底:可迭代对象转为字典
33
+ target_dict = dict(target_headers) if isinstance(
34
+ target_headers, (list, tuple)) else {}
35
+
36
+ # 标准化target_headers:key转为小写,保留原有值
37
+ processed_headers = {k.lower(): v for k, v in target_dict.items()}
38
+ target_original_keys = set(processed_headers.keys())
39
+
40
+ # 修复2:统一处理 source_headers 格式,确保是键值对迭代器
41
+ # 步骤1:将source_headers转为标准的键值对列表
42
+ if source_headers is None:
43
+ source_kv_list = []
44
+ elif hasattr(source_headers, 'items'):
45
+ # 字典/MutableHeaders → 转为键值对列表
46
+ source_kv_list = list(source_headers.items())
47
+ elif isinstance(source_headers, (list, tuple)):
48
+ # 列表/元组 → 校验并过滤合法的键值对(仅保留长度为2的元组/列表)
49
+ source_kv_list = []
50
+ for item in source_headers:
51
+ if isinstance(item, (list, tuple)) and len(item) == 2:
52
+ source_kv_list.append(item)
53
+ else:
54
+ # 跳过非法格式(如长度≠2的元素),避免解包报错
55
+ continue
56
+ else:
57
+ # 其他类型 → 空列表(避免迭代报错)
58
+ source_kv_list = []
59
+
60
+ # 处理来源headers的键值转换和合并(遍历标准化后的键值对)
61
+ for key, value in source_kv_list:
62
+ # 转换key为字符串并统一转为小写(判断用)
63
+ if not isinstance(key, str):
64
+ try:
65
+ key = key.decode(encoding, errors='replace') if isinstance(
66
+ key, bytes) else str(key)
67
+ except Exception:
68
+ # 极端情况:无法转换的key直接跳过
69
+ continue
70
+
71
+ key_lower = key.lower()
72
+
73
+ # 转换value为字符串
74
+ if not isinstance(value, str):
75
+ try:
76
+ value = value.decode(encoding, errors='replace') if isinstance(
77
+ value, bytes) else str(value)
78
+ except Exception:
79
+ # 无法转换的value设为空字符串
80
+ value = ""
81
+
82
+ # 过滤1:source的key在删除列表 → 直接跳过
83
+ if key_lower in delete_keys:
84
+ continue
85
+
86
+ # 过滤2:仅保留指定的key(如果设置了keep_keys)
87
+ if keep_keys and key_lower not in keep_keys:
88
+ continue
89
+
90
+ # 过滤3:target已有同名key → 直接跳过(target值覆盖source)
91
+ if key_lower in target_original_keys:
92
+ continue
93
+
94
+ # 仅添加符合条件的key-value(最终key为小写)
95
+ processed_headers[key_lower] = value
96
+
97
+ return processed_headers
98
+
99
+
100
+ def get_header_value(headers: list, target_key: str, default=None):
101
+ """
102
+ 从列表中查找指定 header 的值
103
+
104
+ Args:
105
+ headers: header 列表,例如 [('Content-Type', 'application/json'), ...]
106
+ target_key: 要查找的 key
107
+ default: 如果没找到返回的默认值
108
+ """
109
+ if not headers:
110
+ return default
111
+
112
+ for item in headers:
113
+ # 兼容 list 和 tuple,确保长度为2
114
+ if isinstance(item, (list, tuple)) and len(item) == 2 and item[0] == target_key:
115
+ return item[1]
116
+
117
+ return default