ErisPulse 1.1.14__py3-none-any.whl → 1.1.15__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 CHANGED
@@ -8,12 +8,6 @@
8
8
  - 预注册核心错误类型
9
9
  - 提供SDK初始化入口
10
10
  - 集成各核心模块
11
- - 支持项目内模块加载(优先于SDK模块)
12
-
13
- ## 模块加载机制
14
- - **SDK 内置模块**:位于 `src/ErisPulse/modules/`,会被写入数据库并支持状态管理。
15
- - **项目自定义模块**:位于项目根目录下的 `modules/`,仅运行时加载,不会写入数据库。
16
- - **冲突处理**:若同一模块存在于多个路径,选择权重更高的路径(项目模块 > SDK 模块)。
17
11
 
18
12
  ## API 文档
19
13
  ### 核心对象:
@@ -24,13 +18,13 @@
24
18
  - CaughtExternalError: 外部捕获异常
25
19
  - InitError: 初始化错误
26
20
  - MissingDependencyError: 缺少依赖错误
27
- - InvalidDependencyError: 依赖无效错误
21
+ - InvalidDependencyError: 无效依赖错误
28
22
  - CycleDependencyError: 循环依赖错误
29
23
  - ModuleLoadError: 模块加载错误
30
24
 
31
25
  ### 示例用法:
32
26
 
33
- ```python
27
+ ```
34
28
  from ErisPulse import sdk
35
29
 
36
30
  # 初始化SDK
@@ -42,15 +36,10 @@ sdk.logger.info("SDK已初始化")
42
36
 
