sycommon-python-lib 0.1.16__py3-none-any.whl → 0.1.56b1__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 (36) hide show
  1. sycommon/config/Config.py +6 -2
  2. sycommon/config/RerankerConfig.py +1 -0
  3. sycommon/database/async_base_db_service.py +36 -0
  4. sycommon/database/async_database_service.py +96 -0
  5. sycommon/database/database_service.py +6 -1
  6. sycommon/health/metrics.py +13 -0
  7. sycommon/llm/__init__.py +0 -0
  8. sycommon/llm/embedding.py +149 -0
  9. sycommon/llm/get_llm.py +177 -0
  10. sycommon/llm/llm_logger.py +126 -0
  11. sycommon/logging/async_sql_logger.py +65 -0
  12. sycommon/logging/kafka_log.py +36 -14
  13. sycommon/logging/logger_levels.py +23 -0
  14. sycommon/logging/sql_logger.py +53 -0
  15. sycommon/middleware/context.py +2 -0
  16. sycommon/middleware/middleware.py +4 -0
  17. sycommon/middleware/traceid.py +155 -32
  18. sycommon/models/mqlistener_config.py +1 -0
  19. sycommon/rabbitmq/rabbitmq_client.py +377 -821
  20. sycommon/rabbitmq/rabbitmq_pool.py +338 -0
  21. sycommon/rabbitmq/rabbitmq_service.py +411 -229
  22. sycommon/services.py +116 -61
  23. sycommon/synacos/example.py +153 -0
  24. sycommon/synacos/example2.py +129 -0
  25. sycommon/synacos/feign.py +90 -413
  26. sycommon/synacos/feign_client.py +335 -0
  27. sycommon/synacos/nacos_service.py +159 -106
  28. sycommon/synacos/param.py +75 -0
  29. sycommon/tools/merge_headers.py +97 -0
  30. sycommon/tools/snowflake.py +296 -7
  31. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/METADATA +19 -13
  32. sycommon_python_lib-0.1.56b1.dist-info/RECORD +68 -0
  33. sycommon_python_lib-0.1.16.dist-info/RECORD +0 -52
  34. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/WHEEL +0 -0
  35. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/entry_points.txt +0 -0
  36. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import threading
2
3
  import json
3
4
  from typing import Callable, Dict, List, Optional
@@ -10,9 +11,8 @@ import yaml
10
11
  import time
11
12
  import atexit
12
13
  import random
13
- from concurrent.futures import ThreadPoolExecutor
14
14
 
15
- from sycommon.config.Config import SingletonMeta
15
+ from sycommon.config.Config import Config, SingletonMeta
16
16
  from sycommon.logging.kafka_log import SYLogger
17
17
 
18
18
 
@@ -34,22 +34,17 @@ class NacosService(metaclass=SingletonMeta):
34
34
  # 添加可重入锁用于状态同步
35
35
  self._state_lock = threading.RLock()
36
36
 
37
- # 优化线程池配置,增加工作线程数量
38
- self._executor = ThreadPoolExecutor(max_workers=1) # 主线程池
39
- self._heartbeat_executor = ThreadPoolExecutor(
40
- max_workers=1) # 增加心跳线程
41
- self._monitor_executor = ThreadPoolExecutor(
42
- max_workers=1) # 增加监控线程
43
-
44
37
  # 配置参数
45
38
  self.max_retries = self.nacos_config.get('maxRetries', 5)
46
- self.retry_delay = self.nacos_config.get('retryDelay', 1)
47
- self.retry_backoff = self.nacos_config.get('retryBackoff', 1.5)
39
+ self.retry_delay = self.nacos_config.get('retryDelay', 5)
48
40
  self.max_retry_delay = self.nacos_config.get('maxRetryDelay', 30)
41
+ # 心跳间隔:优先从配置读取,默认15秒(可通过配置修改)
49
42
  self.heartbeat_interval = self.nacos_config.get(
50
- 'heartbeatInterval', 5)
43
+ 'heartbeatInterval', 15)
44
+ # 心跳超时:固定设置为15秒
45
+ self.heartbeat_timeout = 15
51
46
  self.register_retry_interval = self.nacos_config.get(
52
- 'registerRetryInterval', 5) # 注册重试间隔
47
+ 'registerRetryInterval', 15) # 注册重试间隔
53
48
 
