ErisPulse 1.0.13__py3-none-any.whl → 1.0.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 +148 -139
- ErisPulse/__main__.py +695 -931
- ErisPulse/db.py +97 -0
- ErisPulse/logger.py +152 -48
- ErisPulse/mods.py +86 -0
- ErisPulse/raiserr.py +44 -0
- ErisPulse/util.py +37 -4
- {erispulse-1.0.13.dist-info → erispulse-1.0.14.dist-info}/METADATA +73 -96
- erispulse-1.0.14.dist-info/RECORD +12 -0
- {erispulse-1.0.13.dist-info → erispulse-1.0.14.dist-info}/WHEEL +1 -1
- ErisPulse/envManager.py +0 -227
- ErisPulse/errors.py +0 -16
- erispulse-1.0.13.dist-info/RECORD +0 -11
- {erispulse-1.0.13.dist-info → erispulse-1.0.14.dist-info}/entry_points.txt +0 -0
- {erispulse-1.0.13.dist-info → erispulse-1.0.14.dist-info}/top_level.txt +0 -0
ErisPulse/db.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import sqlite3
|
|
4
|
+
import importlib.util
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
class EnvManager:
|
|
8
|
+
_instance = None
|
|
9
|
+
db_path = os.path.join(os.path.dirname(__file__), "config.db")
|
|
10
|
+
|
|
11
|
+
def __new__(cls, *args, **kwargs):
|
|
12
|
+
if not cls._instance:
|
|
13
|
+
cls._instance = super().__new__(cls)
|
|
14
|
+
return cls._instance
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
if not hasattr(self, "_initialized"):
|
|
18
|
+
self._init_db()
|
|
19
|
+
self._initialized = True
|
|
20
|
+
|
|
21
|
+
def _init_db(self):
|
|
22
|
+
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
|
|
23
|
+
conn = sqlite3.connect(self.db_path)
|
|
24
|
+
cursor = conn.cursor()
|
|
25
|
+
cursor.execute("""
|
|
26
|
+
CREATE TABLE IF NOT EXISTS config (
|
|
27
|
+
key TEXT PRIMARY KEY,
|
|
28
|
+
value TEXT NOT NULL
|
|
29
|
+
)
|
|
30
|
+
""")
|
|
31
|
+
conn.commit()
|
|
32
|
+
conn.close()
|
|
33
|
+
|
|
34
|
+
def get(self, key, default=None):
|
|
35
|
+
try:
|
|
36
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
37
|
+
cursor = conn.cursor()
|
|
38
|
+
cursor.execute("SELECT value FROM config WHERE key = ?", (key,))
|
|
39
|
+
result = cursor.fetchone()
|
|
40
|
+
if result:
|
|
41
|
+
try:
|
|
42
|
+
return json.loads(result[0])
|
|
43
|
+
except json.JSONDecodeError:
|
|
44
|
+
return result[0]
|
|
45
|
+
return default
|
|
46
|
+
except sqlite3.OperationalError as e:
|
|
47
|
+
if "no such table" in str(e):
|
|
48
|
+
self._init_db()
|
|
49
|
+
return self.get(key, default)
|
|
50
|
+
else:
|
|
51
|
+
raise
|
|
52
|
+
|
|
53
|
+
def get_all_keys(self) -> list:
|
|
54
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
55
|
+
cursor = conn.cursor()
|
|
56
|
+
cursor.execute("SELECT key FROM config")
|
|
57
|
+
return [row[0] for row in cursor.fetchall()]
|
|
58
|
+
|
|
59
|
+
def set(self, key, value):
|
|
60
|
+
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
61
|
+
conn = sqlite3.connect(self.db_path)
|
|
62
|
+
cursor = conn.cursor()
|
|
63
|
+
cursor.execute("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)", (key, serialized_value))
|
|
64
|
+
conn.commit()
|
|
65
|
+
conn.close()
|
|
66
|
+
|
|
67
|
+
def delete(self, key):
|
|
68
|
+
conn = sqlite3.connect(self.db_path)
|
|
69
|
+
cursor = conn.cursor()
|
|
70
|
+
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
71
|
+
conn.commit()
|
|
72
|
+
conn.close()
|
|
73
|
+
|
|
74
|
+
def clear(self):
|
|
75
|
+
conn = sqlite3.connect(self.db_path)
|
|
76
|
+
cursor = conn.cursor()
|
|
77
|
+
cursor.execute("DELETE FROM config")
|
|
78
|
+
conn.commit()
|
|
79
|
+
conn.close()
|
|
80
|
+
|
|
81
|
+
def load_env_file(self):
|
|
82
|
+
env_file = Path("env.py")
|
|
83
|
+
if env_file.exists():
|
|
84
|
+
spec = importlib.util.spec_from_file_location("env_module", env_file)
|
|
85
|
+
env_module = importlib.util.module_from_spec(spec)
|
|
86
|
+
spec.loader.exec_module(env_module)
|
|
87
|
+
for key, value in vars(env_module).items():
|
|
88
|
+
if not key.startswith("__") and isinstance(value, (dict, list, str, int, float, bool)):
|
|
89
|
+
self.set(key, value)
|
|
90
|
+
|
|
91
|
+
def __getattr__(self, key):
|
|
92
|
+
try:
|
|
93
|
+
return self.get(key)
|
|
94
|
+
except KeyError:
|
|
95
|
+
raise AttributeError(f"配置项 {key} 不存在")
|
|
96
|
+
|
|
97
|
+
env = EnvManager()
|
ErisPulse/logger.py
CHANGED
|
@@ -1,48 +1,152 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import inspect
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import inspect
|
|
3
|
+
import datetime
|
|
4
|
+
import functools
|
|
5
|
+
|
|
6
|
+
class Logger:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self._logs = {}
|
|
9
|
+
self._module_levels = {}
|
|
10
|
+
self._logger = logging.getLogger("ErisPulse")
|
|
11
|
+
self._logger.setLevel(logging.DEBUG)
|
|
12
|
+
self._file_handler = None
|
|
13
|
+
if not self._logger.handlers:
|
|
14
|
+
console_handler = logging.StreamHandler()
|
|
15
|
+
console_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
16
|
+
self._logger.addHandler(console_handler)
|
|
17
|
+
|
|
18
|
+
def set_level(self, level: str):
|
|
19
|
+
level = level.upper()
|
|
20
|
+
if hasattr(logging, level):
|
|
21
|
+
self._logger.setLevel(getattr(logging, level))
|
|
22
|
+
|
|
23
|
+
def set_module_level(self, module_name: str, level: str) -> bool:
|
|
24
|
+
from .db import env
|
|
25
|
+
if not env.get_module_status(module_name):
|
|
26
|
+
self._logger.warning(f"模块 {module_name} 未启用,无法设置日志等级。")
|
|
27
|
+
return False
|
|
28
|
+
level = level.upper()
|
|
29
|
+
if hasattr(logging, level):
|
|
30
|
+
self._module_levels[module_name] = getattr(logging, level)
|
|
31
|
+
self._logger.info(f"模块 {module_name} 日志等级已设置为 {level}")
|
|
32
|
+
return True
|
|
33
|
+
else:
|
|
34
|
+
self._logger.error(f"无效的日志等级: {level}")
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def set_output_file(self, path: str | list):
|
|
38
|
+
if self._file_handler:
|
|
39
|
+
self._logger.removeHandler(self._file_handler)
|
|
40
|
+
self._file_handler.close()
|
|
41
|
+
|
|
42
|
+
if isinstance(path, str):
|
|
43
|
+
path = [path]
|
|
44
|
+
|
|
45
|
+
for p in path:
|
|
46
|
+
try:
|
|
47
|
+
file_handler = logging.FileHandler(p, encoding='utf-8')
|
|
48
|
+
file_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
49
|
+
self._logger.addHandler(file_handler)
|
|
50
|
+
self._logger.info(f"日志输出已设置到文件: {p}")
|
|
51
|
+
except Exception as e:
|
|
52
|
+
self._logger.error(f"无法设置日志文件 {p}: {e}")
|
|
53
|
+
raise e
|
|
54
|
+
|
|
55
|
+
def save_logs(self, path: str | list):
|
|
56
|
+
if self._logs == None:
|
|
57
|
+
self._logger.warning("没有log记录可供保存。")
|
|
58
|
+
return
|
|
59
|
+
if isinstance(path, str):
|
|
60
|
+
path = [path]
|
|
61
|
+
|
|
62
|
+
for p in path:
|
|
63
|
+
try:
|
|
64
|
+
with open(p, "w", encoding="utf-8") as file:
|
|
65
|
+
for module, logs in self._logs.items():
|
|
66
|
+
file.write(f"Module: {module}\n")
|
|
67
|
+
for log in logs:
|
|
68
|
+
file.write(f" {log}\n")
|
|
69
|
+
self._logger.info(f"日志已被保存到:{p}。")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
self._logger.error(f"无法保存日志到 {p}: {e}。")
|
|
72
|
+
raise e
|
|
73
|
+
|
|
74
|
+
def catch(self, func_or_level=None, level="error"):
|
|
75
|
+
if isinstance(func_or_level, str):
|
|
76
|
+
return lambda func: self.catch(func, level=func_or_level)
|
|
77
|
+
if func_or_level is None:
|
|
78
|
+
return lambda func: self.catch(func, level=level)
|
|
79
|
+
func = func_or_level
|
|
80
|
+
|
|
81
|
+
@functools.wraps(func)
|
|
82
|
+
def wrapper(*args, **kwargs):
|
|
83
|
+
try:
|
|
84
|
+
return func(*args, **kwargs)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
import traceback
|
|
87
|
+
error_info = traceback.format_exc()
|
|
88
|
+
|
|
89
|
+
module_name = func.__module__
|
|
90
|
+
if module_name == "__main__":
|
|
91
|
+
module_name = "Main"
|
|
92
|
+
func_name = func.__name__
|
|
93
|
+
|
|
94
|
+
error_msg = f"Exception in {func_name}: {str(e)}\n{error_info}"
|
|
95
|
+
|
|
96
|
+
log_method = getattr(self, level, self.error)
|
|
97
|
+
log_method(error_msg)
|
|
98
|
+
|
|
99
|
+
return None
|
|
100
|
+
return wrapper
|
|
101
|
+
|
|
102
|
+
def _save_in_memory(self, ModuleName, msg):
|
|
103
|
+
if ModuleName not in self._logs:
|
|
104
|
+
self._logs[ModuleName] = []
|
|
105
|
+
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
106
|
+
msg = f"{timestamp} - {msg}"
|
|
107
|
+
self._logs[ModuleName].append(msg)
|
|
108
|
+
|
|
109
|
+
def _get_effective_level(self, module_name):
|
|
110
|
+
return self._module_levels.get(module_name, self._logger.level)
|
|
111
|
+
|
|
112
|
+
def _get_caller(self):
|
|
113
|
+
frame = inspect.currentframe().f_back.f_back
|
|
114
|
+
module = inspect.getmodule(frame)
|
|
115
|
+
module_name = module.__name__
|
|
116
|
+
if module_name == "__main__":
|
|
117
|
+
module_name = "Main"
|
|
118
|
+
if module_name.endswith(".Core"):
|
|
119
|
+
module_name = module_name[:-5]
|
|
120
|
+
return module_name
|
|
121
|
+
|
|
122
|
+
def debug(self, msg, *args, **kwargs):
|
|
123
|
+
caller_module = self._get_caller()
|
|
124
|
+
if self._get_effective_level(caller_module) <= logging.DEBUG:
|
|
125
|
+
self._save_in_memory(caller_module, msg)
|
|
126
|
+
self._logger.debug(f"[{caller_module}] {msg}", *args, **kwargs)
|
|
127
|
+
|
|
128
|
+
def info(self, msg, *args, **kwargs):
|
|
129
|
+
caller_module = self._get_caller()
|
|
130
|
+
if self._get_effective_level(caller_module) <= logging.INFO:
|
|
131
|
+
self._save_in_memory(caller_module, msg)
|
|
132
|
+
self._logger.info(f"[{caller_module}] {msg}", *args, **kwargs)
|
|
133
|
+
|
|
134
|
+
def warning(self, msg, *args, **kwargs):
|
|
135
|
+
caller_module = self._get_caller()
|
|
136
|
+
if self._get_effective_level(caller_module) <= logging.WARNING:
|
|
137
|
+
self._save_in_memory(caller_module, msg)
|
|
138
|
+
self._logger.warning(f"[{caller_module}] {msg}", *args, **kwargs)
|
|
139
|
+
|
|
140
|
+
def error(self, msg, *args, **kwargs):
|
|
141
|
+
caller_module = self._get_caller()
|
|
142
|
+
if self._get_effective_level(caller_module) <= logging.ERROR:
|
|
143
|
+
self._save_in_memory(caller_module, msg)
|
|
144
|
+
self._logger.error(f"[{caller_module}] {msg}", *args, **kwargs)
|
|
145
|
+
|
|
146
|
+
def critical(self, msg, *args, **kwargs):
|
|
147
|
+
caller_module = self._get_caller()
|
|
148
|
+
if self._get_effective_level(caller_module) <= logging.CRITICAL:
|
|
149
|
+
self._save_in_memory(caller_module, msg)
|
|
150
|
+
self._logger.critical(f"[{caller_module}] {msg}", *args, **kwargs)
|
|
151
|
+
|
|
152
|
+
logger = Logger()
|
ErisPulse/mods.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Optional
|
|
3
|
+
|
|
4
|
+
class ModuleManager:
|
|
5
|
+
DEFAULT_MODULE_PREFIX = "erispulse.module.data:"
|
|
6
|
+
DEFAULT_STATUS_PREFIX = "erispulse.module.status:"
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
from .db import env
|
|
10
|
+
self.env = env
|
|
11
|
+
self._ensure_prefixes()
|
|
12
|
+
|
|
13
|
+
def _ensure_prefixes(self):
|
|
14
|
+
if not self.env.get("erispulse.system.module_prefix"):
|
|
15
|
+
self.env.set("erispulse.system.module_prefix", self.DEFAULT_MODULE_PREFIX)
|
|
16
|
+
if not self.env.get("erispulse.system.status_prefix"):
|
|
17
|
+
self.env.set("erispulse.system.status_prefix", self.DEFAULT_STATUS_PREFIX)
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def module_prefix(self) -> str:
|
|
21
|
+
return self.env.get("erispulse.system.module_prefix")
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def status_prefix(self) -> str:
|
|
25
|
+
return self.env.get("erispulse.system.status_prefix")
|
|
26
|
+
|
|
27
|
+
def set_module_status(self, module_name: str, status: bool) -> None:
|
|
28
|
+
self.env.set(f"{self.status_prefix}{module_name}", bool(status))
|
|
29
|
+
|
|
30
|
+
def get_module_status(self, module_name: str) -> bool:
|
|
31
|
+
status = self.env.get(f"{self.status_prefix}{module_name}", True)
|
|
32
|
+
if isinstance(status, str):
|
|
33
|
+
return status.lower() == 'true'
|
|
34
|
+
return bool(status)
|
|
35
|
+
|
|
36
|
+
def set_module(self, module_name: str, module_info: dict) -> None:
|
|
37
|
+
self.env.set(f"{self.module_prefix}{module_name}", module_info)
|
|
38
|
+
self.set_module_status(module_name, module_info.get('status', True))
|
|
39
|
+
|
|
40
|
+
def get_module(self, module_name: str) -> Optional[dict]:
|
|
41
|
+
return self.env.get(f"{self.module_prefix}{module_name}")
|
|
42
|
+
|
|
43
|
+
def set_all_modules(self, modules_info: Dict[str, dict]) -> None:
|
|
44
|
+
for module_name, module_info in modules_info.items():
|
|
45
|
+
self.set_module(module_name, module_info)
|
|
46
|
+
|
|
47
|
+
def get_all_modules(self) -> dict:
|
|
48
|
+
modules_info = {}
|
|
49
|
+
all_keys = self.env.get_all_keys()
|
|
50
|
+
prefix_len = len(self.module_prefix)
|
|
51
|
+
|
|
52
|
+
for key in all_keys:
|
|
53
|
+
if key.startswith(self.module_prefix):
|
|
54
|
+
module_name = key[prefix_len:]
|
|
55
|
+
module_info = self.get_module(module_name)
|
|
56
|
+
if module_info:
|
|
57
|
+
status = self.get_module_status(module_name)
|
|
58
|
+
module_info['status'] = bool(status)
|
|
59
|
+
modules_info[module_name] = module_info
|
|
60
|
+
return modules_info
|
|
61
|
+
|
|
62
|
+
def update_module(self, module_name: str, module_info: dict) -> None:
|
|
63
|
+
self.set_module(module_name, module_info)
|
|
64
|
+
|
|
65
|
+
def remove_module(self, module_name: str) -> bool:
|
|
66
|
+
module_key = f"{self.module_prefix}{module_name}"
|
|
67
|
+
status_key = f"{self.status_prefix}{module_name}"
|
|
68
|
+
|
|
69
|
+
if self.env.get(module_key) is not None:
|
|
70
|
+
self.env.delete(module_key)
|
|
71
|
+
self.env.delete(status_key)
|
|
72
|
+
return True
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
def update_prefixes(self, module_prefix: str = None, status_prefix: str = None) -> None:
|
|
76
|
+
if module_prefix:
|
|
77
|
+
if not module_prefix.endswith(':'):
|
|
78
|
+
module_prefix += ':'
|
|
79
|
+
self.env.set("erispulse.system.module_prefix", module_prefix)
|
|
80
|
+
|
|
81
|
+
if status_prefix:
|
|
82
|
+
if not status_prefix.endswith(':'):
|
|
83
|
+
status_prefix += ':'
|
|
84
|
+
self.env.set("erispulse.system.status_prefix", status_prefix)
|
|
85
|
+
|
|
86
|
+
mods = ModuleManager()
|
ErisPulse/raiserr.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
class Error:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self._types = {}
|
|
7
|
+
|
|
8
|
+
def register(self, name, doc="", base=Exception):
|
|
9
|
+
if name not in self._types:
|
|
10
|
+
err_cls = type(name, (base,), {"__doc__": doc})
|
|
11
|
+
self._types[name] = err_cls
|
|
12
|
+
return self._types[name]
|
|
13
|
+
|
|
14
|
+
def __getattr__(self, name):
|
|
15
|
+
def raiser(msg, exit=False):
|
|
16
|
+
from .logger import logger
|
|
17
|
+
err_cls = self._types.get(name) or self.register(name)
|
|
18
|
+
exc = err_cls(msg)
|
|
19
|
+
logger.error(f"{name}: {msg} | {err_cls.__doc__}")
|
|
20
|
+
logger.error("".join(traceback.format_stack()))
|
|
21
|
+
if exit:
|
|
22
|
+
raise exc
|
|
23
|
+
return raiser
|
|
24
|
+
|
|
25
|
+
def info(self, name: str = None):
|
|
26
|
+
result = {}
|
|
27
|
+
for err_name, err_cls in self._types.items():
|
|
28
|
+
result[err_name] = {
|
|
29
|
+
"type": err_name,
|
|
30
|
+
"doc": getattr(err_cls, "__doc__", ""),
|
|
31
|
+
"class": err_cls,
|
|
32
|
+
}
|
|
33
|
+
if name is None:
|
|
34
|
+
return result
|
|
35
|
+
err_cls = self._types.get(name)
|
|
36
|
+
if not err_cls:
|
|
37
|
+
return None
|
|
38
|
+
return {
|
|
39
|
+
"type": name,
|
|
40
|
+
"doc": getattr(err_cls, "__doc__", ""),
|
|
41
|
+
"class": err_cls,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
raiserr = Error()
|
ErisPulse/util.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import time
|
|
2
2
|
import asyncio
|
|
3
|
-
import
|
|
4
|
-
import shutil
|
|
3
|
+
import functools
|
|
5
4
|
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
from collections import defaultdict, deque
|
|
6
6
|
|
|
7
7
|
executor = ThreadPoolExecutor()
|
|
8
8
|
|
|
@@ -28,4 +28,37 @@ def topological_sort(elements, dependencies, error):
|
|
|
28
28
|
|
|
29
29
|
def ExecAsync(async_func, *args, **kwargs):
|
|
30
30
|
loop = asyncio.get_event_loop()
|
|
31
|
-
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
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,96 +1,73 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ErisPulse
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Classifier:
|
|
12
|
-
Classifier:
|
|
13
|
-
Classifier:
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier:
|
|
18
|
-
Classifier:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if __name__ == "__main__":
|
|
75
|
-
asyncio.run(main())
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## 🛠️ 常用命令
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
epsdk update # 更新模块源
|
|
82
|
-
epsdk install AIChat # 安装模块
|
|
83
|
-
epsdk enable AIChat # 启用模块
|
|
84
|
-
epsdk list # 查看所有模块
|
|
85
|
-
```
|
|
86
|
-
更多命令详见 [命令行工具文档](docs/CLI.md)。
|
|
87
|
-
|
|
88
|
-
## 🧩 模块开发
|
|
89
|
-
|
|
90
|
-
你可以通过实现自定义模块扩展 ErisPulse 功能。详见 [开发指南](docs/DEVELOPMENT.md)。
|
|
91
|
-
|
|
92
|
-
## 📖 文档导航
|
|
93
|
-
- [开发指南](docs/DEVELOPMENT.md) - 完整的开发文档
|
|
94
|
-
- [命令行工具](docs/CLI.md) - CLI 使用手册
|
|
95
|
-
- [源配置指南](docs/ORIGIN.md) - 模块源配置说明
|
|
96
|
-
- [更新日志](docs/CHANGELOG.md) - 版本更新历史
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ErisPulse
|
|
3
|
+
Version: 1.0.14
|
|
4
|
+
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
|
+
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.7
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: aiohttp
|
|
22
|
+
|
|
23
|
+
⚠ 当前版本处于开发中,请谨慎使用。
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
基于 [RyhBotPythonSDK V2](https://github.com/runoneall/RyhBotPythonSDK2) 构建,由 [sdkFrame](https://github.com/runoneall/sdkFrame) 提供支持的异步机器人开发框架。
|
|
28
|
+
|
|
29
|
+
## ✨ 核心特性
|
|
30
|
+
- ⚡ 完全异步架构设计(async/await)
|
|
31
|
+
- 🧩 模块化插件系统
|
|
32
|
+
- 📜 内置日志系统
|
|
33
|
+
- 🛑 统一的错误管理
|
|
34
|
+
- 🛠️ 灵活的配置管理
|
|
35
|
+
|
|
36
|
+
## 📦 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install ErisPulse --upgrade
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**要求**:Python ≥ 3.7,pip ≥ 20.0
|
|
43
|
+
|
|
44
|
+
## 🚀 快速开始
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import asyncio
|
|
48
|
+
from ErisPulse import sdk, logger
|
|
49
|
+
|
|
50
|
+
async def main():
|
|
51
|
+
sdk.init()
|
|
52
|
+
logger.info("ErisPulse 已启动")
|
|
53
|
+
# 这里可以添加自定义逻辑 | 如模块的 AddHandle,AddTrigger 等
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 导航
|
|
60
|
+
- [开发指南](docs/DEVELOPMENT.md) - 完整的开发文档
|
|
61
|
+
- [命令行工具](docs/CLI.md) - CLI 使用手册
|
|
62
|
+
- [源配置指南](docs/ORIGIN.md) - 模块源配置说明
|
|
63
|
+
- [更新日志](docs/CHANGELOG.md) - 版本更新历史
|
|
64
|
+
- [底层API](docs/REFERENCE.md) - 方法与接口
|
|
65
|
+
- [GitHub Discussions](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
66
|
+
|
|
67
|
+
## 🤝 贡献
|
|
68
|
+
|
|
69
|
+
欢迎任何形式的贡献!无论是报告 bug、提出新功能请求,还是直接提交代码,都非常感谢。
|
|
70
|
+
|
|
71
|
+
## 📄 许可证
|
|
72
|
+
|
|
73
|
+
本项目采用 [MIT 许可证](LICENSE)。
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=eLYg5fmwxwXsH7YK7z2f5AZRZA5SQIoS0xqMJN7KhVg,6717
|
|
2
|
+
ErisPulse/__main__.py,sha256=-UdhsYP_X7EagomCjo73Y_qQdXwtMbDS5KoOSrI-OcU,32181
|
|
3
|
+
ErisPulse/db.py,sha256=CnREeykvRmEDx9HE4bS4gakcAUYWuBOfN3WgPsQMAd4,3208
|
|
4
|
+
ErisPulse/logger.py,sha256=HwceD81nLiLCh_WGcCVsEG6OqL-NL5HmdONFTGH-8Og,6023
|
|
5
|
+
ErisPulse/mods.py,sha256=o6yT5YhNyfeCpXrEFZYSBCr3YQqjFXZ0hN3u1XoKHJ4,3360
|
|
6
|
+
ErisPulse/raiserr.py,sha256=WTMQEUbUSx4PS9_l9cEnuNuvm6zulgwQFipqVZZg3zc,1296
|
|
7
|
+
ErisPulse/util.py,sha256=01kUQwVFeE7gyD1skCwjpANYzVUHjLC6J1UUzfQXPco,2139
|
|
8
|
+
erispulse-1.0.14.dist-info/METADATA,sha256=ti21P0tgTbDVG25EHIJ-T1k17zKszLLvo3v_VVaOKUY,2497
|
|
9
|
+
erispulse-1.0.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
erispulse-1.0.14.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
11
|
+
erispulse-1.0.14.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
12
|
+
erispulse-1.0.14.dist-info/RECORD,,
|