ErisPulse 1.1.12__py3-none-any.whl → 1.1.14.dev1__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/__init__.py +40 -2
- ErisPulse/__main__.py +111 -38
- ErisPulse/adapter.py +47 -0
- ErisPulse/db.py +289 -8
- ErisPulse/logger.py +47 -1
- ErisPulse/mods.py +38 -0
- ErisPulse/raiserr.py +36 -1
- ErisPulse/util.py +45 -1
- erispulse-1.1.14.dev1.dist-info/METADATA +165 -0
- erispulse-1.1.14.dev1.dist-info/RECORD +13 -0
- {erispulse-1.1.12.dist-info → erispulse-1.1.14.dev1.dist-info}/WHEEL +1 -2
- erispulse-1.1.12.dist-info/METADATA +0 -79
- erispulse-1.1.12.dist-info/RECORD +0 -14
- erispulse-1.1.12.dist-info/top_level.txt +0 -1
- {erispulse-1.1.12.dist-info → erispulse-1.1.14.dev1.dist-info}/entry_points.txt +0 -0
- {erispulse-1.1.12.dist-info → erispulse-1.1.14.dev1.dist-info}/licenses/LICENSE +0 -0
ErisPulse/__init__.py
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# SDK 核心初始化
|
|
3
|
+
|
|
4
|
+
提供SDK全局对象构建和初始化功能。
|
|
5
|
+
|
|
6
|
+
## 主要功能
|
|
7
|
+
- 构建全局sdk对象
|
|
8
|
+
- 预注册核心错误类型
|
|
9
|
+
- 提供SDK初始化入口
|
|
10
|
+
- 集成各核心模块
|
|
11
|
+
|
|
12
|
+
## API 文档
|
|
13
|
+
### 核心对象:
|
|
14
|
+
- sdk: 全局SDK命名空间对象
|
|
15
|
+
- sdk.init(): SDK初始化入口函数
|
|
16
|
+
|
|
17
|
+
### 预注册错误类型:
|
|
18
|
+
- CaughtExternalError: 外部捕获异常
|
|
19
|
+
- InitError: 初始化错误
|
|
20
|
+
- MissingDependencyError: 缺少依赖错误
|
|
21
|
+
- InvalidDependencyError: 无效依赖错误
|
|
22
|
+
- CycleDependencyError: 循环依赖错误
|
|
23
|
+
- ModuleLoadError: 模块加载错误
|
|
24
|
+
|
|
25
|
+
### 示例用法:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
from ErisPulse import sdk
|
|
29
|
+
|
|
30
|
+
# 初始化SDK
|
|
31
|
+
sdk.init()
|
|
32
|
+
|
|
33
|
+
# 访问各模块功能
|
|
34
|
+
sdk.logger.info("SDK已初始化")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
"""
|
|
1
38
|
|
|
2
39
|
import types
|
|
3
40
|
sdk = types.SimpleNamespace()
|
|
@@ -35,7 +72,8 @@ def init() -> None:
|
|
|
35
72
|
logger.info("[Init] SDK 正在初始化...")
|
|
36
73
|
if env.create_env_file_if_not_exists():
|
|
37
74
|
logger.info("[Init] 项目首次初始化,建议先配置环境变量")
|
|
38
|
-
|
|
75
|
+
if input("是否立即退出?(y/n): ").strip().lower() == "y":
|
|
76
|
+
sys.exit(0)
|
|
39
77
|
env.load_env_file()
|
|
40
78
|
|
|
41
79
|
sdkModulePath = os.path.join(os.path.dirname(__file__), "modules")
|
|
@@ -185,4 +223,4 @@ def init() -> None:
|
|
|
185
223
|
raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
|
|
186
224
|
|
|
187
225
|
|
|
188
|
-
sdk.init = init
|
|
226
|
+
sdk.init = init
|
ErisPulse/__main__.py
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# CLI 入口
|
|
3
|
+
|
|
4
|
+
提供命令行界面(CLI)用于模块管理、源管理和开发调试。
|
|
5
|
+
|
|
6
|
+
## 主要功能
|
|
7
|
+
- 模块管理: 安装/卸载/启用/禁用
|
|
8
|
+
- 源管理: 添加/删除/更新源
|
|
9
|
+
- 热重载: 开发时自动重启
|
|
10
|
+
- 彩色终端输出
|
|
11
|
+
|
|
12
|
+
## 主要命令
|
|
13
|
+
### 模块管理:
|
|
14
|
+
install: 安装模块
|
|
15
|
+
uninstall: 卸载模块
|
|
16
|
+
enable: 启用模块
|
|
17
|
+
disable: 禁用模块
|
|
18
|
+
list: 列出模块
|
|
19
|
+
update: 更新模块列表
|
|
20
|
+
upgrade: 升级模块
|
|
21
|
+
|
|
22
|
+
### 源管理:
|
|
23
|
+
origin add: 添加源
|
|
24
|
+
origin del: 删除源
|
|
25
|
+
origin list: 列出源
|
|
26
|
+
|
|
27
|
+
### 开发调试:
|
|
28
|
+
run: 运行脚本
|
|
29
|
+
--reload: 启用热重载
|
|
30
|
+
|
|
31
|
+
### 示例用法:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
# 安装模块
|
|
35
|
+
epsdk install MyModule
|
|
36
|
+
|
|
37
|
+
# 启用热重载
|
|
38
|
+
epsdk run main.py --reload
|
|
39
|
+
|
|
40
|
+
# 管理源
|
|
41
|
+
epsdk origin add https://example.com/map.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
|
|
1
46
|
import argparse
|
|
2
47
|
import os
|
|
3
48
|
import sys
|
|
@@ -9,6 +54,7 @@ import fnmatch
|
|
|
9
54
|
import asyncio
|
|
10
55
|
import subprocess
|
|
11
56
|
import json
|
|
57
|
+
import json
|
|
12
58
|
from .db import env
|
|
13
59
|
from .mods import mods
|
|
14
60
|
from watchdog.observers import Observer
|
|
@@ -37,7 +83,6 @@ class Shell_Printer:
|
|
|
37
83
|
|
|
38
84
|
@classmethod
|
|
39
85
|
def _get_color(cls, level):
|
|
40
|
-
"""根据消息级别返回颜色"""
|
|
41
86
|
return {
|
|
42
87
|
"info": cls.CYAN,
|
|
43
88
|
"success": cls.GREEN,
|
|
@@ -49,77 +94,87 @@ class Shell_Printer:
|
|
|
49
94
|
|
|
50
95
|
@classmethod
|
|
51
96
|
def panel(cls, msg: str, title: str = None, level: str = "info") -> None:
|
|
52
|
-
"""带标题和边框的面板,支持颜色编码"""
|
|
53
97
|
color = cls._get_color(level)
|
|
54
|
-
|
|
98
|
+
width = 70
|
|
99
|
+
border_char = "─" * width
|
|
100
|
+
|
|
101
|
+
if level == "error":
|
|
102
|
+
border_char = "═" * width
|
|
103
|
+
msg = f"{cls.RED}✗ {msg}{cls.RESET}"
|
|
104
|
+
elif level == "warning":
|
|
105
|
+
border_char = "─" * width
|
|
106
|
+
msg = f"{cls.YELLOW}⚠ {msg}{cls.RESET}"
|
|
55
107
|
|
|
56
|
-
# 标题行
|
|
57
108
|
title_line = ""
|
|
58
109
|
if title:
|
|
59
110
|
title = f" {title.upper()} "
|
|
60
|
-
title_padding = (
|
|
111
|
+
title_padding = (width - len(title)) // 2
|
|
61
112
|
left_pad = " " * title_padding
|
|
62
|
-
right_pad = " " * (
|
|
63
|
-
title_line = f"{cls.DIM}
|
|
113
|
+
right_pad = " " * (width - len(title) - title_padding)
|
|
114
|
+
title_line = f"{cls.DIM}┌{left_pad}{cls.BOLD}{color}{title}{cls.RESET}{cls.DIM}{right_pad}┐{cls.RESET}\n"
|
|
64
115
|
|
|
65
|
-
# 内容行
|
|
66
116
|
lines = []
|
|
67
117
|
for line in msg.split("\n"):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
118
|
+
if len(line) > width - 4:
|
|
119
|
+
words = line.split()
|
|
120
|
+
current_line = ""
|
|
121
|
+
for word in words:
|
|
122
|
+
if len(current_line) + len(word) + 1 > width - 4:
|
|
123
|
+
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
124
|
+
current_line = word
|
|
125
|
+
else:
|
|
126
|
+
current_line += (" " + word) if current_line else word
|
|
127
|
+
if current_line:
|
|
128
|
+
lines.append(f"{cls.DIM}│{cls.RESET} {current_line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
129
|
+
else:
|
|
130
|
+
lines.append(f"{cls.DIM}│{cls.RESET} {line.ljust(width-4)} {cls.DIM}│{cls.RESET}")
|
|
72
131
|
|
|
73
|
-
|
|
74
|
-
|
|
132
|
+
if level == "error":
|
|
133
|
+
border_style = "╘"
|
|
134
|
+
elif level == "warning":
|
|
135
|
+
border_style = "╧"
|
|
136
|
+
else:
|
|
137
|
+
border_style = "└"
|
|
138
|
+
bottom_border = f"{cls.DIM}{border_style}{border_char}┘{cls.RESET}"
|
|
75
139
|
|
|
76
|
-
|
|
77
|
-
panel
|
|
140
|
+
panel = f"{title_line}"
|
|
141
|
+
panel += f"{cls.DIM}├{border_char}┤{cls.RESET}\n"
|
|
78
142
|
panel += "\n".join(lines) + "\n"
|
|
79
|
-
panel += f"{
|
|
143
|
+
panel += f"{bottom_border}\n"
|
|
80
144
|
|
|
81
145
|
print(panel)
|
|
82
146
|
|
|
83
147
|
@classmethod
|
|
84
148
|
def table(cls, headers, rows, title=None, level="info") -> None:
|
|
85
|
-
"""改进的表格输出,带有颜色和分隔线"""
|
|
86
149
|
color = cls._get_color(level)
|
|
87
150
|
if title:
|
|
88
151
|
print(f"{cls.BOLD}{color}== {title} =={cls.RESET}")
|
|
89
152
|
|
|
90
|
-
# 计算列宽
|
|
91
153
|
col_widths = [len(h) for h in headers]
|
|
92
154
|
for row in rows:
|
|
93
155
|
for i, cell in enumerate(row):
|
|
94
156
|
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
95
157
|
|
|
96
|
-
# 构建标题格式
|
|
97
158
|
fmt = "│".join(f" {{:<{w}}} " for w in col_widths)
|
|
98
159
|
|
|
99
|
-
# 顶部边框
|
|
100
160
|
top_border = "┌" + "┬".join("─" * (w+2) for w in col_widths) + "┐"
|
|
101
161
|
print(f"{cls.DIM}{top_border}{cls.RESET}")
|
|
102
162
|
|
|
103
|
-
# 表头
|
|
104
163
|
header_line = fmt.format(*headers)
|
|
105
164
|
print(f"{cls.BOLD}{color}│{header_line}│{cls.RESET}")
|
|
106
165
|
|
|
107
|
-
# 表头分隔线
|
|
108
166
|
separator = "├" + "┼".join("─" * (w+2) for w in col_widths) + "┤"
|
|
109
167
|
print(f"{cls.DIM}{separator}{cls.RESET}")
|
|
110
168
|
|
|
111
|
-
# 表格内容
|
|
112
169
|
for row in rows:
|
|
113
170
|
row_line = fmt.format(*row)
|
|
114
171
|
print(f"│{row_line}│")
|
|
115
172
|
|
|
116
|
-
# 底部边框
|
|
117
173
|
bottom_border = "└" + "┴".join("─" * (w+2) for w in col_widths) + "┘"
|
|
118
174
|
print(f"{cls.DIM}{bottom_border}{cls.RESET}")
|
|
119
175
|
|
|
120
176
|
@classmethod
|
|
121
177
|
def progress_bar(cls, current, total, prefix="", suffix="", length=50):
|
|
122
|
-
"""显示进度条"""
|
|
123
178
|
filled_length = int(length * current // total)
|
|
124
179
|
percent = min(100.0, 100 * (current / float(total)))
|
|
125
180
|
bar = f"{cls.GREEN}{'█' * filled_length}{cls.WHITE}{'░' * (length - filled_length)}{cls.RESET}"
|
|
@@ -130,7 +185,6 @@ class Shell_Printer:
|
|
|
130
185
|
|
|
131
186
|
@classmethod
|
|
132
187
|
def confirm(cls, msg, default=False) -> bool:
|
|
133
|
-
"""带颜色和默认选择的确认对话框"""
|
|
134
188
|
yes_options = {'y', 'yes'}
|
|
135
189
|
no_options = {'n', 'no'}
|
|
136
190
|
default_str = "Y/n" if default else "y/N"
|
|
@@ -148,7 +202,6 @@ class Shell_Printer:
|
|
|
148
202
|
|
|
149
203
|
@classmethod
|
|
150
204
|
def ask(cls, msg, choices=None, default=None) -> str | None:
|
|
151
|
-
"""带颜色和选择的提问"""
|
|
152
205
|
prompt = f"{cls.BOLD}{msg}{cls.RESET}"
|
|
153
206
|
if choices:
|
|
154
207
|
prompt += f" ({cls.CYAN}{'/'.join(choices)}{cls.RESET})"
|
|
@@ -166,7 +219,6 @@ class Shell_Printer:
|
|
|
166
219
|
|
|
167
220
|
@classmethod
|
|
168
221
|
def status(cls, msg, success=True):
|
|
169
|
-
"""显示状态指示器"""
|
|
170
222
|
symbol = f"{cls.GREEN}✓" if success else f"{cls.RED}✗"
|
|
171
223
|
print(f"\r{symbol}{cls.RESET} {msg}")
|
|
172
224
|
|
|
@@ -179,10 +231,29 @@ class SourceManager:
|
|
|
179
231
|
def _init_sources(self):
|
|
180
232
|
if not env.get('origins'):
|
|
181
233
|
env.set('origins', [])
|
|
234
|
+
|
|
235
|
+
primary_source = "https://erisdev.com/map.json"
|
|
236
|
+
secondary_source = "https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/refs/heads/main/map.json"
|
|
237
|
+
|
|
238
|
+
shellprint.status("正在验证主源...")
|
|
239
|
+
validated_url = asyncio.run(self._validate_url(primary_source))
|
|
240
|
+
|
|
241
|
+
if validated_url:
|
|
242
|
+
env.set('origins', [validated_url])
|
|
243
|
+
shellprint.status(f"主源 {validated_url} 已成功添加")
|
|
244
|
+
else:
|
|
245
|
+
if secondary_source not in env.get('origins', []):
|
|
246
|
+
env.set('origins', [secondary_source])
|
|
247
|
+
shellprint.panel(
|
|
248
|
+
f"主源不可用,已添加备用源 {secondary_source}\n\n"
|
|
249
|
+
f"{Shell_Printer.YELLOW}提示:{Shell_Printer.RESET} 建议尽快升级 ErisPulse SDK 版本",
|
|
250
|
+
"源初始化",
|
|
251
|
+
"warning"
|
|
252
|
+
)
|
|
182
253
|
|
|
183
254
|
async def _validate_url(self, url):
|
|
184
255
|
if not url.startswith(('http://', 'https://')):
|
|
185
|
-
protocol = shellprint.
|
|
256
|
+
protocol = shellprint.ask("未指定协议,请输入使用的协议", choices=['http', 'https'], default="https")
|
|
186
257
|
url = f"{protocol}://{url}"
|
|
187
258
|
if not url.endswith('.json'):
|
|
188
259
|
url = f"{url}/map.json"
|
|
@@ -190,10 +261,12 @@ class SourceManager:
|
|
|
190
261
|
async with aiohttp.ClientSession() as session:
|
|
191
262
|
async with session.get(url) as response:
|
|
192
263
|
response.raise_for_status()
|
|
193
|
-
|
|
264
|
+
try:
|
|
265
|
+
content = await response.text()
|
|
266
|
+
json.loads(content)
|
|
194
267
|
return url
|
|
195
|
-
|
|
196
|
-
shellprint.panel(f"源 {url} 返回的内容不是有效的 JSON
|
|
268
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
269
|
+
shellprint.panel(f"源 {url} 返回的内容不是有效的 JSON 格式: {e}", "错误", "error")
|
|
197
270
|
return None
|
|
198
271
|
except Exception as e:
|
|
199
272
|
shellprint.panel(f"访问源 {url} 失败: {e}", "错误", "error")
|
|
@@ -230,8 +303,9 @@ class SourceManager:
|
|
|
230
303
|
async with aiohttp.ClientSession() as session:
|
|
231
304
|
async with session.get(origin) as response:
|
|
232
305
|
response.raise_for_status()
|
|
233
|
-
|
|
234
|
-
|
|
306
|
+
try:
|
|
307
|
+
text = await response.text()
|
|
308
|
+
content = json.loads(text)
|
|
235
309
|
providers[content["name"]] = content["base"]
|
|
236
310
|
for module in content["modules"].keys():
|
|
237
311
|
module_content = content["modules"][module]
|
|
@@ -244,8 +318,8 @@ class SourceManager:
|
|
|
244
318
|
module,
|
|
245
319
|
f"{providers[content['name']]}{module_origin_name}"
|
|
246
320
|
])
|
|
247
|
-
|
|
248
|
-
shellprint.panel(f"源 {origin} 返回的内容不是有效的 JSON
|
|
321
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
322
|
+
shellprint.panel(f"源 {origin} 返回的内容不是有效的 JSON 格式: {e}", "错误", "error")
|
|
249
323
|
except Exception as e:
|
|
250
324
|
shellprint.panel(f"获取 {origin} 时出错: {e}", "错误", "error")
|
|
251
325
|
|
|
@@ -1090,7 +1164,6 @@ def main():
|
|
|
1090
1164
|
else:
|
|
1091
1165
|
origin_parser.print_help()
|
|
1092
1166
|
else:
|
|
1093
|
-
# 如果没有提供命令,显示帮助信息
|
|
1094
1167
|
parser.print_help()
|
|
1095
1168
|
|
|
1096
1169
|
if __name__ == "__main__":
|
ErisPulse/adapter.py
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 适配器系统
|
|
3
|
+
|
|
4
|
+
提供平台适配器基类、消息发送DSL和适配器管理功能。
|
|
5
|
+
|
|
6
|
+
## 主要功能
|
|
7
|
+
- BaseAdapter: 适配器基类
|
|
8
|
+
- SendDSL: 链式消息发送接口
|
|
9
|
+
- AdapterManager: 适配器管理
|
|
10
|
+
- 适配器注册和生命周期管理
|
|
11
|
+
|
|
12
|
+
## API 文档
|
|
13
|
+
### 适配器基类 (BaseAdapter):
|
|
14
|
+
- call_api(): 必须实现的API调用方法
|
|
15
|
+
- start(): 启动适配器
|
|
16
|
+
- shutdown(): 关闭适配器
|
|
17
|
+
- on(): 事件监听装饰器
|
|
18
|
+
- emit(): 触发事件
|
|
19
|
+
|
|
20
|
+
### 消息发送DSL (SendDSL):
|
|
21
|
+
- To(): 设置消息目标
|
|
22
|
+
- Text(): 发送文本消息
|
|
23
|
+
- 可扩展其他消息类型
|
|
24
|
+
|
|
25
|
+
### 适配器管理 (AdapterManager):
|
|
26
|
+
- register(): 注册适配器
|
|
27
|
+
- startup(): 启动适配器
|
|
28
|
+
- shutdown(): 关闭所有适配器
|
|
29
|
+
- get(): 获取适配器实例
|
|
30
|
+
|
|
31
|
+
### 示例用法:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
from ErisPulse import sdk
|
|
35
|
+
|
|
36
|
+
# 注册适配器
|
|
37
|
+
sdk.adapter.register("MyPlatform", MyAdapter)
|
|
38
|
+
|
|
39
|
+
# 发送消息
|
|
40
|
+
sdk.adapter.MyPlatform.Send.To("user", "123").Text("Hello")
|
|
41
|
+
|
|
42
|
+
# 启动适配器
|
|
43
|
+
await sdk.adapter.startup()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
1
48
|
import functools
|
|
2
49
|
import asyncio
|
|
3
50
|
from typing import Callable, Any, Dict, List, Type, Optional, Set
|
ErisPulse/db.py
CHANGED
|
@@ -1,12 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 环境配置
|
|
3
|
+
|
|
4
|
+
提供键值存储、事务支持、快照和恢复功能,用于管理框架配置数据。
|
|
5
|
+
|
|
6
|
+
## API 文档
|
|
7
|
+
### 基本操作:
|
|
8
|
+
- get(key, default=None) -> any: 获取配置项
|
|
9
|
+
- set(key, value) -> bool: 设置配置项
|
|
10
|
+
- delete(key) -> bool: 删除配置项
|
|
11
|
+
- get_all_keys() -> list[str]: 获取所有键名
|
|
12
|
+
|
|
13
|
+
### 批量操作:
|
|
14
|
+
- get_multi(keys) -> dict: 批量获取键值
|
|
15
|
+
- set_multi(items) -> bool: 批量设置键值
|
|
16
|
+
- delete_multi(keys) -> bool: 批量删除键值
|
|
17
|
+
|
|
18
|
+
### 事务管理:
|
|
19
|
+
- transaction() -> contextmanager: 创建事务上下文
|
|
20
|
+
|
|
21
|
+
### 快照管理:
|
|
22
|
+
- snapshot(name=None) -> str: 创建数据库快照
|
|
23
|
+
- restore(snapshot_name) -> bool: 从快照恢复
|
|
24
|
+
- list_snapshots() -> list: 列出所有快照
|
|
25
|
+
- delete_snapshot(name) -> bool: 删除指定快照
|
|
26
|
+
- set_snapshot_interval(seconds): 设置自动快照间隔
|
|
27
|
+
|
|
28
|
+
### 其他功能:
|
|
29
|
+
- clear(): 清空所有配置
|
|
30
|
+
- load_env_file(): 从env.py加载配置(SDK自动)
|
|
31
|
+
|
|
32
|
+
### 示例用法:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
from ErisPulse import sdk
|
|
36
|
+
|
|
37
|
+
env = sdk.env
|
|
38
|
+
|
|
39
|
+
# 基本操作
|
|
40
|
+
env.set('config_key', 'value')
|
|
41
|
+
value = env.get('config_key')
|
|
42
|
+
value_another = env.config_key # 通过属性访问
|
|
43
|
+
env.config_key = 'value' # 通过属性赋值
|
|
44
|
+
|
|
45
|
+
# 事务使用
|
|
46
|
+
with env.transaction():
|
|
47
|
+
env.set('key1', 'value1')
|
|
48
|
+
env.set('key2', 'value2')
|
|
49
|
+
|
|
50
|
+
# 快照管理
|
|
51
|
+
snapshot_path = env.snapshot()
|
|
52
|
+
env.restore('snapshot_name')
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
|
|
1
57
|
import os
|
|
2
58
|
import json
|
|
3
59
|
import sqlite3
|
|
4
60
|
import importlib.util
|
|
61
|
+
import shutil
|
|
62
|
+
import time
|
|
63
|
+
import threading
|
|
5
64
|
from pathlib import Path
|
|
65
|
+
from datetime import datetime
|
|
66
|
+
from functools import lru_cache
|
|
67
|
+
from .raiserr import raiserr
|
|
6
68
|
|
|
7
69
|
class EnvManager:
|
|
8
70
|
_instance = None
|
|
9
71
|
db_path = os.path.join(os.path.dirname(__file__), "config.db")
|
|
72
|
+
SNAPSHOT_DIR = os.path.join(os.path.dirname(__file__), "snapshots")
|
|
10
73
|
|
|
11
74
|
def __new__(cls, *args, **kwargs):
|
|
12
75
|
if not cls._instance:
|
|
@@ -15,12 +78,21 @@ class EnvManager:
|
|
|
15
78
|
|
|
16
79
|
def __init__(self):
|
|
17
80
|
if not hasattr(self, "_initialized"):
|
|
81
|
+
# 确保关键属性在初始化时都有默认值
|
|
82
|
+
self._last_snapshot_time = time.time()
|
|
83
|
+
self._snapshot_interval = 3600
|
|
18
84
|
self._init_db()
|
|
19
85
|
self._initialized = True
|
|
20
86
|
|
|
21
87
|
def _init_db(self):
|
|
22
88
|
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
|
|
89
|
+
os.makedirs(self.SNAPSHOT_DIR, exist_ok=True)
|
|
23
90
|
conn = sqlite3.connect(self.db_path)
|
|
91
|
+
|
|
92
|
+
# 启用WAL模式提高并发性能
|
|
93
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
94
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
95
|
+
|
|
24
96
|
cursor = conn.cursor()
|
|
25
97
|
cursor.execute("""
|
|
26
98
|
CREATE TABLE IF NOT EXISTS config (
|
|
@@ -30,6 +102,10 @@ class EnvManager:
|
|
|
30
102
|
""")
|
|
31
103
|
conn.commit()
|
|
32
104
|
conn.close()
|
|
105
|
+
|
|
106
|
+
# 初始化自动快照调度器
|
|
107
|
+
self._last_snapshot_time = time.time() # 初始化为当前时间
|
|
108
|
+
self._snapshot_interval = 3600 # 默认每小时自动快照
|
|
33
109
|
|
|
34
110
|
def get(self, key, default=None):
|
|
35
111
|
try:
|
|
@@ -59,18 +135,128 @@ class EnvManager:
|
|
|
59
135
|
|
|
60
136
|
def set(self, key, value):
|
|
61
137
|
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
138
|
+
with self.transaction():
|
|
139
|
+
conn = sqlite3.connect(self.db_path)
|
|
140
|
+
cursor = conn.cursor()
|
|
141
|
+
cursor.execute("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)", (key, serialized_value))
|
|
142
|
+
conn.commit()
|
|
143
|
+
conn.close()
|
|
144
|
+
|
|
145
|
+
self._check_auto_snapshot()
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
def set_multi(self, items):
|
|
149
|
+
with self.transaction():
|
|
150
|
+
conn = sqlite3.connect(self.db_path)
|
|
151
|
+
cursor = conn.cursor()
|
|
152
|
+
for key, value in items.items():
|
|
153
|
+
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
154
|
+
cursor.execute("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)",
|
|
155
|
+
(key, serialized_value))
|
|
156
|
+
conn.commit()
|
|
157
|
+
conn.close()
|
|
158
|
+
|
|
159
|
+
self._check_auto_snapshot()
|
|
160
|
+
return True
|
|
67
161
|
|
|
68
162
|
def delete(self, key):
|
|
163
|
+
with self.transaction():
|
|
164
|
+
conn = sqlite3.connect(self.db_path)
|
|
165
|
+
cursor = conn.cursor()
|
|
166
|
+
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
167
|
+
conn.commit()
|
|
168
|
+
conn.close()
|
|
169
|
+
|
|
170
|
+
self._check_auto_snapshot()
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
def delete_multi(self, keys):
|
|
174
|
+
with self.transaction():
|
|
175
|
+
conn = sqlite3.connect(self.db_path)
|
|
176
|
+
cursor = conn.cursor()
|
|
177
|
+
cursor.executemany("DELETE FROM config WHERE key = ?", [(k,) for k in keys])
|
|
178
|
+
conn.commit()
|
|
179
|
+
conn.close()
|
|
180
|
+
|
|
181
|
+
self._check_auto_snapshot()
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
def get_multi(self, keys):
|
|
69
185
|
conn = sqlite3.connect(self.db_path)
|
|
70
186
|
cursor = conn.cursor()
|
|
71
|
-
|
|
72
|
-
|
|
187
|
+
placeholders = ','.join(['?'] * len(keys))
|
|
188
|
+
cursor.execute(f"SELECT key, value FROM config WHERE key IN ({placeholders})", keys)
|
|
189
|
+
results = {row[0]: json.loads(row[1]) if row[1].startswith(('{', '[')) else row[1]
|
|
190
|
+
for row in cursor.fetchall()}
|
|
73
191
|
conn.close()
|
|
192
|
+
return results
|
|
193
|
+
|
|
194
|
+
def transaction(self):
|
|
195
|
+
return self._Transaction(self)
|
|
196
|
+
|
|
197
|
+
class _Transaction:
|
|
198
|
+
def __init__(self, env_manager):
|
|
199
|
+
self.env_manager = env_manager
|
|
200
|
+
self.conn = None
|
|
201
|
+
self.cursor = None
|
|
202
|
+
|
|
203
|
+
def __enter__(self):
|
|
204
|
+
self.conn = sqlite3.connect(self.env_manager.db_path)
|
|
205
|
+
self.cursor = self.conn.cursor()
|
|
206
|
+
self.cursor.execute("BEGIN TRANSACTION")
|
|
207
|
+
return self
|
|
208
|
+
|
|
209
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
210
|
+
if exc_type is None:
|
|
211
|
+
self.conn.commit()
|
|
212
|
+
else:
|
|
213
|
+
self.conn.rollback()
|
|
214
|
+
from .logger import logger
|
|
215
|
+
logger.error(f"事务执行失败: {exc_val}")
|
|
216
|
+
self.conn.close()
|
|
217
|
+
|
|
218
|
+
def _check_auto_snapshot(self):
|
|
219
|
+
from .logger import logger
|
|
220
|
+
|
|
221
|
+
if not hasattr(self, '_last_snapshot_time') or self._last_snapshot_time is None:
|
|
222
|
+
self._last_snapshot_time = time.time()
|
|
223
|
+
|
|
224
|
+
if not hasattr(self, '_snapshot_interval') or self._snapshot_interval is None:
|
|
225
|
+
self._snapshot_interval = 3600
|
|
226
|
+
|
|
227
|
+
current_time = time.time()
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
time_diff = current_time - self._last_snapshot_time
|
|
231
|
+
if not isinstance(time_diff, (int, float)):
|
|
232
|
+
raiserr.register(
|
|
233
|
+
"ErisPulseEnvTimeDiffTypeError",
|
|
234
|
+
doc = "时间差应为数值类型",
|
|
235
|
+
)
|
|
236
|
+
raiserr.ErisPulseEnvTimeDiffTypeError(
|
|
237
|
+
f"时间差应为数值类型,实际为: {type(time_diff)}"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if not isinstance(self._snapshot_interval, (int, float)):
|
|
241
|
+
raiserr.register(
|
|
242
|
+
"ErisPulseEnvSnapshotIntervalTypeError",
|
|
243
|
+
doc = "快照间隔应为数值类型",
|
|
244
|
+
)
|
|
245
|
+
raiserr.ErisPulseEnvSnapshotIntervalTypeError(
|
|
246
|
+
f"快照间隔应为数值类型,实际为: {type(self._snapshot_interval)}"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if time_diff > self._snapshot_interval:
|
|
250
|
+
self._last_snapshot_time = current_time
|
|
251
|
+
self.snapshot(f"auto_{datetime.now().strftime('%Y%m%d_%H%M%S')}")
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error(f"自动快照检查失败: {e}")
|
|
255
|
+
self._last_snapshot_time = current_time
|
|
256
|
+
self._snapshot_interval = 3600
|
|
257
|
+
|
|
258
|
+
def set_snapshot_interval(self, seconds):
|
|
259
|
+
self._snapshot_interval = seconds
|
|
74
260
|
|
|
75
261
|
def clear(self):
|
|
76
262
|
conn = sqlite3.connect(self.db_path)
|
|
@@ -127,4 +313,99 @@ from ErisPulse import sdk
|
|
|
127
313
|
from .logger import logger
|
|
128
314
|
logger.error(f"配置项 {key} 不存在")
|
|
129
315
|
|
|
130
|
-
|
|
316
|
+
def __setattr__(self, key, value):
|
|
317
|
+
try:
|
|
318
|
+
self.set(key, value)
|
|
319
|
+
except Exception as e:
|
|
320
|
+
from .logger import logger
|
|
321
|
+
logger.error(f"设置配置项 {key} 失败: {e}")
|
|
322
|
+
|
|
323
|
+
def snapshot(self, name=None):
|
|
324
|
+
if not name:
|
|
325
|
+
name = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
326
|
+
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{name}.db")
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
# 快照目录
|
|
330
|
+
os.makedirs(self.SNAPSHOT_DIR, exist_ok=True)
|
|
331
|
+
|
|
332
|
+
# 安全关闭连接
|
|
333
|
+
if hasattr(self, "_conn") and self._conn is not None:
|
|
334
|
+
try:
|
|
335
|
+
self._conn.close()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
from .logger import logger
|
|
338
|
+
logger.warning(f"关闭数据库连接时出错: {e}")
|
|
339
|
+
|
|
340
|
+
# 创建快照
|
|
341
|
+
shutil.copy2(self.db_path, snapshot_path)
|
|
342
|
+
from .logger import logger
|
|
343
|
+
logger.info(f"数据库快照已创建: {snapshot_path}")
|
|
344
|
+
return snapshot_path
|
|
345
|
+
except Exception as e:
|
|
346
|
+
from .logger import logger
|
|
347
|
+
logger.error(f"创建快照失败: {e}")
|
|
348
|
+
raise
|
|
349
|
+
|
|
350
|
+
def restore(self, snapshot_name):
|
|
351
|
+
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{snapshot_name}.db") \
|
|
352
|
+
if not snapshot_name.endswith('.db') else snapshot_name
|
|
353
|
+
|
|
354
|
+
if not os.path.exists(snapshot_path):
|
|
355
|
+
from .logger import logger
|
|
356
|
+
logger.error(f"快照文件不存在: {snapshot_path}")
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
# 安全关闭连接
|
|
361
|
+
if hasattr(self, "_conn") and self._conn is not None:
|
|
362
|
+
try:
|
|
363
|
+
self._conn.close()
|
|
364
|
+
except Exception as e:
|
|
365
|
+
from .logger import logger
|
|
366
|
+
logger.warning(f"关闭数据库连接时出错: {e}")
|
|
367
|
+
|
|
368
|
+
# 执行恢复操作
|
|
369
|
+
shutil.copy2(snapshot_path, self.db_path)
|
|
370
|
+
self._init_db() # 恢复后重新初始化数据库连接
|
|
371
|
+
from .logger import logger
|
|
372
|
+
logger.info(f"数据库已从快照恢复: {snapshot_path}")
|
|
373
|
+
return True
|
|
374
|
+
except Exception as e:
|
|
375
|
+
from .logger import logger
|
|
376
|
+
logger.error(f"恢复快照失败: {e}")
|
|
377
|
+
return False
|
|
378
|
+
|
|
379
|
+
def list_snapshots(self):
|
|
380
|
+
snapshots = []
|
|
381
|
+
for f in os.listdir(self.SNAPSHOT_DIR):
|
|
382
|
+
if f.endswith('.db'):
|
|
383
|
+
path = os.path.join(self.SNAPSHOT_DIR, f)
|
|
384
|
+
stat = os.stat(path)
|
|
385
|
+
snapshots.append((
|
|
386
|
+
f[:-3], # 去掉.db后缀
|
|
387
|
+
datetime.fromtimestamp(stat.st_ctime),
|
|
388
|
+
stat.st_size
|
|
389
|
+
))
|
|
390
|
+
return sorted(snapshots, key=lambda x: x[1], reverse=True)
|
|
391
|
+
|
|
392
|
+
def delete_snapshot(self, snapshot_name):
|
|
393
|
+
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{snapshot_name}.db") \
|
|
394
|
+
if not snapshot_name.endswith('.db') else snapshot_name
|
|
395
|
+
|
|
396
|
+
if not os.path.exists(snapshot_path):
|
|
397
|
+
from .logger import logger
|
|
398
|
+
logger.error(f"快照文件不存在: {snapshot_path}")
|
|
399
|
+
return False
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
os.remove(snapshot_path)
|
|
403
|
+
from .logger import logger
|
|
404
|
+
logger.info(f"快照已删除: {snapshot_path}")
|
|
405
|
+
return True
|
|
406
|
+
except Exception as e:
|
|
407
|
+
from .logger import logger
|
|
408
|
+
logger.error(f"删除快照失败: {e}")
|
|
409
|
+
return False
|
|
410
|
+
|
|
411
|
+
env = EnvManager()
|
ErisPulse/logger.py
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 日志系统
|
|
3
|
+
|
|
4
|
+
提供模块化、多级别的日志记录功能,支持内存存储和文件输出。
|
|
5
|
+
|
|
6
|
+
## API 文档
|
|
7
|
+
### 基本操作:
|
|
8
|
+
- debug(msg, *args, **kwargs): 调试信息
|
|
9
|
+
- info(msg, *args, **kwargs): 运行信息
|
|
10
|
+
- warning(msg, *args, **kwargs): 警告信息
|
|
11
|
+
- error(msg, *args, **kwargs): 错误信息
|
|
12
|
+
- critical(msg, *args, **kwargs): 致命错误并终止程序
|
|
13
|
+
|
|
14
|
+
### 日志控制:
|
|
15
|
+
- set_level(level): 设置全局日志级别
|
|
16
|
+
- set_module_level(module_name, level): 设置模块日志级别
|
|
17
|
+
|
|
18
|
+
### 日志存储:
|
|
19
|
+
- save_logs(path): 保存内存中的日志到文件
|
|
20
|
+
- set_output_file(path): 设置日志输出文件
|
|
21
|
+
|
|
22
|
+
### 示例用法:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
from ErisPulse import sdk
|
|
26
|
+
|
|
27
|
+
# 基本日志记录
|
|
28
|
+
sdk.logger.debug("调试信息")
|
|
29
|
+
sdk.logger.info("运行状态")
|
|
30
|
+
|
|
31
|
+
# 模块级日志控制
|
|
32
|
+
sdk.logger.set_module_level("MyModule", "DEBUG")
|
|
33
|
+
|
|
34
|
+
# 异常捕获
|
|
35
|
+
@sdk.logger.catch
|
|
36
|
+
def risky_function():
|
|
37
|
+
raise Exception("出错了")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 准备弃用
|
|
41
|
+
|
|
42
|
+
catch(func_or_level=None, level="error"): 异常捕获装饰器
|
|
43
|
+
- 原因: 异常捕获功能已集成到 raiserr 模块中,建议使用 raiserr 进行异常处理。
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
1
47
|
import logging
|
|
2
48
|
import inspect
|
|
3
49
|
import datetime
|
|
@@ -152,4 +198,4 @@ class Logger:
|
|
|
152
198
|
raiserr.register("CriticalError", doc="发生致命错误")
|
|
153
199
|
raiserr.CriticalError(f"程序发生致命错误:{msg}", exit=True)
|
|
154
200
|
|
|
155
|
-
logger = Logger()
|
|
201
|
+
logger = Logger()
|
ErisPulse/mods.py
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 模块管理系统
|
|
3
|
+
|
|
4
|
+
提供模块的注册、状态管理和依赖解析功能。
|
|
5
|
+
|
|
6
|
+
## API 文档
|
|
7
|
+
### 模块状态:
|
|
8
|
+
- set_module_status(module_name, status): 设置模块启用状态
|
|
9
|
+
- get_module_status(module_name): 获取模块状态
|
|
10
|
+
|
|
11
|
+
### 模块信息:
|
|
12
|
+
- set_module(module_name, module_info): 设置模块信息
|
|
13
|
+
- get_module(module_name): 获取模块信息
|
|
14
|
+
- get_all_modules(): 获取所有模块信息
|
|
15
|
+
- remove_module(module_name): 删除模块
|
|
16
|
+
|
|
17
|
+
### 前缀管理:
|
|
18
|
+
- update_prefixes(module_prefix, status_prefix): 更新存储前缀
|
|
19
|
+
- module_prefix: 模块存储前缀属性
|
|
20
|
+
- status_prefix: 状态存储前缀属性
|
|
21
|
+
|
|
22
|
+
### 示例用法:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
from ErisPulse import sdk
|
|
26
|
+
|
|
27
|
+
# 设置模块状态
|
|
28
|
+
sdk.mods.set_module_status("MyModule", True)
|
|
29
|
+
|
|
30
|
+
# 获取模块信息
|
|
31
|
+
module_info = sdk.mods.get_module("MyModule")
|
|
32
|
+
|
|
33
|
+
# 批量操作
|
|
34
|
+
sdk.mods.set_all_modules({"Module1": {...}, "Module2": {...}})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
1
39
|
import json
|
|
2
40
|
from typing import Dict, Optional
|
|
3
41
|
|
ErisPulse/raiserr.py
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 错误管理系统
|
|
3
|
+
|
|
4
|
+
提供错误类型注册、抛出和管理功能,集成全局异常处理。
|
|
5
|
+
|
|
6
|
+
## API 文档
|
|
7
|
+
### 错误注册:
|
|
8
|
+
- register(name, doc="", base=Exception): 注册新的错误类型
|
|
9
|
+
- info(name: str = None): 获取错误类型信息
|
|
10
|
+
|
|
11
|
+
### 错误抛出:
|
|
12
|
+
- __getattr__(name): 动态获取错误抛出函数
|
|
13
|
+
- ErrorType(msg, exit=False): 抛出注册的错误类型
|
|
14
|
+
|
|
15
|
+
### 全局处理:
|
|
16
|
+
- global_exception_handler: 全局同步异常处理器
|
|
17
|
+
- async_exception_handler: 全局异步异常处理器
|
|
18
|
+
|
|
19
|
+
### 示例用法:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
from ErisPulse import sdk
|
|
23
|
+
|
|
24
|
+
# 注册自定义错误
|
|
25
|
+
sdk.raiserr.register("MyError", doc="自定义错误描述")
|
|
26
|
+
|
|
27
|
+
# 抛出错误
|
|
28
|
+
sdk.raiserr.MyError("发生了错误", exit=False)
|
|
29
|
+
|
|
30
|
+
# 获取错误信息
|
|
31
|
+
error_info = sdk.raiserr.info("MyError")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
1
36
|
import sys
|
|
2
37
|
import traceback
|
|
3
38
|
import asyncio
|
|
@@ -71,4 +106,4 @@ def async_exception_handler(loop, context):
|
|
|
71
106
|
)
|
|
72
107
|
else:
|
|
73
108
|
logger.warning(f"异步任务警告: {message}")
|
|
74
|
-
asyncio.get_event_loop().set_exception_handler(async_exception_handler)
|
|
109
|
+
asyncio.get_event_loop().set_exception_handler(async_exception_handler)
|
ErisPulse/util.py
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# 工具函数集合
|
|
3
|
+
|
|
4
|
+
提供各种实用工具函数和装饰器,简化开发流程。
|
|
5
|
+
|
|
6
|
+
## API 文档
|
|
7
|
+
### 拓扑排序:
|
|
8
|
+
- topological_sort(elements, dependencies, error): 拓扑排序依赖关系
|
|
9
|
+
- show_topology(): 可视化模块依赖关系
|
|
10
|
+
|
|
11
|
+
### 装饰器:
|
|
12
|
+
- @cache: 缓存函数结果
|
|
13
|
+
- @run_in_executor: 将同步函数转为异步
|
|
14
|
+
- @retry(max_attempts=3, delay=1): 失败自动重试
|
|
15
|
+
|
|
16
|
+
### 异步执行:
|
|
17
|
+
- ExecAsync(async_func, *args, **kwargs): 异步执行函数
|
|
18
|
+
|
|
19
|
+
### 示例用法:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
from ErisPulse import sdk
|
|
23
|
+
|
|
24
|
+
# 拓扑排序
|
|
25
|
+
sorted_modules = sdk.util.topological_sort(modules, dependencies, error)
|
|
26
|
+
|
|
27
|
+
# 缓存装饰器
|
|
28
|
+
@sdk.util.cache
|
|
29
|
+
def expensive_operation(param):
|
|
30
|
+
return heavy_computation(param)
|
|
31
|
+
|
|
32
|
+
# 异步执行
|
|
33
|
+
@sdk.util.run_in_executor
|
|
34
|
+
def sync_task():
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
# 重试机制
|
|
38
|
+
@sdk.util.retry(max_attempts=3, delay=1)
|
|
39
|
+
def unreliable_operation():
|
|
40
|
+
pass
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
1
45
|
import time
|
|
2
46
|
import asyncio
|
|
3
47
|
import functools
|
|
@@ -96,4 +140,4 @@ def retry(max_attempts=3, delay=1):
|
|
|
96
140
|
raise
|
|
97
141
|
time.sleep(delay)
|
|
98
142
|
return wrapper
|
|
99
|
-
return decorator
|
|
143
|
+
return decorator
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ErisPulse
|
|
3
|
+
Version: 1.1.14.dev1
|
|
4
|
+
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
|
+
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 ErisPulse
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
29
|
+
Classifier: Intended Audience :: Developers
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
|
33
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
39
|
+
Requires-Python: >=3.9
|
|
40
|
+
Requires-Dist: aiohttp
|
|
41
|
+
Requires-Dist: watchdog
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
**ErisPulse** 是基于 [Framer](https://github.com/FramerOrg/Framer) 构建的异步机器人开发框架。
|
|
46
|
+
|
|
47
|
+
[](https://github.com/FramerOrg)
|
|
48
|
+
[](https://github.com/ErisPulse/ErisPulse/blob/main/LICENSE)
|
|
49
|
+
|
|
50
|
+
[](https://pypi.org/project/ErisPulse/)
|
|
51
|
+
|
|
52
|
+
> 文档站:
|
|
53
|
+
|
|
54
|
+
[](https://www.erisdev.com/docs.html)
|
|
55
|
+
[](https://erispulse.pages.dev/docs.html)
|
|
56
|
+
[](https://erispulse.github.io/docs.html)
|
|
57
|
+
[](https://erispulse.netlify.app/docs.htm)
|
|
58
|
+
|
|
59
|
+
- [GitHub 社区讨论](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
60
|
+
|
|
61
|
+
### 框架选型指南
|
|
62
|
+
| 需求 | 推荐框架 | 理由 |
|
|
63
|
+
|-------------------|----------------|-----------------------------|
|
|
64
|
+
| 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
|
|
65
|
+
| 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
|
|
66
|
+
|
|
67
|
+
## ✨ 核心特性
|
|
68
|
+
- ⚡ 完全异步架构设计(async/await)
|
|
69
|
+
- 🧩 模块化插件系统
|
|
70
|
+
- 🔁 支持python热重载
|
|
71
|
+
- 🛑 统一的错误管理
|
|
72
|
+
- 🛠️ 灵活的配置管理
|
|
73
|
+
|
|
74
|
+
## 📦 安装
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install ErisPulse --upgrade
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 开发者快速入门
|
|
83
|
+
|
|
84
|
+
ErisPulse SDK 支持使用 [`uv`](https://github.com/astral-sh/uv) 进行完整的开发环境管理。你可以**无需手动安装 Python**,直接通过 `uv` 下载 Python、创建虚拟环境并开始开发。
|
|
85
|
+
|
|
86
|
+
### 安装 `uv`
|
|
87
|
+
|
|
88
|
+
#### macOS / Linux:
|
|
89
|
+
```bash
|
|
90
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Windows (PowerShell):
|
|
94
|
+
```powershell
|
|
95
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
验证是否安装成功:
|
|
99
|
+
```bash
|
|
100
|
+
uv --version
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 克隆项目并进入目录
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/ErisPulse/ErisPulse.git
|
|
107
|
+
cd ErisPulse
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 使用 `uv` 自动下载 Python 并创建虚拟环境
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv python install 3.12 # 自动下载并安装 Python 3.12
|
|
114
|
+
uv venv # 创建默认 .venv 虚拟环境
|
|
115
|
+
source .venv/bin/activate
|
|
116
|
+
# Windows: .venv\Scripts\activate
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> ✅ 如果你切换分支或需要不同 Python 版本,只需替换 `3.12` 为其他版本即可。
|
|
120
|
+
|
|
121
|
+
### 安装依赖并开始开发
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
uv pip install -e .
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
这将以“开发模式”安装 SDK,所有本地修改都会立即生效。
|
|
128
|
+
|
|
129
|
+
### 验证安装
|
|
130
|
+
|
|
131
|
+
运行以下命令确认 SDK 正常加载:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python -c "from ErisPulse import sdk; sdk.init()"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 运行测试
|
|
138
|
+
|
|
139
|
+
我们提供了一个交互式测试脚本,可以帮助您快速验证SDK功能:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv run devs/test.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
测试脚本提供以下功能:
|
|
146
|
+
- 日志功能测试
|
|
147
|
+
- 环境配置测试
|
|
148
|
+
- 错误管理测试
|
|
149
|
+
- 工具函数测试
|
|
150
|
+
- 适配器功能测试
|
|
151
|
+
- 版本信息查看
|
|
152
|
+
|
|
153
|
+
### 开始开发
|
|
154
|
+
|
|
155
|
+
你可以通过 CLI 工具进行模块调试、热重载开发等操作:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
epsdk run your_script.py --reload
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 🤝 贡献
|
|
164
|
+
|
|
165
|
+
欢迎任何形式的贡献!无论是报告 bug、提出新功能请求,还是直接提交代码,都非常感谢。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=k-X8WV9sIrYeYDNb8qZyBZ8s8yNL2bKWhZdkPbzRWII,8936
|
|
2
|
+
ErisPulse/__main__.py,sha256=aZK7FdvFTgsKgHPYpaeKpA_l2JFhEy9TNy3vndkONFk,50001
|
|
3
|
+
ErisPulse/adapter.py,sha256=g5npw-RAmGKXY3gTO8oNA63DRJ3Z9xVrl9BWuus-EQs,9837
|
|
4
|
+
ErisPulse/db.py,sha256=rXxpTug5NMZQTJ_07lJTe-PWdoePNPAkYoQDfazFapY,14411
|
|
5
|
+
ErisPulse/logger.py,sha256=832gGOiRUfXhYjz9JZ3DfVzB6ttqIimyLU0xk5k7lZ0,7319
|
|
6
|
+
ErisPulse/mods.py,sha256=lBCWM0VfsCJehnMgpswKYuHitg30JERg0H4WN6kqgbA,4509
|
|
7
|
+
ErisPulse/raiserr.py,sha256=2dJA3g6aje4yxhr1JPeZsrxudblKAOq6T_LMHeRjQ24,3438
|
|
8
|
+
ErisPulse/util.py,sha256=tmmZA4Ef5ElB01KZg2fTZLrkZOJbORjnkAd8sXj7xqw,4335
|
|
9
|
+
erispulse-1.1.14.dev1.dist-info/METADATA,sha256=I03H-qlpVtNAxuX_H60HgnTaMqLrKGpPfe5zIq9EhfE,5882
|
|
10
|
+
erispulse-1.1.14.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
+
erispulse-1.1.14.dev1.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
12
|
+
erispulse-1.1.14.dev1.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
|
|
13
|
+
erispulse-1.1.14.dev1.dist-info/RECORD,,
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ErisPulse
|
|
3
|
-
Version: 1.1.12
|
|
4
|
-
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
|
-
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
7
|
-
Classifier: Intended Audience :: Developers
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Operating System :: OS Independent
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
-
Requires-Python: >=3.7
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
License-File: LICENSE
|
|
22
|
-
Requires-Dist: aiohttp
|
|
23
|
-
Requires-Dist: watchdog
|
|
24
|
-
Dynamic: license-file
|
|
25
|
-
|
|
26
|
-

|
|
27
|
-
**ErisPulse** 是基于 [Framer](https://github.com/FramerOrg/Framer) 构建的异步机器人开发框架。
|
|
28
|
-
|
|
29
|
-
## 合作伙伴
|
|
30
|
-
[](https://github.com/FramerOrg)
|
|
31
|
-
|
|
32
|
-
### 框架选型指南
|
|
33
|
-
| 需求 | 推荐框架 | 理由 |
|
|
34
|
-
|-------------------|----------------|-----------------------------|
|
|
35
|
-
| 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
|
|
36
|
-
| 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## ✨ 核心特性
|
|
40
|
-
- ⚡ 完全异步架构设计(async/await)
|
|
41
|
-
- 🧩 模块化插件系统
|
|
42
|
-
- 📜 内置日志系统
|
|
43
|
-
- 🛑 统一的错误管理
|
|
44
|
-
- 🛠️ 灵活的配置管理
|
|
45
|
-
|
|
46
|
-
## 📦 安装
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
pip install ErisPulse --upgrade
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**要求**:Python ≥ 3.7,pip ≥ 20.0
|
|
53
|
-
|
|
54
|
-
## 🚀 快速开始
|
|
55
|
-
|
|
56
|
-
```python
|
|
57
|
-
import asyncio
|
|
58
|
-
from ErisPulse import sdk, logger
|
|
59
|
-
|
|
60
|
-
async def main():
|
|
61
|
-
sdk.init()
|
|
62
|
-
logger.info("ErisPulse 已启动")
|
|
63
|
-
# 这里可以添加自定义逻辑
|
|
64
|
-
|
|
65
|
-
if __name__ == "__main__":
|
|
66
|
-
asyncio.run(main())
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## 导航
|
|
70
|
-
- [开发者指南](docs/DEVELOPMENT.md)
|
|
71
|
-
- [底层方法与接口](docs/REFERENCE.md)
|
|
72
|
-
- [命令行工具](docs/CLI.md)
|
|
73
|
-
- [源配置指南](docs/ORIGIN.md)
|
|
74
|
-
- [更新日志](docs/CHANGELOG.md)
|
|
75
|
-
> [GitHub 社区](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
76
|
-
|
|
77
|
-
## 🤝 贡献
|
|
78
|
-
|
|
79
|
-
欢迎任何形式的贡献!无论是报告 bug、提出新功能请求,还是直接提交代码,都非常感谢。
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=tCgjl-0rUeiHV_2rdFlQ-CVTFzbKdjdqXvUApRc6Avk,8095
|
|
2
|
-
ErisPulse/__main__.py,sha256=ZdrOqsZU1C5wJ4CycTge9l7IUldVUDtak17yWRxnxhs,47728
|
|
3
|
-
ErisPulse/adapter.py,sha256=5ntqm5Xrzdi1F6N4kWhqiRzMavSO92Fj9WZ-d3-V2VM,8798
|
|
4
|
-
ErisPulse/db.py,sha256=DuIc19GbCQMi17BWfLjaDHnthIbdysTSRxaYVFur07w,4424
|
|
5
|
-
ErisPulse/logger.py,sha256=sMA1mUZvwJ8wHwdyCrgIf_VRICv_uBCkx3tmd1stF3E,6094
|
|
6
|
-
ErisPulse/mods.py,sha256=lNiZP2EcfUhYRnOQwROyVnmhsfmk8JAZhmbhxfC2-VQ,3513
|
|
7
|
-
ErisPulse/raiserr.py,sha256=z8BigWkVrBE9dD_dJa5np2YYREwdugyWXKE4_-LEO_Q,2616
|
|
8
|
-
ErisPulse/util.py,sha256=ux3-QRT0_JjabL6S9KChhyR1E_CSRiVYEFYV5txML1M,3406
|
|
9
|
-
erispulse-1.1.12.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
|
|
10
|
-
erispulse-1.1.12.dist-info/METADATA,sha256=BDRDFwkbWqz7WsebH1cKCbyLaCPBPumDkwFiMG2LJq4,2751
|
|
11
|
-
erispulse-1.1.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
erispulse-1.1.12.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
13
|
-
erispulse-1.1.12.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
14
|
-
erispulse-1.1.12.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ErisPulse
|
|
File without changes
|
|
File without changes
|