54
49
  # 长期重试配置
55
50
  self.long_term_retry_delay = self.nacos_config.get(
@@ -57,11 +52,13 @@ class NacosService(metaclass=SingletonMeta):
57
52
  self.max_long_term_retries = self.nacos_config.get(
58
53
  'maxLongTermRetries', -1) # -1表示无限重试
59
54
 
60
- # 注册验证配置
55
+ # 注册验证配置:优化默认值(增加次数+延长间隔)
61
56
  self.registration_verify_count = self.nacos_config.get(
62
57
  'registrationVerifyCount', 1) # 验证次数
63
58
  self.registration_verify_interval = self.nacos_config.get(
64
59
  'registrationVerifyInterval', 1) # 验证间隔
60
+ self.registration_post_delay = self.nacos_config.get(
61
+ 'registrationPostDelay', 3) # 注册后延迟3秒再开始验证
65
62
 
66
63
  self.real_ip = self.get_service_ip(self.host)
67
64
  self._long_term_retry_count = 0 # 长期重试计数器
@@ -79,6 +76,8 @@ class NacosService(metaclass=SingletonMeta):
79
76
  SYLogger.info("nacos:本地开发模式,不初始化Nacos客户端")
80
77
 
81
78
  self.share_configs = self.read_configs()
79
+ # 设置llm相关配置
80
+ Config().set_attr(self.share_configs)
82
81
 
83
82
  # 配置监听器
84
83
  self._config_listeners = {}
@@ -87,10 +86,18 @@ class NacosService(metaclass=SingletonMeta):
87
86
  # 心跳相关
88
87
  self._last_heartbeat_time = 0
89
88
  self._heartbeat_fail_count = 0
89
+ self._heartbeat_lock = threading.Lock()
90
90
  self._heartbeat_thread = None
91
+
91
92
  self.max_heartbeat_timeout = self.nacos_config.get(
92
- 'maxHeartbeatTimeout', 30) # 最大无心跳时间(秒)
93
- self._last_successful_heartbeat = time.time() # 上次成功心跳时间戳
93
+ 'maxHeartbeatTimeout', 30)
94
+ self._last_successful_heartbeat = time.time()
95
+ # 连接监控检查间隔(新增配置,默认30秒,避免硬编码)
96
+ self.connection_check_interval = self.nacos_config.get(
97
+ 'connectionCheckInterval', 30)
98
+ # 配置监视线程检查间隔(默认30秒)
99
+ self.config_watch_interval = self.nacos_config.get(
100
+ 'configWatchInterval', 30)
94
101
 
95
102
  # 启动配置监视线程
96
103
  self._watch_thread = threading.Thread(
@@ -121,8 +128,7 @@ class NacosService(metaclass=SingletonMeta):
121
128
  self._client_initialized = True
122
129
  return True
123
130
  except Exception as e:
124
- delay = min(self.retry_delay * (self.retry_backoff **
125
- attempt), self.max_retry_delay)
131
+ delay = min(self.retry_delay, self.max_retry_delay)
126
132
  SYLogger.error(
127
133
  f"nacos:客户端初始化失败 (尝试 {attempt+1}/{self.max_retries}): {e}")
128
134
  time.sleep(delay)
@@ -185,8 +191,7 @@ class NacosService(metaclass=SingletonMeta):
185
191
 
186
192
  except Exception as e:
187
193
  attempt += 1
188
- delay = min(self.retry_delay * (self.retry_backoff **
189
- (attempt - 1)), self.max_retry_delay)
194
+ delay = min(self.retry_delay, self.max_retry_delay)
190
195
 
191
196
  SYLogger.error(
192
197
  f"nacos:客户端初始化失败 (尝试 {attempt}/{max_attempts}): {e}")
@@ -300,12 +305,12 @@ class NacosService(metaclass=SingletonMeta):
300
305
  if not register_success:
301
306
  raise RuntimeError("nacos:服务注册请求失败")
302
307
 
303
- # 注册请求发送成功后,等待一小段时间让Nacos服务器处理
308
+ # 关键优化1:注册请求发送后,延迟一段时间再验证(默认3秒)
304
309
  SYLogger.info(
305
- f"nacos:服务注册请求已发送,等待 {self.registration_verify_interval} 秒后验证")
306
- time.sleep(self.registration_verify_interval)
310
+ f"nacos:服务注册请求已发送,延迟 {self.registration_post_delay} 秒后开始验证(确保Nacos服务器完成实例写入)")
311
+ time.sleep(self.registration_post_delay)
307
312
 
