pymud 0.20.0a2__py3-none-any.whl → 0.20.0a4__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,4 +1,4 @@
1
- import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig
1
+ import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig, time
2
2
  from collections.abc import Iterable
3
3
  from collections import OrderedDict
4
4
  import logging, queue
@@ -8,7 +8,7 @@ from logging.handlers import QueueHandler, QueueListener
8
8
  from .logger import Logger
9
9
  from .extras import SessionBuffer, DotDict, Plugin
10
10
  from .protocol import MudClientProtocol
11
- from .objects import Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
11
+ from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
12
12
  from .settings import Settings
13
13
 
14
14
 
@@ -29,13 +29,14 @@ class Session:
29
29
 
30
30
  """
31
31
  #_esc_regx = re.compile("\x1b\\[[^mz]+[mz]")
32
- _esc_regx = re.compile("\x1b\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
32
+ _esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
33
33
 
34
34
  _sys_commands = (
35
35
  "help",
36
36
  "exit",
37
37
  "close",
38
38
  "connect", # 连接到服务器
39
+ "disconnect", # 从服务器断开连接
39
40
 
40
41
  "info", # 输出蓝色info
41
42
  "warning", # 输出黄色warning
@@ -89,6 +90,7 @@ class Session:
89
90
  "var" : "variable",
90
91
  "rep" : "repeat",
91
92
  "con" : "connect",
93
+ "dis" : "disconnect",
92
94
  "wa" : "wait",
93
95
  "mess": "message",
94
96
  "action": "trigger",
@@ -139,6 +141,8 @@ class Session:
139
141
  self._status_maker = None # 创建状态窗口的函数(属性)
140
142
  self.display_line = ""
141
143
 
144
+ self._activetime = time.time()
145
+
142
146
  self.initialize()
143
147
 
144
148
  self._loggers = dict()
@@ -294,6 +298,15 @@ class Session:
294
298
 
295
299
  return dura
296
300
 
301
+ @property
302
+ def idletime(self) -> float:
303
+ "只读属性,返回当前会话空闲时间(即最后一次向服务器写入数据到现在的时间),以秒为单位。当服务器未连接时,返回-1"
304
+ idle = -1
305
+ if self._protocol and self._protocol.connected:
306
+ idle = time.time() - self._activetime
307
+
308
+ return idle
309
+
297
310
  @property
298
311
  def status_maker(self):
299
312
  """
@@ -770,6 +783,8 @@ class Session:
770
783
 
771
784
  cmd = line + self.newline
772
785
  self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
786
+
787
+ self._activetime = time.time()
773
788
 
774
789
  async def waitfor(self, line: str, awaitable, wait_time = 0.05) -> None:
775
790
  """
@@ -808,7 +823,7 @@ class Session:
808
823
  示例:
809
824
  .. code:: Python
810
825
 
811
- 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"))
826
+ 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"))
812
827
  session.exec("cb j1a")
813
828
  """
814
829
  name = name or self.name
@@ -829,12 +844,10 @@ class Session:
829
844
  name = name or self.name
830
845
  if name in self.application.sessions.keys():
831
846
  session = self.application.sessions[name]
832
- await session.exec_command_async(cmd, *args, **kwargs)
847
+ return await session.exec_command_async(cmd, *args, **kwargs)
833
848
  else:
834
849
  self.error(f"不存在名称为{name}的会话")
835
850
 
836
-
837
-
838
851
  def exec_code(self, cl: CodeLine, *args, **kwargs):
839
852
  """
840
853
  执行解析为CodeLine形式的MUD命令(必定为单个命令)。一般情况下,脚本中不应调用该方法,而应使用exec/exec_command。
@@ -908,6 +921,7 @@ class Session:
908
921
  :param args: 保留兼容与扩展性所需
909
922
  :param kwargs: 保留兼容与扩展性所需
910
923
  """
924
+
911
925
  if cl.length == 0:
912
926
  self.writeline("")
913
927
 
@@ -935,9 +949,9 @@ class Session:
935
949
  else:
936
950
  try:
937
951
  cb = CodeBlock(sess_cmd)