43
37
  """
44
38
 
39
+ import types
40
+ sdk = types.SimpleNamespace()
45
41
  import os
46
42
  import sys
47
- import logging
48
- from types import SimpleNamespace
49
-
50
- # 初始化全局 sdk 对象
51
- sdk = SimpleNamespace()
52
-
53
- # 导入内部依赖
54
43
  from . import util
55
44
  from .raiserr import raiserr
56
45
  from .logger import logger
@@ -58,220 +47,180 @@ from .db import env
58
47
  from .mods import mods
59
48
  from .adapter import adapter, BaseAdapter, SendDSL
60
49
 
61
- # Windows 下启用 ANSI 颜色输出
50
+ # 这里不能删,确保windows下的shell能正确显示颜色
62
51
  os.system('')
63
52
 
64
- # 挂载基础组件
65
- for name in ['env', 'mods', 'util', 'raiserr', 'logger', 'adapter', 'SendDSL', 'BaseAdapter']:
66
- setattr(sdk, name, eval(name))
67
-
68
- # 注册错误类型
69
- raiserr.register("CaughtExternalError", doc="捕获的非SDK抛出的异常")
70
- raiserr.register("InitError", doc="SDK初始化错误")
71
- raiserr.register("MissingDependencyError", doc="缺少依赖错误")
72
- raiserr.register("InvalidDependencyError", doc="依赖无效错误")
73
- raiserr.register("CycleDependencyError", doc="循环依赖错误")
74
- raiserr.register("ModuleLoadError", doc="模块加载错误")
53
+ setattr(sdk, "env", env)
54
+ setattr(sdk, "mods", mods)
55
+ setattr(sdk, "util", util)
56
+ setattr(sdk, "raiserr", raiserr)
57
+ setattr(sdk, "logger", logger)
58
+ setattr(sdk, "adapter", adapter)
59
+ setattr(sdk, "SendDSL", SendDSL)
60
+ setattr(sdk, "BaseAdapter", BaseAdapter)
61
+
62
+ # 注册 ErrorHook 并预注册常用错误类型
63
+ raiserr.register("CaughtExternalError" , doc="捕获的非SDK抛出的异常")
64
+ raiserr.register("InitError" , doc="SDK初始化错误")
65
+ raiserr.register("MissingDependencyError" , doc="缺少依赖错误")
66
+ raiserr.register("InvalidDependencyError" , doc="依赖无效错误")
67
+ raiserr.register("CycleDependencyError" , doc="依赖循环错误")
68
+ raiserr.register("ModuleLoadError" , doc="模块加载错误")
75
69
 
76
70
  def init() -> None:
77
71
  try:
78
72
  logger.info("[Init] SDK 正在初始化...")
79
-
80
73
  if env.create_env_file_if_not_exists():
81
74
  logger.info("[Init] 项目首次初始化,建议先配置环境变量")
82
75
  if input("是否立即退出?(y/n): ").strip().lower() == "y":
83
76
  sys.exit(0)
84
77
  env.load_env_file()
85
78
 
86
- # 定义模块路径
87
- projectModulePath = os.path.join(os.getcwd(), "modules") # 项目模块
88
- sdkModulePath = os.path.join(os.path.dirname(__file__), "modules") # SDK 模块
89
-
90
- # 构建模块路径字典(权重控制)
91
- modulePaths = {
92
- projectModulePath: 100, # 项目模块优先级更高
93
- sdkModulePath: 50 # SDK 内置模块次之
94
- }
95
-
96
- # 存储模块名到路径的映射
97
- moduleSourceMap = {} # {module_name: [(path, weight), ...]}
98
-
99
- # 扫描模块并记录来源路径和权重
100
- for modulePath, weight in modulePaths.items():
101
- if os.path.exists(modulePath) and os.path.isdir(modulePath):
102
- modules = [
103
- x for x in os.listdir(modulePath)
104
- if os.path.isdir(os.path.join(modulePath, x))
105
- ]
106
- for module in modules:
107
- if module not in moduleSourceMap:
108
- moduleSourceMap[module] = []
109
- moduleSourceMap[module].append((modulePath, weight))
110
-
111
- # 处理模块冲突(按权重选择)
112
- TempModules = []
113
-
114
- for module, sources in moduleSourceMap.items():
115
- if len(sources) > 1:
116
- # 按权重排序
117
- sources.sort(key=lambda x: x[1], reverse=True)
79
+ sdkModulePath = os.path.join(os.path.dirname(__file__), "modules")
118
80
 
119
- selected_path = sources[0][0]
120
- logger.warning(f"模块 {module} 在多个路径存在,选择权重更高的: {selected_path}")
81
+ if not os.path.exists(sdkModulePath):
82
+ os.makedirs(sdkModulePath)
121
83
 
122
- # 确保选中的路径在 sys.path 最前面
123
- if selected_path in sys.path:
124
- sys.path.remove(selected_path)
125
- sys.path.insert(0, selected_path)
84
+ sys.path.append(sdkModulePath)
126
85
 
127
- TempModules.append(module)
86
+ TempModules = [
87
+ x for x in os.listdir(sdkModulePath)
88
+ if os.path.isdir(os.path.join(sdkModulePath, x))
89
+ ]
128
90
 
129
- # 动态导入模块(不存入数据库)
130
- module_objs = {}
91
+ sdkInstalledModuleNames: list[str] = []
92
+ disabledModules: list[str] = []
131
93
 
94
+ # ==== 扫描模块并收集基本信息 ====
95
+ module_objs = {} # {module_name: moduleObj}
132
96
  for module_name in TempModules:
133
97
  try:
134
98
  moduleObj = __import__(module_name)
135
-
136
- # 验证必要属性
137
99
  if not hasattr(moduleObj, "moduleInfo") or not isinstance(moduleObj.moduleInfo, dict):
138
100
  logger.warning(f"模块 {module_name} 缺少有效的 'moduleInfo' 字典.")
139
101
  continue
140
102
  if "name" not in moduleObj.moduleInfo.get("meta", {}):
141
- logger.warning(f"模块 {module_name} 的 'moduleInfo' 缺少必要 'name' 键.")
103
+ logger.warning(f"模块 {module_name} 的 'moduleInfo' 字典 缺少必要 'name' 键.")
142
104
  continue
143
105
  if not hasattr(moduleObj, "Main"):
144
106
  logger.warning(f"模块 {module_name} 缺少 'Main' 类.")
145
107
  continue
146
108
 
147
109
  meta_name = moduleObj.moduleInfo["meta"]["name"]
148
- module_status = mods.get_module_status(meta_name)
110
+ module_info = mods.get_module(meta_name)
111
+ if module_info is None:
112
+ module_info = {
113
+ "status": True,
114
+ "info": moduleObj.moduleInfo
115
+ }
116
+ mods.set_module(meta_name, module_info)
117
+ logger.info(f"模块 {meta_name} 信息已初始化并存储到数据库")
149
118
 
150
- if not module_status:
119
+ if not module_info.get('status', True):
120
+ disabledModules.append(module_name)
151
121
  logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
152
122
  continue
153
123
 
154
124
  required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
155
- missing_required_deps = []
156
-
157
- for dep in required_deps:
158
- dep_name, operator, version = util.parse_dependency_with_version(dep)
159
-
160
- if dep_name not in TempModules:
161
- missing_required_deps.append(dep)
162
- continue
163
-
164
- if operator and version:
165
- try:
166
- dep_module = __import__(dep_name)
167
- if not hasattr(dep_module, "moduleInfo"):
168
- missing_required_deps.append(dep)
169
- continue
170
-
171
- dep_version = dep_module.moduleInfo.get("meta", {}).get("version", "0.0.0")
172
- if not util.check_version_requirement(dep_version, operator, version):
173
- logger.error(
174
- f"模块 {module_name} 的依赖 {dep_name} 版本不匹配: 需要 {operator}{version}, 实际为 {dep_version}")
175
- missing_required_deps.append(dep)
176
- except Exception as e:
177
- logger.error(f"检查模块 {dep_name} 版本时出错: {e}")
178
- missing_required_deps.append(dep)
179
-
125
+ missing_required_deps = [dep for dep in required_deps if dep not in TempModules]
180
126
  if missing_required_deps:
181
- logger.error(
182
- f"模块 {module_name} 缺少必需依赖或版本不匹配: {missing_required_deps}")
183
- raiserr.MissingDependencyError(
184
- f"模块 {module_name} 缺少必需依赖或版本不匹配: {missing_required_deps}")
127
+ logger.error(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
128
+ raiserr.MissingDependencyError(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
185
129
 
186
130
  optional_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
187
131
  available_optional_deps = []
188
-
189
132
  for dep in optional_deps:
190
- d_name, operator, version = util.parse_dependency_with_version(dep)
191
-
192
- if d_name not in TempModules:
193
- continue
194
-
195
- if operator and version:
196
- try:
197
- d_module = __import__(d_name)
198
- if not hasattr(d_module, "moduleInfo"):
199
- continue
200
-
201
- d_version = d_module.moduleInfo.get("meta", {}).get("version", "0.0.0")
202
- if not util.check_version_requirement(d_version, operator, version):
203
- logger.warning(
204
- f"模块 {module_name} 的可选依赖 {d_name} 版本不匹配: 需要 {operator}{version}, 实际为 {d_version}")
205
- continue
206
- except Exception as e:
207
- logger.warning(f"检查模块 {d_name} 版本时出错: {e}")
208
- continue
209
-
210
- available_optional_deps.append(d_name)
133
+ if isinstance(dep, list):
134
+ available_deps = [d for d in dep if d in TempModules]
135
+ if available_deps:
136
+ available_optional_deps.extend(available_deps)
137
+ elif dep in TempModules:
138
+ available_optional_deps.append(dep)
211
139
 
212
140
  if optional_deps and not available_optional_deps:
213
141
  logger.warning(f"模块 {module_name} 缺少所有可选依赖: {optional_deps}")
214
142
 
215
143
  module_objs[module_name] = moduleObj
144
+ sdkInstalledModuleNames.append(module_name)
216
145
 
217
146
  except Exception as e:
218
147
  logger.warning(f"模块 {module_name} 加载失败: {e}")
219
148
  continue
220
149
 
221
- # ==== 创建 README 提示用户模块目录用途 ====
222
- readme_content = """# 项目模块目录
150
+ # ==== 构建依赖图并进行拓扑排序 ====
151
+ sdkModuleDependencies = {}
152
+ for module_name in sdkInstalledModuleNames:
153
+ moduleObj = module_objs[module_name]
154
+ meta_name = moduleObj.moduleInfo["meta"]["name"]
155
+
156
+ req_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
157
+ opt_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
223
158
 
