pymud 0.20.4__py3-none-any.whl → 0.21.0__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/__init__.py +7 -2
- pymud/decorators.py +234 -0
- pymud/dialogs.py +37 -31
- pymud/extras.py +7 -135
- pymud/i18n.py +63 -0
- pymud/lang/i18n_chs.py +227 -0
- pymud/lang/i18n_eng.py +851 -0
- pymud/logger.py +9 -4
- pymud/main.py +123 -44
- pymud/modules.py +104 -31
- pymud/objects.py +102 -98
- pymud/pkuxkx.py +267 -142
- pymud/protocol.py +15 -13
- pymud/pymud.py +80 -75
- pymud/session.py +582 -356
- pymud/settings.py +15 -2
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/METADATA +118 -5
- pymud-0.21.0.dist-info/RECORD +23 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/WHEEL +1 -1
- pymud-0.20.4.dist-info/RECORD +0 -19
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/entry_points.txt +0 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/top_level.txt +0 -0
pymud/objects.py
CHANGED
@@ -4,10 +4,12 @@ MUD会话(session)中, 支持的对象列表
|
|
4
4
|
|
5
5
|
import asyncio, logging, re, importlib
|
6
6
|
from abc import ABC, ABCMeta, abstractmethod
|
7
|
+
from typing import Optional, Union, List, Dict, Tuple
|
7
8
|
from collections.abc import Iterable
|
8
9
|
from collections import namedtuple
|
9
10
|
from typing import Any
|
10
11
|
from .settings import Settings
|
12
|
+
from .decorators import exception, async_exception, print_exception
|
11
13
|
|
12
14
|
class CodeLine:
|
13
15
|
"""
|
@@ -37,7 +39,7 @@ class CodeLine:
|
|
37
39
|
elif ch == "}":
|
38
40
|
brace_count -= 1
|
39
41
|
if brace_count < 0:
|
40
|
-
raise Exception("
|
42
|
+
raise Exception(Settings.gettext("excpetion_brace_not_matched"))
|
41
43
|
arg += ch
|
42
44
|
elif ch == "'":
|
43
45
|
if single_quote == 0:
|
@@ -60,7 +62,7 @@ class CodeLine:
|
|
60
62
|
arg += ch
|
61
63
|
|
62
64
|
if (single_quote > 0) or (double_quote > 0):
|
63
|
-
raise Exception("
|
65
|
+
raise Exception(Settings.gettext("exception_quote_not_matched"))
|
64
66
|
|
65
67
|
if arg:
|
66
68
|
code_params.append(arg)
|
@@ -77,7 +79,7 @@ class CodeLine:
|
|
77
79
|
|
78
80
|
return syncmode, hasvar, tuple(code_params),
|
79
81
|
else:
|
80
|
-
return
|
82
|
+
return "dontcare", hasvar, tuple()
|
81
83
|
|
82
84
|
def __init__(self, _code: str) -> None:
|
83
85
|
self.__code = _code
|
@@ -108,7 +110,7 @@ class CodeLine:
|
|
108
110
|
|
109
111
|
line = kwargs.get("line", None) or session.getVariable("%line", "None")
|
110
112
|
raw = kwargs.get("raw", None) or session.getVariable("%raw", "None")
|
111
|
-
wildcards = kwargs.get("wildcards",
|
113
|
+
wildcards = kwargs.get("wildcards", ())
|
112
114
|
|
113
115
|
for item in self.code:
|
114
116
|
if len(item) == 0: continue
|
@@ -178,7 +180,7 @@ class CodeBlock:
|
|
178
180
|
elif ch == "}":
|
179
181
|
brace_count -= 1
|
180
182
|
if brace_count < 0:
|
181
|
-
raise Exception("
|
183
|
+
raise Exception(Settings.gettext("excpetion_brace_not_matched"))
|
182
184
|
line += ch
|
183
185
|
elif ch == ";":
|
184
186
|
if brace_count == 0:
|
@@ -264,7 +266,7 @@ class CodeBlock:
|
|
264
266
|
elif self.syncmode == "sync":
|
265
267
|
sync = True
|
266
268
|
elif self.syncmode == "conflict":
|
267
|
-
session.warning("
|
269
|
+
session.warning(Settings.gettext("exception_forced_async"))
|
268
270
|
sync = False
|
269
271
|
|
270
272
|
if sync:
|
@@ -326,11 +328,11 @@ class BaseObject:
|
|
326
328
|
if isinstance(session, Session):
|
327
329
|
self.session = session
|
328
330
|
else:
|
329
|
-
assert("
|
331
|
+
assert(Settings.gettext("exception_session_type_fail"))
|
330
332
|
|
331
333
|
self._enabled = True # give a default value
|
332
334
|
self.log = logging.getLogger(f"pymud.{self.__class__.__name__}")
|
333
|
-
self.id = kwargs.get("id", session.getUniqueID(self.__class__.__abbr__))
|
335
|
+
self.id = kwargs.get("id", self.session.getUniqueID(self.__class__.__abbr__))
|
334
336
|
self.group = kwargs.get("group", "") # 组
|
335
337
|
self.enabled = kwargs.get("enabled", True) # 使能与否
|
336
338
|
self.priority = kwargs.get("priority", 100) # 优先级
|
@@ -417,6 +419,8 @@ class GMCPTrigger(BaseObject):
|
|
417
419
|
def __init__(self, session, name, *args, **kwargs):
|
418
420
|
self.event = asyncio.Event()
|
419
421
|
self.value = None
|
422
|
+
# 确保不要有重复的id
|
423
|
+
kwargs.pop("id", None)
|
420
424
|
super().__init__(session, id = name, *args, **kwargs)
|
421
425
|
|
422
426
|
def __del__(self):
|
@@ -445,10 +449,13 @@ class GMCPTrigger(BaseObject):
|
|
445
449
|
|
446
450
|
self.line = value
|
447
451
|
self.value = value_exp
|
448
|
-
|
452
|
+
|
449
453
|
if callable(self._onSuccess):
|
450
454
|
self.event.set()
|
451
|
-
|
455
|
+
try:
|
456
|
+
self._onSuccess(self.id, value, value_exp)
|
457
|
+
except Exception as e:
|
458
|
+
print_exception(self.session, e)
|
452
459
|
|
453
460
|
def __detailed__(self) -> str:
|
454
461
|
group = f'group = "{self.group}" ' if self.group else ''
|
@@ -502,7 +509,7 @@ class MatchObject(BaseObject):
|
|
502
509
|
return self._patterns
|
503
510
|
|
504
511
|
@patterns.setter
|
505
|
-
def patterns(self, patterns):
|
512
|
+
def patterns(self, patterns: Union[str, Union[Tuple[str], List[str]]]):
|
506
513
|
self._patterns = patterns
|
507
514
|
|
508
515
|
if isinstance(patterns, str):
|
@@ -515,14 +522,17 @@ class MatchObject(BaseObject):
|
|
515
522
|
if self.isRegExp:
|
516
523
|
flag = 0
|
517
524
|
if self.ignoreCase: flag = re.I
|
518
|
-
if
|
519
|
-
self.
|
525
|
+
if isinstance(patterns, str):
|
526
|
+
self.multiline = False
|
527
|
+
self.linesToMatch = 1
|
528
|
+
self._regExp = re.compile(patterns, flag) # 此处可考虑增加flags
|
520
529
|
else:
|
521
530
|
self._regExps = []
|
522
|
-
for line in
|
531
|
+
for line in patterns:
|
523
532
|
self._regExps.append(re.compile(line, flag))
|
524
533
|
|
525
534
|
self.linesToMatch = len(self._regExps)
|
535
|
+
self.multiline = True
|
526
536
|
self._mline = 0
|
527
537
|
|
528
538
|
def reset(self):
|
@@ -542,76 +552,79 @@ class MatchObject(BaseObject):
|
|
542
552
|
|
543
553
|
:return: BaseObject.State 类型,一个包含 result, id, name, line, wildcards 的命名元组对象
|
544
554
|
"""
|
545
|
-
|
546
|
-
|
547
|
-
if not self.multiline: # 非多行
|
548
|
-
if self.isRegExp:
|
549
|
-
m = self._regExp.match(line)
|
550
|
-
if m:
|
551
|
-
result = self.SUCCESS
|
552
|
-
self.wildcards.clear()
|
553
|
-
if len(m.groups()) > 0:
|
554
|
-
self.wildcards.extend(m.groups())
|
555
|
+
try:
|
556
|
+
result = self.NOTSET
|
555
557
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
else: # 多行匹配情况
|
568
|
-
# multilines match. 多行匹配时,受限于行的捕获方式,必须一行一行来,设置状态标志进行处理。
|
569
|
-
if self._mline == 0: # 当尚未开始匹配时,匹配第1行
|
570
|
-
m = self._regExps[0].match(line)
|
571
|
-
if m:
|
572
|
-
self.lines.clear()
|
573
|
-
self.lines.append(line)
|
574
|
-
self.wildcards.clear()
|
575
|
-
if len(m.groups()) > 0:
|
576
|
-
self.wildcards.extend(m.groups())
|
577
|
-
self._mline = 1 # 下一状态 (中间行)
|
578
|
-
elif (self._mline > 0) and (self._mline < self.linesToMatch - 1):
|
579
|
-
m = self._regExps[self._mline].match(line)
|
580
|
-
if m:
|
581
|
-
self.lines.append(line)
|
582
|
-
if len(m.groups()) > 0:
|
583
|
-
self.wildcards.extend(m.groups())
|
584
|
-
self._mline += 1
|
558
|
+
if not self.multiline: # 非多行
|
559
|
+
if self.isRegExp:
|
560
|
+
m = self._regExp.match(line)
|
561
|
+
if m:
|
562
|
+
result = self.SUCCESS
|
563
|
+
self.wildcards.clear()
|
564
|
+
if len(m.groups()) > 0:
|
565
|
+
self.wildcards.extend(m.groups())
|
566
|
+
|
567
|
+
self.lines.clear()
|
568
|
+
self.lines.append(line)
|
585
569
|
else:
|
586
|
-
self.
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
self.wildcards.
|
593
|
-
|
594
|
-
|
595
|
-
|
570
|
+
#if line.find(self.patterns) >= 0:
|
571
|
+
#if line == self.patterns:
|
572
|
+
if isinstance(self.patterns, str) and (self.patterns in line):
|
573
|
+
result = self.SUCCESS
|
574
|
+
self.lines.clear()
|
575
|
+
self.lines.append(line)
|
576
|
+
self.wildcards.clear()
|
577
|
+
|
578
|
+
else: # 多行匹配情况
|
579
|
+
# multilines match. 多行匹配时,受限于行的捕获方式,必须一行一行来,设置状态标志进行处理。
|
580
|
+
if self._mline == 0: # 当尚未开始匹配时,匹配第1行
|
581
|
+
m = self._regExps[0].match(line)
|
582
|
+
if m:
|
583
|
+
self.lines.clear()
|
584
|
+
self.lines.append(line)
|
585
|
+
self.wildcards.clear()
|
586
|
+
if len(m.groups()) > 0:
|
587
|
+
self.wildcards.extend(m.groups())
|
588
|
+
self._mline = 1 # 下一状态 (中间行)
|
589
|
+
elif (self._mline > 0) and (self._mline < self.linesToMatch - 1):
|
590
|
+
m = self._regExps[self._mline].match(line)
|
591
|
+
if m:
|
592
|
+
self.lines.append(line)
|
593
|
+
if len(m.groups()) > 0:
|
594
|
+
self.wildcards.extend(m.groups())
|
595
|
+
self._mline += 1
|
596
|
+
else:
|
597
|
+
self._mline = 0
|
598
|
+
elif self._mline == self.linesToMatch - 1: # 最终行
|
599
|
+
m = self._regExps[self._mline].match(line)
|
600
|
+
if m:
|
601
|
+
self.lines.append(line)
|
602
|
+
if len(m.groups()) > 0:
|
603
|
+
self.wildcards.extend(m.groups())
|
604
|
+
result = self.SUCCESS
|
596
605
|
|
597
|
-
|
606
|
+
self._mline = 0
|
598
607
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
self.event.set()
|
608
|
+
state = BaseObject.State(result, self.id, "\n".join(self.lines), tuple(self.wildcards))
|
609
|
+
|
610
|
+
# 采用回调方式执行的时候,执行函数回调(仅当self.sync和docallback均为真时才执行同步
|
611
|
+
# 当docallback为真时,是真正的进行匹配和触发,为false时,仅返回匹配结果,不实际触发
|
612
|
+
if docallback:
|
613
|
+
if self.sync:
|
614
|
+
if state.result == self.SUCCESS:
|
615
|
+
self._onSuccess(state.id, state.line, state.wildcards)
|
616
|
+
elif state.result == self.FAILURE:
|
617
|
+
self._onFailure(state.id, state.line, state.wildcards)
|
618
|
+
elif state.result == self.TIMEOUT:
|
619
|
+
self._onTimeout(state.id, state.line, state.wildcards)
|
612
620
|
|
613
|
-
|
614
|
-
|
621
|
+
if state.result == self.SUCCESS:
|
622
|
+
self.event.set()
|
623
|
+
|
624
|
+
self.state = state
|
625
|
+
return state
|
626
|
+
except Exception as e:
|
627
|
+
print_exception(self.session, e)
|
615
628
|
|
616
629
|
async def matched(self) -> BaseObject.State:
|
617
630
|
"""
|
@@ -624,10 +637,10 @@ class MatchObject(BaseObject):
|
|
624
637
|
self.reset()
|
625
638
|
await self.event.wait()
|
626
639
|
self.reset()
|
627
|
-
except Exception as e:
|
628
|
-
self.error(f"异步执行中遇到异常, {e}")
|
629
640
|
|
630
|
-
|
641
|
+
return self.state
|
642
|
+
except Exception as e:
|
643
|
+
print_exception(self.session, e)
|
631
644
|
|
632
645
|
def __detailed__(self) -> str:
|
633
646
|
group = f'group = "{self.group}" ' if self.group else ''
|
@@ -676,7 +689,7 @@ class Trigger(MatchObject):
|
|
676
689
|
|
677
690
|
__abbr__ = "tri"
|
678
691
|
|
679
|
-
def __init__(self, session, patterns, *args, **kwargs):
|
692
|
+
def __init__(self, session, patterns: Union[str, Union[Tuple[str], List[str]]], *args, **kwargs):
|
680
693
|
super().__init__(session, patterns, *args, **kwargs)
|
681
694
|
self._task = None
|
682
695
|
|
@@ -787,7 +800,7 @@ class Command(MatchObject):
|
|
787
800
|
if isinstance(task, asyncio.Task) and (not task.done()):
|
788
801
|
self.remove_task(task)
|
789
802
|
|
790
|
-
async def execute(self, cmd, *args, **kwargs):
|
803
|
+
async def execute(self, cmd, *args, **kwargs) -> Any:
|
791
804
|
"""
|
792
805
|
命令调用的入口函数。该函数由 Session 进行自动调用。
|
793
806
|
通过 ``Session.exec`` 系列方法调用的命令,最终是执行该命令的 execute 方法。
|
@@ -815,7 +828,7 @@ class SimpleCommand(Command):
|
|
815
828
|
|
816
829
|
MAX_RETRY = 20
|
817
830
|
|
818
|
-
def __init__(self, session, patterns, succ_tri, *args, **kwargs):
|
831
|
+
def __init__(self, session, patterns: str, succ_tri, *args, **kwargs):
|
819
832
|
super().__init__(session, patterns, succ_tri, *args, **kwargs)
|
820
833
|
self._succ_tris = list()
|
821
834
|
self._fail_tris = list()
|
@@ -857,7 +870,7 @@ class SimpleCommand(Command):
|
|
857
870
|
"""
|
858
871
|
self.reset()
|
859
872
|
# 0. check command
|
860
|
-
cmd = cmd or self.patterns
|
873
|
+
cmd = cmd or self.patterns.__str__()
|
861
874
|
# 1. save the command, to use later.
|
862
875
|
self._executed_cmd = cmd
|
863
876
|
# 2. writer command
|
@@ -912,23 +925,14 @@ class SimpleCommand(Command):
|
|
912
925
|
break
|
913
926
|
|
914
927
|
if result == self.SUCCESS:
|
915
|
-
self._onSuccess(name = self.id, cmd = cmd, line =
|
916
|
-
_outer_onSuccess = kwargs.get("onSuccess", None)
|
917
|
-
if callable(_outer_onSuccess):
|
918
|
-
_outer_onSuccess(name = self.id, cmd = cmd, line = line, wildcards = wildcards)
|
928
|
+
self._onSuccess(name = self.id, cmd = cmd, line = "", wildcards = [])
|
919
929
|
|
920
930
|
elif result == self.FAILURE:
|
921
|
-
self._onFailure(name = self.id, cmd = cmd, line =
|
922
|
-
_outer_onFailure = kwargs.get("onFailure", None)
|
923
|
-
if callable(_outer_onFailure):
|
924
|
-
_outer_onFailure(name = self.id, cmd = cmd, line = line, wildcards = wildcards)
|
931
|
+
self._onFailure(name = self.id, cmd = cmd, line = "", wildcards = [])
|
925
932
|
|
926
933
|
elif result == self.TIMEOUT:
|
927
934
|
self._onTimeout(name = self.id, cmd = cmd, timeout = self.timeout)
|
928
|
-
|
929
|
-
if callable(_outer_onTimeout):
|
930
|
-
_outer_onTimeout(name = self.id, cmd = cmd, timeout = self.timeout)
|
931
|
-
|
935
|
+
|
932
936
|
return result
|
933
937
|
|
934
938
|
class Timer(BaseObject):
|