ErisPulse 2.4.2.dev0__py3-none-any.whl → 2.4.3.dev0__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.
@@ -1,14 +1,12 @@
1
1
  """
2
2
  Run 命令实现
3
3
 
4
- 运行主程序
4
+ 直接运行主程序,支持热重载模式
5
5
  """
6
6
 
7
7
  import os
8
8
  import time
9
- import sys
10
- import subprocess
11
- import tempfile
9
+ import asyncio
12
10
  from argparse import ArgumentParser
13
11
  from watchdog.observers import Observer
14
12
  from rich.panel import Panel
@@ -22,127 +20,38 @@ class ReloadHandler(FileSystemEventHandler):
22
20
  """
23
21
  文件系统事件处理器
24
22
 
25
- 实现热重载功能,监控 .py 文件变更并自动重启
26
-
23
+ 监控 .py 文件变更并触发 sdk.restart() 热重载
24
+
27
25
  {!--< tips >!--}
28
- 1. 仅在 --reload 模式下启用监控
29
- 2. 监控 .py 文件,修改后创建清理信号文件并重启
30
- 3. 子进程检测到清理信号文件后自动调用 sdk.uninit()
31
- 4. 进程终止时使用清理信号文件确保资源正确释放
26
+ 1. 文件监控运行在独立线程
27
+ 2. 通过 run_coroutine_threadsafe 安全调度到事件循环
28
+ 3. 内置 1 秒防抖,避免短时间内多次重载
32
29
  {!--< /tips >!--}
33
30
  """
34
31
 
35
- def __init__(self, script_path: str, reload_mode: bool = False):
36
- """
37
- 初始化处理器
38
-
39
- :param script_path: [str] 要监控的脚本路径
40
- :param reload_mode: [bool] 是否启用重载模式 (默认: False)
41
- """
32
+ def __init__(self, loop: asyncio.AbstractEventLoop):
42
33
  super().__init__()
43
- self.script_path = os.path.abspath(script_path)
44
- self.process = None
45
- self.last_reload = time.time()
46
- self.reload_mode = reload_mode
47
- # 清理信号文件
48
- self.cleanup_file = os.path.join(
49
- tempfile.gettempdir(),
50
- f"erispulse_cleanup_{os.getpid()}.signal"
51
- )
52
- self.start_process()
53
-
54
- def start_process(self):
55
- """
56
- 启动监控进程
57
-
58
- 注入环境变量,让子进程知道自己是由 CLI 启动的
59
- """
60
- if self.process:
61
- self._terminate_process()
62
-
63
- console.print(f"[bold]启动进程: [path]{self.script_path}[/][/]")
64
- env = os.environ.copy()
65
- env['ERISPULSE_SUBPROCESS'] = 'true'
66
- env['ERISPULSE_CLEANUP_FILE'] = self.cleanup_file
67
-
68
- self.process = subprocess.Popen(
69
- [sys.executable, self.script_path],
70
- stdin=sys.stdin,
71
- stdout=sys.stdout,
72
- stderr=sys.stderr,
73
- env=env
74
- )
75
- self.last_reload = time.time()
76
-
77
- def _terminate_process(self):
78
- """
79
- 终止当前进程
80
-
81
- 创建清理信号文件,通知子进程执行清理操作
82
- 等待最多 10 秒让进程正常退出
83
- """
84
- try:
85
- # console.print(f"[dim]正在终止进程 (PID: {self.process.pid})[/]")
86
- console.print(f"[dim]正在终止进程[/]")
87
-
88
- # 创建清理信号文件
89
- try:
90
- with open(self.cleanup_file, 'w') as f:
91
- f.write('cleanup')
92
- # console.print(f"[dim]已创建清理信号文件: {self.cleanup_file}[/]")
93
- except Exception as e:
94
- console.print(f"[warning]创建清理信号文件失败: {e}[/]")
95
-
96
- # 等待子进程开始清理
97
- time.sleep(0.5)
98
-
99
- # 终止进程
100
- self.process.terminate()
101
-
102
- # 等待进程退出
103
- returncode = self.process.wait(timeout=10)
104
- console.print(f"[dim]进程已退出[/]")
105
-
106
- # 清理信号文件
107
- try:
108
- if os.path.exists(self.cleanup_file):
109
- os.remove(self.cleanup_file)
110
- except Exception:
111
- pass
112
- except subprocess.TimeoutExpired:
113
- console.print("[warning]进程未在10秒内正常退出,强制终止[/]")
114
- self.process.kill()
115
- self.process.wait()
116
- except Exception as e:
117
- console.print(f"[error]终止进程时出错: {e}[/]")
34
+ self._loop = loop
35
+ self._last_reload = 0.0
118
36
 