224
- 此目录 (`./modules`) 用于存放项目专属模块。这些模块会优先于 SDK 内置模块被加载,但不会写入数据库。
159
+ available_optional_deps = [dep for dep in opt_deps if dep in sdkInstalledModuleNames]
160
+ deps = req_deps + available_optional_deps
225
161
 
226
- 你可以将自定义模块放入此目录,SDK 会自动识别并加载它们。
227
- """
162
+ for dep in deps:
163
+ if dep in disabledModules:
164
+ logger.warning(f"模块 {meta_name} 的依赖模块 {dep} 已禁用,跳过加载")
165
+ continue
166
+
167
+ if not all(dep in sdkInstalledModuleNames for dep in deps):
168
+ raiserr.InvalidDependencyError(f"模块 {meta_name} 的依赖无效: {deps}")
169
+ sdkModuleDependencies[module_name] = deps
170
+
171
+ sdkInstalledModuleNames: list[str] = sdk.util.topological_sort(
172
+ sdkInstalledModuleNames, sdkModuleDependencies, raiserr.CycleDependencyError
173
+ )
174
+ # 存储模块依赖关系到env
175
+ env.set('module_dependencies', {
176
+ 'modules': sdkInstalledModuleNames,
177
+ 'dependencies': sdkModuleDependencies
178
+ })
179
+
180
+ # ==== 注册适配器 ====
181
+ logger.debug("[Init] 开始注册适配器...")
182
+ for module_name in sdkInstalledModuleNames:
183
+ moduleObj = module_objs[module_name]
184
+ meta_name = moduleObj.moduleInfo["meta"]["name"]
185
+
186
+ try:
187
+ if hasattr(moduleObj, "adapterInfo") and isinstance(moduleObj.adapterInfo, dict):
188
+ for platform_name, adapter_class in moduleObj.adapterInfo.items():
189
+ sdk.adapter.register(platform_name, adapter_class)
190
+ logger.info(f"模块 {meta_name} 注册了适配器: {platform_name}")
191
+ except Exception as e:
192
+ logger.error(f"模块 {meta_name} 注册适配器失败: {e}")
228
193
 
229
- if not os.path.exists(projectModulePath):
230
- os.makedirs(projectModulePath)
194
+ # ==== 存储模块信息到数据库 ====
195
+ all_modules_info = {}
196
+ for module_name in sdkInstalledModuleNames:
197
+ moduleObj = module_objs[module_name]
198
+ moduleInfo: dict = moduleObj.moduleInfo
231
199
 
232
- readme_path = os.path.join(projectModulePath, "README.md")
233
- if not os.path.exists(readme_path):
234
- with open(readme_path, "w", encoding="utf-8") as f:
235
- f.write(readme_content)
236
- logger.info(f"您的项目模块目录已创建 | 请查看 {readme_path} 了解如何使用")
200
+ meta_name = moduleInfo.get("meta", {}).get("name", None)
201
+ module_info = mods.get_module(meta_name)
202
+ mods.set_module(meta_name, {
203
+ "status": True,
204
+ "info": moduleInfo
205
+ })
206
+ logger.debug("所有模块信息已加载并存储到数据库")
237
207
 
238
208
  # ==== 实例化 Main 类并挂载到 sdk ====
239
209
  logger.debug("[Init] 开始实例化模块 Main 类...")
240
-
241
- for module_name in module_objs:
210
+ for module_name in sdkInstalledModuleNames:
242
211
  moduleObj = module_objs[module_name]
243
212
  meta_name = moduleObj.moduleInfo["meta"]["name"]
244
213
 
245
- # 获取模块路径来源
246
- source_path = None
247
- for path, _ in moduleSourceMap.get(module_name, []):
248
- if module_name in os.listdir(path) and os.path.isdir(os.path.join(path, module_name)):
249
- source_path = path
250
- break
251
-
252
- # 判断是否是 SDK 模块(来自 sdkModulePath)
253
- is_sdk_module = source_path == sdkModulePath
254
-
255
- # 如果是 SDK 模块,检查是否已注册到数据库
256
- if is_sdk_module:
257
- module_info = mods.get_module(meta_name)
258
- if module_info is None:
259
- # 首次加载,写入数据库
260
- mods.set_module(meta_name, {
261
- "status": True,
262
- "info": moduleObj.moduleInfo
263
- })
264
- logger.info(f"模块 {meta_name} 信息已初始化并存储到数据库")
265
- else:
266
- logger.debug(f"模块 {meta_name} 已存在于数据库中")
214
+ module_status = mods.get_module_status(meta_name)
215
+ if not module_status:
216
+ continue
267
217
 
268
- # 实例化模块
269
218
  moduleMain = moduleObj.Main(sdk)
270
219
  setattr(moduleMain, "moduleInfo", moduleObj.moduleInfo)
271
220
  setattr(sdk, meta_name, moduleMain)
272
221
  logger.debug(f"模块 {meta_name} 正在初始化")
273
-
274
222
  except Exception as e:
275
223
  raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
276
224
 
225
+
277
226
  sdk.init = init
ErisPulse/__main__.py CHANGED
@@ -549,7 +549,74 @@ def install_pip_dependencies(dependencies):
549
549
  shellprint.panel(f"安装pip依赖失败: {e.stderr}", "错误", "error")
550
550
  return False
551
551
 
552
+ def install_local_module(module_path, force=False):
553
+ """安装本地目录中的模块"""
554
+ module_path = os.path.abspath(module_path)
555
+ if not os.path.exists(module_path):
556
+ shellprint.panel(f"路径不存在: {module_path}", "错误", "error")
557
+ return False
558
+
559
+ # 尝试从目录名获取模块名
560
+ module_name = os.path.basename(module_path.rstrip('/\\'))
561
+
562
+ # 检查是否是有效的模块目录
563
+ init_py = os.path.join(module_path, '__init__.py')
564
+ if not os.path.exists(init_py):
565
+ shellprint.panel(f"目录 {module_path} 不是一个有效的Python模块", "错误", "error")
566
+ return False
567
+
568
+ # 尝试导入模块获取moduleInfo
569
+ try:
570
+ spec = importlib.util.spec_from_file_location(module_name, init_py)
571
+ module = importlib.util.module_from_spec(spec)
572
+ spec.loader.exec_module(module)
573
+ if not hasattr(module, 'moduleInfo'):
574
+ shellprint.panel(f"模块 {module_name} 缺少 moduleInfo 定义", "错误", "error")
575
+ return False
576
+ except Exception as e:
577
+ shellprint.panel(f"导入模块 {module_name} 失败: {e}", "错误", "error")
578
+ return False
579
+
580
+ module_info = mods.get_module(module_name)
581
+ if module_info and not force:
582
+ meta = module_info.get('info', {}).get('meta', {})
583
+ shellprint.panel(
584
+ f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}\n版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}",
585
+ "模块已存在",
586
+ "info"
587
+ )
588
+ if not shellprint.confirm("是否要强制重新安装?", default=False):
589
+ return False
590
+
591
+ # 复制模块到modules目录
592
+ script_dir = os.path.dirname(os.path.abspath(__file__))
593
+ target_dir = os.path.join(script_dir, 'modules', module_name)
594
+
595
+ try:
596
+ if os.path.exists(target_dir):
597
+ shutil.rmtree(target_dir)
598
+ shutil.copytree(module_path, target_dir)
599
+ except Exception as e:
600
+ shellprint.panel(f"复制模块文件失败: {e}", "错误", "error")
601
+ return False
602
+
603
+ # 注册模块信息
604
+ mods.set_module(module_name, {
605
+ 'status': True,
606
+ 'info': {
607
+ 'meta': module.moduleInfo.get('meta', {}),
608
+ 'dependencies': module.moduleInfo.get('dependencies', {})
609
+ }
610
+ })
611
+
612
+ shellprint.panel(f"本地模块 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 安装成功", "成功", "success")
613
+ return True
614
+
552
615
  def install_module(module_name, force=False):
616
+ # 检查是否是本地路径
617
+ if module_name.startswith('.') or os.path.isabs(module_name):
618
+ return install_local_module(module_name, force)
619
+
553
620
  shellprint.panel(f"准备安装模块: {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}", "安装摘要", "info")
554
621
  last_update_time = env.get('last_origin_update_time', None)
555
622
  if last_update_time:
ErisPulse/util.py CHANGED
@@ -1,379 +1,51 @@
1
1
  """