938
- await cb.async_execute(session, *args, **kwargs)
952
+ return await cb.async_execute(session, *args, **kwargs)
939
953
  except Exception as e:
940
- await session.exec_command_async(sess_cmd)
954
+ return await session.exec_command_async(sess_cmd)
941
955
 
942
956
  else:
943
957
  if cmd in self._commands_alias.keys():
@@ -954,7 +968,7 @@ class Session:
954
968
 
955
969
  else:
956
970
  cmdtext, code = cl.expand(self, *args, **kwargs)
957
- await self.exec_text_async(cmdtext)
971
+ return await self.exec_text_async(cmdtext)
958
972
 
959
973
  def exec_text(self, cmdtext: str):
960
974
  """
@@ -994,14 +1008,14 @@ class Session:
994
1008
 
995
1009
  异步调用时,该函数要等待对应的代码执行完毕后才会返回。可以用于确保命令执行完毕。
996
1010
  """
997
-
1011
+ result = None
998
1012
  isNotCmd = True
999
1013
  for command in self._commands.values():
1000
1014
  if isinstance(command, Command) and command.enabled:
1001
1015
  state = command.match(cmdtext)
1002
1016
  if state.result == Command.SUCCESS:
1003
1017
  # 命令的任务名称采用命令id,以便于后续查错
1004
- await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
1018
+ result = await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
1005
1019
  isNotCmd = False
1006
1020
  break
1007
1021
 
@@ -1019,6 +1033,8 @@ class Session:
1019
1033
  if notAlias:
1020
1034
  self.writeline(cmdtext)
1021
1035
 
1036
+ return result
1037
+
1022
1038
  def exec_command(self, line: str, *args, **kwargs) -> None:
1023
1039
  """
1024
1040
  在当前会话中执行MUD命令。多个命令可以用分隔符隔开。
@@ -1069,15 +1085,18 @@ class Session:
1069
1085
  """
1070
1086
 
1071
1087
  ## 以下为函数执行本体
1088
+ result = None
1072
1089
  if (not "#" in line) and (not "@" in line) and (not "%" in line):
1073
1090
  cmds = line.split(self.seperator)
1074
1091
  for cmd in cmds:
1075
- await self.exec_text_async(cmd)
1092
+ result = await self.exec_text_async(cmd)
1076
1093
  if Settings.client["interval"] > 0:
1077
1094
  await asyncio.sleep(Settings.client["interval"] / 1000.0)
1078
1095
  else:
1079
1096
  cb = CodeBlock(line)
1080
- await cb.async_execute(self)
1097
+ result = await cb.async_execute(self)
1098
+
1099
+ return result
1081
1100
 
1082
1101
  def write_eof(self) -> None:
