ErisPulse 2.2.1.dev0__py3-none-any.whl → 2.3.0.dev5__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 (35) hide show
  1. ErisPulse/Core/Bases/__init__.py +14 -0
  2. ErisPulse/Core/Bases/adapter.py +196 -0
  3. ErisPulse/Core/Bases/module.py +54 -0
  4. ErisPulse/Core/Event/__init__.py +14 -0
  5. ErisPulse/Core/Event/base.py +15 -2
  6. ErisPulse/Core/Event/command.py +21 -2
  7. ErisPulse/Core/Event/message.py +15 -0
  8. ErisPulse/Core/Event/meta.py +15 -0
  9. ErisPulse/Core/Event/notice.py +15 -0
  10. ErisPulse/Core/Event/request.py +16 -1
  11. ErisPulse/Core/__init__.py +38 -19
  12. ErisPulse/Core/{erispulse_config.py → _self_config.py} +16 -3
  13. ErisPulse/Core/adapter.py +374 -377
  14. ErisPulse/Core/config.py +137 -38
  15. ErisPulse/Core/exceptions.py +6 -1
  16. ErisPulse/Core/lifecycle.py +167 -0
  17. ErisPulse/Core/logger.py +97 -49
  18. ErisPulse/Core/module.py +279 -56
  19. ErisPulse/Core/router.py +112 -23
  20. ErisPulse/Core/storage.py +258 -77
  21. ErisPulse/Core/ux.py +664 -0
  22. ErisPulse/__init__.py +587 -242
  23. ErisPulse/__main__.py +1 -1999
  24. ErisPulse/utils/__init__.py +15 -0
  25. ErisPulse/utils/cli.py +1102 -0
  26. ErisPulse/utils/package_manager.py +847 -0
  27. ErisPulse/utils/reload_handler.py +135 -0
  28. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/METADATA +24 -6
  29. erispulse-2.3.0.dev5.dist-info/RECORD +33 -0
  30. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/WHEEL +1 -1
  31. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/licenses/LICENSE +1 -1
  32. ErisPulse/Core/env.py +0 -15
  33. ErisPulse/Core/module_registry.py +0 -227
  34. erispulse-2.2.1.dev0.dist-info/RECORD +0 -26
  35. {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/entry_points.txt +0 -0
ErisPulse/Core/config.py CHANGED
@@ -3,74 +3,173 @@ ErisPulse 配置中心
3
3
 
4
4
  集中管理所有配置项,避免循环导入问题
5
5
  提供自动补全缺失配置项的功能
6
+ 添加内存缓存和延迟写入机制以提高性能
6
7
  """
8
+
7
9
  import os
10
+ import time
8
11
  import toml
9
- from typing import Dict, Any, Optional
12
+ import threading
13
+ from typing import Any, Dict
10
14
 
11
15
  class ConfigManager:
12
16
  def __init__(self, config_file: str = "config.toml"):
13
- self.CONFIG_FILE = config_file
17
+ self.CONFIG_FILE: str = config_file
18
+ self._cache: Dict[str, Any] = {} # 内存缓存
19
+ self._dirty_keys: Dict[str, Any] = {} # 待写入的键值对
20
+ self._cache_timestamp = 0 # 缓存时间戳
21
+ self._cache_timeout = 60 # 缓存超时时间(秒)
22
+ self._write_delay = 5 # 写入延迟(秒)
23
+ self._write_timer = None # 写入定时器
24
+ self._lock = threading.RLock() # 线程安全锁
25
+ self._load_config() # 初始化时加载配置
26
+
27
+ def _load_config(self) -> None:
28
+ """
29
+ 从文件加载配置到缓存
30
+ """
31
+ with self._lock:
32
+ try:
33
+ if not os.path.exists(self.CONFIG_FILE):
34
+ self._cache = {}
35
+ self._cache_timestamp = time.time()
36
+ return
37
+
38
+ with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
39
+ config = toml.load(f)
40
+ self._cache = config
41
+ self._cache_timestamp = time.time()
42
+ except Exception as e:
43
+ from .logger import logger
44
+ logger.error(f"加载配置文件 {self.CONFIG_FILE} 失败: {e}")
45
+ self._cache = {}
46
+ self._cache_timestamp = time.time()
47
+
48
+ def _flush_config(self) -> None:
49
+ """
50
+ 将待写入的配置刷新到文件
51
+ """
52
+ with self._lock:
53
+ if not self._dirty_keys:
54
+ return # 没有需要写入的内容
55
+
56
+ try:
57
+ # 从文件读取完整配置
58
+ if os.path.exists(self.CONFIG_FILE):
59
+ with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
60
+ config = toml.load(f)
61
+ else:
62
+ config = {}
63
+
64
+ # 应用待写入的更改
65
+ for key, value in self._dirty_keys.items():
66
+ keys = key.split('.')
67
+ current = config
68
+ for k in keys[:-1]:
69
+ if k not in current:
70
+ current[k] = {}
71
+ current = current[k]
72
+ current[keys[-1]] = value
73
+
74
+ # 写入文件
75
+ with open(self.CONFIG_FILE, "w", encoding="utf-8") as f:
76
+ toml.dump(config, f)
77
+
78
+ # 更新缓存并清除待写入队列
79
+ self._cache = config
80
+ self._cache_timestamp = time.time()
81
+ self._dirty_keys.clear()
82
+
83
+ except Exception as e:
84
+ from .logger import logger
85
+ logger.error(f"写入配置文件 {self.CONFIG_FILE} 失败: {e}")
86
+
87
+ def _schedule_write(self) -> None:
88
+ """
89
+ 安排延迟写入
90
+ """
91
+ if self._write_timer:
92
+ self._write_timer.cancel()
93
+
94
+ self._write_timer = threading.Timer(self._write_delay, self._flush_config)
95
+ self._write_timer.daemon = True
96
+ self._write_timer.start()
97
+
98
+ def _check_cache_validity(self) -> None:
99
+ """
100
+ 检查缓存有效性,必要时重新加载
101
+ """
102
+ current_time = time.time()
103
+ if current_time - self._cache_timestamp > self._cache_timeout:
104
+ self._load_config()
14
105
 
15
106
  def getConfig(self, key: str, default: Any = None) -> Any:
16
107
  """
17
- 获取模块/适配器配置项
108
+ 获取模块/适配器配置项(优先从缓存获取)
18
109
  :param key: 配置项的键(支持点分隔符如"module.sub.key")
19
110
  :param default: 默认值
20
111
  :return: 配置项的值
21
112
  """
22
- try:
23
- if not os.path.exists(self.CONFIG_FILE):
24
- return default
25
-
26
- with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
27
- config = toml.load(f)
28
-
29
- # 支持点分隔符访问嵌套配置
113
+ with self._lock:
114
+ self._check_cache_validity()
115
+
116
+ # 优先检查待写入队列
117
+ if key in self._dirty_keys:
118
+ return self._dirty_keys[key]
119
+
120
+ # 然后检查缓存
30
121
  keys = key.split('.')
31
- value = config
122
+ value = self._cache
32
123
  for k in keys:
33
124
  if k not in value:
34
125
  return default
35
126
  value = value[k]
36
-
127
+
37
128
  return value
38
- except Exception as e:
39
- from . import logger
40
- logger.error(f"读取配置文件 {self.CONFIG_FILE} 失败: {e}")
41
- return default
42
-
43
- def setConfig(self, key: str, value: Any) -> bool:
129
+
130
+ def setConfig(self, key: str, value: Any, immediate: bool = False) -> bool:
44
131
  """
45
- 设置模块/适配器配置
132
+ 设置模块/适配器配置(缓存+延迟写入)
46
133
  :param key: 配置项键名(支持点分隔符如"module.sub.key")
47
134
  :param value: 配置项值
135
+ :param immediate: 是否立即写入磁盘(默认为False,延迟写入)
48
136
  :return: 操作是否成功
49
137
  """
50
138
  try:
51
- config = {}
52
- if os.path.exists(self.CONFIG_FILE):
53
- with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
54
- config = toml.load(f)
55
-
56
- # 支持点分隔符设置嵌套配置
57
- keys = key.split('.')
58
- current = config
59
- for k in keys[:-1]:
60
- if k not in current:
61
- current[k] = {}
62
- current = current[k]
63
- current[keys[-1]] = value
64
-
65
- with open(self.CONFIG_FILE, "w", encoding="utf-8") as f:
66
- toml.dump(config, f)
139
+ with self._lock:
140
+ # 先更新待写入队列
141
+ self._dirty_keys[key] = value
142
+
143
+ if immediate:
144
+ # 立即写入磁盘
145
+ self._flush_config()
146
+ else:
147
+ # 安排延迟写入
148
+ self._schedule_write()
67
149
 
68
150
  return True
69
151
  except Exception as e:
70
- from . import logger
71
- logger.error(f"写入配置文件 {self.CONFIG_FILE} 失败: {e}")
152
+ from .logger import logger
153
+ logger.error(f"设置配置项 {key} 失败: {e}")
72
154
  return False
73
155
 
156
+ def force_save(self) -> None:
157
+ """
158
+ 强制立即保存所有待写入的配置到磁盘
159
+ """
160
+ with self._lock:
161
+ self._flush_config()
162
+
163
+ def reload(self) -> None:
164
+ """
165
+ 重新从磁盘加载配置,丢弃所有未保存的更改
166
+ """
167
+ with self._lock:
168
+ if self._write_timer:
169
+ self._write_timer.cancel()
170
+ self._dirty_keys.clear()
171
+ self._load_config()
172
+
74
173
  config = ConfigManager()
75
174
 
76
175
  __all__ = [
@@ -87,7 +87,7 @@ def async_exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str,
87
87
  msg = context.get('message', '未知异步错误')
88
88
  err_logger(f"ERROR: 未处理的异步错误: {msg}\n")
89
89
 
90
- def setup_async_loop(loop: asyncio.AbstractEventLoop = None) -> None:
90
+ def setup_async_loop(loop: asyncio.AbstractEventLoop = None) -> None: # type: ignore || 原因: 在实现中,已经完成了对于本方法的类型检查
91
91
  """
92
92
  为指定的事件循环设置异常处理器
93
93
 
@@ -99,6 +99,11 @@ def setup_async_loop(loop: asyncio.AbstractEventLoop = None) -> None:
99
99
  except RuntimeError:
100
100
  loop = asyncio.get_event_loop()
101
101
 
102
+ if loop is not None:
103
+ loop.set_exception_handler(async_exception_handler)
104
+ else:
105
+ raise RuntimeError("无法获取有效的事件循环")
106
+
102
107
  loop.set_exception_handler(async_exception_handler)
103
108
 
104
109
  sys.excepthook = global_exception_handler
@@ -0,0 +1,167 @@
1
+ """
2
+ ErisPulse 生命周期管理模块
3
+
4
+ 提供统一的生命周期事件管理和触发机制
5
+
6
+ 事件标准格式:
7
+ {
8
+ "event": "事件名称", # 必填
9
+ "timestamp": float, # 必填,Unix时间戳
10
+ "data": dict, # 可选,事件相关数据
11
+ "source": str, # 必填,事件来源
12
+ "msg": str # 可选,事件描述
13
+ }
14
+ """
15
+
16
+ import asyncio
17
+ import time
18
+ from typing import Callable, List, Dict, Any
19
+ from .logger import logger
20
+
21
+ class LifecycleManager:
22
+ """
23
+ 生命周期管理器
24
+
25
+ 管理SDK的生命周期事件,提供事件注册和触发功能
26
+ 支持点式结构事件监听,例如 module.init 可以被 module 监听到
27
+ """
28
+
29
+ # 预定义的标准事件列表
30
+ STANDARD_EVENTS = {
31
+ "core": ["init.start", "init.complete"],
32
+ "module": ["load", "init", "unload"],
33
+ "adapter": ["load", "start", "status.change", "stop", "stopped"],
34
+ "server": ["start", "stop"]
35
+ }
36
+
37
+ def __init__(self):
38
+ self._handlers: Dict[str, List[Callable]] = {}
39
+ self._timers: Dict[str, float] = {} # 用于存储计时器
40
+
41
+ def _validate_event(self, event_data: Dict[str, Any]) -> bool:
42
+ """
43
+ 验证事件数据格式
44
+
45
+ :param event_data: 事件数据字典
46
+ :return: 是否有效
47
+ """
48
+ if not isinstance(event_data, dict):
49
+ logger.error("事件数据必须是字典")
50
+
51
+ required_fields = ["event", "timestamp", "source"]
52
+ for field in required_fields:
53
+ if field not in event_data:
54
+ logger.error(f"事件缺少必填字段: {field}")
55
+
56
+ if not isinstance(event_data["event"], str):
57
+ logger.error("event字段必须是字符串")
58
+
59
+ if not isinstance(event_data["timestamp"], (int, float)):
60
+ logger.error("timestamp字段必须是数字")
61
+
62
+ if "data" in event_data and not isinstance(event_data["data"], dict):
63
+ logger.error("data字段必须是字典")
64
+
65
+ return True
66
+
67
+ def on(self, event: str) -> Callable:
68
+ """
69
+ 注册生命周期事件处理器
70
+
71
+ :param event: 事件名称,支持点式结构如 module.init
72
+ :return: 装饰器函数
73
+
74
+ :raises ValueError: 当事件名无效时抛出
75
+ """
76
+ if not isinstance(event, str) or not event:
77
+ raise ValueError("事件名称必须是非空字符串")
78
+ def decorator(func: Callable) -> Callable:
79
+ if event not in self._handlers:
80
+ self._handlers[event] = []
81
+ self._handlers[event].append(func)
82
+ return func
83
+ return decorator
84
+
85
+ def start_timer(self, timer_id: str) -> None:
86
+ """
87
+ 开始计时
88
+
89
+ :param timer_id: 计时器ID
90
+ """
91
+ self._timers[timer_id] = time.time()
92
+
93
+ def get_duration(self, timer_id: str) -> float:
94
+ """
95
+ 获取指定计时器的持续时间
96
+
97
+ :param timer_id: 计时器ID
98
+ :return: 持续时间(秒)
99
+ """
100
+ if timer_id in self._timers:
101
+ return time.time() - self._timers[timer_id]
102
+ return 0.0
103
+
104
+ def stop_timer(self, timer_id: str) -> float:
105
+ """
106
+ 停止计时并返回持续时间
107
+
108
+ :param timer_id: 计时器ID
109
+ :return: 持续时间(秒)
110
+ """
111
+ duration = self.get_duration(timer_id)
112
+ if timer_id in self._timers:
113
+ del self._timers[timer_id]
114
+ return duration
115
+
116
+ async def submit_event(self, event_type: str, *, source: str = "ErisPulse", msg: str = "", data: dict = {}, timestamp = time.time()) -> None:
117
+ """
118
+ 提交生命周期事件
119
+
120
+ :param event: 事件名称
121
+ :param event_data: 事件数据字典
122
+ """
123
+ # 构建完整事件数据
124
+ event_data = {
125
+ "event": event_type,
126
+ "timestamp": timestamp,
127
+ "data": data,
128
+ "source": source,
129
+ "msg": msg
130
+ }
131
+
132
+ # 验证事件格式
133
+ self._validate_event(event_data)
134
+
135
+ # 触发通配符处理器(如果存在)
136
+ if "*" in self._handlers:
137
+ await self._execute_handlers("*", event_data)
138
+
139
+ # 触发完整事件名的处理器
140
+ if event_type in self._handlers:
141
+ await self._execute_handlers(event_type, event_data)
142
+
143
+ # 触发父级事件名的处理器(点式结构)
144
+ parts = event_type.split('.')
145
+ for i in range(len(parts) - 1, 0, -1):
146
+ parent_event = '.'.join(parts[:i])
147
+ if parent_event in self._handlers:
148
+ await self._execute_handlers(parent_event, event_data)
149
+
150
+ async def _execute_handlers(self, event: str, event_data: Dict[str, Any]) -> None:
151
+ """
152
+ 执行事件处理器
153
+
154
+ :param event: 事件名称
155
+ :param event_data: 事件数据
156
+ """
157
+ logger.debug(f"触发生命周期事件: {event}")
158
+ for handler in self._handlers[event]:
159
+ try:
160
+ if asyncio.iscoroutinefunction(handler):
161
+ await handler(event_data)
162
+ else:
163
+ handler(event_data)
164
+ except Exception as e:
165
+ logger.error(f"生命周期事件处理器执行错误 {event}: {e}")
166
+
167
+ lifecycle = LifecycleManager()