2
2
  # 工具函数集合
3
3
 
4
- 提供各种实用工具函数和装饰器,简化开发流程。包括依赖关系处理、性能优化、异步执行和错误重试等功能。
5
-
6
- ## 核心功能
7
- 1. 依赖关系管理
8
- 2. 函数结果缓存
9
- 3. 异步执行支持
10
- 4. 自动重试机制
11
- 5. 可视化工具
12
- 6. 版本管理和比较
13
-
4
+ 提供各种实用工具函数和装饰器,简化开发流程。
14
5
 
15
6
  ## API 文档
7
+ ### 拓扑排序:
8
+ - topological_sort(elements, dependencies, error): 拓扑排序依赖关系
9
+ - show_topology(): 可视化模块依赖关系
16
10
 
17
- ### 依赖关系处理
18
- #### topological_sort(elements: list, dependencies: dict, error: callable) -> list
19
- 对元素进行拓扑排序,解析依赖关系。
20
- - 参数:
21
- - elements: 需要排序的元素列表
22
- - dependencies: 依赖关系字典,键为元素,值为该元素依赖的元素列表
23
- - error: 发生循环依赖时调用的错误处理函数
24
- - 返回:
25
- - list: 排序后的元素列表
26
- - 异常:
27
- - 当存在循环依赖时,调用error函数
28
- - 示例:
29
- ```python
30
- # 基本使用
31
- modules = ["ModuleA", "ModuleB", "ModuleC"]
32
- dependencies = {
33
- "ModuleB": ["ModuleA"],
34
- "ModuleC": ["ModuleB"]
35
- }
36
- sorted_modules = sdk.util.topological_sort(modules, dependencies, sdk.raiserr.CycleDependencyError)
11
+ ### 装饰器:
12
+ - @cache: 缓存函数结果
13
+ - @run_in_executor: 将同步函数转为异步
14
+ - @retry(max_attempts=3, delay=1): 失败自动重试
37
15
 
38
- # 复杂依赖处理
39
- modules = ["Database", "Cache", "API", "UI"]
40
- dependencies = {
41
- "Cache": ["Database"],
42
- "API": ["Database", "Cache"],
43
- "UI": ["API"]
44
- }
45
- try:
46
- sorted_modules = sdk.util.topological_sort(
47
- modules,
48
- dependencies,
49
- sdk.raiserr.CycleDependencyError
50
- )
51
- print("加载顺序:", sorted_modules)
52
- except Exception as e:
53
- print(f"依赖解析失败: {e}")
54
- ```
16
+ ### 异步执行:
17
+ - ExecAsync(async_func, *args, **kwargs): 异步执行函数
55
18
 
