ErisPulse 2.1.13rc3__py3-none-any.whl → 2.1.14.dev2__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.
@@ -2,9 +2,10 @@ from .adapter import AdapterFather, SendDSL, adapter
2
2
  from .env import env
3
3
  from .logger import logger
4
4
  from .mods import mods
5
- from .raiserr import raiserr
6
- from .util import util
7
- from .server import adapter_server
5
+ from .router import router, adapter_server
6
+ from .config import config
7
+ from . import exceptions
8
+
8
9
  BaseAdapter = AdapterFather
9
10
 
10
11
  __all__ = [
@@ -15,7 +16,8 @@ __all__ = [
15
16
  'env',
16
17
  'logger',
17
18
  'mods',
18
- 'raiserr',
19
- 'util',
20
- 'adapter_server'
19
+ 'exceptions',
20
+ 'router',
21
+ 'adapter_server',
22
+ 'config'
21
23
  ]
ErisPulse/Core/adapter.py CHANGED
@@ -13,14 +13,12 @@ ErisPulse 适配器系统
13
13
 
14
14
  import functools
15
15
  import asyncio
16
- import uuid
17
- import time
18
16
  from typing import (
19
17
  Callable, Any, Dict, List, Type, Optional, Set,
20
- Union, Awaitable, TypeVar, Generic, Tuple, Coroutine, FrozenSet
18
+ Union, Awaitable
21
19
  )
22
20
  from collections import defaultdict
23
-
21
+ from .logger import logger
24
22
 
25
23
  class SendDSLBase:
26
24
  """
@@ -114,7 +112,6 @@ class BaseAdapter:
114
112
  :example:
115
113
  >>> await adapter.Send.To("123").Example("Hello")
116
114
  """
117
- from .logger import logger
118
115
  logger.debug(f"适配器 {self._adapter.__class__.__name__} 发送了实例类型的消息: {text}")
119
116
 
120
117
 
@@ -378,7 +375,7 @@ class AdapterManager:
378
375
  for name in set(combinations):
379
376
  setattr(self, name, instance)
380
377
  else:
381
- self.logger.warning(f"平台名 {platform} 过长,如果您是开发者,请考虑使用更短的名称")
378
+ logger.warning(f"平台名 {platform} 过长,如果您是开发者,请考虑使用更短的名称")
382
379
  setattr(self, platform.lower(), instance)
383
380
  setattr(self, platform.upper(), instance)
384
381
  setattr(self, platform.capitalize(), instance)
@@ -401,10 +398,18 @@ class AdapterManager:
401
398
  """
402
399
  if platforms is None:
403
400
  platforms = list(self._adapters.keys())
401
+ if not isinstance(platforms, list):
402
+ platforms = [platforms]
403
+ for platform in platforms:
404
+ if platform not in self._adapters:
405
+ raise ValueError(f"平台 {platform} 未注册")
406
+
407
+ logger.info(f"启动适配器 {platforms}")
404
408
 
405
- from .server import adapter_server
406
- from .config import get_server_config
409
+ from .router import adapter_server
410
+ from .erispulse_config import get_server_config
407
411
  server_config = get_server_config()
412
+
408
413
  host = server_config["host"]
409
414
  port = server_config["port"]
410
415
  ssl_cert = server_config.get("ssl_certfile", None)
@@ -441,16 +446,14 @@ class AdapterManager:
441
446
  :param adapter: 适配器实例
442
447
  :param platform: 平台名称
443
448
  """
444
- from .. import sdk
445
449
 
446
- # 加锁防止并发启动
447
450
  if not getattr(adapter, "_starting_lock", None):
448
451
  adapter._starting_lock = asyncio.Lock()
449
452
 
450
453
  async with adapter._starting_lock:
451
454
  # 再次确认是否已经被启动
452
455
  if adapter in self._started_instances:
453
- sdk.logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已被其他协程启动,跳过")
456
+ logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已被其他协程启动,跳过")
454
457
  return
455
458
 
456
459
  retry_count = 0
@@ -464,12 +467,12 @@ class AdapterManager:
464
467
  return
465
468
  except Exception as e:
466
469
  retry_count += 1
467
- sdk.logger.error(f"平台 {platform} 启动失败(第{retry_count}次重试): {e}")
470
+ logger.error(f"平台 {platform} 启动失败(第{retry_count}次重试): {e}")
468
471
 
469
472
  try:
470
473
  await adapter.shutdown()
471
474
  except Exception as stop_err:
472
- sdk.logger.warning(f"停止适配器失败: {stop_err}")
475
+ logger.warning(f"停止适配器失败: {stop_err}")
473
476
 
474
477
  # 计算等待时间
475
478
  if retry_count <= len(backoff_intervals):
@@ -477,7 +480,7 @@ class AdapterManager:
477
480
  else:
478
481
  wait_time = fixed_delay
479
482
 
480
- sdk.logger.info(f"将在 {wait_time // 60} 分钟后再次尝试重启 {platform}")
483
+ logger.info(f"将在 {wait_time // 60} 分钟后再次尝试重启 {platform}")
481
484
  await asyncio.sleep(wait_time)
482
485
 
483
486
  async def shutdown(self) -> None:
@@ -490,7 +493,7 @@ class AdapterManager:
490
493
  for adapter in self._adapters.values():
491
494
  await adapter.shutdown()
492
495
 
493
- from .server import adapter_server
496
+ from .router import adapter_server
494
497
  await adapter_server.stop()
495
498
 
496
499
  def get(self, platform: str) -> Optional[BaseAdapter]:
ErisPulse/Core/config.py CHANGED
@@ -72,101 +72,3 @@ class ConfigManager:
72
72
  return False
73
73
 
74
74
  config = ConfigManager()
75
-
76
- # 默认配置
77
- DEFAULT_CONFIG = {
78
- "server": {
79
- "host": "0.0.0.0",
80
- "port": 8000,
81
- "ssl_certfile": None,
82
- "ssl_keyfile": None
83
- },
84
- "logger": {
85
- "level": "INFO",
86
- "log_files": [],
87
- "memory_limit": 1000
88
- }
89
- }
90
-
91
- def _ensure_config_structure(config: Dict[str, Any]) -> Dict[str, Any]:
92
- """
93
- 确保配置结构完整,补全缺失的配置项
94
-
95
- :param config: 当前配置
96
- :return: 补全后的完整配置
97
- """
98
- merged_config = DEFAULT_CONFIG.copy()
99
-
100
- # 深度合并配置
101
- for section, default_values in DEFAULT_CONFIG.items():
102
- if section not in config:
103
- config[section] = default_values.copy()
104
- continue
105
-
106
- if not isinstance(config[section], dict):
107
- config[section] = default_values.copy()
108
- continue
109
-
110
- for key, default_value in default_values.items():
111
- if key not in config[section]:
112
- config[section][key] = default_value
113
-
114
- return config
115
-
116
- def get_config() -> Dict[str, Any]:
117
- """
118
- 获取当前配置,自动补全缺失的配置项并保存
119
-
120
- :return: 完整的配置字典
121
- """
122
-
123
- # 获取现有配置
124
- current_config = config.getConfig("ErisPulse")
125
-
126
- # 如果完全没有配置,设置默认配置
127
- if current_config is None:
128
- config.setConfig("ErisPulse", DEFAULT_CONFIG)
129
- return DEFAULT_CONFIG
130
-
131
- # 检查并补全缺失的配置项
132
- complete_config = _ensure_config_structure(current_config)
133
-
134
- # 如果配置有变化,更新到存储
135
- if current_config != complete_config:
136
- config.setConfig("ErisPulse", complete_config)
137
-
138
- return complete_config
139
-
140
- def update_config(new_config: Dict[str, Any]) -> bool:
141
- """
142
- 更新配置,自动补全缺失的配置项
143
-
144
- :param new_config: 新的配置字典
145
- :return: 是否更新成功
146
- """
147
- # 获取当前配置并合并新配置
148
- current = get_config()
149
- merged = {**current, **new_config}
150
-
151
- # 确保合并后的配置结构完整
152
- complete_config = _ensure_config_structure(merged)
153
-
154
- return config.setConfig("ErisPulse", complete_config)
155
-
156
- def get_server_config() -> Dict[str, Any]:
157
- """
158
- 获取服务器配置,确保结构完整
159
-
160
- :return: 服务器配置字典
161
- """
162
- config = get_config()
163
- return config["server"]
164
-
165
- def get_logger_config() -> Dict[str, Any]:
166
- """
167
- 获取日志配置,确保结构完整
168
-
169
- :return: 日志配置字典
170
- """
171
- config = get_config()
172
- return config["logger"]
ErisPulse/Core/env.py CHANGED
@@ -13,14 +13,10 @@ ErisPulse 环境配置模块
13
13
  import os
14
14
  import json
15
15
  import sqlite3
16
- import importlib.util
17
16
  import shutil
18
17
  import time
19
- import toml
20
- from pathlib import Path
21
18
  from datetime import datetime
22
- from functools import lru_cache
23
- from typing import List, Dict, Optional, Any, Set, Tuple, Union, Type, FrozenSet
19
+ from typing import List, Dict, Optional, Any, Tuple, Type
24
20
 
25
21
  class EnvManager:
26
22
  """
@@ -39,7 +35,6 @@ class EnvManager:
39
35
  db_path = os.path.join(os.path.dirname(__file__), "../data/config.db")
40
36
  SNAPSHOT_DIR = os.path.join(os.path.dirname(__file__), "../data/snapshots")
41
37
 
42
- CONFIG_FILE = "config.toml"
43
38
 
44
39
  def __new__(cls, *args, **kwargs):
45
40
  if not cls._instance:
@@ -192,8 +187,6 @@ class EnvManager:
192
187
  from .config import config
193
188
  return config.getConfig(key, default)
194
189
  except Exception as e:
195
- from . import logger
196
- logger.error(f"读取配置文件 {self.CONFIG_FILE} 失败: {e}")
197
190
  return default
198
191
 
199
192
  def setConfig(self, key: str, value: Any) -> bool:
@@ -207,8 +200,6 @@ class EnvManager:
207
200
  from .config import config
208
201
  return config.setConfig(key, value)
209
202
  except Exception as e:
210
- from . import logger
211
- logger.error(f"写入配置文件 {self.CONFIG_FILE} 失败: {e}")
212
203
  return False
213
204
 
214
205
  def delete(self, key: str) -> bool:
@@ -385,29 +376,7 @@ class EnvManager:
385
376
  return True
386
377
  except Exception as e:
387
378
  return False
388
-
389
- def load_env_file(self) -> bool:
390
- """
391
- 加载env.py文件中的配置项
392
379
 
393
- :return: 操作是否成功
394
-
395
- :example:
396
- >>> env.load_env_file() # 加载env.py中的配置
397
- """
398
- try:
399
- env_file = Path("env.py")
400
- if env_file.exists():
401
- spec = importlib.util.spec_from_file_location("env_module", env_file)
402
- env_module = importlib.util.module_from_spec(spec)
403
- spec.loader.exec_module(env_module)
404
- for key, value in vars(env_module).items():
405
- if not key.startswith("__") and isinstance(value, (dict, list, str, int, float, bool)):
406
- self.set(key, value)
407
- return True
408
- except Exception as e:
409
- return False
410
-
411
380
  def __getattr__(self, key: str) -> Any:
412
381
  """
413
382
  通过属性访问配置项
@@ -0,0 +1,105 @@
1
+ """
2
+ ErisPulse 框架配置管理
3
+
4
+ 专门管理 ErisPulse 框架自身的配置项。
5
+ """
6
+
7
+ from typing import Dict, Any
8
+ from .config import config
9
+
10
+ # 默认配置
11
+ DEFAULT_ERISPULSE_CONFIG = {
12
+ "server": {
13
+ "host": "0.0.0.0",
14
+ "port": 8000,
15
+ "ssl_certfile": None,
16
+ "ssl_keyfile": None
17
+ },
18
+ "logger": {
19
+ "level": "INFO",
20
+ "log_files": [],
21
+ "memory_limit": 1000
22
+ }
23
+ }
24
+
25
+ def _ensure_erispulse_config_structure(config_dict: Dict[str, Any]) -> Dict[str, Any]:
26
+ """
27
+ 确保 ErisPulse 配置结构完整,补全缺失的配置项
28
+
29
+ :param config_dict: 当前配置
30
+ :return: 补全后的完整配置
31
+ """
32
+ merged_config = DEFAULT_ERISPULSE_CONFIG.copy()
33
+
34
+ # 深度合并配置
35
+ for section, default_values in DEFAULT_ERISPULSE_CONFIG.items():
36
+ if section not in config_dict:
37
+ config_dict[section] = default_values.copy()
38
+ continue
39
+
40
+ if not isinstance(config_dict[section], dict):
41
+ config_dict[section] = default_values.copy()
42
+ continue
43
+
44
+ for key, default_value in default_values.items():
45
+ if key not in config_dict[section]:
46
+ config_dict[section][key] = default_value
47
+
48
+ return config_dict
49
+
50
+ def get_erispulse_config() -> Dict[str, Any]:
51
+ """
52
+ 获取 ErisPulse 框架配置,自动补全缺失的配置项并保存
53
+
54
+ :return: 完整的 ErisPulse 配置字典
55
+ """
56
+ # 获取现有配置
57
+ current_config = config.getConfig("ErisPulse")
58
+
59
+ # 如果完全没有配置,设置默认配置
60
+ if current_config is None:
61
+ config.setConfig("ErisPulse", DEFAULT_ERISPULSE_CONFIG)
62
+ return DEFAULT_ERISPULSE_CONFIG
63
+
64
+ # 检查并补全缺失的配置项
65
+ complete_config = _ensure_erispulse_config_structure(current_config)
66
+
67
+ # 如果配置有变化,更新到存储
68
+ if current_config != complete_config:
69
+ config.setConfig("ErisPulse", complete_config)
70
+
71
+ return complete_config
72
+
73
+ def update_erispulse_config(new_config: Dict[str, Any]) -> bool:
74
+ """
75
+ 更新 ErisPulse 配置,自动补全缺失的配置项
76
+
77
+ :param new_config: 新的配置字典
78
+ :return: 是否更新成功
79
+ """
80
+ # 获取当前配置并合并新配置
81
+ current = get_erispulse_config()
82
+ merged = {**current, **new_config}
83
+
84
+ # 确保合并后的配置结构完整
85
+ complete_config = _ensure_erispulse_config_structure(merged)
86
+
87
+ return config.setConfig("ErisPulse", complete_config)
88
+
89
+ def get_server_config() -> Dict[str, Any]:
90
+ """
91
+ 获取服务器配置,确保结构完整
92
+
93
+ :return: 服务器配置字典
94
+ """
95
+ erispulse_config = get_erispulse_config()
96
+ return erispulse_config["server"]
97
+
98
+ def get_logger_config() -> Dict[str, Any]:
99
+ """
100
+ 获取日志配置,确保结构完整
101
+
102
+ :return: 日志配置字典
103
+ """
104
+ erispulse_config = get_erispulse_config()
105
+ return erispulse_config["logger"]
@@ -0,0 +1,108 @@
1
+ """
2
+ ErisPulse 全局异常处理系统
3
+
4
+ 提供统一的异常捕获和格式化功能,支持同步和异步代码的异常处理。
5
+ """
6
+
7
+ import sys
8
+ import traceback
9
+ import asyncio
10
+ import os
11
+ from typing import Dict, Any, Type
12
+
13
+ class ExceptionHandler:
14
+ @staticmethod
15
+ def format_exception(exc_type: Type[Exception], exc_value: Exception, exc_traceback: Any) -> str:
16
+ """
17
+ :param exc_type: 异常类型
18
+ :param exc_value: 异常值
19
+ :param exc_traceback: 追踪信息
20
+ :return: 格式化后的异常信息
21
+ """
22
+ tb_list = traceback.extract_tb(exc_traceback)
23
+ if tb_list:
24
+ last_frame = tb_list[-1]
25
+ filename = os.path.basename(last_frame.filename)
26
+ line_number = last_frame.lineno
27
+ function_name = last_frame.name
28
+ return f"ERROR: {filename}:{function_name}:{line_number}: {exc_type.__name__}: {exc_value}"
29
+ else:
30
+ return f"ERROR: {exc_type.__name__}: {exc_value}"
31
+
32
+ @staticmethod
33
+ def format_async_exception(exception: Exception) -> str:
34
+ """
35
+ :param exception: 异常对象
36
+ :return: 格式化后的异常信息
37
+ """
38
+ if exception.__traceback__:
39
+ tb_list = traceback.extract_tb(exception.__traceback__)
40
+ if tb_list:
41
+ last_frame = tb_list[-1]
42
+ filename = os.path.basename(last_frame.filename)
43
+ line_number = last_frame.lineno
44
+ function_name = last_frame.name
45
+ return f"ERROR: {filename}:{function_name}:{line_number}: {type(exception).__name__}: {exception}"
46
+
47
+ return f"ERROR: {type(exception).__name__}: {exception}"
48
+
49
+ def global_exception_handler(exc_type: Type[Exception], exc_value: Exception, exc_traceback: Any) -> None:
50
+ """
51
+ 全局异常处理器
52
+
53
+ :param exc_type: 异常类型
54
+ :param exc_value: 异常值
55
+ :param exc_traceback: 追踪信息
56
+ """
57
+ try:
58
+ from ErisPulse import logger
59
+ err_logger = logger.error
60
+ except ImportError:
61
+ err_logger = sys.stderr.write
62
+
63
+ formatted_error = ExceptionHandler.format_exception(exc_type, exc_value, exc_traceback)
64
+ err_logger(formatted_error)
65
+
66
+ def async_exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None:
67
+ """
68
+ 异步异常处理器
69
+
70
+ :param loop: 事件循环
71
+ :param context: 上下文字典
72
+ """
73
+ try:
74
+ from ErisPulse import logger
75
+ err_logger = logger.error
76
+ except ImportError:
77
+ err_logger = sys.stderr.write
78
+
79
+ exception = context.get('exception')
80
+ if exception:
81
+ try:
82
+ formatted_error = ExceptionHandler.format_async_exception(exception)
83
+ err_logger(formatted_error + '\n')
84
+ except Exception:
85
+ err_logger(f"ERROR: 捕捉器发生错误,原始异常信息:\n\n{exception}\n\n" + traceback.format_exc())
86
+ else:
87
+ msg = context.get('message', '未知异步错误')
88
+ err_logger(f"ERROR: 未处理的异步错误: {msg}\n")
89
+
90
+ def setup_async_loop(loop: asyncio.AbstractEventLoop = None) -> None:
91
+ """
92
+ 为指定的事件循环设置异常处理器
93
+
94
+ :param loop: 事件循环实例,如果为None则使用当前事件循环
95
+ """
96
+ if loop is None:
97
+ try:
98
+ loop = asyncio.get_running_loop()
99
+ except RuntimeError:
100
+ loop = asyncio.get_event_loop()
101
+
102
+ loop.set_exception_handler(async_exception_handler)
103
+
104
+ sys.excepthook = global_exception_handler
105
+ try:
106
+ asyncio.get_event_loop().set_exception_handler(async_exception_handler)
107
+ except RuntimeError:
108
+ pass
ErisPulse/Core/logger.py CHANGED
@@ -174,7 +174,7 @@ class Logger:
174
174
  self._logs[ModuleName].append(msg)
175
175
 
176
176
  def _setup_config(self):
177
- from .config import get_logger_config
177
+ from .erispulse_config import get_logger_config
178
178
  logger_config = get_logger_config()
179
179
  if "level" in logger_config:
180
180
  self.set_level(logger_config["level"])
@@ -1,6 +1,8 @@
1
+ # router.py (新文件名)
1
2
  """
2
- ErisPulse Adapter Server
3
- 提供统一的适配器服务入口,支持HTTP和WebSocket路由
3
+ ErisPulse 路由系统
4
+
5
+ 提供统一的HTTP和WebSocket路由管理,支持多适配器路由注册和生命周期管理。
4
6
 
5
7
  {!--< tips >!--}
6
8
  1. 适配器只需注册路由,无需自行管理服务器
@@ -19,9 +21,9 @@ from hypercorn.config import Config
19
21
  from hypercorn.asyncio import serve
20
22
 
21
23
 
22
- class AdapterServer:
24
+ class RouterManager:
23
25
  """
24
- 适配器服务器管理器
26
+ 路由管理器
25
27
 
26
28
  {!--< tips >!--}
27
29
  核心功能:
@@ -33,18 +35,18 @@ class AdapterServer:
33
35
 
34
36
  def __init__(self):
35
37
  """
36
- 初始化适配器服务器
38
+ 初始化路由管理器
37
39
 
38
40
  {!--< tips >!--}
39
41
  会自动创建FastAPI实例并设置核心路由
40
42
  {!--< /tips >!--}
41
43
  """
42
44
  self.app = FastAPI(
43
- title="ErisPulse Adapter Server",
44
- description="统一适配器服务入口点",
45
+ title="ErisPulse Router",
46
+ description="统一路由管理入口点",
45
47
  version="1.0.0"
46
48
  )
47
- self._webhook_routes: Dict[str, Dict[str, Callable]] = defaultdict(dict)
49
+ self._http_routes: Dict[str, Dict[str, Callable]] = defaultdict(dict)
48
50
  self._websocket_routes: Dict[str, Dict[str, Tuple[Callable, Optional[Callable]]]] = defaultdict(dict)
49
51
  self.base_url = ""
50
52
  self._server_task: Optional[asyncio.Task] = None
@@ -66,7 +68,7 @@ class AdapterServer:
66
68
  :return:
67
69
  Dict[str, str]: 包含服务状态的字典
68
70
  """
69
- return {"status": "ok", "service": "ErisPulse Adapter Server"}
71
+ return {"status": "ok", "service": "ErisPulse Router"}
70
72
 
71
73
  @self.app.get("/routes")
72
74
  async def list_routes() -> Dict[str, Any]:
@@ -74,36 +76,23 @@ class AdapterServer:
74
76
  列出所有已注册路由
75
77
 
76
78
  :return:
77
- Dict[str, Any]: 包含所有路由信息的字典,格式为:
78
- {
79
- "http_routes": [
80
- {
81
- "path": "/adapter1/route1",
82
- "adapter": "adapter1",
83
- "methods": ["POST"]
84
- },
85
- ...
86
- ],
87
- "websocket_routes": [
88
- {
89
- "path": "/adapter1/ws",
90
- "adapter": "adapter1",
91
- "requires_auth": true
92
- },
93
- ...
94
- ],
95
- "base_url": self.base_url
96
- }
79
+ Dict[str, Any]: 包含所有路由信息的字典
97
80
  """
98
81
  http_routes = []
99
- for adapter, routes in self._webhook_routes.items():
82
+ for adapter, routes in self._http_routes.items():
100
83
  for path, handler in routes.items():
101
- route = self.app.router.routes[-1] # 获取最后添加的路由
102
- if isinstance(route, APIRoute) and route.path == path:
84
+ # 查找对应的路由对象
85
+ route_obj = None
86
+ for route in self.app.router.routes:
87
+ if isinstance(route, APIRoute) and route.path == path:
88
+ route_obj = route
89
+ break
90
+
91
+ if route_obj:
103
92
  http_routes.append({
104
93
  "path": path,
105
94
  "adapter": adapter,
106
- "methods": route.methods
95
+ "methods": list(route_obj.methods)
107
96
  })
108
97
 
109
98
  websocket_routes = []
@@ -121,9 +110,9 @@ class AdapterServer:
121
110
  "base_url": self.base_url
122
111
  }