308
- # 多次验证服务是否真正注册成功
313
+ # 关键优化2:多次验证服务是否真正注册成功(默认3次,每次间隔2秒)
309
314
  registered = self.verify_registration()
310
315
 
311
316
  # 带锁更新注册状态
@@ -334,8 +339,7 @@ class NacosService(metaclass=SingletonMeta):
334
339
  except Exception as e:
335
340
  last_error = str(e)
336
341
  retry_count += 1
337
- delay = min(self.register_retry_interval *
338
- (self.retry_backoff ** (retry_count - 1)), self.max_retry_delay)
342
+ delay = min(self.register_retry_interval, self.max_retry_delay)
339
343
 
340
344
  SYLogger.warning(
341
345
  f"nacos:服务注册尝试 {retry_count} 失败: {last_error},{delay}秒后重试")
@@ -367,9 +371,9 @@ class NacosService(metaclass=SingletonMeta):
367
371
 
368
372
  metadata = {
369
373
  "ignore-metrics": "true",
370
- "preserved.heart.beat.interval": "3000", # 心跳间隔 3 秒
371
- "preserved.heart.beat.timeout": "15000", # 心跳超时 15 秒
372
- "preserved.ip.delete.timeout": "30000" # 实例删除超时 30 秒
374
+ # "preserved.heart.beat.interval": "3000", # 心跳间隔 3 秒
375
+ # "preserved.heart.beat.timeout": "15000", # 心跳超时 15 秒
376
+ # "preserved.ip.delete.timeout": "30000" # 实例删除超时 30 秒
373
377
  }
374
378
  if self.version:
375
379
  metadata["version"] = self.version
@@ -421,8 +425,13 @@ class NacosService(metaclass=SingletonMeta):
421
425
  timeout = 60 # 60秒超时
422
426
  start_time = time.time()
423
427
 
424
- # 启动注册线程,不阻塞主线程
425
- instance._executor.submit(instance.register_with_retry)
428
+ # 启动注册线程,不阻塞主线程(替换原线程池)
429
+ register_thread = threading.Thread(
430
+ target=instance.register_with_retry,
431
+ daemon=True,
432
+ name="NacosRegisterThread"
433
+ )
434
+ register_thread.start()
426
435
 
427
436
  # 等待注册完成或超时
428
437
  while True:
@@ -453,97 +462,135 @@ class NacosService(metaclass=SingletonMeta):
453
462
 
454
463
  # 启动连接监控线程
455
464
  threading.Thread(target=instance.monitor_connection,
456
- daemon=True).start()
465
+ daemon=True, name="NacosConnectionMonitorThread").start()
457
466
  else:
458
467
  SYLogger.info("nacos:本地开发模式,跳过服务注册流程")
459
468
 
460
469
  return instance
461
470
 
462
471
  def start_heartbeat(self):
463
- """启动心跳线程"""
464
- if self._heartbeat_thread and self._heartbeat_thread.is_alive():
465
- return
472
+ """启动心跳线程(确保单例)"""
473
+ with self._heartbeat_lock: # 加锁确保线程安全
474
+ # 双重检查:先判断线程是否已存在且存活
475
+ if self._heartbeat_thread is not None and self._heartbeat_thread.is_alive():
476
+ return
466
477
 
467
- self._heartbeat_thread = threading.Thread(
468
- target=self._send_heartbeat_loop,
469
- name="NacosHeartbeatThread",
470
- daemon=True
471
- )
472
- # 设置线程为守护线程并尝试提高优先级
473
- self._heartbeat_thread.daemon = True
474
- self._heartbeat_thread.start()
478
+ # 彻底清理可能的残留线程引用
479
+ self._heartbeat_thread = None
475
480
 