56
- #### show_topology() -> str
57
- 可视化显示当前模块的依赖关系。
58
- - 参数: 无
59
- - 返回:
60
- - str: 格式化的依赖关系树文本
61
- - 示例:
62
- ```python
63
- # 显示所有模块依赖
64
- topology = sdk.util.show_topology()
65
- print(topology)
19
+ ### 示例用法:
66
20
 
67
- # 在日志中记录依赖关系
68
- sdk.logger.info("模块依赖关系:\n" + sdk.util.show_topology())
69
21
  ```
22
+ from ErisPulse import sdk
70
23
 
71
- ### 性能优化装饰器
72
- #### @cache
73
- 缓存函数调用结果的装饰器。
74
- - 参数: 无
75
- - 返回:
76
- - function: 被装饰的函数
77
- - 示例:
78
- ```python
79
- # 缓存计算密集型函数结果
80
- @sdk.util.cache
81
- def calculate_complex_data(param1: int, param2: str) -> dict:
82
- # 复杂计算...
83
- return result
84
-
85
- # 缓存配置读取
86
- @sdk.util.cache
87
- def get_config(config_name: str) -> dict:
88
- return load_config_from_file(config_name)
24
+ # 拓扑排序
25
+ sorted_modules = sdk.util.topological_sort(modules, dependencies, error)
89
26
 
90
- # 带有可变参数的缓存
27
+ # 缓存装饰器
91
28
  @sdk.util.cache
92
- def process_data(*args, **kwargs) -> Any:
93
- return complex_processing(args, kwargs)
94
- ```
95
-
96
- ### 异步执行工具
97
- #### @run_in_executor
98
- 将同步函数转换为异步执行的装饰器。
99
- - 参数: 无
100
- - 返回:
101
- - function: 异步包装的函数
102
- - 示例:
103
- ```python
104
- # 包装同步IO操作
105
- @sdk.util.run_in_executor
106
- def read_large_file(file_path: str) -> str:
107
- with open(file_path, 'r') as f:
108
- return f.read()
109
-
110
- # 包装CPU密集型操作
111
- @sdk.util.run_in_executor
112
- def process_image(image_data: bytes) -> bytes:
113
- # 图像处理...
114
- return processed_data
115
-
116
- # 在异步环境中使用
117
- async def main():
118
- # 这些操作会在线程池中执行,不会阻塞事件循环
119
- file_content = await read_large_file("large_file.txt")
120
- processed_image = await process_image(image_data)
121
- ```
122
-
123
- #### ExecAsync(async_func: Callable, *args, **kwargs) -> Any
124
- 在当前线程中执行异步函数。
125
- - 参数:
126
- - async_func: 要执行的异步函数
127
- - *args: 传递给异步函数的位置参数
128
- - **kwargs: 传递给异步函数的关键字参数
129
- - 返回:
130
- - Any: 异步函数的执行结果
131
- - 示例:
132
- ```python
133
- # 在同步环境中执行异步函数
134
- async def fetch_data(url: str) -> dict:
135
- async with aiohttp.ClientSession() as session:
136
- async with session.get(url) as response:
137
- return await response.json()
138
-
139
- # 同步环境中调用
140
- result = sdk.util.ExecAsync(fetch_data, "https://api.example.com/data")
141
-
142
- # 批量异步操作
143
- async def process_multiple(items: list) -> list:
144
- results = []
145
- for item in items:
146
- result = await process_item(item)
147
- results.append(result)
148
- return results
149
-
150
- # 在同步代码中执行
151
- results = sdk.util.ExecAsync(process_multiple, items_list)
152
- ```
153
-
154
- ### 错误重试机制
155
- #### @retry(max_attempts: int = 3, delay: int = 1)
156
- 为不稳定的操作添加自动重试机制的装饰器。
157
- - 参数:
158
- - max_attempts: 最大重试次数,默认3次
159
- - delay: 重试间隔时间(秒),默认1秒
160
- - 返回:
161
- - function: 包装了重试逻辑的函数
162
- - 示例:
163
- ```python
164
- # 基本重试
165
- @sdk.util.retry()
166
- def unstable_network_call() -> dict:
167
- return requests.get("https://api.example.com/data").json()
168
-
169
- # 自定义重试参数
170
- @sdk.util.retry(max_attempts=5, delay=2)
171
- def connect_database() -> Connection:
172
- return create_database_connection()
173
-
174
- # 带有条件的重试
175
- @sdk.util.retry(max_attempts=3)
176
- def process_with_retry(data: dict) -> bool:
177
- if not validate_data(data):
178
- raise ValueError("Invalid data")
179
- return process_data(data)
180
- ```
181
-
182
- ### 版本管理工具
183
- #### parse_dependency_with_version(dependency_str: str) -> tuple
184
- 解析带有版本要求的依赖字符串。
185
- - 参数:
186
- - dependency_str: 依赖字符串,如 "ModuleA==1.0.0", "ModuleB>=2.0.0"
187
- - 返回:
188
- - tuple: (模块名, 操作符, 版本号) 或 (模块名, None, None)
189
- - 示例:
190
- ```python
191
- # 解析带版本要求的依赖
192
- module_name, operator, version = sdk.util.parse_dependency_with_version("ModuleA==1.0.0")
193
- print(f"模块: {module_name}, 操作符: {operator}, 版本: {version}")
194
- # 输出: 模块: ModuleA, 操作符: ==, 版本: 1.0.0
195
-
196
- # 解析不带版本要求的依赖
197
- module_name, operator, version = sdk.util.parse_dependency_with_version("ModuleB")
198
- print(f"模块: {module_name}, 操作符: {operator}, 版本: {version}")
199
- # 输出: 模块: ModuleB, 操作符: None, 版本: None
200
- ```
201
-
202
- #### compare_versions(version1: str, version2: str) -> int
203
- 比较两个版本号。
204
- - 参数:
205
- - version1: 第一个版本号字符串,如 "1.0.0"
206
- - version2: 第二个版本号字符串,如 "2.0.0"
207
- - 返回:
208
- - int: 如果 version1 < version2 返回 -1,如果 version1 == version2 返回 0,如果 version1 > version2 返回 1
209
- - 示例:
210
- ```python
211
- # 比较版本号
212
- result = sdk.util.compare_versions("1.0.0", "2.0.0")
213
- print(f"比较结果: {result}") # 输出: 比较结果: -1
214
-
215
- result = sdk.util.compare_versions("2.0.0", "2.0.0")
216
- print(f"比较结果: {result}") # 输出: 比较结果: 0
217
-
218
- result = sdk.util.compare_versions("2.1.0", "2.0.5")
219
- print(f"比较结果: {result}") # 输出: 比较结果: 1
220
- ```
221
-
222
- #### check_version_requirement(current_version: str, operator: str, required_version: str) -> bool
223
- 检查当前版本是否满足版本要求。
224
- - 参数:
225
- - current_version: 当前版本号字符串,如 "1.0.0"
226
- - operator: 操作符,如 "==", ">=", "<="
227
- - required_version: 要求的版本号字符串,如 "2.0.0"
228
- - 返回:
229
- - bool: 如果满足要求返回 True,否则返回 False
230
- - 示例:
231
- ```python
232
- # 检查版本要求
233
- result = sdk.util.check_version_requirement("1.0.0", "==", "1.0.0")
234
- print(f"版本匹配: {result}") # 输出: 版本匹配: True
235
-
236
- result = sdk.util.check_version_requirement("1.5.0", ">=", "1.0.0")
237
- print(f"版本匹配: {result}") # 输出: 版本匹配: True
238
-
239
- result = sdk.util.check_version_requirement("2.0.0", "<", "1.0.0")
240
- print(f"版本匹配: {result}") # 输出: 版本匹配: False
241
- ```
242
-
243
- ## 最佳实践
244
- 1. 依赖管理
245
- ```python
246
- # 模块依赖定义
247
- module_deps = {
248
- "core": [],
249
- "database": ["core"],
250
- "api": ["database"],
251
- "ui": ["api"]
252
- }
253
-
254
- # 验证并排序依赖
255
- try:
256
- load_order = sdk.util.topological_sort(
257
- list(module_deps.keys()),
258
- module_deps,
259
- sdk.raiserr.CycleDependencyError
260
- )
29
+ def expensive_operation(param):
30
+ return heavy_computation(param)
261
31
 
262
- # 按顺序加载模块
263
- for module in load_order:
264
- load_module(module)
265
- except Exception as e:
266
- sdk.logger.error(f"模块加载失败: {e}")
267
- ```
268
-
269
- 2. 性能优化
270
- ```python
271
- # 合理使用缓存
272
- @sdk.util.cache
273
- def get_user_preferences(user_id: str) -> dict:
274
- return database.fetch_user_preferences(user_id)
275
-
276
- # 异步处理耗时操作
277
- @sdk.util.run_in_executor
278
- def process_large_dataset(data: list) -> list:
279
- return [complex_calculation(item) for item in data]
280
- ```
281
-
282
- 3. 错误处理和重试
283
- ```python
284
- # 组合使用重试和异步
285
- @sdk.util.retry(max_attempts=3, delay=2)
32
+ # 异步执行
286
33
  @sdk.util.run_in_executor
287
- def reliable_network_operation():
288
- response = requests.get("https://api.example.com")
289
- response.raise_for_status()
290
- return response.json()
291
-
292
- # 带有自定义错误处理的重试
293
- @sdk.util.retry(max_attempts=5)
294
- def safe_operation():
295
- try:
296
- return perform_risky_operation()
297
- except Exception as e:
298
- sdk.logger.warning(f"操作失败,准备重试: {e}")
299
- raise
300
- ```
301
-
302
- 4. 版本管理
303
- ```python
304
- # 在模块中定义依赖
305
- moduleInfo = {
306
- "meta": {
307
- "name": "AdvancedFeatures",
308
- "version": "1.2.0"
309
- },
310
- "dependencies": {
311
- "requires": [
312
- "CoreModule>=1.0.0",
313
- "DatabaseModule==2.1.0"
314
- ],
315
- "optional": [
316
- "VisualizationModule>=1.5.0",
317
- ["CacheModule>2.0.0", "FastCacheModule>=1.0.0"]
318
- ]
319
- }
320
- }
321
-
322
- # 手动检查版本兼容性
323
- def check_plugin_compatibility(plugin_info):
324
- required_version = "2.0.0"
325
- plugin_version = plugin_info.get("version", "0.0.0")
34
+ def sync_task():
35
+ pass
326
36
 
327
- if sdk.util.check_version_requirement(plugin_version, ">=", required_version):
328
- sdk.logger.info(f"插件 '{plugin_info['name']}' 版本兼容")
329
- return True
330
- else:
331
- sdk.logger.warning(f"插件 '{plugin_info['name']}' 版本 {plugin_version} 不兼容,需要 >={required_version}")
332
- return False
333
-
334
- # 解析带版本要求的依赖字符串
335
- def process_dependency(dependency_str):
336
- module_name, operator, version = sdk.util.parse_dependency_with_version(dependency_str)
337
- if operator and version:
338
- return f"需要模块 {module_name} {operator}{version}"
339
- else:
340
- return f"需要模块 {module_name},无版本要求"
37
+ # 重试机制
38
+ @sdk.util.retry(max_attempts=3, delay=1)
39
+ def unreliable_operation():
40
+ pass
341
41
  ```
