ErisPulse 1.1.12__py3-none-any.whl → 1.1.14__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
@@ -1,8 +1,56 @@
1
+ """
2
+ # SDK 核心初始化
3
+
4
+ 提供SDK全局对象构建和初始化功能。
5
+
6
+ ## 主要功能
7
+ - 构建全局sdk对象
8
+ - 预注册核心错误类型
9
+ - 提供SDK初始化入口
10
+ - 集成各核心模块
11
+ - 支持项目内模块加载(优先于SDK模块)
12
+
13
+ ## 模块加载机制
14
+ - **SDK 内置模块**:位于 `src/ErisPulse/modules/`,会被写入数据库并支持状态管理。
15
+ - **项目自定义模块**:位于项目根目录下的 `modules/`,仅运行时加载,不会写入数据库。
16
+ - **冲突处理**:若同一模块存在于多个路径,选择权重更高的路径(项目模块 > SDK 模块)。
17
+
18
+ ## API 文档
19
+ ### 核心对象:
20
+ - sdk: 全局SDK命名空间对象
21
+ - sdk.init(): SDK初始化入口函数
22
+
23
+ ### 预注册错误类型:
24
+ - CaughtExternalError: 外部捕获异常
25
+ - InitError: 初始化错误
26
+ - MissingDependencyError: 缺少依赖错误
27
+ - InvalidDependencyError: 依赖无效错误
28
+ - CycleDependencyError: 循环依赖错误
29
+ - ModuleLoadError: 模块加载错误
30
+
31
+ ### 示例用法:
32
+
33
+ ```python
34
+ from ErisPulse import sdk
35
+
36
+ # 初始化SDK
37
+ sdk.init()
38
+
39
+ # 访问各模块功能
40
+ sdk.logger.info("SDK已初始化")
41
+ ```
42
+
43
+ """
1
44
 
2
- import types
3
- sdk = types.SimpleNamespace()
4
45
  import os
5
46
  import sys
47
+ import logging
48
+ from types import SimpleNamespace
49
+
50
+ # 初始化全局 sdk 对象
51
+ sdk = SimpleNamespace()
52
+
53
+ # 导入内部依赖
6
54
  from . import util
7
55
  from .raiserr import raiserr
8
56
  from .logger import logger
@@ -10,179 +58,220 @@ from .db import env
10
58
  from .mods import mods
11
59
  from .adapter import adapter, BaseAdapter, SendDSL
12
60
 
13
- # 这里不能删,确保windows下的shell能正确显示颜色
61
+ # Windows 下启用 ANSI 颜色输出
14
62
  os.system('')
15
63
 
16
- setattr(sdk, "env", env)
17
- setattr(sdk, "mods", mods)
18
- setattr(sdk, "util", util)
19
- setattr(sdk, "raiserr", raiserr)
20
- setattr(sdk, "logger", logger)
21
- setattr(sdk, "adapter", adapter)
22
- setattr(sdk, "SendDSL", SendDSL)
23
- setattr(sdk, "BaseAdapter", BaseAdapter)
24
-
25
- # 注册 ErrorHook 并预注册常用错误类型
26
- raiserr.register("CaughtExternalError" , doc="捕获的非SDK抛出的异常")
27
- raiserr.register("InitError" , doc="SDK初始化错误")
28
- raiserr.register("MissingDependencyError" , doc="缺少依赖错误")
29
- raiserr.register("InvalidDependencyError" , doc="依赖无效错误")
30
- raiserr.register("CycleDependencyError" , doc="依赖循环错误")
31
- raiserr.register("ModuleLoadError" , doc="模块加载错误")
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="模块加载错误")
32
75
 
33
76
  def init() -> None:
34
77
  try:
35
78
  logger.info("[Init] SDK 正在初始化...")
79
+
36
80
  if env.create_env_file_if_not_exists():
37
81
  logger.info("[Init] 项目首次初始化,建议先配置环境变量")
38
- sys.exit(0)
82
+ if input("是否立即退出?(y/n): ").strip().lower() == "y":
83
+ sys.exit(0)
39
84
  env.load_env_file()
40
85
 
41
- sdkModulePath = os.path.join(os.path.dirname(__file__), "modules")
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))
42
110
 