476
- try:
477
- # 尝试提高线程优先级
478
- if hasattr(os, 'sched_setscheduler'):
479
- os.sched_setscheduler(
480
- self._heartbeat_thread.ident, os.SCHED_FIFO, os.sched_param(99))
481
- elif hasattr(os, 'nice'):
482
- os.nice(-10) # 在Unix系统上尝试提高优先级
483
- SYLogger.info("nacos:心跳线程已启动并尝试提高优先级")
484
- except Exception as e:
485
- SYLogger.warning(f"nacos:无法提高心跳线程优先级: {e}")
481
+ # 创建新的心跳线程
482
+ self._heartbeat_thread = threading.Thread(
483
+ target=self._send_heartbeat_loop,
484
+ name="NacosHeartbeatThread",
485
+ daemon=True
486
+ )
487
+ self._heartbeat_thread.daemon = True
488
+ self._heartbeat_thread.start()
489
+ SYLogger.info(
490
+ f"nacos:心跳线程启动,线程ID: {self._heartbeat_thread.ident}"
491
+ f"心跳间隔: {self.heartbeat_interval}秒,"
492
+ f"心跳超时: {self.heartbeat_timeout}秒"
493
+ )
486
494
 
487
495
  def _send_heartbeat_loop(self):
488
496
  """优化后的心跳发送循环,确保严格按间隔执行"""
489
- SYLogger.info("nacos:优化版心跳线程开始运行")
490
- last_heartbeat_time = time.time() # 初始时间戳
497
+ current_thread = threading.current_thread()
498
+ thread_ident = current_thread.ident
499
+ SYLogger.info(
500
+ f"nacos:心跳循环启动 - 线程ID: {thread_ident}, "
501
+ f"配置间隔: {self.heartbeat_interval}秒, "
502
+ f"超时时间: {self.heartbeat_timeout}秒"
503
+ )
504
+
505
+ consecutive_fail = 0 # 连续失败计数器
491
506
 
492
507
  while not self._shutdown_event.is_set():
508
+ # 记录当前时间,作为本次心跳的基准
493
509
  current_time = time.time()
494
- # 计算距离下次心跳的时间(确保严格按间隔执行)
495
- sleep_time = max(0, self.heartbeat_interval -
496
- (current_time - last_heartbeat_time))
497
510
 
498
- if sleep_time > 0:
499
- self._shutdown_event.wait(sleep_time) # 精准等待
500
-
501
- # 执行心跳逻辑
502
511
  try:
503
- # 带锁检查注册状态
512
+ # 检查注册状态(带锁读取)
504
513
  with self._state_lock:
505
514
  registered_status = self.registered
506
515
 
507
- if registered_status and self.check_service_registered():
516
+ if not registered_status:
517
+ SYLogger.warning(
518
+ f"nacos:服务未注册,跳过心跳 - 线程ID: {thread_ident}")
519
+ consecutive_fail = 0
520
+ else:
521
+ # 发送心跳(10秒超时)
508
522
  success = self.send_heartbeat()
509
523
  if success:
510
- self._last_successful_heartbeat = time.time()
524
+ consecutive_fail = 0
525
+ self._last_successful_heartbeat = current_time
511
526
  SYLogger.info(
512
- f"nacos:心跳发送成功,时间: {self._last_successful_heartbeat:.3f}")
527
+ f"nacos:心跳发送成功 - 时间: {current_time:.3f}, "
528
+ f"间隔: {self.heartbeat_interval}秒"
529
+ )
513
530
  else:
531
+ consecutive_fail += 1
514
532
  SYLogger.warning(
515
- f"nacos:心跳发送失败,已持续{current_time - self._last_successful_heartbeat:.1f}")
516
- else:
517
- SYLogger.warning("nacos:服务未注册,尝试重新注册")
518
- # 检查线程池是否已关闭
519
- if not self._executor._shutdown:
520
- self._executor.submit(self.register_with_retry)
521
- else:
522
- SYLogger.warning("nacos:线程池已关闭,无法提交注册任务")
523
- finally:
524
- last_heartbeat_time = time.time() # 更新上次执行时间
533
+ f"nacos:心跳发送失败 - 连续失败: {consecutive_fail}"
534
+ )
535
+ if consecutive_fail >= 5:
536
+ SYLogger.error("nacos:心跳连续失败5次,尝试重连")
537
+ self.reconnect_nacos_client()
538
+ consecutive_fail = 0
539
+
540
+ except Exception as e:
541
+ consecutive_fail += 1
542
+ SYLogger.error(
543
+ f"nacos:心跳异常: {str(e)}, 连续失败: {consecutive_fail}次")
544
+
545
+ # 计算下次执行时间(当前时间 + 配置间隔),确保间隔稳定
546
+ next_run_time = current_time + self.heartbeat_interval
547
+ sleep_time = max(0, next_run_time - time.time()
548
+ ) # 避免负数(处理耗时超过间隔的情况)
549
+ self._shutdown_event.wait(sleep_time) # 精准休眠至下次执行时间
550
+
551
+ SYLogger.info(f"nacos:心跳循环已停止 - 线程ID: {thread_ident}")
525
552
 