119
37
  def on_modified(self, event):
120
- """
121
- 文件修改事件处理
122
-
123
- 仅在 --reload 模式下监控 .py 文件变更
124
-
125
- :param event: [FileSystemEvent] 文件系统事件
126
- """
127
38
  now = time.time()
128
- if now - self.last_reload < 1.0: # 防抖
39
+ if now - self._last_reload < 1.0:
129
40
  return
130
-
131
- if event.src_path.endswith(".py") and self.reload_mode:
132
- self._handle_reload(event, "文件变动")
41
+ if event.src_path.endswith(".py"):
42
+ self._last_reload = now
43
+ self._schedule_reload(event)
133
44
 
134
- def _handle_reload(self, event, reason: str):
135
- """
136
- 处理热重载逻辑
137
-
138
- 创建清理信号文件,通知子进程执行清理,然后重启进程
139
-
140
- :param event: [FileSystemEvent] 文件系统事件
141
- :param reason: [str] 重载原因
142
- """
143
- console.print(f"检测到文件变更 ({reason}),正在重启...")
144
- self._terminate_process()
145
- self.start_process()
45
+ def _schedule_reload(self, event):
46
+ async def _do_reload():
47
+ try:
48
+ from ... import sdk
49
+ await sdk.restart()
50
+ except Exception as e:
51
+ console.print(f"[error]热重载失败: {e}[/]")
52
+
53
+ console.print(f"检测到文件变更 ({os.path.basename(event.src_path)}),正在热重载...")
54
+ asyncio.run_coroutine_threadsafe(_do_reload(), self._loop)
146
55
 
147
56
 
148
57
  class RunCommand(Command):
@@ -156,16 +65,11 @@ class RunCommand(Command):
156
65
  description = "运行主程序"
157
66
 
158
67
  def add_arguments(self, parser: ArgumentParser):
159
- """
160
- 添加命令行参数
161
-
162
- :param parser: [ArgumentParser] 参数解析器
163
- """
164
68
  parser.add_argument(
165
69
  'script',
166
70
  nargs='?',
167
- default='main.py',
168
- help='要运行的主程序路径 (默认: main.py)'
71
+ default=None,
72
+ help='要运行的主程序路径 (不指定则直接运行 SDK)'
169
73
  )
170
74
  parser.add_argument(
171
75
  '--reload',
@@ -175,78 +79,74 @@ class RunCommand(Command):
175
79
  )
176
80
 
177
81
  def execute(self, args):
178
- """
179
- 执行命令
180
-
181
- :param args: [Namespace] 命令行参数
182
- """
183
82
  script = args.script
184
83
  reload_mode = args.reload
185
84
 
