pymud 0.20.0__py3-none-any.whl → 0.20.0a1__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.
pymud/session.py CHANGED
@@ -1,15 +1,14 @@
1
- import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig, time
1
+ import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig
2
2
  from collections.abc import Iterable
3
3
  from collections import OrderedDict
4
4
  import logging, queue
5
5
  from logging import FileHandler
6
6
  from logging.handlers import QueueHandler, QueueListener
7
- from wcwidth import wcswidth, wcwidth
7
+
8
8
  from .logger import Logger
9
- from .extras import SessionBuffer, DotDict
9
+ from .extras import SessionBuffer, DotDict, Plugin
10
10
  from .protocol import MudClientProtocol
11
- from .modules import ModuleInfo
12
- from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
11
+ from .objects import Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
13
12
  from .settings import Settings
14
13
 
15
14
 
@@ -30,14 +29,13 @@ class Session:
30
29
 
31
30
  """
32
31
  #_esc_regx = re.compile("\x1b\\[[^mz]+[mz]")
33
- _esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
32
+ _esc_regx = re.compile("\x1b\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
34
33
 
35
34
  _sys_commands = (
36
35
  "help",
37
36
  "exit",
38
37
  "close",
39
38
  "connect", # 连接到服务器
40
- "disconnect", # 从服务器断开连接
41
39
 
42
40
  "info", # 输出蓝色info
43
41
  "warning", # 输出黄色warning
@@ -91,7 +89,6 @@ class Session:
91
89
  "var" : "variable",
92
90
  "rep" : "repeat",
93
91
  "con" : "connect",
94
- "dis" : "disconnect",
95
92
  "wa" : "wait",
96
93
  "mess": "message",
97
94
  "action": "trigger",
@@ -119,6 +116,9 @@ class Session:
119
116
  self._events["connected"] = None
120
117
  self._events["disconnected"] = None
121
118
 
119
+ self._loggers = dict()
120
+ self.log = self.getLogger(name)
121
+
122
122
  self._auto_script = kwargs.get("scripts", None)
123
123
 
124
124
  self._cmds_handler = dict() # 支持的命令的处理函数字典
@@ -142,13 +142,8 @@ class Session:
142
142
  self._status_maker = None # 创建状态窗口的函数(属性)
143
143
  self.display_line = ""
144
144
 
145
- self._activetime = time.time()
146
-
147
145
  self.initialize()
148
146
 
149
- self._loggers = dict()
150
- self.log = self.getLogger(name)
151
-
152
147
  self.host = host
153
148
  self.port = port
154
149
  self.encoding = encoding or self.encoding
@@ -181,10 +176,6 @@ class Session:
181
176
  if Settings.client["auto_connect"]:
182
177
  self.open()
183
178
 
184
- def __del__(self):
185
- self.clean()
186
- self.closeLoggers()
187
-
188
179
  def initialize(self):
189
180
  "初始化Session有关对象。 **无需脚本调用。**"
190
181
  self._line_buffer = bytearray()
@@ -257,6 +248,11 @@ class Session:
257
248
  if self.connected:
258
249
  self.write_eof()
259
250
 
251
+ # 两次保存,删掉一次
252
+ # # 断开时自动保存变量数据
253
+ # if Settings.client["var_autosave"]:
254
+ # self.handle_save()
255
+
260
256
  def onDisconnected(self, protocol):
261
257
  "当从服务器连接断开时执行的操作。包括保存变量(若设置)、打印断开时间、执行自定义事件(若设置)等。"
262
258
  # 断开时自动保存变量数据
@@ -294,15 +290,6 @@ class Session:
294
290
 
295
291
  return dura
296
292
 
297
- @property
298
- def idletime(self) -> float:
299
- "只读属性,返回当前会话空闲时间(即最后一次向服务器写入数据到现在的时间),以秒为单位。当服务器未连接时,返回-1"
300
- idle = -1
301
- if self._protocol and self._protocol.connected:
302
- idle = time.time() - self._activetime
303
-
304
- return idle
305
-
306
293
  @property
307
294
  def status_maker(self):
308
295
  """
@@ -389,15 +376,6 @@ class Session:
389
376
 
390
377
  return logger
391
378
 
392
- def closeLoggers(self):
393
- "移除本会话所有相关Logger"
394
- for name in self._loggers.keys():
395
- if isinstance(self._loggers[name], Logger):
396
- self._loggers[name].enabled = False
397
-
398
- if name in self.application.loggers.keys():
399
- self.application.loggers.pop(name)
400
-
401
379
  @property
402
380
  def modules(self) -> OrderedDict:
