ErisPulse 1.0.14.dev3__tar.gz → 1.0.15__tar.gz
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-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/__init__.py +21 -7
- erispulse-1.0.15/ErisPulse/adapter.py +81 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/db.py +5 -5
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/logger.py +14 -14
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/mods.py +9 -9
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/raiserr.py +1 -1
- erispulse-1.0.15/ErisPulse/util.py +64 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/PKG-INFO +1 -3
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/SOURCES.txt +1 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/PKG-INFO +1 -3
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/README.md +0 -2
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/pyproject.toml +1 -1
- erispulse-1.0.14.dev3/ErisPulse/util.py +0 -29
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse/__main__.py +0 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/dependency_links.txt +0 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/entry_points.txt +0 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/requires.txt +0 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/ErisPulse.egg-info/top_level.txt +0 -0
- {erispulse-1.0.14.dev3 → erispulse-1.0.15}/setup.cfg +0 -0
|
@@ -6,6 +6,7 @@ from .raiserr import raiserr
|
|
|
6
6
|
from .logger import logger
|
|
7
7
|
from .db import env
|
|
8
8
|
from .mods import mods
|
|
9
|
+
from .adapter import adapter, adapterbase
|
|
9
10
|
|
|
10
11
|
# 注册 ErrorHook 并预注册常用错误类型
|
|
11
12
|
raiserr.register("MissingDependencyError", doc="缺少依赖错误")
|
|
@@ -16,16 +17,18 @@ raiserr.register("ModuleLoadError" , doc="模块加载错误")
|
|
|
16
17
|
sdk = types.SimpleNamespace()
|
|
17
18
|
setattr(sdk, "env", env)
|
|
18
19
|
setattr(sdk, "mods", mods)
|
|
20
|
+
setattr(sdk, "util", util)
|
|
19
21
|
setattr(sdk, "raiserr", raiserr)
|
|
20
22
|
setattr(sdk, "logger", logger)
|
|
21
|
-
setattr(sdk, "
|
|
23
|
+
setattr(sdk, "adapter", adapter)
|
|
24
|
+
setattr(sdk, "BaseAdapter", adapterbase)
|
|
22
25
|
|
|
23
26
|
env.load_env_file()
|
|
24
27
|
|
|
25
28
|
def init():
|
|
26
29
|
try:
|
|
27
30
|
sdkModulePath = os.path.join(os.path.dirname(__file__), "modules")
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
if not os.path.exists(sdkModulePath):
|
|
30
33
|
os.makedirs(sdkModulePath)
|
|
31
34
|
|
|
@@ -51,7 +54,7 @@ def init():
|
|
|
51
54
|
if not hasattr(moduleObj, "Main"):
|
|
52
55
|
logger.warning(f"模块 {module_name} 缺少 'Main' 类.")
|
|
53
56
|
continue
|
|
54
|
-
|
|
57
|
+
|
|
55
58
|
module_info = mods.get_module(moduleObj.moduleInfo.get("meta", {}).get("name", None))
|
|
56
59
|
if module_info is None:
|
|
57
60
|
module_info = {
|
|
@@ -60,12 +63,12 @@ def init():
|
|
|
60
63
|
}
|
|
61
64
|
mods.set_module(moduleObj.moduleInfo.get("meta", {}).get("name", None), module_info)
|
|
62
65
|
logger.info(f"模块 {moduleObj.moduleInfo.get('meta', {}).get('name', None)} 信息已初始化并存储到数据库")
|
|
63
|
-
|
|
66
|
+
|
|
64
67
|
if not module_info.get('status', True):
|
|
65
68
|
disabledModules.append(module_name)
|
|
66
69
|
logger.warning(f"模块 {moduleObj.moduleInfo.get('meta', {}).get('name', None)} 已禁用,跳过加载")
|
|
67
70
|
continue
|
|
68
|
-
|
|
71
|
+
|
|
69
72
|
required_deps = moduleObj.moduleInfo.get("dependencies", []).get("requires", [])
|
|
70
73
|
missing_required_deps = [dep for dep in required_deps if dep not in TempModules]
|
|
71
74
|
if missing_required_deps:
|
|
@@ -107,7 +110,7 @@ def init():
|
|
|
107
110
|
if dep in disabledModules:
|
|
108
111
|
logger.warning(f"模块 {module_name} 的依赖模块 {dep} 已禁用,跳过加载")
|
|
109
112
|
continue
|
|
110
|
-
|
|
113
|
+
|
|
111
114
|
if not all(dep in sdkInstalledModuleNames for dep in moduleDependecies):
|
|
112
115
|
raiserr.InvalidDependencyError(
|
|
113
116
|
f"模块 {module_name} 的依赖无效: {moduleDependecies}"
|
|
@@ -136,11 +139,22 @@ def init():
|
|
|
136
139
|
module_status = mods.get_module_status(moduleInfo.get("meta", {}).get("name", None))
|
|
137
140
|
if not module_status:
|
|
138
141
|
continue
|
|
139
|
-
|
|
142
|
+
|
|
140
143
|
moduleMain = moduleObj.Main(sdk)
|
|
141
144
|
setattr(moduleMain, "moduleInfo", moduleInfo)
|
|
142
145
|
setattr(sdk, moduleInfo.get("meta", {}).get("name", None), moduleMain)
|
|
143
146
|
logger.debug(f"模块 {moduleInfo.get('meta', {}).get('name', None)} 正在初始化")
|
|
147
|
+
|
|
148
|
+
if hasattr(moduleMain, "register_adapters"):
|
|
149
|
+
try:
|
|
150
|
+
adapters = moduleMain.register_adapters()
|
|
151
|
+
if isinstance(adapters, dict):
|
|
152
|
+
for platform_name, adapter_class in adapters.items():
|
|
153
|
+
sdk.adapter.register(platform_name, adapter_class)
|
|
154
|
+
logger.info(f"模块 {moduleInfo['meta']['name']} 注册了适配器: {platform_name}")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"注册适配器失败: {e}")
|
|
157
|
+
|
|
144
158
|
except Exception as e:
|
|
145
159
|
logger.error(f"初始化失败: {e}")
|
|
146
160
|
raise e
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Callable, Any, Dict, List, Type
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
|
|
6
|
+
class BaseAdapter:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self._handlers = defaultdict(list)
|
|
9
|
+
self._middlewares = []
|
|
10
|
+
|
|
11
|
+
def on(self, event_type: str):
|
|
12
|
+
def decorator(func: Callable):
|
|
13
|
+
@functools.wraps(func)
|
|
14
|
+
async def wrapper(*args, **kwargs):
|
|
15
|
+
return await func(*args, **kwargs)
|
|
16
|
+
self._handlers[event_type].append(wrapper)
|
|
17
|
+
return wrapper
|
|
18
|
+
return decorator
|
|
19
|
+
|
|
20
|
+
def middleware(self, func: Callable):
|
|
21
|
+
self._middlewares.append(func)
|
|
22
|
+
return func
|
|
23
|
+
|
|
24
|
+
async def send(self, target: Any, message: Any, **kwargs):
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
async def call_api(self, endpoint: str, **params):
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
async def emit(self, event_type: str, data: Any):
|
|
31
|
+
for middleware in self._middlewares:
|
|
32
|
+
data = await middleware(data)
|
|
33
|
+
|
|
34
|
+
for handler in self._handlers.get(event_type, []):
|
|
35
|
+
await handler(data)
|
|
36
|
+
|
|
37
|
+
class AdapterManager:
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self._adapters: Dict[str, BaseAdapter] = {}
|
|
40
|
+
|
|
41
|
+
def register(self, platform: str, adapter_class: Type[BaseAdapter]) -> bool:
|
|
42
|
+
if not issubclass(adapter_class, BaseAdapter):
|
|
43
|
+
raise TypeError("适配器必须继承自BaseAdapter")
|
|
44
|
+
from . import sdk
|
|
45
|
+
self._adapters[platform] = adapter_class(sdk)
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
async def startup(self, platforms: List[str] = None):
|
|
49
|
+
if platforms is None:
|
|
50
|
+
platforms = self._adapters.keys()
|
|
51
|
+
|
|
52
|
+
for platform in platforms:
|
|
53
|
+
if platform not in self._adapters:
|
|
54
|
+
raise ValueError(f"平台 {platform} 未注册")
|
|
55
|
+
adapter = self._adapters[platform]
|
|
56
|
+
asyncio.create_task(self._run_adapter(adapter, platform))
|
|
57
|
+
|
|
58
|
+
async def _run_adapter(self, adapter: BaseAdapter, platform: str):
|
|
59
|
+
try:
|
|
60
|
+
await adapter.start()
|
|
61
|
+
except Exception as e:
|
|
62
|
+
self.logger.error(f"平台 {platform} 停止时遇到了错误: {e}")
|
|
63
|
+
|
|
64
|
+
async def shutdown(self):
|
|
65
|
+
for adapter in self._adapters.values():
|
|
66
|
+
await adapter.shutdown()
|
|
67
|
+
|
|
68
|
+
def get(self, platform: str) -> BaseAdapter:
|
|
69
|
+
return self._adapters.get(platform)
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, platform: str) -> BaseAdapter:
|
|
72
|
+
if platform not in self._adapters:
|
|
73
|
+
raise AttributeError(f"平台 {platform} 的适配器未注册")
|
|
74
|
+
return self._adapters[platform]
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def platforms(self) -> list:
|
|
78
|
+
return list(self._adapters.keys())
|
|
79
|
+
|
|
80
|
+
adapter = AdapterManager()
|
|
81
|
+
adapterbase = BaseAdapter
|
|
@@ -17,7 +17,7 @@ class EnvManager:
|
|
|
17
17
|
if not hasattr(self, "_initialized"):
|
|
18
18
|
self._init_db()
|
|
19
19
|
self._initialized = True
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
def _init_db(self):
|
|
22
22
|
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
|
|
23
23
|
conn = sqlite3.connect(self.db_path)
|
|
@@ -49,13 +49,13 @@ class EnvManager:
|
|
|
49
49
|
return self.get(key, default)
|
|
50
50
|
else:
|
|
51
51
|
raise
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
def get_all_keys(self) -> list:
|
|
54
54
|
with sqlite3.connect(self.db_path) as conn:
|
|
55
55
|
cursor = conn.cursor()
|
|
56
56
|
cursor.execute("SELECT key FROM config")
|
|
57
57
|
return [row[0] for row in cursor.fetchall()]
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
def set(self, key, value):
|
|
60
60
|
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
61
61
|
conn = sqlite3.connect(self.db_path)
|
|
@@ -70,7 +70,7 @@ class EnvManager:
|
|
|
70
70
|
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
71
71
|
conn.commit()
|
|
72
72
|
conn.close()
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
def clear(self):
|
|
75
75
|
conn = sqlite3.connect(self.db_path)
|
|
76
76
|
cursor = conn.cursor()
|
|
@@ -87,7 +87,7 @@ class EnvManager:
|
|
|
87
87
|
for key, value in vars(env_module).items():
|
|
88
88
|
if not key.startswith("__") and isinstance(value, (dict, list, str, int, float, bool)):
|
|
89
89
|
self.set(key, value)
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
def __getattr__(self, key):
|
|
92
92
|
try:
|
|
93
93
|
return self.get(key)
|
|
@@ -19,7 +19,7 @@ class Logger:
|
|
|
19
19
|
level = level.upper()
|
|
20
20
|
if hasattr(logging, level):
|
|
21
21
|
self._logger.setLevel(getattr(logging, level))
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def set_module_level(self, module_name: str, level: str) -> bool:
|
|
24
24
|
from .db import env
|
|
25
25
|
if not env.get_module_status(module_name):
|
|
@@ -33,15 +33,15 @@ class Logger:
|
|
|
33
33
|
else:
|
|
34
34
|
self._logger.error(f"无效的日志等级: {level}")
|
|
35
35
|
return False
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def set_output_file(self, path: str | list):
|
|
38
38
|
if self._file_handler:
|
|
39
39
|
self._logger.removeHandler(self._file_handler)
|
|
40
40
|
self._file_handler.close()
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
if isinstance(path, str):
|
|
43
43
|
path = [path]
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
for p in path:
|
|
46
46
|
try:
|
|
47
47
|
file_handler = logging.FileHandler(p, encoding='utf-8')
|
|
@@ -51,14 +51,14 @@ class Logger:
|
|
|
51
51
|
except Exception as e:
|
|
52
52
|
self._logger.error(f"无法设置日志文件 {p}: {e}")
|
|
53
53
|
raise e
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
def save_logs(self, path: str | list):
|
|
56
56
|
if self._logs == None:
|
|
57
57
|
self._logger.warning("没有log记录可供保存。")
|
|
58
58
|
return
|
|
59
59
|
if isinstance(path, str):
|
|
60
60
|
path = [path]
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
for p in path:
|
|
63
63
|
try:
|
|
64
64
|
with open(p, "w", encoding="utf-8") as file:
|
|
@@ -70,14 +70,14 @@ class Logger:
|
|
|
70
70
|
except Exception as e:
|
|
71
71
|
self._logger.error(f"无法保存日志到 {p}: {e}。")
|
|
72
72
|
raise e
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
def catch(self, func_or_level=None, level="error"):
|
|
75
75
|
if isinstance(func_or_level, str):
|
|
76
76
|
return lambda func: self.catch(func, level=func_or_level)
|
|
77
77
|
if func_or_level is None:
|
|
78
78
|
return lambda func: self.catch(func, level=level)
|
|
79
79
|
func = func_or_level
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
@functools.wraps(func)
|
|
82
82
|
def wrapper(*args, **kwargs):
|
|
83
83
|
try:
|
|
@@ -85,17 +85,17 @@ class Logger:
|
|
|
85
85
|
except Exception as e:
|
|
86
86
|
import traceback
|
|
87
87
|
error_info = traceback.format_exc()
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
module_name = func.__module__
|
|
90
90
|
if module_name == "__main__":
|
|
91
91
|
module_name = "Main"
|
|
92
92
|
func_name = func.__name__
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
error_msg = f"Exception in {func_name}: {str(e)}\n{error_info}"
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
log_method = getattr(self, level, self.error)
|
|
97
97
|
log_method(error_msg)
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
return None
|
|
100
100
|
return wrapper
|
|
101
101
|
|
|
@@ -105,10 +105,10 @@ class Logger:
|
|
|
105
105
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
106
106
|
msg = f"{timestamp} - {msg}"
|
|
107
107
|
self._logs[ModuleName].append(msg)
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
def _get_effective_level(self, module_name):
|
|
110
110
|
return self._module_levels.get(module_name, self._logger.level)
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
def _get_caller(self):
|
|
113
113
|
frame = inspect.currentframe().f_back.f_back
|
|
114
114
|
module = inspect.getmodule(frame)
|
|
@@ -4,29 +4,29 @@ from typing import Dict, Optional
|
|
|
4
4
|
class ModuleManager:
|
|
5
5
|
DEFAULT_MODULE_PREFIX = "erispulse.module.data:"
|
|
6
6
|
DEFAULT_STATUS_PREFIX = "erispulse.module.status:"
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def __init__(self):
|
|
9
9
|
from .db import env
|
|
10
10
|
self.env = env
|
|
11
11
|
self._ensure_prefixes()
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def _ensure_prefixes(self):
|
|
14
14
|
if not self.env.get("erispulse.system.module_prefix"):
|
|
15
15
|
self.env.set("erispulse.system.module_prefix", self.DEFAULT_MODULE_PREFIX)
|
|
16
16
|
if not self.env.get("erispulse.system.status_prefix"):
|
|
17
17
|
self.env.set("erispulse.system.status_prefix", self.DEFAULT_STATUS_PREFIX)
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
@property
|
|
20
20
|
def module_prefix(self) -> str:
|
|
21
21
|
return self.env.get("erispulse.system.module_prefix")
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
@property
|
|
24
24
|
def status_prefix(self) -> str:
|
|
25
25
|
return self.env.get("erispulse.system.status_prefix")
|
|
26
26
|
|
|
27
27
|
def set_module_status(self, module_name: str, status: bool) -> None:
|
|
28
28
|
self.env.set(f"{self.status_prefix}{module_name}", bool(status))
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
def get_module_status(self, module_name: str) -> bool:
|
|
31
31
|
status = self.env.get(f"{self.status_prefix}{module_name}", True)
|
|
32
32
|
if isinstance(status, str):
|
|
@@ -48,7 +48,7 @@ class ModuleManager:
|
|
|
48
48
|
modules_info = {}
|
|
49
49
|
all_keys = self.env.get_all_keys()
|
|
50
50
|
prefix_len = len(self.module_prefix)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
for key in all_keys:
|
|
53
53
|
if key.startswith(self.module_prefix):
|
|
54
54
|
module_name = key[prefix_len:]
|
|
@@ -58,14 +58,14 @@ class ModuleManager:
|
|
|
58
58
|
module_info['status'] = bool(status)
|
|
59
59
|
modules_info[module_name] = module_info
|
|
60
60
|
return modules_info
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
def update_module(self, module_name: str, module_info: dict) -> None:
|
|
63
63
|
self.set_module(module_name, module_info)
|
|
64
64
|
|
|
65
65
|
def remove_module(self, module_name: str) -> bool:
|
|
66
66
|
module_key = f"{self.module_prefix}{module_name}"
|
|
67
67
|
status_key = f"{self.status_prefix}{module_name}"
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
if self.env.get(module_key) is not None:
|
|
70
70
|
self.env.delete(module_key)
|
|
71
71
|
self.env.delete(status_key)
|
|
@@ -77,7 +77,7 @@ class ModuleManager:
|
|
|
77
77
|
if not module_prefix.endswith(':'):
|
|
78
78
|
module_prefix += ':'
|
|
79
79
|
self.env.set("erispulse.system.module_prefix", module_prefix)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
if status_prefix:
|
|
82
82
|
if not status_prefix.endswith(':'):
|
|
83
83
|
status_prefix += ':'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import asyncio
|
|
3
|
+
import functools
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
from collections import defaultdict, deque
|
|
6
|
+
|
|
7
|
+
executor = ThreadPoolExecutor()
|
|
8
|
+
|
|
9
|
+
def topological_sort(elements, dependencies, error):
|
|
10
|
+
graph = defaultdict(list)
|
|
11
|
+
in_degree = {element: 0 for element in elements}
|
|
12
|
+
for element, deps in dependencies.items():
|
|
13
|
+
for dep in deps:
|
|
14
|
+
graph[dep].append(element)
|
|
15
|
+
in_degree[element] += 1
|
|
16
|
+
queue = deque([element for element in elements if in_degree[element] == 0])
|
|
17
|
+
sorted_list = []
|
|
18
|
+
while queue:
|
|
19
|
+
node = queue.popleft()
|
|
20
|
+
sorted_list.append(node)
|
|
21
|
+
for neighbor in graph[node]:
|
|
22
|
+
in_degree[neighbor] -= 1
|
|
23
|
+
if in_degree[neighbor] == 0:
|
|
24
|
+
queue.append(neighbor)
|
|
25
|
+
if len(sorted_list) != len(elements):
|
|
26
|
+
raise error(f"Cycle detected in the dependencies: {elements} -> {dependencies}")
|
|
27
|
+
return sorted_list
|
|
28
|
+
|
|
29
|
+
def ExecAsync(async_func, *args, **kwargs):
|
|
30
|
+
loop = asyncio.get_event_loop()
|
|
31
|
+
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
32
|
+
|
|
33
|
+
def cache(func):
|
|
34
|
+
cache_dict = {}
|
|
35
|
+
@functools.wraps(func)
|
|
36
|
+
def wrapper(*args, **kwargs):
|
|
37
|
+
key = (args, tuple(sorted(kwargs.items())))
|
|
38
|
+
if key not in cache_dict:
|
|
39
|
+
cache_dict[key] = func(*args, **kwargs)
|
|
40
|
+
return cache_dict[key]
|
|
41
|
+
return wrapper
|
|
42
|
+
|
|
43
|
+
def run_in_executor(func):
|
|
44
|
+
@functools.wraps(func)
|
|
45
|
+
async def wrapper(*args, **kwargs):
|
|
46
|
+
loop = asyncio.get_event_loop()
|
|
47
|
+
return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
|
|
48
|
+
return wrapper
|
|
49
|
+
|
|
50
|
+
def retry(max_attempts=3, delay=1):
|
|
51
|
+
def decorator(func):
|
|
52
|
+
@functools.wraps(func)
|
|
53
|
+
def wrapper(*args, **kwargs):
|
|
54
|
+
attempts = 0
|
|
55
|
+
while attempts < max_attempts:
|
|
56
|
+
try:
|
|
57
|
+
return func(*args, **kwargs)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
attempts += 1
|
|
60
|
+
if attempts == max_attempts:
|
|
61
|
+
raise
|
|
62
|
+
time.sleep(delay)
|
|
63
|
+
return wrapper
|
|
64
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.15
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -20,8 +20,6 @@ Requires-Python: >=3.7
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: aiohttp
|
|
22
22
|
|
|
23
|
-
⚠ 当前版本处于开发中,请谨慎使用。
|
|
24
|
-
|
|
25
23
|

