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.
- ErisPulse/Core/Bases/__init__.py +14 -0
- ErisPulse/Core/Bases/adapter.py +196 -0
- ErisPulse/Core/Bases/module.py +54 -0
- ErisPulse/Core/Event/__init__.py +14 -0
- ErisPulse/Core/Event/base.py +15 -2
- ErisPulse/Core/Event/command.py +21 -2
- ErisPulse/Core/Event/message.py +15 -0
- ErisPulse/Core/Event/meta.py +15 -0
- ErisPulse/Core/Event/notice.py +15 -0
- ErisPulse/Core/Event/request.py +16 -1
- ErisPulse/Core/__init__.py +38 -19
- ErisPulse/Core/{erispulse_config.py → _self_config.py} +16 -3
- ErisPulse/Core/adapter.py +374 -377
- ErisPulse/Core/config.py +137 -38
- ErisPulse/Core/exceptions.py +6 -1
- ErisPulse/Core/lifecycle.py +167 -0
- ErisPulse/Core/logger.py +97 -49
- ErisPulse/Core/module.py +279 -56
- ErisPulse/Core/router.py +112 -23
- ErisPulse/Core/storage.py +258 -77
- ErisPulse/Core/ux.py +664 -0
- ErisPulse/__init__.py +587 -242
- ErisPulse/__main__.py +1 -1999
- ErisPulse/utils/__init__.py +15 -0
- ErisPulse/utils/cli.py +1102 -0
- ErisPulse/utils/package_manager.py +847 -0
- ErisPulse/utils/reload_handler.py +135 -0
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/METADATA +24 -6
- erispulse-2.3.0.dev5.dist-info/RECORD +33 -0
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/WHEEL +1 -1
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/licenses/LICENSE +1 -1
- ErisPulse/Core/env.py +0 -15
- ErisPulse/Core/module_registry.py +0 -227
- erispulse-2.2.1.dev0.dist-info/RECORD +0 -26
- {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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 =
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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"
|
|
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__ = [
|
ErisPulse/Core/exceptions.py
CHANGED
|
@@ -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()
|