186
- # 检查脚本是否存在
187
- if not os.path.exists(script):
188
- console.print(f"[info]脚本 [path]{script}[/] 不存在,正在创建...[/]")
189
- from ... import _prepare_environment
190
- import asyncio
191
- asyncio.run(_prepare_environment(script))
192
-
193
- # 设置文件监控
194
- self.observer = Observer()
195
- self.handler = ReloadHandler(script, reload_mode)
196
- self._setup_watchdog(script, reload_mode)
197
-
198
- try:
199
- while True:
200
- time.sleep(0.5)
201
- except KeyboardInterrupt:
202
- self._cleanup()
203
-
204
- def _setup_watchdog(self, script_path: str, reload_mode: bool) -> list:
85
+ if script:
86
+ if not os.path.exists(script):
87
+ console.print(f"[error]脚本 [path]{script}[/] 不存在[/]")
88
+ console.print("[info]使用 [cyan]epsdk init[/cyan] 创建新项目[/]")
89
+ return
90
+ self._run_script(script, reload_mode)
91
+ else:
92
+ self._run_internal(reload_mode)
93
+
94
+ def _run_internal(self, reload_mode: bool):
205
95
  """
206
- 设置文件监控
207
-
208
- :param script_path: [str] 要监控的脚本路径
209
- :param reload_mode: [bool] 是否启用重载模式
210
- :return: [list] 监控的目录列表
96
+ 直接运行 SDK(不指定脚本时)
211
97
  """
212
- watch_dirs = [
213
- os.path.dirname(os.path.abspath(script_path)),
214
- ]
215
-
216
- for d in watch_dirs:
217
- if os.path.exists(d):
218
- self.observer.schedule(
219
- self.handler,
220
- d,
221
- recursive=reload_mode
222
- )
223
- console.print(f"[dim]监控目录: [path]{d}[/][/]")
224
-
225
- if reload_mode:
226
- console.print(Panel(
227
- f"[bold]开发重载模式[/]\n监控目录: [path]{', '.join(watch_dirs)}[/]",
228
- title="热重载已启动",
229
- border_style="info"
230
- ))
231
- else:
232
- console.print(Panel(
233
- f"主程序: [path]{script_path}[/]",
234
- title="程序已启动",
235
- border_style="info"
236
- ))
237
-
238
- # 启动 observer
239
- self.observer.start()
240
- return watch_dirs
241
-
242
- def _cleanup(self):
98
+ async def _run():
99
+ from ... import sdk
100
+
101
+ if reload_mode:
102
+ loop = asyncio.get_running_loop()
103
+ self._setup_watchdog(".", loop)
104
+
105
+ await sdk.run(keep_running=True)
106
+
107
+ try:
108
+ asyncio.run(_run())
109
+ except KeyboardInterrupt:
110
+ pass
111
+ finally:
112
+ if reload_mode and hasattr(self, '_observer'):
113
+ self._observer.stop()
114
+ self._observer.join()
115
+
116
+ def _run_script(self, script_path: str, reload_mode: bool):
243
117
  """
244
- 清理资源
245
-
246
- 停止文件监控,终止子进程
118
+ 运行指定脚本文件
247
119
  """
248
- if self.observer:
249
- self.observer.stop()
250
- self.observer.join()
251
- if self.handler and self.handler.process:
252
- self.handler._terminate_process()
120
+ async def _run():
121
+ from ... import sdk
122
+
123
+ if reload_mode:
124
+ loop = asyncio.get_running_loop()
125
+ watch_dir = os.path.dirname(os.path.abspath(script_path))
126
+ self._setup_watchdog(watch_dir, loop)
127
+
128
+ await sdk.run(keep_running=True)
129
+
130
+ try:
131
+ asyncio.run(_run())
132
+ except KeyboardInterrupt:
133
+ pass
134
+ finally:
135
+ if reload_mode and hasattr(self, '_observer'):
136
+ self._observer.stop()
137
+ self._observer.join()
138
+
139
+ def _setup_watchdog(self, watch_dir: str, loop: asyncio.AbstractEventLoop):
140
+ if not os.path.exists(watch_dir):
141
+ return
142
+
143
+ self._observer = Observer()
144
+ self._handler = ReloadHandler(loop=loop)
145
+ self._observer.schedule(self._handler, watch_dir, recursive=True)
146
+ self._observer.start()
147
+
148
+ console.print(Panel(
149
+ f"[bold]开发重载模式[/]\n监控目录: [path]{watch_dir}[/]",
150
+ title="热重载已启动",
151
+ border_style="info"
152
+ ))
@@ -7,14 +7,12 @@
7
7
  """
8
8
  Run 命令实现
9
9
 
10
- 运行主程序
10
+ 直接运行主程序,支持热重载模式
11
11
  """