1083
1102
  """
@@ -1141,50 +1160,202 @@ class Session:
1141
1160
 
1142
1161
  return counts
1143
1162
 
1144
- def _addObjects(self, objs: dict, cls: type):
1145
- if cls == Alias:
1146
- self._aliases.update(objs)
1147
- elif cls == Command:
1148
- self._commands.update(objs)
1149
- elif cls == Trigger:
1150
- self._triggers.update(objs)
1151
- elif cls == Timer:
1152
- self._timers.update(objs)
1153
- elif cls == GMCPTrigger:
1154
- self._gmcp.update(objs)
1155
-
1156
- def _addObject(self, obj, cls: type):
1157
- #if type(obj) == cls:
1158
- if isinstance(obj, cls):
1159
- if cls == Alias:
1160
- self._aliases[obj.id] = obj
1161
- elif cls == Command:
1162
- self._commands[obj.id] = obj
1163
- elif cls == Trigger:
1164
- self._triggers[obj.id] = obj
1165
- elif cls == Timer:
1166
- self._timers[obj.id] = obj
1167
- elif cls == GMCPTrigger:
1168
- self._gmcp[obj.id] = obj
1163
+ # def _addObjects(self, objs: dict, cls: type):
1164
+ # if cls == Alias:
1165
+ # self._aliases.update(objs)
1166
+ # elif cls == Command:
1167
+ # self._commands.update(objs)
1168
+ # elif cls == Trigger:
1169
+ # self._triggers.update(objs)
1170
+ # elif cls == Timer:
1171
+ # self._timers.update(objs)
1172
+ # elif cls == GMCPTrigger:
1173
+ # self._gmcp.update(objs)
1174
+
1175
+ def _addObjects(self, objs):
1176
+ if isinstance(objs, list) or isinstance(objs, tuple):
1177
+ for item in objs:
1178
+ self._addObject(item)
1179
+
1180
+ elif isinstance(objs, dict):
1181
+ for key, item in objs.items():
1182
+ if isinstance(item, BaseObject):
1183
+ if key != item.id:
1184
+ self.warning(f'对象 {item} 字典键值 {key} 与其id {item.id} 不一致,将丢弃键值,以其id添加到会话中...')
1185
+
1186
+ self._addObject(item)
1187
+
1188
+ # def _addObject(self, obj, cls: type):
1189
+ # #if type(obj) == cls:
1190
+ # if isinstance(obj, cls):
1191
+ # if cls == Alias:
1192
+ # self._aliases[obj.id] = obj
1193
+ # elif cls == Command:
1194
+ # self._commands[obj.id] = obj
1195
+ # elif cls == Trigger:
1196
+ # self._triggers[obj.id] = obj
1197
+ # elif cls == Timer:
1198
+ # self._timers[obj.id] = obj
1199
+ # elif cls == GMCPTrigger:
1200
+ # self._gmcp[obj.id] = obj
1201
+
1202
+ def _addObject(self, obj):
1203
+ if isinstance(obj, Alias):
1204
+ self._aliases[obj.id] = obj
1205
+ elif isinstance(obj, Command):
1206
+ self._commands[obj.id] = obj
1207
+ elif isinstance(obj, Trigger):
1208
+ self._triggers[obj.id] = obj
1209
+ elif isinstance(obj, Timer):
1210
+ self._timers[obj.id] = obj
1211
+ elif isinstance(obj, GMCPTrigger):
1212
+ self._gmcp[obj.id] = obj
1213
+
1214
+ def addObject(self, obj: BaseObject):
1215
+ """
1216
+ 向会话中增加单个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
1217
+
1218
+ :param obj: 特定对象本身,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1219
+
1220
+ 示例:
1221
+ .. code:: Python
1222
+
1223
+ class Configuration:
1224
+ def __init__(self, session):
1225
+ self.session = session
1226
+
1227
+ self.session.addObject(SimpleAlias(session, r'^gta$', 'get all'),)
1228
+ self.session.addObject(SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'))
1229
+ self.session.addObject(SimpleTimer(session, 'xixi', timeout = 10))
1230
+
1231
+ """
1232
+ self._addObject(obj)
1233
+
1234
+ def addObjects(self, objs):
1235
+ """
1236
+ 向会话中增加多个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类的元组、列表或者字典(保持兼容性)
1237
+
1238
+ :param objs: 多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1239
+
1240
+ 示例:
1241
+ .. code:: Python
1242
+
1243
+ class Configuration:
1244
+ def __init__(self, session):
1245
+ self.session = session
1246
+
1247
+ self.objs = [
1248
+ SimpleAlias(session, r'^gta$', 'get all;xixi'),
1249
+ SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
1250
+ SimpleTimer(session, 'xixi', timeout = 10)
1251
+ ]
1252
+
1253
+ self.session.addObjects(self.objs)
1254
+
1255
+ """
1256
+ self._addObjects(objs)
1169
1257
 
1170
1258
  def _delObject(self, id, cls: type):
1171
1259
  if cls == Alias:
1172
1260
  self._aliases.pop(id, None)
1173
1261
  elif cls == Command:
1174
- self._commands.pop(id, None)
1262
+ cmd = self._commands.pop(id, None)
1263
+ if isinstance(cmd, Command):
1264
+ cmd.reset()
1265
+ cmd.unload()
1266
+ cmd.__unload__()
1267
+
1175
1268
  elif cls == Trigger:
1176
1269
  self._triggers.pop(id, None)
1177
1270
  elif cls == Timer:
1178
- self._timers.pop(id, None)
1271
+ timer = self._timers.pop(id, None)
1272
+ if isinstance(timer, Timer):
1273
+ timer.enabled = False
1179
1274
  elif cls == GMCPTrigger:
1180
1275
  self._gmcp.pop(id, None)
1181
1276
 
1277
+
1182
1278
  def _delObjects(self, ids: Iterable, cls: type):
1183
1279
  "删除多个指定元素"
1184
1280
  for id in ids:
1185
1281
  self._delObject(id, cls)
1186
1282
 
1187
- def addAliases(self, alis: dict):
1283
+ def delObject(self, obj):
1284
+ """
1285
+ 从会话中移除一个对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类本身
1286
+
1287
+ :param obj: 要删除的多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1288
+
1289
+ 示例:
1290
+ .. code:: Python
1291
+
1292
+ class Configuration:
1293
+ def __init__(self, session):
1294
+ self.session = session
1295
+
1296
+ ali = SimpleAlias(session, r'^gta$', 'get all', id = 'my_ali1')
1297
+
1298
+ # 以下几种方式均可将该别名添加到会话
1299
+ session.addObject(ali)
1300
+ session.addAlias(ali)
1301
+
1302
+ # 以下三种方式均可以删除该别名
1303
+ session.delObject(ali)
1304
+ session.delAlias(ali)
1305
+ session.delAlias("my_ali1")
1306
+
1307
+ """
1308
+ if isinstance(obj, Alias):
1309
+ self._aliases.pop(obj.id, None)
1310
+ elif isinstance(obj, Command):
1311
+ obj.reset()
1312
+ obj.unload()
1313
+ obj.__unload__()
1314
+ self._commands.pop(obj.id, None)
1315
+ elif isinstance(obj, Trigger):
1316
+ self._triggers.pop(obj.id, None)
1317
+ elif isinstance(obj, Timer):
1318
+ obj.enabled = False
1319
+ self._timers.pop(obj.id, None)
1320
+ elif isinstance(obj, GMCPTrigger):
1321
+ self._gmcp.pop(obj.id, None)
1322
+
1323
+ def delObjects(self, objs):
1324
+ """
1325
+ 从会话中移除一组对象,可直接删除多个 Alias, Trigger, GMCPTrigger, Command, Timer
1326
+
1327
+ :param objs: 要删除的一组对象的元组、列表或者字典(保持兼容性),其中对象可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
1328
+
1329
+ 示例:
1330
+
1331
+ .. code:: Python
1332
+
1333
+ class Configuration:
1334
+ def __init__(self, session):
1335
+ self.session = session
1336
+
1337
+ self.objs = [
1338
+ SimpleAlias(session, r'^gta$', 'get all;xixi'),
1339
+ SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
1340
+ SimpleTimer(session, 'xixi', timeout = 10)
1341
+ ]
1342
+
1343
+ self.session.addObjects(self.objs)
1344
+
1345
+ def __unload__(self):
1346
+ "卸载本模块时,删除所有本模块添加的对象"
1347
+ self.session.delObjects(self.objs)
1348
+
1349
+ """
1350
+ if isinstance(objs, list) or isinstance(objs, tuple):
1351
+ for item in objs:
1352
+ self.delObject(item)
1353
+
1354
+ elif isinstance(objs, dict):
1355
+ for key, item in objs.items():
1356
+ self.delObject(item)
1357
+
1358
+ def addAliases(self, alis):
1188
1359
  """
1189
1360
  向会话中增加多个别名
1190
1361
 
@@ -1205,55 +1376,55 @@ class Session:
1205
1376
  self._aliases['my_ali2'] = SimpleAlias(self.session, "s", "south", id = "my_ali2")
1206
1377
  self.session.addAliases(self._aliases)
1207
1378
  """
1208
- self._addObjects(alis, Alias)
1379
+ self._addObjects(alis)
1209
1380
 
1210
- def addCommands(self, cmds: dict):
1381
+ def addCommands(self, cmds):
1211
1382
  """
1212
1383
  向会话中增加多个命令。使用方法与 addAliases 类似。
1213
1384
 
1214
1385
  :param cmds: 多个命令的字典。字典 key 应为每个命令的 id。
1215
1386
  """
1216
- self._addObjects(cmds, Command)
1387
+ self._addObjects(cmds)
1217
1388
 
1218
- def addTriggers(self, tris: dict):
1389
+ def addTriggers(self, tris):
1219
1390
  """
1220
1391
  向会话中增加多个触发器。使用方法与 addAliases 类似。
1221
1392
 
1222
1393
  :param tris: 多个触发器的字典。字典 key 应为每个触发器的 id。
1223
1394
  """
1224
- self._addObjects(tris, Trigger)
1395
+ self._addObjects(tris)
1225
1396
 
1226
- def addGMCPs(self, gmcps: dict):
1397
+ def addGMCPs(self, gmcps):
1227
1398
  """
1228
1399
  向会话中增加多个GMCPTrigger。使用方法与 addAliases 类似。
1229
1400
 
1230
1401
  :param gmcps: 多个GMCPTrigger的字典。字典 key 应为每个GMCPTrigger的 id。
1231
1402
  """
1232
- self._addObjects(gmcps, GMCPTrigger)
1403
+ self._addObjects(gmcps)
1233
1404
 
1234
- def addTimers(self, tis: dict):
1405
+ def addTimers(self, tis):
1235
1406
  """
1236
1407
  向会话中增加多个定时器。使用方法与 addAliases 类似。
1237
1408
 
1238
1409
  :param tis: 多个定时器的字典。字典 key 应为每个定时器的 id。
1239
1410
  """
1240
- self._addObjects(tis, Timer)
1411
+ self._addObjects(tis)
1241
1412
 
1242
- def addAlias(self, ali: Alias):
1413
+ def addAlias(self, ali):
1243
1414
  """
1244
1415
  向会话中增加一个别名。
1245
1416
 
1246
1417
  :param ali: 要增加的别名对象,应为 Alias 类型或其子类
1247
1418
  """
1248
- self._addObject(ali, Alias)
1419
+ self._addObject(ali)
1249
1420
 
1250
- def addCommand(self, cmd: Command):
1421
+ def addCommand(self, cmd):
1251
1422
  """
1252
1423
  向会话中增加一个命令。
1253
1424
 
1254
1425
  :param cmd: 要增加的命令对象,应为 Command 类型或其子类
1255
1426
  """
1256
- self._addObject(cmd, Command)
1427
+ self._addObject(cmd)
1257
1428
 
1258
1429
  def addTrigger(self, tri: Trigger):
1259
1430
  """
@@ -1261,7 +1432,7 @@ class Session:
1261
1432
 
1262
1433
  :param tri: 要增加的触发器对象,应为 Trigger 类型或其子类
1263
1434
  """
1264
- self._addObject(tri, Trigger)
1435
+ self._addObject(tri)
1265
1436
 
1266
1437
  def addTimer(self, ti: Timer):
1267
1438
  """
@@ -1269,7 +1440,7 @@ class Session:
1269
1440
 
1270
1441
  :param ti: 要增加的定时器对象,应为 Timer 类型或其子类
1271
1442
  """
1272
- self._addObject(ti, Timer)
1443
+ self._addObject(ti)
1273
1444
 
1274
1445
  def addGMCP(self, gmcp: GMCPTrigger):
1275
1446
  """
@@ -1278,7 +1449,7 @@ class Session:
1278
1449
  :param gmcp: 要增加的GMCP触发器对象,应为 GMCPTrigger 类型或其子类
1279
1450
  """
1280
1451
 
1281
- self._addObject(gmcp, GMCPTrigger)
1452
+ self._addObject(gmcp)
1282
1453
 
1283
1454
  def delAlias(self, ali):
1284
1455
  """
@@ -1397,7 +1568,7 @@ class Session:
1397
1568
  for ti in ti_s:
1398
1569
  self.delTimer(ti)
1399
1570
 
1400
- def delGMCP(self, gmcp: GMCPTrigger):
1571
+ def delGMCP(self, gmcp):
1401
1572
  """
1402
1573
  从会话中移除一个GMCP触发器,可接受 GMCPTrigger 对象或其的id。使用方法与 delAlias 类似
1403
1574
 
@@ -1713,6 +1884,7 @@ class Session:
1713
1884
  该函数不应该在代码中直接调用。
1714
1885
 
1715
1886
  相关命令:
1887
+ - #disconnect
1716
1888
  - #close
1717
1889
  - #exit
1718
1890
  '''
@@ -1734,6 +1906,18 @@ class Session:
1734
1906
 
1735
1907
  self.info("已经与服务器连接了 {}".format(time_msg))
1736
1908
 
1909
+ def handle_disconnect(self, code: CodeLine = None, *args, **kwargs):
1910
+ '''
1911
+ 嵌入命令 #disconnect / #dis 的执行函数,断开到远程服务器的连接(仅当远程服务器已连接时有效)。
1912
+ 该函数不应该在代码中直接调用。
1913
+
1914
+ 相关命令:
1915
+ - #connect
1916
+ - #close
1917
+ '''
1918
+
1919
+ self.disconnect()
1920
+
1737
1921
  def handle_variable(self, code: CodeLine = None, *args, **kwargs):
1738
1922
  '''
1739
1923
  嵌入命令 #variable / #var 的执行函数,操作会话变量。
@@ -1967,7 +2151,7 @@ class Session:
1967
2151
  self.info("创建Timer {} 成功: {}".format(ti.id, ti.__repr__()))
1968
2152
 
1969
2153
  def handle_alias(self, code: CodeLine = None, *args, **kwargs):
1970
- '''
2154
+ """
1971
2155
  嵌入命令 #alias / #ali 的执行函数,操作别名。该命令可以不带参数、带一个参数或者两个参数。
1972
2156
  该函数不应该在代码中直接调用。
1973
2157
 
@@ -1998,7 +2182,7 @@ class Session:
1998
2182
  - #trigger
1999
2183
  - #timer
2000
2184
  - #command
2001
- '''
2185
+ """
2002
2186
 
2003
2187
  self._handle_objs("Alias", self._aliases, *code.code[2:])
2004
2188
 
@@ -2393,10 +2577,11 @@ class Session:
2393
2577
  mod = self._modules[module_name]["module"]
2394
2578
  config = self._modules[module_name]["config"]
2395
2579
  if config:
2396
- if hasattr(config, "unload"):
2397
- unload = getattr(config, "unload", None)
2580
+ if hasattr(config, "__unload__") or hasattr(config, "unload"):
2581
+ unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
2398
2582
  if callable(unload):
2399
- unload(config)
2583
+ unload()
2584
+
2400
2585
  del config
2401
2586
  mod = importlib.reload(mod)
2402
2587
  if hasattr(mod, 'Configuration'):
@@ -2431,15 +2616,15 @@ class Session:
2431
2616
  self._unload_module(mod)
2432
2617
 
2433
2618
  def _unload_module(self, module_name):
2434
- "卸载指定名称模块。卸载支持需要模块的Configuration实现__del__方法"
2619
+ "卸载指定名称模块。卸载支持需要模块的Configuration实现 __unload__ 或 unload 方法"
2435
2620
  if module_name in self._modules.keys():
2436
2621
  mod = self._modules[module_name]["module"]
2437
2622
  config = self._modules[module_name]["config"]
2438
2623
  if config:
2439
- if hasattr(config, "unload"):
2440
- unload = getattr(config, "unload", None)
2624
+ if hasattr(config, "__unload__") or hasattr(config, "unload"):
2625
+ unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
2441
2626
  if callable(unload):
2442
- unload(config)
2627
+ unload()
2443
2628
 
2444
2629
  del config
2445
2630
  del mod
pymud/settings.py CHANGED
@@ -11,7 +11,7 @@ class Settings:
11
11
  "APP 名称, 默认PYMUD"
12
12
  __appdesc__ = "a MUD client written in Python"
13
13
  "APP 简要描述"
14
- __version__ = "0.20.0a2"
14
+ __version__ = "0.20.0"
15
15
  "APP 当前版本"
16
16
  __release__ = "2024-08-19"
17
17
  "APP 当前版本发布日期"