43
- if not os.path.exists(sdkModulePath):
44
- os.makedirs(sdkModulePath)
111
+ # 处理模块冲突(按权重选择)
112
+ TempModules = []
45
113
 
46
- sys.path.append(sdkModulePath)
114
+ for module, sources in moduleSourceMap.items():
115
+ if len(sources) > 1:
116
+ # 按权重排序
117
+ sources.sort(key=lambda x: x[1], reverse=True)
47
118
 
48
- TempModules = [
49
- x for x in os.listdir(sdkModulePath)
50
- if os.path.isdir(os.path.join(sdkModulePath, x))
51
- ]
119
+ selected_path = sources[0][0]
120
+ logger.warning(f"模块 {module} 在多个路径存在,选择权重更高的: {selected_path}")
52
121
 
53
- sdkInstalledModuleNames: list[str] = []
54
- disabledModules: list[str] = []
122
+ # 确保选中的路径在 sys.path 最前面
123
+ if selected_path in sys.path:
124
+ sys.path.remove(selected_path)
125
+ sys.path.insert(0, selected_path)
126
+
127
+ TempModules.append(module)
128
+
129
+ # 动态导入模块(不存入数据库)
130
+ module_objs = {}
55
131
 
56
- # ==== 扫描模块并收集基本信息 ====
57
- module_objs = {} # {module_name: moduleObj}
58
132
  for module_name in TempModules:
59
133
  try:
60
134
  moduleObj = __import__(module_name)
135
+
136
+ # 验证必要属性
61
137
  if not hasattr(moduleObj, "moduleInfo") or not isinstance(moduleObj.moduleInfo, dict):
62
138
  logger.warning(f"模块 {module_name} 缺少有效的 'moduleInfo' 字典.")
63
139
  continue
64
140
  if "name" not in moduleObj.moduleInfo.get("meta", {}):
65
- logger.warning(f"模块 {module_name} 的 'moduleInfo' 字典 缺少必要 'name' 键.")
141
+ logger.warning(f"模块 {module_name} 的 'moduleInfo' 缺少必要 'name' 键.")
66
142
  continue
67
143
  if not hasattr(moduleObj, "Main"):
68
144
  logger.warning(f"模块 {module_name} 缺少 'Main' 类.")
69
145
  continue
70
146
 
71
147
  meta_name = moduleObj.moduleInfo["meta"]["name"]
72
- module_info = mods.get_module(meta_name)
73
- if module_info is None:
74
- module_info = {
75
- "status": True,
76
- "info": moduleObj.moduleInfo
77
- }
78
- mods.set_module(meta_name, module_info)
79
- logger.info(f"模块 {meta_name} 信息已初始化并存储到数据库")
148
+ module_status = mods.get_module_status(meta_name)
80
149
 
81
- if not module_info.get('status', True):
82
- disabledModules.append(module_name)
150
+ if not module_status:
83
151
  logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
84
152
  continue
85
153
 
86
154
  required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
87
- missing_required_deps = [dep for dep in required_deps if dep not in TempModules]
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
+
88
180
  if missing_required_deps:
89
- logger.error(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
90
- raiserr.MissingDependencyError(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
181
+ logger.error(
182
+ f"模块 {module_name} 缺少必需依赖或版本不匹配: {missing_required_deps}")
183
+ raiserr.MissingDependencyError(
184
+ f"模块 {module_name} 缺少必需依赖或版本不匹配: {missing_required_deps}")
91
185
 
92
186
  optional_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
93
187
  available_optional_deps = []
188
+
94
189
  for dep in optional_deps:
95
- if isinstance(dep, list):
96
- available_deps = [d for d in dep if d in TempModules]
97
- if available_deps:
98
- available_optional_deps.extend(available_deps)
99
- elif dep in TempModules:
100
- available_optional_deps.append(dep)
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)
101
211
 
102
212
  if optional_deps and not available_optional_deps:
103
213
  logger.warning(f"模块 {module_name} 缺少所有可选依赖: {optional_deps}")
104
214
 
105
215
  module_objs[module_name] = moduleObj
106
- sdkInstalledModuleNames.append(module_name)
107
216
 
108
217
  except Exception as e:
109
218
  logger.warning(f"模块 {module_name} 加载失败: {e}")
110
219
  continue
111
220
 
112
- # ==== 构建依赖图并进行拓扑排序 ====
113
- sdkModuleDependencies = {}
114
- for module_name in sdkInstalledModuleNames:
115
- moduleObj = module_objs[module_name]
116
- meta_name = moduleObj.moduleInfo["meta"]["name"]
221
+ # ==== 创建 README 提示用户模块目录用途 ====
222
+ readme_content = """# 项目模块目录
117
223
 
118
- req_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
119
- opt_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
224
+ 此目录 (`./modules`) 用于存放项目专属模块。这些模块会优先于 SDK 内置模块被加载,但不会写入数据库。
120
225
 
121
- available_optional_deps = [dep for dep in opt_deps if dep in sdkInstalledModuleNames]
122
- deps = req_deps + available_optional_deps
226
+ 你可以将自定义模块放入此目录,SDK 会自动识别并加载它们。
227
+ """
123
228
 
124
- for dep in deps:
125
- if dep in disabledModules:
126
- logger.warning(f"模块 {meta_name} 的依赖模块 {dep} 已禁用,跳过加载")
127
- continue
128
-
129
- if not all(dep in sdkInstalledModuleNames for dep in deps):
130
- raiserr.InvalidDependencyError(f"模块 {meta_name} 的依赖无效: {deps}")
131
- sdkModuleDependencies[module_name] = deps
132
-
133
- sdkInstalledModuleNames: list[str] = sdk.util.topological_sort(
134
- sdkInstalledModuleNames, sdkModuleDependencies, raiserr.CycleDependencyError
135
- )
136
- # 存储模块依赖关系到env
137
- env.set('module_dependencies', {
138
- 'modules': sdkInstalledModuleNames,
139
- 'dependencies': sdkModuleDependencies
140
- })
141
-
142
- # ==== 注册适配器 ====
143
- logger.debug("[Init] 开始注册适配器...")
144
- for module_name in sdkInstalledModuleNames:
145
- moduleObj = module_objs[module_name]
146
- meta_name = moduleObj.moduleInfo["meta"]["name"]
229
+ if not os.path.exists(projectModulePath):
230
+ os.makedirs(projectModulePath)
147
231
 
148
- try:
149
- if hasattr(moduleObj, "adapterInfo") and isinstance(moduleObj.adapterInfo, dict):
150
- for platform_name, adapter_class in moduleObj.adapterInfo.items():
151
- sdk.adapter.register(platform_name, adapter_class)
152
- logger.info(f"模块 {meta_name} 注册了适配器: {platform_name}")
153
- except Exception as e:
154
- logger.error(f"模块 {meta_name} 注册适配器失败: {e}")
155
-
156
- # ==== 存储模块信息到数据库 ====
157
- all_modules_info = {}
158
- for module_name in sdkInstalledModuleNames:
159
- moduleObj = module_objs[module_name]
160
- moduleInfo: dict = moduleObj.moduleInfo
161
-
162
- meta_name = moduleInfo.get("meta", {}).get("name", None)
163
- module_info = mods.get_module(meta_name)
164
- mods.set_module(meta_name, {
165
- "status": True,
166
- "info": moduleInfo
167
- })
168
- logger.debug("所有模块信息已加载并存储到数据库")
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} 了解如何使用")
169
237
 
170
238
  # ==== 实例化 Main 类并挂载到 sdk ====
171
239
  logger.debug("[Init] 开始实例化模块 Main 类...")
172
- for module_name in sdkInstalledModuleNames:
240
+
241
+ for module_name in module_objs:
173
242
  moduleObj = module_objs[module_name]
174
243
  meta_name = moduleObj.moduleInfo["meta"]["name"]
175
244
 
176
- module_status = mods.get_module_status(meta_name)
177
- if not module_status:
178
- continue
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} 已存在于数据库中")
179
267
 
268
+ # 实例化模块
180
269
  moduleMain = moduleObj.Main(sdk)
181
270
  setattr(moduleMain, "moduleInfo", moduleObj.moduleInfo)
182
271
  setattr(sdk, meta_name, moduleMain)
183
272
  logger.debug(f"模块 {meta_name} 正在初始化")
273
+
184
274
  except Exception as e:
185
275
  raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
186
276
 
187
-
188
- sdk.init = init
277
+ sdk.init = init