12
12
 
13
13
  import os
14
14
  import time
15
- import sys
16
- import subprocess
17
- import tempfile
15
+ import asyncio
18
16
  from argparse import ArgumentParser
19
17
  from watchdog.observers import Observer
20
18
  from rich.panel import Panel
@@ -26,56 +24,19 @@ class ReloadHandler(FileSystemEventHandler):
26
24
  """
27
25
  文件系统事件处理器
28
26
 
29
- 实现热重载功能,监控 .py 文件变更并自动重启
27
+ 监控 .py 文件变更并触发 sdk.restart() 热重载
30
28
 
31
29
  {!--< tips >!--}
32
- 1. 仅在 --reload 模式下启用监控
33
- 2. 监控 .py 文件,修改后创建清理信号文件并重启
34
- 3. 子进程检测到清理信号文件后自动调用 sdk.uninit()
35
- 4. 进程终止时使用清理信号文件确保资源正确释放
30
+ 1. 文件监控运行在独立线程
31
+ 2. 通过 run_coroutine_threadsafe 安全调度到事件循环
32
+ 3. 内置 1 秒防抖,避免短时间内多次重载
36
33
  {!--< /tips >!--}
37
34
  """
38
- def __init__(self: None, script_path: str, reload_mode: bool = ...) -> ...:
39
- """
40
- 初始化处理器
41
-
42
- :param script_path: [str] 要监控的脚本路径
43
- :param reload_mode: [bool] 是否启用重载模式 (默认: False)
44
- """
45
- ...
46
- def start_process(self: object) -> ...:
47
- """
48
- 启动监控进程
49
-
50
- 注入环境变量,让子进程知道自己是由 CLI 启动的
51
- """
52
- ...
53
- def _terminate_process(self: object) -> ...:
54
- """
55
- 终止当前进程
56
-
57
- 创建清理信号文件,通知子进程执行清理操作
58
- 等待最多 10 秒让进程正常退出
59
- """
35
+ def __init__(self: None, loop: asyncio.AbstractEventLoop) -> ...:
60
36
  ...
61
37
  def on_modified(self: object, event: ...) -> ...:
62
- """
63
- 文件修改事件处理
64
-
65
- 仅在 --reload 模式下监控 .py 文件变更
66
-
67
- :param event: [FileSystemEvent] 文件系统事件
68
- """
69
38
  ...
70
- def _handle_reload(self: object, event: ..., reason: str) -> ...:
71
- """
72
- 处理热重载逻辑
73
-
74
- 创建清理信号文件,通知子进程执行清理,然后重启进程
75
-
76
- :param event: [FileSystemEvent] 文件系统事件
77
- :param reason: [str] 重载原因
78
- """
39
+ def _schedule_reload(self: object, event: ...) -> ...:
79
40
  ...
80
41
 
81
42
  class RunCommand(Command):
@@ -85,32 +46,18 @@ class RunCommand(Command):
85
46
  运行主程序,支持热重载模式
86
47
  """
87
48
  def add_arguments(self: object, parser: ArgumentParser) -> ...:
88
- """
89
- 添加命令行参数
90
-
91
- :param parser: [ArgumentParser] 参数解析器
92
- """
93
49
  ...
94
50
  def execute(self: object, args: ...) -> ...:
95
- """
96
- 执行命令
97
-
98
- :param args: [Namespace] 命令行参数
99
- """
100
51
  ...
101
- def _setup_watchdog(self: object, script_path: str, reload_mode: bool) -> list:
52
+ def _run_internal(self: object, reload_mode: bool) -> ...:
102
53
  """
103
- 设置文件监控
104
-
105
- :param script_path: [str] 要监控的脚本路径
106
- :param reload_mode: [bool] 是否启用重载模式
107
- :return: [list] 监控的目录列表
54
+ 直接运行 SDK(不指定脚本时)
108
55
  """
109
56
  ...
110
- def _cleanup(self: object) -> ...:
57
+ def _run_script(self: object, script_path: str, reload_mode: bool) -> ...:
111
58
  """