|
|
26
24
|
|
|
27
25
|
基于 [RyhBotPythonSDK V2](https://github.com/runoneall/RyhBotPythonSDK2) 构建,由 [sdkFrame](https://github.com/runoneall/sdkFrame) 提供支持的异步机器人开发框架。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.15
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -20,8 +20,6 @@ Requires-Python: >=3.7
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: aiohttp
|
|
22
22
|
|
|
23
|
-
⚠ 当前版本处于开发中,请谨慎使用。
|
|
24
|
-
|
|
25
23
|

|
|
26
24
|
|
|
27
25
|
基于 [RyhBotPythonSDK V2](https://github.com/runoneall/RyhBotPythonSDK2) 构建,由 [sdkFrame](https://github.com/runoneall/sdkFrame) 提供支持的异步机器人开发框架。
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
3
|
-
from collections import defaultdict, deque
|
|
4
|
-
|
|
5
|
-
executor = ThreadPoolExecutor()
|
|
6
|
-
|
|
7
|
-
def topological_sort(elements, dependencies, error):
|
|
8
|
-
graph = defaultdict(list)
|
|
9
|
-
in_degree = {element: 0 for element in elements}
|
|
10
|
-
for element, deps in dependencies.items():
|
|
11
|
-
for dep in deps:
|
|
12
|
-
graph[dep].append(element)
|
|
13
|
-
in_degree[element] += 1
|
|
14
|
-
queue = deque([element for element in elements if in_degree[element] == 0])
|
|
15
|
-
sorted_list = []
|
|
16
|
-
while queue:
|
|
17
|
-
node = queue.popleft()
|
|
18
|
-
sorted_list.append(node)
|
|
19
|
-
for neighbor in graph[node]:
|
|
20
|
-
in_degree[neighbor] -= 1
|
|
21
|
-
if in_degree[neighbor] == 0:
|
|
22
|
-
queue.append(neighbor)
|
|
23
|
-
if len(sorted_list) != len(elements):
|
|
24
|
-
raise error(f"Cycle detected in the dependencies: {elements} -> {dependencies}")
|
|
25
|
-
return sorted_list
|
|
26
|
-
|
|
27
|
-
def ExecAsync(async_func, *args, **kwargs):
|
|
28
|
-
loop = asyncio.get_event_loop()
|
|
29
|
-
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|