342
42
 
343
- ## 注意事项
344
- 1. 缓存使用
345
- - 注意内存占用,避免缓存过大数据
346
- - 考虑缓存失效策略
347
- - 不要缓存频繁变化的数据
348
-
349
- 2. 异步执行
350
- - 避免在 run_in_executor 中执行过长的操作
351
- - 注意异常处理和资源清理
352
- - 合理使用线程池
353
-
354
- 3. 重试机制
355
- - 设置合适的重试次数和间隔
356
- - 只对可重试的操作使用重试装饰器
357
- - 注意避免重试导致的资源浪费
358
-
359
- 4. 依赖管理
360
- - 保持依赖关系清晰简单
361
- - 避免循环依赖
362
- - 定期检查和更新依赖关系
363
-
364
- 5. 版本管理
365
- - 遵循语义化版本规范(主版本.次版本.修订版本)
366
- - 明确指定版本要求,避免使用过于宽松的版本约束
367
- - 在主版本更新时,注意可能的不兼容变更
368
- - 测试不同版本依赖组合的兼容性
369
- - 为模块提供明确的版本号和更新日志
370
43
  """
371
44
 
372
45
  import time
373
46
  import asyncio
374
47
  import functools
375
48
  import traceback
376
- import re
377
49
  from concurrent.futures import ThreadPoolExecutor
378
50
  from collections import defaultdict, deque
379
51
 
@@ -468,53 +140,4 @@ def retry(max_attempts=3, delay=1):
468
140
  raise
469
141
  time.sleep(delay)
470
142
  return wrapper
471
- return decorator
472
-
473
- def parse_dependency_with_version(dependency_str):
474
- pattern = r'^([a-zA-Z0-9_\-]+)(?:([=<>!]+)([0-9]+(?:\.[0-9]+)*))?\s**$'
475
- match = re.match(pattern, dependency_str)
476
-
477
- if not match:
478
- return dependency_str, None, None
479
-
480
- module_name, operator, version = match.groups()
481
- return module_name, operator, version
482
-
483
- def compare_versions(version1, version2):
484
- v1_parts = [int(x) for x in version1.split('.')]
485
- v2_parts = [int(x) for x in version2.split('.')]
486
-
487
- # 确保两个版本号有相同的部分数
488
- while len(v1_parts) < len(v2_parts):
489
- v1_parts.append(0)
490
- while len(v2_parts) < len(v1_parts):
491
- v2_parts.append(0)
492
-
493
- for i in range(len(v1_parts)):
494
- if v1_parts[i] < v2_parts[i]:
495
- return -1
496
- elif v1_parts[i] > v2_parts[i]:
497
- return 1
498
-
499
- return 0
500
-
501
- def check_version_requirement(current_version, operator, required_version):
502
- if not operator or not required_version:
503
- return True
504
-
505
- comparison = compare_versions(current_version, required_version)
506
-
507
- if operator == '==':
508
- return comparison == 0
509
- elif operator == '!=':
510
- return comparison != 0
511
- elif operator == '>':
512
- return comparison > 0
513
- elif operator == '>=':
514
- return comparison >= 0
515
- elif operator == '<':
516
- return comparison < 0
517
- elif operator == '<=':
518
- return comparison <= 0
519
-
520
- return False
143
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse
3
- Version: 1.1.14
3
+ Version: 1.1.15
4
4
  Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
5
5
  Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
6
6
  License: MIT License
@@ -0,0 +1,13 @@
1
+ ErisPulse/__init__.py,sha256=k-X8WV9sIrYeYDNb8qZyBZ8s8yNL2bKWhZdkPbzRWII,8936
2
+ ErisPulse/__main__.py,sha256=KHBHedKC2bYWeqOi2rENgv_l5Pz-d2y7Bf4ST3R0TY4,52862
3
+ ErisPulse/adapter.py,sha256=3DayPbvWit2D8DTLIvhbP3862arv2UZuAkFnE9QWGro,17479
4
+ ErisPulse/db.py,sha256=a_5uTQk8w2TDp6J6AIQ4rF8Z2IvswuSg8q-Tx0SHfGA,24900
5
+ ErisPulse/logger.py,sha256=np6H4-x3xNpmoOGou5znsfv-iVlX56cVrsX8446cY-M,14764
6
+ ErisPulse/mods.py,sha256=oDVgEh56IUTLZDIxxWTQe5QvQSyWVAb8vhgd1fzFlYo,12253
7
+ ErisPulse/raiserr.py,sha256=Un8VGcVYDOSi7ptRVmyBzUTW7mQzDatqIza6919dQ1w,7034
8
+ ErisPulse/util.py,sha256=tmmZA4Ef5ElB01KZg2fTZLrkZOJbORjnkAd8sXj7xqw,4335
9
+ erispulse-1.1.15.dist-info/METADATA,sha256=4gCjt0C4F_qGRW3rKd0Rn9n2Tr9liDB0eCslFwex7zY,5877
10
+ erispulse-1.1.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ erispulse-1.1.15.dist-info/entry_points.txt,sha256=y8ppdsQhk-azC7_ikQgRr4rITSZ4VC4crVKtzHh4TBY,146
12
+ erispulse-1.1.15.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
13
+ erispulse-1.1.15.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  [console_scripts]
2
- ErisPulse = ErisPulse.__main__:main
3
- ErisPulse-CLI = ErisPulse.__main__:main
4
2
  ep = ErisPulse.__main__:main
3
+ ep-cli = ErisPulse.__main__:main
4
+ ep-init = ErisPulse.__init__:init
5
5
  epsdk = ErisPulse.__main__:main
@@ -1,13 +0,0 @@
1
- ErisPulse/__init__.py,sha256=14y4ywMLOCN-w35UDpERJ-deV2DR0olZFcjY68hzeXw,11188
2
- ErisPulse/__main__.py,sha256=CVk0bCv5zgJZKA7_LLyYOQCxPFUzTwhkOYVqf3M1YQE,50187
3
- ErisPulse/adapter.py,sha256=3DayPbvWit2D8DTLIvhbP3862arv2UZuAkFnE9QWGro,17479
4
- ErisPulse/db.py,sha256=a_5uTQk8w2TDp6J6AIQ4rF8Z2IvswuSg8q-Tx0SHfGA,24900
5
- ErisPulse/logger.py,sha256=np6H4-x3xNpmoOGou5znsfv-iVlX56cVrsX8446cY-M,14764
6
- ErisPulse/mods.py,sha256=oDVgEh56IUTLZDIxxWTQe5QvQSyWVAb8vhgd1fzFlYo,12253
7
- ErisPulse/raiserr.py,sha256=Un8VGcVYDOSi7ptRVmyBzUTW7mQzDatqIza6919dQ1w,7034
8
- ErisPulse/util.py,sha256=oa31SrzH-54exEgUhqkWxZuYScErl-rE7StDH4rkG3Y,15629
9
- erispulse-1.1.14.dist-info/METADATA,sha256=MllU6eXjXF97rohrAKZul81ZYcKXZXYcjgJWOwa-xiA,5877
10
- erispulse-1.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- erispulse-1.1.14.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
12
- erispulse-1.1.14.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
13
- erispulse-1.1.14.dist-info/RECORD,,