403
381
  """
@@ -764,7 +742,7 @@ class Session:
764
742
  lines = line.split(self.seperator)
765
743
  for ln in lines:
766
744
  if Settings.client["echo_input"]:
767
- self.writetobuffer(f"\x1b[32m{ln}\x1b[0m", True)
745
+ self.writetobuffer(f"\x1b[32m{ln}\x1b[0m")
768
746
  else:
769
747
  self.log.log(f"\x1b[32m{ln}\x1b[0m\n")
770
748
 
@@ -773,38 +751,15 @@ class Session:
773
751
 
774
752
  else:
775
753
  if Settings.client["echo_input"]:
776
- self.writetobuffer(f"\x1b[32m{line}\x1b[0m", True)
754
+ self.writetobuffer(f"\x1b[32m{line}\x1b[0m")
777
755
  else:
778
756
  self.log.log(f"\x1b[32m{line}\x1b[0m\n")
779
757
 
780
758
  cmd = line + self.newline
781
759
  self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
782
-
783
- self._activetime = time.time()
784
760
 
785
- async def waitfor(self, line: str, awaitable, wait_time = 0.05) -> None:
786
- """
787
- 调用writline向服务器中写入一行后,等待到可等待对象再返回。
788
-
789
- :param line: 使用writeline写入的行
790
- :param awaitable: 等待的可等待对象
791
- :param wait_time: 写入行前等待的延时,单位为s。默认0.05
792
-
793
- 由于异步的消息循环机制,如果在写入命令之后再创建可等待对象,则有可能服务器响应在可等待对象的创建之前
794
- 此时使用await就无法等待到可等待对象的响应,会导致任务出错。
795
- 一种解决方式是先创建可等待对象,然后写入命令,然后再等待可等待对象,但这种情况下需要写入三行代码,书写麻烦
796
- 因此该函数是用于简化此类使用时的写法。
797
-
798
- 示例:
799
- await session.waitfor('a_cmd', self.create_task(a_tri.triggered()))
800
- done, pending = await session.waitfor('a_cmd', asyncio.wait([self.create_task(a_tri.triggered()), self.create_task(b_tri.triggered())], return_when = 'FIRST_COMPLETED'))
801
- """
802
- await asyncio.sleep(wait_time)
803
- self.writeline(line)
804
- return await awaitable
805
-
806
761
  def exec(self, cmd: str, name = None, *args, **kwargs):
807
- r"""
762
+ """
808
763
  在名称为name的会话中使用exec_command执行MUD命令。当不指定name时,在当前会话中执行。
809
764
 
810
765
  - exec 与 writeline 都会向服务器写入数据。其差异在于,exec执行的内容,会先经过Alias处理和Command处理,实际向远程发送内容与cmd可以不一致。
@@ -819,7 +774,7 @@ class Session:
819
774
  示例:
820
775
  .. code:: Python
821
776
 
822
- session.addAlias(SimpleAlias(self.session, r"^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
777
+ session.addAlias(SimpleAlias(self.session, "^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
823
778
  session.exec("cb j1a")
824
779
  """
825
780
  name = name or self.name
@@ -840,10 +795,12 @@ class Session:
840
795
  name = name or self.name
841
796
  if name in self.application.sessions.keys():
842
797
  session = self.application.sessions[name]
843
- return await session.exec_command_async(cmd, *args, **kwargs)
798
+ await session.exec_command_async(cmd, *args, **kwargs)
844
799
  else:
845
800
  self.error(f"不存在名称为{name}的会话")
846
801
 
802
+
803
+
847
804
  def exec_code(self, cl: CodeLine, *args, **kwargs):
848
805
  """
849
806
  执行解析为CodeLine形式的MUD命令(必定为单个命令)。一般情况下,脚本中不应调用该方法,而应使用exec/exec_command。
@@ -917,7 +874,6 @@ class Session:
917
874
  :param args: 保留兼容与扩展性所需
918
875
  :param kwargs: 保留兼容与扩展性所需
919
876
  """
920
-
921
877
  if cl.length == 0:
922
878
  self.writeline("")
923
879
 
@@ -945,9 +901,9 @@ class Session:
945
901
  else:
946
902
  try:
947
903
  cb = CodeBlock(sess_cmd)
948
- return await cb.async_execute(session, *args, **kwargs)
904
+ await cb.async_execute(session, *args, **kwargs)
949
905
  except Exception as e:
950
- return await session.exec_command_async(sess_cmd)
906
+ await session.exec_command_async(sess_cmd)
951
907
 
952
908
  else:
953
909
  if cmd in self._commands_alias.keys():
@@ -964,7 +920,7 @@ class Session:
964
920
 
965
921
  else:
966
922
  cmdtext, code = cl.expand(self, *args, **kwargs)
967
- return await self.exec_text_async(cmdtext)
923
+ await self.exec_text_async(cmdtext)
968
924
 
969
925
  def exec_text(self, cmdtext: str):
970
926
  """
@@ -1004,14 +960,14 @@ class Session:
1004
960
 
1005
961
  异步调用时,该函数要等待对应的代码执行完毕后才会返回。可以用于确保命令执行完毕。
1006
962
  """
1007
- result = None
963
+
1008
964
  isNotCmd = True
1009
965
  for command in self._commands.values():
1010
966
  if isinstance(command, Command) and command.enabled:
1011
967
  state = command.match(cmdtext)
1012
968
  if state.result == Command.SUCCESS:
1013
969
  # 命令的任务名称采用命令id,以便于后续查错
1014
- result = await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
970
+ await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
1015
971
  isNotCmd = False
1016
972
  break
1017
973
 
@@ -1029,8 +985,6 @@ class Session:
1029
985
  if notAlias:
1030
986
  self.writeline(cmdtext)
1031
987
 