526
553
  def send_heartbeat(self):
527
- """发送心跳并添加超时控制"""
554
+ """发送心跳并添加10秒超时控制(替换线程池实现)"""
528
555
  if not self.ensure_client_connected():
529
556
  SYLogger.warning("nacos:客户端未连接,心跳发送失败")
530
557
  return False
531
558
 
532
- try:
533
- # 严格控制心跳超时(3秒)
534
- future = self._heartbeat_executor.submit(
535
- self._send_heartbeat_internal)
536
- result = future.result(timeout=3) # 超时后抛出异常
537
-
538
- if result:
539
- # 只有确认成功才更新时间戳
540
- self._last_successful_heartbeat = time.time()
541
- return result
542
- except Exception as e:
543
- SYLogger.error(f"nacos:发送心跳时发生异常: {e}")
559
+ # 用线程+join实现10秒超时控制
560
+ result_list = [] # 用于线程间传递结果
561
+
562
+ def heartbeat_task():
563
+ """心跳实际执行任务"""
564
+ try:
565
+ result = self._send_heartbeat_internal()
566
+ result_list.append(result)
567
+ except Exception as e:
568
+ SYLogger.error(f"nacos:心跳任务执行异常: {e}")
569
+ result_list.append(False)
570
+
571
+ # 启动心跳任务线程
572
+ task_thread = threading.Thread(
573
+ target=heartbeat_task,
574
+ daemon=True,
575
+ name="NacosHeartbeatTaskThread"
576
+ )
577
+ task_thread.start()
578
+
579
+ # 等待线程完成,最多等待10秒
580
+ task_thread.join(timeout=self.heartbeat_timeout)
581
+
582
+ # 处理结果
583
+ if not result_list:
584
+ # 超时未返回
585
+ SYLogger.error(f"nacos:心跳发送超时({self.heartbeat_timeout}秒)")
544
586
  self._client_initialized = False # 强制重连
545
587
  return False
546
588
 
589
+ # 检查心跳结果
590
+ if result_list[0]:
591
+ self._last_successful_heartbeat = time.time()
592
+ return result_list[0]
593
+
547
594
  def _send_heartbeat_internal(self):
548
595
  """实际的心跳发送逻辑"""