123
112
 
124
- def register_webhook(
113
+ def register_http_route(
125
114
  self,
126
- adapter_name: str,
115
+ module_name: str,
127
116
  path: str,
128
117
  handler: Callable,
129
118
  methods: List[str] = ["POST"]
@@ -131,35 +120,37 @@ class AdapterServer:
131
120
  """
132
121
  注册HTTP路由
133
122
 
134
- :param adapter_name: str 适配器名称
135
- :param path: str 路由路径(如"/message")
123
+ :param module_name: str 模块名称
124
+ :param path: str 路由路径
136
125
  :param handler: Callable 处理函数
137
126
  :param methods: List[str] HTTP方法列表(默认["POST"])
138
127
 
139
128
  :raises ValueError: 当路径已注册时抛出
140
-
141
- {!--< tips >!--}
142
- 路径会自动添加适配器前缀,如:/adapter_name/path
143
- {!--< /tips >!--}
144
129
  """
145
- full_path = f"/{adapter_name}{path}"
130
+ full_path = f"/{module_name}{path}"
146
131
 
147
- if full_path in self._webhook_routes[adapter_name]:
132
+ if full_path in self._http_routes[module_name]:
148
133
  raise ValueError(f"路径 {full_path} 已注册")
149
134
 
150
135
  route = APIRoute(
151
136
  path=full_path,
152
137
  endpoint=handler,
153
138
  methods=methods,
154
- name=f"{adapter_name}{path}"
139
+ name=f"{module_name}_{path.replace('/', '_')}"
155
140
  )
156
141
  self.app.router.routes.append(route)
157
- self._webhook_routes[adapter_name][full_path] = handler
142
+ self._http_routes[module_name][full_path] = handler
158
143
  logger.info(f"注册HTTP路由: {self.base_url}{full_path} 方法: {methods}")
159
144
 
145
+ def register_webhook(self, *args, **kwargs) -> None:
146
+ """
147
+ 兼容性方法:注册HTTP路由(适配器旧接口)
148
+ """
149
+ return self.register_http_route(*args, **kwargs)
150
+
160
151
  def register_websocket(
161
152
  self,
162
- adapter_name: str,
153
+ module_name: str,
163
154
  path: str,
164
155
  handler: Callable[[WebSocket], Awaitable[Any]],
165
156
  auth_handler: Optional[Callable[[WebSocket], Awaitable[bool]]] = None,
@@ -167,29 +158,21 @@ class AdapterServer:
167
158
  """
168
159
  注册WebSocket路由
169
160
 
170
- :param adapter_name: str 适配器名称
171
- :param path: str WebSocket路径(如"/ws")
161
+ :param module_name: str 模块名称
162
+ :param path: str WebSocket路径
172
163
  :param handler: Callable[[WebSocket], Awaitable[Any]] 主处理函数
173
164
  :param auth_handler: Optional[Callable[[WebSocket], Awaitable[bool]]] 认证函数
174
165
 
175
166
  :raises ValueError: 当路径已注册时抛出
176
-
177
- {!--< tips >!--}
178
- 认证函数应返回布尔值,False将拒绝连接
179
- {!--< /tips >!--}
180
167
  """
181
- full_path = f"/{adapter_name}{path}"
168
+ full_path = f"/{module_name}{path}"
182
169
 
183
- if full_path in self._websocket_routes[adapter_name]:
170
+ if full_path in self._websocket_routes[module_name]:
184
171
  raise ValueError(f"WebSocket路径 {full_path} 已注册")
185
172
 
186
173
  async def websocket_endpoint(websocket: WebSocket) -> None:
187
174
  """
188
175
  WebSocket端点包装器
189
-
190
- {!--< internal-use >!--}
191
- 处理连接生命周期和错误处理
192
- {!--< /internal-use >!--}
193
176
  """
194
177
  await websocket.accept()
195
178
 
@@ -209,17 +192,16 @@ class AdapterServer:
209
192
  self.app.add_api_websocket_route(
210
193
  path=full_path,
211
194
  endpoint=websocket_endpoint,
212
- name=f"{adapter_name}{path}"
195
+ name=f"{module_name}_{path.replace('/', '_')}"
213
196
  )
214
- self._websocket_routes[adapter_name][full_path] = (handler, auth_handler)
197
+ self._websocket_routes[module_name][full_path] = (handler, auth_handler)
215
198
  logger.info(f"注册WebSocket: {self.base_url}{full_path} {'(需认证)' if auth_handler else ''}")
216
199
 
217
200
  def get_app(self) -> FastAPI:
218
201
  """
219
202
  获取FastAPI应用实例
220
203
 
221
- :return:
222
- FastAPI: FastAPI应用实例
204
+ :return: FastAPI应用实例
223
205
  """
224
206
  return self.app
225
207
 
@@ -231,7 +213,7 @@ class AdapterServer:
231
213
  ssl_keyfile: Optional[str] = None
232
214
  ) -> None:
233
215
  """
234
- 启动适配器服务器
216
+ 启动路由服务器
235
217
 
236
218
  :param host: str 监听地址(默认"0.0.0.0")
237
219
  :param port: int 监听端口(默认8000)
@@ -252,25 +234,24 @@ class AdapterServer:
252
234
  config.keyfile = ssl_keyfile
253
235
 
254
236
  self.base_url = f"http{'s' if ssl_certfile else ''}://{host}:{port}"
255
- logger.info(f"启动服务器 {self.base_url}")
237
+ logger.info(f"启动路由服务器 {self.base_url}")
256
238
 
257
239
  self._server_task = asyncio.create_task(serve(self.app, config))
258
240
 
259
241
  async def stop(self) -> None:
260
242
  """
261
243
  停止服务器
262
-
263
- {!--< tips >!--}
264
- 会等待所有连接正常关闭
265
- {!--< /tips >!--}
266
244
  """
267
245
  if self._server_task:
268
246
  self._server_task.cancel()
269
247
  try:
270
248
  await self._server_task
271
249
  except asyncio.CancelledError:
272
- logger.info("服务器已停止")
250
+ logger.info("路由服务器已停止")
273
251
  self._server_task = None
274
252
 
253
+ # 主要实例
254
+ router = RouterManager()
275
255
 
276
- adapter_server = AdapterServer()
256
+ # 兼容性实例
257
+ adapter_server = router
ErisPulse/__init__.py CHANGED
@@ -10,6 +10,9 @@ ErisPulse SDK 主模块
10
10
  {!--< /tips >!--}
11
11
  """
12
12
 
13
+ __version__ = "2.1.14dev1"
14
+ __author__ = "ErisPulse"
15
+
13
16
  import os
14
17
  import sys
15
18
  import importlib
@@ -19,28 +22,36 @@ from typing import Dict, List, Tuple, Type, Any
19
22
  from pathlib import Path
20
23
 
21
24
  # BaseModules: SDK核心模块
22
- from .Core import util
23
- from .Core import raiserr
24
25
  from .Core import logger
25
26
  from .Core import env
26
27
  from .Core import mods
27
28
  from .Core import adapter, AdapterFather, SendDSL
28
- from .Core import adapter_server
29
+ from .Core import router, adapter_server
30
+ from .Core import exceptions
31
+ from .Core import config
29
32
 
30
33
  sdk = sys.modules[__name__]
31
34
 
32
35
  BaseModules = {
33
- "util": util,
34
36
  "logger": logger,
35
- "raiserr": raiserr,
37
+ "config": config,
38
+ "exceptions": exceptions,
36
39
  "env": env,
37
40
  "mods": mods,
38
41
  "adapter": adapter,
42
+ "router": router,
43
+ "adapter_server": adapter_server,
39
44
  "SendDSL": SendDSL,
40
45
  "AdapterFather": AdapterFather,
41
46
  "BaseAdapter": AdapterFather
42
47
  }
43
48
 
49
+ import asyncio
50
+
51
+ asyncio_loop = asyncio.get_event_loop()
52
+
53
+ exceptions.setup_async_loop(asyncio_loop)
54
+
44
55
  for module, moduleObj in BaseModules.items():
45
56
  setattr(sdk, module, moduleObj)
46
57
 
@@ -664,23 +675,24 @@ def _prepare_environment() -> bool:
664
675
  {!--< internal-use >!--}
665
676
  准备运行环境
666
677
 
667
- 1. 初始化项目环境文件
668
- 2. 加载环境变量配置
678
+ 初始化项目环境文件
669
679
 
670
680
  :return: bool 环境准备是否成功
671
681
  """
672
682
  logger.info("[Init] 准备初始化环境...")
673
683
  try:
684
+ from .Core.erispulse_config import get_erispulse_config
685
+ get_erispulse_config()
686
+ logger.info("[Init] 配置文件已加载")
687
+
674
688
  main_init = init_progress()
675
689
  if main_init:
676
690
  logger.info("[Init] 项目入口已生成, 你可以在 main.py 中编写一些代码")
677
- env.load_env_file()
678
691
  return True
679
692
  except Exception as e:
680
693
  logger.error(f"环境准备失败: {e}")
681
694
  return False
682
695
 
683
-
684
696
  def init() -> bool:
685
697
  """
686
698
  SDK初始化入口
ErisPulse/__main__.py CHANGED
@@ -19,7 +19,6 @@ import json
19
19
  import asyncio
20
20
  from urllib.parse import urlparse
21
21
  from typing import List, Dict, Tuple, Optional, Callable, Any
22
- from importlib.metadata import version, PackageNotFoundError
23
22
  from watchdog.observers import Observer
24
23
  from watchdog.events import FileSystemEventHandler
25
24
 
@@ -188,26 +187,38 @@ class PackageManager:
188
187
 
189
188
  try:
190
189
  # 查找模块和适配器
191
- for dist in importlib.metadata.distributions():
192
- if "ErisPulse-" in dist.metadata["Name"]:
193
- entry_points = dist.entry_points
194
- for ep in entry_points:
195
- if ep.group == "erispulse.module":
196
- packages["modules"][ep.name] = {
197
- "package": dist.metadata["Name"],
198
- "version": dist.version,
199
- "summary": dist.metadata["Summary"],
200
- "enabled": self._is_module_enabled(ep.name)
201
- }
202
- elif ep.group == "erispulse.adapter":
203
- packages["adapters"][ep.name] = {
204
- "package": dist.metadata["Name"],
205
- "version": dist.version,
206
- "summary": dist.metadata["Summary"]
207
- }
190
+ entry_points = importlib.metadata.entry_points()
191
+
192
+ # 处理模块
193
+ if hasattr(entry_points, 'select'):
194
+ module_entries = entry_points.select(group='erispulse.module')
195
+ else:
196
+ module_entries = entry_points.get('erispulse.module', [])
197
+
198
+ for entry in module_entries:
199
+ dist = entry.dist
200
+ packages["modules"][entry.name] = {
201
+ "package": dist.metadata["Name"],
202
+ "version": dist.version,
203
+ "summary": dist.metadata["Summary"],
204
+ "enabled": self._is_module_enabled(entry.name)
205
+ }
206
+
207
+ # 处理适配器
208
+ if hasattr(entry_points, 'select'):
209
+ adapter_entries = entry_points.select(group='erispulse.adapter')
210
+ else:
211
+ adapter_entries = entry_points.get('erispulse.adapter', [])
212
+
213
+ for entry in adapter_entries:
214
+ dist = entry.dist
215
+ packages["adapters"][entry.name] = {
216
+ "package": dist.metadata["Name"],
217
+ "version": dist.version,
218
+ "summary": dist.metadata["Summary"]
219
+ }
208
220
 
209
221
  # 查找CLI扩展
210
- entry_points = importlib.metadata.entry_points()
211
222
  if hasattr(entry_points, 'select'):
212
223
  cli_entries = entry_points.select(group='erispulse.cli')
213
224
  else:
@@ -222,9 +233,9 @@ class PackageManager:
222
233
  }
223
234
 
224
235
  except Exception as e:
225
- console.print(f"[error]获取已安装包信息失败: {e}[/]")
236
+ print(f"[error] 获取已安装包信息失败: {e}")
226
237
  import traceback
227
- console.print(traceback.format_exc())
238
+ print(traceback.format_exc())
228
239
 
229
240
  return packages
230
241
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse
3
- Version: 2.1.13rc3
3
+ Version: 2.1.14.dev2
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,16 @@
1
+ ErisPulse/__init__.py,sha256=YoqCrqbR1dxjajBii6D4iebuVUl2bZaZYbpPXEgGBz8,26428
2
+ ErisPulse/__main__.py,sha256=aDYN5_11PdL3tj2ruhNoXwNc9TmAUnBtFujQgnEf_sI,37573
3
+ ErisPulse/Core/__init__.py,sha256=hX2yEt9VSD3JubiofoQdcY4v1lnQUU02dhuVADkMTVo,437
4
+ ErisPulse/Core/adapter.py,sha256=oBJOp6SS8sm8NgIxQwetGsHu24wHNXz7ESQ5yKJSo7Q,18234
5
+ ErisPulse/Core/config.py,sha256=2BRWINOqKtHSCP4KfhuiRpGwR96jWGKV7gjZSi_VQHE,2397
6
+ ErisPulse/Core/env.py,sha256=U45f9WtriVyd3tW1N8to-ZvpzcF9gD8DJzNTC1jY2cM,17665
7
+ ErisPulse/Core/erispulse_config.py,sha256=QDx401hNX9JcSHqCSVK33X6VTubl6HI1znAK3T_J0K0,3034
8
+ ErisPulse/Core/exceptions.py,sha256=zuTREGczwGzbYT4Z6dACqHwgNRpiJeLFR8aCxFdOg7k,3667
9
+ ErisPulse/Core/logger.py,sha256=HY6jkTQpztbc07mewHbMpaFNv_2-fX-87G78UEa3DTo,8306
10
+ ErisPulse/Core/mods.py,sha256=2yIq8t9Ca9CBPRiZU0yr8Lc0XGmmkB7LlH-5FWqXjw4,7023
11
+ ErisPulse/Core/router.py,sha256=66hT8VC2dVNX-dANldoOPDcqQ94hidFkNnvKgAPemGQ,8491
12
+ erispulse-2.1.14.dev2.dist-info/METADATA,sha256=h-m8vZWF0_WsUwiSj83NmYEZz0zEA91-cdR39EkyKpc,6264
13
+ erispulse-2.1.14.dev2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ erispulse-2.1.14.dev2.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
15
+ erispulse-2.1.14.dev2.dist-info/licenses/LICENSE,sha256=4jyqikiB0G0n06CEEMMTzTXjE4IShghSlB74skMSPQs,1464
16
+ erispulse-2.1.14.dev2.dist-info/RECORD,,
ErisPulse/Core/raiserr.py DELETED
@@ -1,181 +0,0 @@
1
- """
2
- ErisPulse 错误管理系统
3
-
4
- 提供全局异常捕获功能。不再推荐使用自定义错误注册功能。
5
-
6
- {!--< tips >!--}
7
- 1. 请使用Python原生异常抛出方法
8
- 2. 系统会自动捕获并格式化所有未处理异常
9
- 3. 注册功能已标记为弃用,将在未来版本移除
10
- {!--< /tips >!--}
11
- """
12
-
13
- import sys
14
- import traceback
15
- import asyncio
16
- from typing import Dict, Any, Optional, Type, Callable, List, Set, Tuple, Union
17
-
18
- class Error:
19
- """
20
- 错误管理器
21
-
22
- {!--< deprecated >!--} 请使用Python原生异常抛出方法 | 2025-07-18
23
-
24
- {!--< tips >!--}
25
- 1. 注册功能将在未来版本移除
26
- 2. 请直接使用raise Exception("message")方式抛出异常
27
- {!--< /tips >!--}
28
- """
29
-
30
- def __init__(self):
31
- self._types = {}
32
-
33
- def register(self, name: str, doc: str = "", base: Type[Exception] = Exception) -> Type[Exception]:
34
- """
35
- 注册新的错误类型
36
-
37
- {!--< deprecated >!--} 请使用Python原生异常抛出方法 | 2025-07-18
38
-
39
- :param name: 错误类型名称
40
- :param doc: 错误描述文档
41
- :param base: 基础异常类
42
- :return: 注册的错误类
43
- """
44
- if name not in self._types:
45
- err_cls = type(name, (base,), {"__doc__": doc})
46
- self._types[name] = err_cls
47
- return self._types[name]
48
-
49
- def __getattr__(self, name: str) -> Callable[..., None]:
50
- """
51
- 动态获取错误抛出函数
52
-
53
- {!--< deprecated >!--} 请使用Python原生异常抛出方法 | 2025-07-18
54
-
55
- :param name: 错误类型名称
56
- :return: 错误抛出函数
57
-
58
- :raises AttributeError: 当错误类型未注册时抛出
59
- """
60
- def raiser(msg: str, exit: bool = False) -> None:
61
- """
62
- 错误抛出函数
63
-
64
- :param msg: 错误消息
65
- :param exit: 是否退出程序
66
- """
67
- from .logger import logger
68
- err_cls = self._types.get(name) or self.register(name)
69
- exc = err_cls(msg)
70
-
71
- red = '\033[91m'
72
- reset = '\033[0m'
73
-
74
- logger.error(f"{red}{name}: {msg} | {err_cls.__doc__}{reset}")
75
- logger.error(f"{red}{ ''.join(traceback.format_stack()) }{reset}")
76
-
77
- if exit:
78
- raise exc
79
- return raiser
80
-
81
- def info(self, name: Optional[str] = None) -> Dict[str, Any]:
82
- """
83
- 获取错误信息
84
-
85
- {!--< deprecated >!--} 此功能将在未来版本移除 | 2025-07-18
86
-
87
- :param name: 错误类型名称(可选)
88
- :return: 错误信息字典
89
- """
90
- result = {}
91
- for err_name, err_cls in self._types.items():
92
- result[err_name] = {
93
- "type": err_name,
94
- "doc": getattr(err_cls, "__doc__", ""),
95
- "class": err_cls,
96
- }
97
- if name is None:
98
- return result
99
- err_cls = self._types.get(name)
100
- if not err_cls:
101
- return {
102
- "type": None,
103
- "doc": None,
104
- "class": None,
105
- }
106
- return {
107
- "type": name,
108
- "doc": getattr(err_cls, "__doc__", ""),
109
- "class": err_cls,
110
- }
111
-
112
-
113
- raiserr = Error()
114
-
115
- def global_exception_handler(exc_type: Type[Exception], exc_value: Exception, exc_traceback: Any) -> None:
116
- """
117
- 全局异常处理器
118
-
119
- :param exc_type: 异常类型
120
- :param exc_value: 异常值
121
- :param exc_traceback: 追踪信息
122
- """
123
- RED = '\033[91m'
124
- YELLOW = '\033[93m'
125
- BLUE = '\033[94m'
126
- RESET = '\033[0m'
127
-
128
- error_title = f"{RED}{exc_type.__name__}{RESET}: {YELLOW}{exc_value}{RESET}"
129
- traceback_lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
130
-
131
- colored_traceback = []
132
- for line in traceback_lines:
133
- if "File " in line and ", line " in line:
134
- parts = line.split(', line ')
135
- colored_line = f"{BLUE}{parts[0]}{RESET}, line {parts[1]}"
136
- colored_traceback.append(colored_line)
137
- else:
138
- colored_traceback.append(f"{RED}{line}{RESET}")
139
-
140
- full_error = f"""
141
- {error_title}
142
- {RED}Traceback:{RESET}
143
- {colored_traceback}"""
144
-
145
- sys.stderr.write(full_error)
146
-
147
- def async_exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None:
148
- """
149
- 异步异常处理器
150
-
151
- :param loop: 事件循环
152
- :param context: 上下文字典
153
- """
154
- RED = '\033[91m'
155
- YELLOW = '\033[93m'
156
- BLUE = '\033[94m'
157
- RESET = '\033[0m'
158
-
159
- exception = context.get('exception')
160
- if exception:
161
- tb = ''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))
162
-
163
- colored_tb = []
164
- for line in tb.split('\n'):
165
- if "File " in line and ", line " in line:
166
- parts = line.split(', line ')
167
- colored_line = f"{BLUE}{parts[0]}{RESET}, line {parts[1]}"
168
- colored_tb.append(colored_line)
169
- else:
170
- colored_tb.append(f"{RED}{line}{RESET}")
171
-
172
- error_msg = f"""{RED}{type(exception).__name__}{RESET}: {YELLOW}{exception}{RESET}
173
- {RED}Traceback:{RESET}
174
- {colored_tb}"""
175
- sys.stderr.write(error_msg)
176
- else:
177
- msg = context.get('message', 'Unknown async error')
178
- sys.stderr.write(f"{RED}Async Error{RESET}: {YELLOW}{msg}{RESET}")
179
-
180
- sys.excepthook = global_exception_handler
181
- asyncio.get_event_loop().set_exception_handler(async_exception_handler)
ErisPulse/Core/util.py DELETED
@@ -1,123 +0,0 @@
1
- """
2
- ErisPulse 工具函数集合
3
-
4
- 提供常用工具函数,包括拓扑排序、缓存装饰器、异步执行等实用功能。
5
-
6
- {!--< tips >!--}
7
- 1. 使用@cache装饰器缓存函数结果
8
- 2. 使用@run_in_executor在独立线程中运行同步函数
9
- 3. 使用@retry实现自动重试机制
10
- {!--< /tips >!--}
11
- """
12
-
13
- import time
14
- import asyncio
15
- import functools
16
- import traceback
17
- from concurrent.futures import ThreadPoolExecutor
18
- from collections import defaultdict, deque
19
- from typing import List, Dict, Type, Callable, Any, Optional, Set
20
-
21
- executor = ThreadPoolExecutor()
22
-
23
- class Util:
24
- """
25
- 工具函数集合
26
-
27
- 提供各种实用功能,简化开发流程
28
-
29
- {!--< tips >!--}
30
- 1. 拓扑排序用于解决依赖关系
31
- 2. 装饰器简化常见模式实现
32
- 3. 异步执行提升性能
33
- {!--< /tips >!--}
34
- """
35
- def ExecAsync(self, async_func: Callable, *args: Any, **kwargs: Any) -> Any:
36
- """
37
- 异步执行函数
38
-
39
- :param async_func: 异步函数
40
- :param args: 位置参数
41
- :param kwargs: 关键字参数
42
- :return: 函数执行结果
43
-
44
- :example:
45
- >>> result = util.ExecAsync(my_async_func, arg1, arg2)
46
- """
47
- loop = asyncio.get_event_loop()
48
- return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
49
-
50
- def cache(self, func: Callable) -> Callable:
51
- """
52
- 缓存装饰器
53
-
54
- :param func: 被装饰函数
55
- :return: 装饰后的函数
56
-
57
- :example:
58
- >>> @util.cache
59
- >>> def expensive_operation(param):
60
- >>> return heavy_computation(param)
61
- """
62
- cache_dict = {}
63
- @functools.wraps(func)
64
- def wrapper(*args, **kwargs):
65
- key = (args, tuple(sorted(kwargs.items())))
66
- if key not in cache_dict:
67
- cache_dict[key] = func(*args, **kwargs)
68
- return cache_dict[key]
69
- return wrapper
70
-
71
- def run_in_executor(self, func: Callable) -> Callable:
72
- """
73
- 在独立线程中执行同步函数的装饰器
74
-
75
- :param func: 被装饰的同步函数
76
- :return: 可等待的协程函数
77
-
78
- :example:
79
- >>> @util.run_in_executor
80
- >>> def blocking_io():
81
- >>> # 执行阻塞IO操作
82
- >>> return result
83
- """
84
- @functools.wraps(func)
85
- async def wrapper(*args, **kwargs):
86
- loop = asyncio.get_event_loop()
87
- try:
88
- return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
89
- except Exception as e:
90
- from . import logger
91
- logger.error(f"线程内发生未处理异常:\n{''.join(traceback.format_exc())}")
92
- return wrapper
93
-
94
- def retry(self, max_attempts: int = 3, delay: int = 1) -> Callable:
95
- """
96
- 自动重试装饰器
97
-
98
- :param max_attempts: 最大重试次数 (默认: 3)
99
- :param delay: 重试间隔(秒) (默认: 1)
100
- :return: 装饰器函数
101
-
102
- :example:
103
- >>> @util.retry(max_attempts=5, delay=2)
104
- >>> def unreliable_operation():
105
- >>> # 可能失败的操作
106
- """
107
- def decorator(func: Callable) -> Callable:
108
- @functools.wraps(func)
109
- def wrapper(*args, **kwargs):
110
- attempts = 0
111
- while attempts < max_attempts:
112
- try:
113
- return func(*args, **kwargs)
114
- except Exception as e:
115
- attempts += 1
116
- if attempts == max_attempts:
117
- raise
118
- time.sleep(delay)
119
- return wrapper
120
- return decorator
121
-
122
-
123
- util = Util()
@@ -1,16 +0,0 @@
1
- ErisPulse/__init__.py,sha256=T-N56UQyBmpvlqwH2wGi5ptPzaJpbgF5tKkJt0DlkaM,26099
2
- ErisPulse/__main__.py,sha256=kGpk-BeztjhrdNwWL2mZxaVy05y4XicCS6SZGtQERho,37409
3
- ErisPulse/Core/__init__.py,sha256=tBYPahQ7-w7U6M69xy8DW0_ECa9ja-Q0y_SQqVswYBo,409
4
- ErisPulse/Core/adapter.py,sha256=ZK81dibJ471FowL0MRXkS113iBcMgj_VzpTw0PzhAEo,18102
5
- ErisPulse/Core/config.py,sha256=ZmwGdtHSOE7K5uOGzLYcyl3ZF3sAmeWAntqcdfDzhpM,5027
6
- ErisPulse/Core/env.py,sha256=HGkzsdbxh8c1GSDJhnGP9B09Sz2ZeNbRxWieaFmAcug,18870
7
- ErisPulse/Core/logger.py,sha256=cJzNXF-EmdWxwgiHg5Itmkwsva2Jhe9l9X4rXKiXHgc,8296
8
- ErisPulse/Core/mods.py,sha256=2yIq8t9Ca9CBPRiZU0yr8Lc0XGmmkB7LlH-5FWqXjw4,7023
9
- ErisPulse/Core/raiserr.py,sha256=vlyaaiOIYkyqm9dAqSW9E54JBzX-9roHDp5_r6I0yUU,5591
10
- ErisPulse/Core/server.py,sha256=FkDTeLuHD5IBnWVxvYU8pHb6yCt8GzyvC1bpOiJ7G7I,9217
11
- ErisPulse/Core/util.py,sha256=7rdMmn6sBFqYd4znxBCcJjuv2eyTExdeKyZopgds868,3796
12
- erispulse-2.1.13rc3.dist-info/METADATA,sha256=ppMW2h_uDFhvpfP9u30vbaHgQA9fr_oPdDzeWaLQRf4,6262
13
- erispulse-2.1.13rc3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- erispulse-2.1.13rc3.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
15
- erispulse-2.1.13rc3.dist-info/licenses/LICENSE,sha256=4jyqikiB0G0n06CEEMMTzTXjE4IShghSlB74skMSPQs,1464
16
- erispulse-2.1.13rc3.dist-info/RECORD,,