1032
- return result
1033
-
1034
988
  def exec_command(self, line: str, *args, **kwargs) -> None:
1035
989
  """
1036
990
  在当前会话中执行MUD命令。多个命令可以用分隔符隔开。
@@ -1081,18 +1035,15 @@ class Session:
1081
1035
  """
1082
1036
 
1083
1037
  ## 以下为函数执行本体
1084
- result = None
1085
1038
  if (not "#" in line) and (not "@" in line) and (not "%" in line):
1086
1039
  cmds = line.split(self.seperator)
1087
1040
  for cmd in cmds:
1088
- result = await self.exec_text_async(cmd)
1041
+ await self.exec_text_async(cmd)
1089
1042
  if Settings.client["interval"] > 0:
1090
1043
  await asyncio.sleep(Settings.client["interval"] / 1000.0)
1091
1044
  else:
1092
1045
  cb = CodeBlock(line)
1093
- result = await cb.async_execute(self)
1094
-
1095
- return result
1046
+ await cb.async_execute(self)
1096
1047
 
1097
1048
  def write_eof(self) -> None:
1098
1049
  """
@@ -1156,212 +1107,50 @@ class Session:
1156
1107
 
1157
1108
  return counts
1158
1109
 
1159
- # def _addObjects(self, objs: dict, cls: type):
1160
- # if cls == Alias:
1161
- # self._aliases.update(objs)
1162
- # elif cls == Command:
1163
- # self._commands.update(objs)
1164
- # elif cls == Trigger:
1165
- # self._triggers.update(objs)
1166
- # elif cls == Timer:
1167
- # self._timers.update(objs)
1168
- # elif cls == GMCPTrigger:
1169
- # self._gmcp.update(objs)
1170
-
1171
- def _addObjects(self, objs):
1172
- if isinstance(objs, list) or isinstance(objs, tuple):
1173
- for item in objs:
1174
- self._addObject(item)
1175
-
1176
- elif isinstance(objs, dict):
1177
- for key, item in objs.items():
1178
- if isinstance(item, BaseObject):
1179
- if key != item.id:
1180
- self.warning(f'对象 {item} 字典键值 {key} 与其id {item.id} 不一致,将丢弃键值,以其id添加到会话中...')
1181
-
1182
- self._addObject(item)
1183
-
1184
- # def _addObject(self, obj, cls: type):
1185
- # #if type(obj) == cls:
1186
- # if isinstance(obj, cls):
1187
- # if cls == Alias:
1188
- # self._aliases[obj.id] = obj
1189
- # elif cls == Command:
1190
- # self._commands[obj.id] = obj
1191
- # elif cls == Trigger:
1192
- # self._triggers[obj.id] = obj
1193
- # elif cls == Timer:
1194
- # self._timers[obj.id] = obj
1195
- # elif cls == GMCPTrigger:
1196
- # self._gmcp[obj.id] = obj
1197
-
1198
- def _addObject(self, obj):
1199
- if isinstance(obj, Alias):
1200
- self._aliases[obj.id] = obj
1201
- elif isinstance(obj, Command):
1202
- self._commands[obj.id] = obj
1203
- elif isinstance(obj, Trigger):
1204
- self._triggers[obj.id] = obj
1205
- elif isinstance(obj, Timer):
1206
- self._timers[obj.id] = obj
1207
- elif isinstance(obj, GMCPTrigger):
1208
- self._gmcp[obj.id] = obj
1209
-
1210
- def addObject(self, obj: BaseObject):
1211
- """
1212
- 向会话中增加单个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
1213
-
1214
- :param obj: 特定对象本身,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1215
-
1216
- 示例:
1217
- .. code:: Python
1218
-
1219
- class Configuration:
1220
- def __init__(self, session):
1221
- self.session = session
1222
-
1223
- self.session.addObject(SimpleAlias(session, r'^gta$', 'get all'),)
1224
- self.session.addObject(SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'))
1225
- self.session.addObject(SimpleTimer(session, 'xixi', timeout = 10))
1226
-
1227
- """
1228
- self._addObject(obj)
1229
-
1230
- def addObjects(self, objs):
1231
- """
1232
- 向会话中增加多个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类的元组、列表或者字典(保持兼容性)
1233
-
1234
- :param objs: 多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1235
-
1236
- 示例:
1237
- .. code:: Python
1238
-
1239
- class Configuration:
1240
- def __init__(self, session):
1241
- self.session = session
1242
-
1243
- self.objs = [
1244
- SimpleAlias(session, r'^gta$', 'get all;xixi'),
1245
- SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
1246
- SimpleTimer(session, 'xixi', timeout = 10)
1247
- ]
1248
-
1249
- self.session.addObjects(self.objs)
1250
-
1251
- """
1252
- self._addObjects(objs)
1110
+ def _addObjects(self, objs: dict, cls: type):
1111
+ if cls == Alias:
1112
+ self._aliases.update(objs)
1113
+ elif cls == Command:
1114
+ self._commands.update(objs)
1115
+ elif cls == Trigger:
1116
+ self._triggers.update(objs)
1117
+ elif cls == Timer:
1118
+ self._timers.update(objs)
1119
+ elif cls == GMCPTrigger:
1120
+ self._gmcp.update(objs)
1121
+
1122
+ def _addObject(self, obj, cls: type):
1123
+ #if type(obj) == cls:
1124
+ if isinstance(obj, cls):
1125
+ if cls == Alias:
1126
+ self._aliases[obj.id] = obj
1127
+ elif cls == Command:
1128
+ self._commands[obj.id] = obj
1129
+ elif cls == Trigger:
1130
+ self._triggers[obj.id] = obj
1131
+ elif cls == Timer:
1132
+ self._timers[obj.id] = obj
1133
+ elif cls == GMCPTrigger:
1134
+ self._gmcp[obj.id] = obj
1253
1135
 
1254
1136
  def _delObject(self, id, cls: type):
1255
1137
  if cls == Alias:
1256
1138
  self._aliases.pop(id, None)
1257
1139
  elif cls == Command:
1258
- cmd = self._commands.pop(id, None)
1259
- if isinstance(cmd, Command):
1260
- cmd.reset()
1261
- cmd.unload()
1262
- cmd.__unload__()
1263
-
1140
+ self._commands.pop(id, None)
1264
1141
  elif cls == Trigger:
1265
1142
  self._triggers.pop(id, None)
1266
1143
  elif cls == Timer:
1267
- timer = self._timers.pop(id, None)
1268
- if isinstance(timer, Timer):
1269
- timer.enabled = False
1144
+ self._timers.pop(id, None)
1270
1145
  elif cls == GMCPTrigger:
1271
1146
  self._gmcp.pop(id, None)
1272
1147
 
1273
-
1274
1148
  def _delObjects(self, ids: Iterable, cls: type):
1275
1149
  "删除多个指定元素"
1276
1150
  for id in ids:
1277
1151
  self._delObject(id, cls)
1278
1152
 
1279
- def delObject(self, obj):
1280
- """
1281
- 从会话中移除一个对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类本身
1282
-
1283
- ** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
1284
-
1285
- :param obj: 要删除的多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1286
-
1287
- 示例:
1288
- .. code:: Python
1289
-
1290
- class Configuration:
1291
- def __init__(self, session):
1292
- self.session = session
1293
-
1294
- ali = SimpleAlias(session, r'^gta$', 'get all', id = 'my_ali1')
1295
-
1296
- # 以下几种方式均可将该别名添加到会话
1297
- session.addObject(ali)
1298
- session.addAlias(ali)
1299
-
1300
- # 以下三种方式均可以删除该别名
1301
- session.delObject(ali)
1302
- session.delAlias(ali)
1303
- session.delAlias("my_ali1")
1304
-
1305
- """
1306
- if isinstance(obj, Alias):
1307
- self._aliases.pop(obj.id, None)
1308
- elif isinstance(obj, Command):
1309
- obj.reset()
1310
- obj.unload()
1311
- obj.__unload__()
1312
- self._commands.pop(obj.id, None)
1313
- elif isinstance(obj, Trigger):
1314
- self._triggers.pop(obj.id, None)
1315
- elif isinstance(obj, Timer):
1316
- obj.enabled = False
1317
- self._timers.pop(obj.id, None)
1318
- elif isinstance(obj, GMCPTrigger):
1319
- self._gmcp.pop(obj.id, None)
1320
-
1321
- elif isinstance(obj, (list, tuple, dict)):
1322
- self.delObjects(obj)
1323
-
1324
- def delObjects(self, objs):
1325
- """
1326
- 从会话中移除一组对象,可直接删除多个 Alias, Trigger, GMCPTrigger, Command, Timer
1327
-
1328
- ** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
1329
-
1330
- :param objs: 要删除的一组对象的元组、列表或者字典(保持兼容性),其中对象可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
1331
-
1332
- 示例:
1333
-
1334
- .. code:: Python
1335
-
1336
- class Configuration:
1337
- def __init__(self, session):
1338
- self.session = session
1339
-
1340
- self.objs = [
1341
- SimpleAlias(session, r'^gta$', 'get all;xixi'),
1342
- SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
1343
- SimpleTimer(session, 'xixi', timeout = 10)
1344
- ]
1345
-
1346
- self.session.addObjects(self.objs)
1347
-
1348
- def __unload__(self):
1349
- "卸载本模块时,删除所有本模块添加的对象"
1350
- self.session.delObjects(self.objs)
1351
-
1352
- """
1353
- if isinstance(objs, list) or isinstance(objs, tuple):
1354
- for item in objs:
1355
- self.delObject(item)
1356
-
1357
- elif isinstance(objs, dict):
1358
- for key, item in objs.items():
1359
- self.delObject(item)
1360
-
1361
- elif isinstance(objs, BaseObject):
1362
- self.delObject(objs)
1363
-
1364
- def addAliases(self, alis):
1153
+ def addAliases(self, alis: dict):
1365
1154
  """
1366
1155
  向会话中增加多个别名
1367
1156
 
@@ -1382,55 +1171,55 @@ class Session:
1382
1171
  self._aliases['my_ali2'] = SimpleAlias(self.session, "s", "south", id = "my_ali2")
1383
1172
  self.session.addAliases(self._aliases)
1384
1173
  """
1385
- self._addObjects(alis)
1174
+ self._addObjects(alis, Alias)
1386
1175
 
1387
- def addCommands(self, cmds):
1176
+ def addCommands(self, cmds: dict):
1388
1177
  """
1389
1178
  向会话中增加多个命令。使用方法与 addAliases 类似。
1390
1179
 
1391
1180
  :param cmds: 多个命令的字典。字典 key 应为每个命令的 id。
1392
1181
  """
1393
- self._addObjects(cmds)
1182
+ self._addObjects(cmds, Command)
1394
1183
 
1395
- def addTriggers(self, tris):
1184
+ def addTriggers(self, tris: dict):
1396
1185
  """
1397
1186
  向会话中增加多个触发器。使用方法与 addAliases 类似。
1398
1187
 
1399
1188
  :param tris: 多个触发器的字典。字典 key 应为每个触发器的 id。
1400
1189
  """
1401
- self._addObjects(tris)
1190
+ self._addObjects(tris, Trigger)
1402
1191
 
1403
- def addGMCPs(self, gmcps):
1192
+ def addGMCPs(self, gmcps: dict):
1404
1193
  """
1405
1194
  向会话中增加多个GMCPTrigger。使用方法与 addAliases 类似。
1406
1195
 
1407
1196
  :param gmcps: 多个GMCPTrigger的字典。字典 key 应为每个GMCPTrigger的 id。
1408
1197
  """
1409
- self._addObjects(gmcps)
1198
+ self._addObjects(gmcps, GMCPTrigger)
1410
1199
 
1411
- def addTimers(self, tis):
1200
+ def addTimers(self, tis: dict):
1412
1201
  """
1413
1202
  向会话中增加多个定时器。使用方法与 addAliases 类似。
1414
1203
 
1415
1204
  :param tis: 多个定时器的字典。字典 key 应为每个定时器的 id。
1416
1205
  """
1417
- self._addObjects(tis)
1206
+ self._addObjects(tis, Timer)
1418
1207
 
1419
- def addAlias(self, ali):
1208
+ def addAlias(self, ali: Alias):
1420
1209
  """
1421
1210
  向会话中增加一个别名。
1422
1211
 
1423
1212
  :param ali: 要增加的别名对象,应为 Alias 类型或其子类
1424
1213
  """
1425
- self._addObject(ali)
1214
+ self._addObject(ali, Alias)
1426
1215
 
1427
- def addCommand(self, cmd):
1216
+ def addCommand(self, cmd: Command):
1428
1217
  """
1429
1218
  向会话中增加一个命令。
1430
1219
 
1431
1220
  :param cmd: 要增加的命令对象,应为 Command 类型或其子类
1432
1221
  """
1433
- self._addObject(cmd)
1222
+ self._addObject(cmd, Command)
1434
1223
 
1435
1224
  def addTrigger(self, tri: Trigger):
1436
1225
  """
@@ -1438,7 +1227,7 @@ class Session:
1438
1227
 
1439
1228
  :param tri: 要增加的触发器对象,应为 Trigger 类型或其子类
1440
1229
  """
1441
- self._addObject(tri)
1230
+ self._addObject(tri, Trigger)
1442
1231
 
1443
1232
  def addTimer(self, ti: Timer):
1444
1233
  """
@@ -1446,7 +1235,7 @@ class Session:
1446
1235
 
1447
1236
  :param ti: 要增加的定时器对象,应为 Timer 类型或其子类
1448
1237
  """
1449
- self._addObject(ti)
1238
+ self._addObject(ti, Timer)
1450
1239
 
1451
1240
  def addGMCP(self, gmcp: GMCPTrigger):
1452
1241
  """
@@ -1455,7 +1244,7 @@ class Session:
1455
1244
  :param gmcp: 要增加的GMCP触发器对象,应为 GMCPTrigger 类型或其子类
1456
1245
  """
1457
1246
 
1458
- self._addObject(gmcp)
1247
+ self._addObject(gmcp, GMCPTrigger)
1459
1248
 
1460
1249
  def delAlias(self, ali):
1461
1250
  """
@@ -1574,7 +1363,7 @@ class Session:
1574
1363
  for ti in ti_s:
1575
1364
  self.delTimer(ti)
1576
1365
 
1577
- def delGMCP(self, gmcp):
1366
+ def delGMCP(self, gmcp: GMCPTrigger):
1578
1367
  """
1579
1368
  从会话中移除一个GMCP触发器,可接受 GMCPTrigger 对象或其的id。使用方法与 delAlias 类似
1580
1369
 
@@ -1890,7 +1679,6 @@ class Session:
1890
1679
  该函数不应该在代码中直接调用。
1891
1680
 
1892
1681
  相关命令:
1893
- - #disconnect
1894
1682
  - #close
1895
1683
  - #exit
1896
1684
  '''
@@ -1912,18 +1700,6 @@ class Session:
1912
1700
 
1913
1701
  self.info("已经与服务器连接了 {}".format(time_msg))
1914
1702
 
1915
- def handle_disconnect(self, code: CodeLine = None, *args, **kwargs):
1916
- '''
1917
- 嵌入命令 #disconnect / #dis 的执行函数,断开到远程服务器的连接(仅当远程服务器已连接时有效)。
1918
- 该函数不应该在代码中直接调用。
1919
-
1920
- 相关命令:
1921
- - #connect
1922
- - #close
1923
- '''
1924
-
1925
- self.disconnect()
1926
-
1927
1703
  def handle_variable(self, code: CodeLine = None, *args, **kwargs):
1928
1704
  '''
1929
1705
  嵌入命令 #variable / #var 的执行函数,操作会话变量。
@@ -1962,7 +1738,7 @@ class Session:
1962
1738
  else:
1963
1739
  vars_simple[k] = v
1964
1740
 
1965
- width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
1741
+ width = self.application.get_width()
1966
1742
 
1967
1743
  title = f" VARIABLE LIST IN SESSION {self.name} "
1968
1744
  left = (width - len(title)) // 2
@@ -1970,7 +1746,6 @@ class Session:
1970
1746
  self.writetobuffer("="*left + title + "="*right, newline = True)
1971
1747
 
1972
1748
  # print vars in simple, 每个变量占40格,一行可以多个变量
1973
- # 这里可以考虑调整一下,默认40, 但如果一个变量值太长,则选择占两个位置
1974
1749
  var_count = len(vars_simple)
1975
1750
  var_per_line = (width - 2) // 40
1976
1751
  lines = math.ceil(var_count / var_per_line)
@@ -1986,18 +1761,14 @@ class Session:
1986
1761
  self.writetobuffer(" " * left_space)
1987
1762
  line_vars = var_keys[start:end]
1988
1763
  for var in line_vars:
1989
- repr = vars_simple[var].__repr__()
1990
- vwidth = 22 - (wcswidth(repr) - len(repr))
1991
- self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
1992
- #self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
1764
+ self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
1993
1765
 
1994
1766
  self.writetobuffer("", newline = True)
1995
1767
 
1996
1768
  # print vars in complex, 每个变量占1行
1997
- var_keys = sorted(vars_complex.keys())
1998
- for key in var_keys:
1769
+ for k, v in vars_complex.items():
1999
1770
  self.writetobuffer(" " * left_space)
2000
- self.writetobuffer("{0:>20} = {1}".format(key, vars_complex[key].__repr__()), newline = True)
1771
+ self.writetobuffer("{0:>18} = {1}".format(k, v.__repr__()), newline = True)
2001
1772
 
2002
1773
  self.writetobuffer("="*width, newline = True)
2003
1774
 
@@ -2050,7 +1821,7 @@ class Session:
2050
1821
  else:
2051
1822
  vars_simple[k] = v
2052
1823
 
2053
- width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
1824
+ width = self.application.get_width()
2054
1825
 
2055
1826
  title = f" GLOBAL VARIABLES LIST "
2056
1827
  left = (width - len(title)) // 2
@@ -2073,23 +1844,21 @@ class Session:
2073
1844
  self.writetobuffer(" " * left_space)
2074
1845
  line_vars = var_keys[start:end]
2075
1846
  for var in line_vars:
2076
- repr = vars_simple[var].__repr__()
2077
- vwidth = 22 - (wcswidth(repr) - len(repr))
2078
- self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
1847
+ self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
2079
1848
 
2080
1849
  self.writetobuffer("", newline = True)
2081
1850
 
2082
1851
  # print vars in complex, 每个变量占1行
2083
1852
  for k, v in vars_complex.items():
2084
1853
  self.writetobuffer(" " * left_space)
2085
- self.writetobuffer("{0:>20} = {1}".format(k, v.__repr__()), newline = True)
1854
+ self.writetobuffer("{0:>18} = {1}".format(k, v.__repr__()), newline = True)
2086
1855
 
2087
1856
  self.writetobuffer("="*width, newline = True)
2088
1857
 
2089
1858
  elif len(args) == 1:
2090
1859
  var = args[0]
2091
1860
  if var in self.application.globals.keys():
2092
- self.info("{0:>20} = {1:<22}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
1861
+ self.info("{0:>18} = {1:<19}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
2093
1862
  else:
2094
1863
  self.info("全局空间不存在名称为 {} 的变量".format(var), "全局变量")
2095
1864
 
@@ -2164,7 +1933,7 @@ class Session:
2164
1933
  self.info("创建Timer {} 成功: {}".format(ti.id, ti.__repr__()))
2165
1934
 
2166
1935
  def handle_alias(self, code: CodeLine = None, *args, **kwargs):
2167
- r"""
1936
+ '''
2168
1937
  嵌入命令 #alias / #ali 的执行函数,操作别名。该命令可以不带参数、带一个参数或者两个参数。
2169
1938
  该函数不应该在代码中直接调用。
2170
1939
 
@@ -2195,7 +1964,7 @@ class Session:
2195
1964
  - #trigger
2196
1965
  - #timer
2197
1966
  - #command
2198
- """
1967
+ '''
2199
1968
 
2200
1969
  self._handle_objs("Alias", self._aliases, *code.code[2:])
2201
1970
 
@@ -2554,7 +2323,6 @@ class Session:
2554
2323
  self._variables.clear()
2555
2324
  self._tasks.clear()
2556
2325
 
2557
-
2558
2326
  def load_module(self, module_names):
2559
2327
  """
2560
2328
  模块加载函数。
@@ -2578,12 +2346,32 @@ class Session:
2578
2346
  "加载指定名称模块"
2579
2347
  try:
2580
2348
  if module_name not in self._modules.keys():
2581
- self._modules[module_name] = ModuleInfo(module_name, self)
2349
+ mod = importlib.import_module(module_name)
2350
+ if hasattr(mod, 'Configuration'):
2351
+ config = mod.Configuration(self)
2352
+ self._modules[module_name] = {"module": mod, "config": config}
2353
+ self.info(f"主配置模块 {module_name} 加载完成.")
2354
+ else:
2355
+ self._modules[module_name] = {"module": mod, "config": None}
2356
+ self.info(f"子配置模块 {module_name} 加载完成.")
2582
2357
 
2583
2358
  else:
2584
- mod = self._modules[module_name]
2585
- if isinstance(mod, ModuleInfo):
2586
- mod.reload()
2359
+ mod = self._modules[module_name]["module"]
2360
+ config = self._modules[module_name]["config"]
2361
+ if config:
2362
+ if hasattr(config, "unload"):
2363
+ unload = getattr(config, "unload", None)
2364
+ if callable(unload):
2365
+ unload(config)
2366
+ del config
2367
+ mod = importlib.reload(mod)
2368
+ if hasattr(mod, 'Configuration'):
2369
+ config = mod.Configuration(self)
2370
+ self._modules[module_name] = {"module": mod, "config": config}
2371
+ self.info(f"主配置模块 {module_name} 重新加载完成.")
2372
+ else:
2373
+ self._modules[module_name] = {"module": mod, "config": None}
2374
+ self.info(f"子配置模块 {module_name} 重新加载完成.")
2587
2375
 
2588
2376
  except Exception as e:
2589
2377
  import traceback
@@ -2609,11 +2397,20 @@ class Session:
2609
2397
  self._unload_module(mod)
2610
2398
 
2611
2399
  def _unload_module(self, module_name):
2612
- "卸载指定名称模块。卸载支持需要模块的Configuration实现 __unload__ 或 unload 方法"
2400
+ "卸载指定名称模块。卸载支持需要模块的Configuration实现__del__方法"
2613
2401
  if module_name in self._modules.keys():
2614
- mod = self._modules.pop(module_name)
2615
- if isinstance(mod, ModuleInfo):
2616
- mod.unload()
2402
+ mod = self._modules[module_name]["module"]
2403
+ config = self._modules[module_name]["config"]
2404
+ if config:
2405
+ if hasattr(config, "unload"):
2406
+ unload = getattr(config, "unload", None)
2407
+ if callable(unload):
2408
+ unload(config)
2409
+
2410
+ del config
2411
+ del mod
2412
+ self._modules.pop(module_name)
2413
+ self.info(f"配置模块 {module_name} 已成功卸载.")
2617
2414
 
2618
2415
  else:
2619
2416
  self.warning(f"指定模块名称 {module_name} 并未加载.")
@@ -2627,9 +2424,9 @@ class Session:
2627
2424
  :param module_names: 要重新加载的模块清单。为元组/列表时,卸载指定名称的系列模块,当名称为字符串时,卸载单个模块。当不指定时,重新加载所有已加载模块。
2628
2425
  """
2629
2426
  if module_names is None:
2630
- for name, module in self._modules.items():
2631
- if isinstance(module, ModuleInfo):
2632
- module.reload()
2427
+ self.clean()
2428
+ mods = list(self._modules.keys())
2429
+ self.load_module(mods)
2633
2430
 
2634
2431
  self.info(f"所有配置模块全部重新加载完成.")
2635
2432
 
@@ -2637,17 +2434,14 @@ class Session:
2637
2434
  for mod in module_names:
2638
2435
  mod = mod.strip()
2639
2436
  if mod in self._modules.keys():
2640
- module = self._modules[mod]
2641
- if isinstance(module, ModuleInfo):
2642
- module.reload()
2437
+ self.load_module(mod)
2643
2438
  else:
2644
2439
  self.warning(f"指定模块名称 {mod} 并未加载,无法重新加载.")
2645
2440
 
2646
2441
  elif isinstance(module_names, str):
2647
2442
  if module_names in self._modules.keys():
2648
- module = self._modules[module_names]
2649
- if isinstance(module, ModuleInfo):
2650
- module.reload()
2443
+ mod = module_names.strip()
2444
+ self.load_module(mod)
2651
2445
  else:
2652
2446
  self.warning(f"指定模块名称 {module_names} 并未加载,无法重新加载.")
2653
2447
 
@@ -2731,7 +2525,7 @@ class Session:
2731
2525
 
2732
2526
  elif mod in self.plugins.keys():
2733
2527
  self.application.reload_plugin(self.plugins[mod])
2734
- self.info(f'插件 {mod} 重新加载完成!')
2528
+
2735
2529
  else:
2736
2530
  self.warning(f"指定名称 {mod} 既未找到模块,也未找到插件,重新加载失败..")
2737
2531
 
@@ -2785,29 +2579,12 @@ class Session:
2785
2579
  - #reload
2786
2580
  '''
2787
2581
 
2788
- args = code.code[2:]
2789
-
2790
- if len(args) == 0:
2791
- count = len(self._modules.keys())
2792
- if count == 0:
2793
- self.info("当前会话并未加载任何模块。", "MODULES")
2794
- else:
2795
- self.info(f"当前会话已加载 {count} 个模块,包括(按加载顺序排列):{list(self._modules.keys())}", "MODULES")
2582
+ count = len(self._modules.keys())
2583
+ if count == 0:
2584
+ self.info("当前会话并未加载任何模块。", "MODULES")
2585
+ else:
2586
+ self.info(f"当前会话已加载 {count} 个模块,包括(按加载顺序排列):{list(self._modules.keys())}", "MODULES")
2796
2587
 
2797
- elif len(args) >= 1:
2798
- modules = ",".join(args).split(",")
2799
- for mod in modules:
2800
- if mod in self._modules.keys():
2801
- module = self._modules[mod]
2802
- if isinstance(module, ModuleInfo):
2803
- if module.ismainmodule:
2804
- self.info(f"模块 {module.name} 中包含的配置包括: {', '.join(module.config.keys())}")
2805
- else:
2806
- self.info(f"模块 {module.name} 为子模块,不包含配置。")
2807
-
2808
- else:
2809
- self.info(f"本会话中不存在指定名称 {mod} 的模块,可能是尚未加载到本会话中")
2810
-
2811
2588
  def handle_reset(self, code: CodeLine = None, *args, **kwargs):
2812
2589
  '''
2813
2590
  嵌入命令 #reset 的执行函数,复位全部脚本。该命令不带参数。
@@ -2827,8 +2604,7 @@ class Session:
2827
2604
 
2828
2605
  def handle_save(self, code: CodeLine = None, *args, **kwargs):
2829
2606
  '''
2830
- 嵌入命令 #save 的执行函数,保存当前会话变量(系统变量和临时变量除外)至文件。该命令不带参数。
2831
- 系统变量包括 %line, %copy 和 %raw 三个,临时变量是指变量名已下划线开头的变量
2607
+ 嵌入命令 #save 的执行函数,保存当前会话变量(系统变量除外)至文件。该命令不带参数。
2832
2608
  该函数不应该在代码中直接调用。
2833
2609
 
2834
2610
  使用:
@@ -2849,10 +2625,10 @@ class Session:
2849
2625
  with open(file, "wb") as fp:
2850
2626
  saved = dict()
2851
2627
  saved.update(self._variables)
2852
- keys = list(saved.keys())
2853
- for key in keys:
2854
- if key.startswith("_"):
2855
- saved.pop(key)
2628
+ # keys = list(saved.keys())
2629
+ # for key in keys:
2630
+ # if key.startswith("%"):
2631
+ # saved.pop(key)
2856
2632
  saved.pop("%line", None)
2857
2633
  saved.pop("%raw", None)
2858
2634
  saved.pop("%copy", None)
@@ -2951,7 +2727,7 @@ class Session:
2951
2727
  if name in self.plugins.keys():
2952
2728
  plugin = self.plugins[name]
2953
2729
  self.info(f"{plugin.desc['DESCRIPTION']}, 版本 {plugin.desc['VERSION']} 作者 {plugin.desc['AUTHOR']} 发布日期 {plugin.desc['RELEASE_DATE']}", f"PLUGIN {name}")
2954
- self.writetobuffer(plugin.help, True)
2730
+ self.writetobuffer(plugin.help)
2955
2731
 
2956
2732
  def handle_replace(self, code: CodeLine = None, *args, **kwargs):
2957
2733
  '''
@@ -3130,8 +2906,8 @@ class Session:
3130
2906
 
3131
2907
  示例:
3132
2908
  - ``#log`` : 在当前会话窗口列出所有记录器状态
3133
- - ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
3134
- - ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
2909
+ - ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
2910
+ - ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
3135
2911
  - ``#log start chat`` : 启动名为 chat 的记录器。该记录器以纯文本模式,记录代码中调用过该记录器 .log 进行记录的信息
3136
2912
  - ``#log stop`` : 停止本会话默认记录器(记录器名为会话名)。
3137
2913
 
@@ -3189,19 +2965,8 @@ class Session:
3189
2965
  elif (args[0] == "show"):
3190
2966
  if len(args) > 1 and not args[1].startswith('-'):
3191
2967
  file = args[1]
3192
- if os.path.exists(file):
3193
- filepath = os.path.abspath(file)
3194
- #self.info(f'file {filepath} exists, will be shown.')
3195
- self.application.logFileShown = filepath
3196
- self.application.showLogInTab()
3197
- elif os.path.exists(os.path.join('./log', file)):
3198
- filepath = os.path.abspath(os.path.join('./log', file))
3199
- #self.info(f'file {filepath} exists, will be shown.')
3200
- self.application.logFileShown = filepath
3201
- self.application.showLogInTab()
3202
- else:
3203
- self.warning(f'指定记录文件 {file} 不存在!')
3204
-
2968
+ self.application.logFileShown = file
2969
+ self.application.showLogInTab()
3205
2970
  else:
3206
2971
  self.application.show_logSelectDialog()
3207
2972