549
596
  result = self.nacos_client.send_heartbeat(
@@ -571,10 +618,9 @@ class NacosService(metaclass=SingletonMeta):
571
618
 
572
619
  def monitor_connection(self):
573
620
  """优化的连接监控线程,缩短检查间隔"""
574
- # 缩短检查间隔(5秒一次)
575
- check_interval = self.nacos_config.get('checkInterval', 5)
576
- thread_start_time = time.time() # 线程启动时间
577
- check_counter = 0 # 检查计数器
621
+ check_interval = self.connection_check_interval
622
+ thread_start_time = time.time()
623
+ check_counter = 0
578
624
 
579
625
  while not self._shutdown_event.is_set():
580
626
  try:
@@ -600,9 +646,13 @@ class NacosService(metaclass=SingletonMeta):
600
646
  else:
601
647
  self.registered = False
602
648
  SYLogger.warning(f"nacos:服务实例未注册,尝试重新注册")
603
- # 不在锁内调用可能耗时的操作
604
- self._monitor_executor.submit(
605
- self.register_with_retry)
649
+ # 启动临时线程执行重新注册(替换原线程池)
650
+ retry_thread = threading.Thread(
651
+ target=self.register_with_retry,
652
+ daemon=True,
653
+ name="NacosRetryRegisterThread"
654
+ )
655
+ retry_thread.start()
606
656
 
607
657
  # 20%的概率执行深度检查
608
658
  if random.random() < 0.2:
@@ -653,13 +703,6 @@ class NacosService(metaclass=SingletonMeta):
653
703
  SYLogger.error(f"nacos:注销服务时发生错误: {e}")
654
704
  finally:
655
705
  self._shutdown_event.set()
656
- # 优雅地关闭线程池
657
- if self._executor and not self._executor._shutdown:
658
- self._executor.shutdown(wait=True)
659
- if self._heartbeat_executor and not self._heartbeat_executor._shutdown:
660
- self._heartbeat_executor.shutdown(wait=True)
661
- if self._monitor_executor and not self._monitor_executor._shutdown:
662
- self._monitor_executor.shutdown(wait=True)
663
706
 
664
707
  def handle_signal(self, signum, frame):
665
708
  """处理退出信号"""
@@ -724,18 +767,28 @@ class NacosService(metaclass=SingletonMeta):
724
767
 
725
768
  def _watch_configs(self):
726
769
  """配置监听线程"""
727
- check_interval = 5 # 固定检查间隔
770
+ check_interval = self.config_watch_interval
728
771
 
729
772
  while not self._shutdown_event.is_set():
730
773
  try:
731
774
  for data_id, callback in list(self._config_listeners.items()):
732
775
  new_config = self.get_config(data_id)
733
776
  if new_config and new_config != self._config_cache.get(data_id):
734
- self._executor.submit(callback, new_config)
777
+ # 直接执行回调(替换原线程池,配置回调通常为轻量操作)
778
+ callback(new_config)
735
779
  self._config_cache[data_id] = new_config
780
+ try:
781
+ self.share_configs[data_id] = json.loads(
782
+ new_config)
783
+ except json.JSONDecodeError:
784
+ try:
785
+ self.share_configs[data_id] = yaml.safe_load(
786
+ new_config)
787
+ except yaml.YAMLError:
788
+ SYLogger.error(f"nacos:无法解析 {data_id} 的内容")
736
789
  except Exception as e:
737
790
  SYLogger.error(f"nacos:配置监视线程异常: {str(e)}")
738
- self._shutdown_event.wait(check_interval) # 检查间隔
791
+ self._shutdown_event.wait(check_interval)
739
792
 
740
793
  def discover_services(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
741
794
  """发现服务实例列表 (与Java格式兼容)"""
@@ -0,0 +1,75 @@
1
+ from typing import Any, Optional
2
+
3
+ # ------------------------------
4
+ # 参数类型标记类(兼容 Pydantic)
5
+ # ------------------------------
6
+
7
+
8
+ class Param:
9
+ """基础参数元信息类"""
10
+
11
+ def __init__(
12
+ self,
13
+ default: Any = ...,
14
+ description: str = "",
15
+ alias: Optional[str] = None,
16
+ deprecated: bool = False
17
+ ):
18
+ self.default = default # ... 表示必填
19
+ self.description = description
20
+ self.alias = alias
21
+ self.deprecated = deprecated
22
+
23
+ def is_required(self) -> bool:
24
+ return self.default is ...
25
+
26
+ def get_key(self, param_name: str) -> str:
27
+ return self.alias if self.alias is not None else param_name
28
+
29
+
30
+ class Path(Param):
31
+ """路径参数"""
32
+ pass
33
+
34
+
35
+ class Query(Param):
36
+ """查询参数"""
37
+ pass
38
+
39
+
40
+ class Header(Param):
41
+ """请求头参数"""
42
+
43
+ def __init__(self, *args, convert_underscores: bool = True, **kwargs):
44
+ super().__init__(*args, **kwargs)
45
+ self.convert_underscores = convert_underscores
46
+
47
+ def get_key(self, param_name: str) -> str:
48
+ key = super().get_key(param_name)
49
+ return key.replace("_", "-") if self.convert_underscores else key
50
+
51
+
52
+ class Cookie(Param):
53
+ """Cookie参数"""
54
+ pass
55
+
56
+
57
+ class Body(Param):
58
+ """JSON请求体参数(支持Pydantic模型)"""
59
+
60
+ def __init__(self, *args, embed: bool = False, ** kwargs):
61
+ super().__init__(*args, **kwargs)
62
+ self.embed = embed # 是否包装在键中
63
+
64
+
65
+ class Form(Param):
66
+ """表单参数(支持Pydantic模型)"""
67
+ pass
68
+
69
+
70
+ class File(Param):
71
+ """文件上传参数"""
72
+
73
+ def __init__(self, *args, field_name: str = "file", ** kwargs):
74
+ super().__init__(*args, **kwargs)
75
+ self.field_name = field_name
@@ -0,0 +1,97 @@
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