112
- 清理资源
113
-
114
- 停止文件监控,终止子进程
59
+ 运行指定脚本文件
115
60
  """
116
61
  ...
62
+ def _setup_watchdog(self: object, watch_dir: str, loop: asyncio.AbstractEventLoop) -> ...:
63
+ ...
@@ -341,11 +341,10 @@ class BaseAdapter(ABC):
341
341
  """
342
342
  raise NotImplementedError("适配器必须实现shutdown方法")
343
343
 
344
- async def emit(self) -> None:
345
- from ..logger import logger
346
-
347
- logger.error(
348
- "适配器调用了一个被弃用的原生方法emit,请检查适配器的实现,如果你是开发者请查看ErisPulse的文档进行更新。如果你是普通用户请查看本适配器是否有更新"
344
+ async def emit(self, *args, **kwargs):
345
+ raise NotImplementedError(
346
+ "适配器的 emit 方法已被弃用。请使用 adapter.emit() 通过 AdapterManager 提交事件。"
347
+ "如果你是适配器开发者,请查看 ErisPulse 文档进行更新。"
349
348
  )
350
349
 
351
350
  def send(
@@ -158,7 +158,7 @@ class BaseAdapter(ABC):
158
158
  :raises NotImplementedError: 必须由子类实现
159
159
  """
160
160
  ...
161
- async def emit(self: object) -> None:
161
+ async def emit(self: object, *args: ..., **kwargs: ...) -> ...:
162
162
  ...
163
163
  def send(self: object, target_type: str, target_id: str, message: Any, **kwargs: Any) -> asyncio.Task:
164
164
  """
@@ -15,44 +15,33 @@ from .config import config, ConfigManager
15
15
  from . import Event
16
16
  from .Event.message_builder import MessageBuilder
17
17
 
18
- # 兼容性别名定义
19
- adapter_server = router # 路由管理器别名
20
18
  env = storage # 存储管理器别名
21
- AdapterFather = BaseAdapter # 适配器基类别名
22
19
 
23
20
  __all__ = [
24
- # 事件模块
25
21
  'Event',
26
22
 
27
- # 适配器相关
28
- 'adapter', # 适配器管理器
29
- 'AdapterManager', # 适配器管理器类
30
- 'AdapterFather', # 适配器基类
31
- 'BaseAdapter', # 适配器基类别名
32
- 'SendDSL', # DSL发送接口基类
33
- 'MessageBuilder', # 消息构建器
34
-
35
- # 模块相关
36
- 'module', # 模块管理器
37
- 'ModuleManager', # 模块管理器类
38
- 'BaseModule', # 模块基类
39
-
40
- # 存储和配置相关
41
- 'storage', # 存储管理器
42
- 'StorageManager', # 存储管理器类
43
- 'config', # 配置管理器
44
- 'ConfigManager', # 配置管理器类
23
+ 'adapter',
24
+ 'AdapterManager',
25
+ 'BaseAdapter',
26
+ 'SendDSL',
27
+ 'MessageBuilder',
28
+
29
+ 'module',
30
+ 'ModuleManager',
31
+ 'BaseModule',
32
+
33
+ 'storage',
34
+ 'StorageManager',
35
+ 'config',
45
36
  'env', # 配置管理器别名
37
+ 'ConfigManager',
38
+
39
+ 'router',
40
+ 'RouterManager',
46
41
 
47
- # 路由相关
48
- 'router', # 路由管理器
49
- 'RouterManager', # 路由管理器类
50
- 'adapter_server', # 路由管理器别名
51
-
52
- # 基础设施
53
- 'logger', # 日志管理器
54
- 'Logger', # 日志管理器类
55
- 'LoggerChild', # 日志子管理器类
56
- 'lifecycle', # 生命周期管理器
57
- 'LifecycleManager', # 生命周期管理器类
42
+ 'logger',
43
+ 'Logger',
44
+ 'LoggerChild',
45
+ 'lifecycle',
46
+ 'LifecycleManager',
58
47
  ]