From 3bebe2ac22674f1f7773f3e034b3bdc9f3eff14a Mon Sep 17 00:00:00 2001 From: Tehom Date: Sun, 22 Apr 2018 06:56:27 -0400 Subject: [PATCH 1/9] Fix regex in CommandTest breaking negative numbers --- evennia/commands/default/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index e3715a30c8..6d122b11d6 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -31,7 +31,7 @@ from evennia import DefaultObject, DefaultCharacter # set up signal here since we are not starting the server -_RE = re.compile(r"^\+|-+\+|\+-+|--*|\|(?:\s|$)", re.MULTILINE) +_RE = re.compile(r"^\+|-+\+|\+-+|--+|\|(?:\s|$)", re.MULTILINE) # ------------------------------------------------------------ From 1480ac9acc0419980f2c57b6b740e5bf9ae3b96a Mon Sep 17 00:00:00 2001 From: Tehom Date: Sun, 22 Apr 2018 07:25:01 -0400 Subject: [PATCH 2/9] Add hyphens to some tests. Probably more to come --- evennia/commands/default/tests.py | 14 +++++++------- evennia/contrib/tests.py | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 6d122b11d6..be889317d5 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -128,7 +128,7 @@ class TestGeneral(CommandTest): self.call(general.CmdPose(), "looks around", "Char looks around") def test_nick(self): - self.call(general.CmdNick(), "testalias = testaliasedstring1", "Inputlinenick 'testalias' mapped to 'testaliasedstring1'.") + self.call(general.CmdNick(), "testalias = testaliasedstring1", "Inputline-nick 'testalias' mapped to 'testaliasedstring1'.") self.call(general.CmdNick(), "/account testalias = testaliasedstring2", "Accountnick 'testalias' mapped to 'testaliasedstring2'.") self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Objectnick 'testalias' mapped to 'testaliasedstring3'.") self.assertEqual(u"testaliasedstring1", self.char1.nicks.get("testalias")) @@ -190,14 +190,14 @@ class TestAdmin(CommandTest): self.call(admin.CmdWall(), "Test", "Announcing to all connected sessions ...") def test_ban(self): - self.call(admin.CmdBan(), "Char", "NameBan char was added.") + self.call(admin.CmdBan(), "Char", "Name-Ban char was added.") class TestAccount(CommandTest): def test_ooc_look(self): if settings.MULTISESSION_MODE < 2: - self.call(account.CmdOOCLook(), "", "You are outofcharacter (OOC).", caller=self.account) + self.call(account.CmdOOCLook(), "", "You are out-of-character (OOC).", caller=self.account) if settings.MULTISESSION_MODE == 2: self.call(account.CmdOOCLook(), "", "Account TestAccount (you are OutofCharacter)", caller=self.account) @@ -250,8 +250,8 @@ class TestBuilding(CommandTest): def test_attribute_commands(self): self.call(building.CmdSetAttribute(), "Obj/test1=\"value1\"", "Created attribute Obj/test1 = 'value1'") self.call(building.CmdSetAttribute(), "Obj2/test2=\"value2\"", "Created attribute Obj2/test2 = 'value2'") - self.call(building.CmdMvAttr(), "Obj2/test2 = Obj/test3", "Moved Obj2.test2 > Obj.test3") - self.call(building.CmdCpAttr(), "Obj/test1 = Obj2/test3", "Copied Obj.test1 > Obj2.test3") + self.call(building.CmdMvAttr(), "Obj2/test2 = Obj/test3", "Moved Obj2.test2 -> Obj.test3") + self.call(building.CmdCpAttr(), "Obj/test1 = Obj2/test3", "Copied Obj.test1 -> Obj2.test3") self.call(building.CmdWipe(), "Obj2/test2/test3", "Wiped attributes test2,test3 on Obj2.") def test_name(self): @@ -277,7 +277,7 @@ class TestBuilding(CommandTest): def test_exit_commands(self): self.call(building.CmdOpen(), "TestExit1=Room2", "Created new Exit 'TestExit1' from Room to Room2") - self.call(building.CmdLink(), "TestExit1=Room", "Link created TestExit1 > Room (one way).") + self.call(building.CmdLink(), "TestExit1=Room", "Link created TestExit1 -> Room (one way).") self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.") def test_set_home(self): @@ -412,7 +412,7 @@ class TestComms(CommandTest): class TestBatchProcess(CommandTest): def test_batch_commands(self): # cannot test batchcode here, it must run inside the server process - self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batchcommand processor Automatic mode for example_batch_cmds") + self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batch-command processor - Automatic mode for example_batch_cmds") # we make sure to delete the button again here to stop the running reactor confirm = building.CmdDestroy.confirm building.CmdDestroy.confirm = False diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index ff6b01d5cf..3ba56d3410 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -688,7 +688,7 @@ class TestMail(CommandTest): "You have received a new @mail from TestAccount2(account 2)|You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2) - self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.account) + self.call(mail.CmdMail(), "", "------------------------------------------------------------------------------| ID: From: Subject:", caller=self.account) self.call(mail.CmdMail(), "2", "From: TestAccount2", caller=self.account) self.call(mail.CmdMail(), "/forward TestAccount2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.account) self.call(mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.account) @@ -714,9 +714,9 @@ class TestMapBuilder(CommandTest): "evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND", """Creating Map...|≈ ≈ ≈ ≈ ≈ -≈ ♣♣♣ ≈ +≈ ♣-♣-♣ ≈ ≈ ♣ ♣ ♣ ≈ - ≈ ♣♣♣ ≈ + ≈ ♣-♣-♣ ≈ ≈ ≈ ≈ ≈ ≈ |Creating Landmass...|""") @@ -759,8 +759,8 @@ from evennia.contrib import simpledoor class TestSimpleDoor(CommandTest): def test_cmdopen(self): self.call(simpledoor.CmdOpen(), "newdoor;door:contrib.simpledoor.SimpleDoor,backdoor;door = Room2", - "Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A doortype exit was " - "created ignored eventual custom returnexit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).") + "Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A door-type exit was " + "created - ignored eventual custom return-exit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You close newdoor.", cmdstring="close") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already closed.", cmdstring="close") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You open newdoor.", cmdstring="open") From b34920059b8e8012ac1dc5ae95ab217b5882c2cf Mon Sep 17 00:00:00 2001 From: Tehom Date: Sun, 22 Apr 2018 07:37:14 -0400 Subject: [PATCH 3/9] Add yet more hyphens. --- evennia/commands/default/tests.py | 4 ++-- evennia/contrib/tests.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index be889317d5..a0492d6aab 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -129,8 +129,8 @@ class TestGeneral(CommandTest): def test_nick(self): self.call(general.CmdNick(), "testalias = testaliasedstring1", "Inputline-nick 'testalias' mapped to 'testaliasedstring1'.") - self.call(general.CmdNick(), "/account testalias = testaliasedstring2", "Accountnick 'testalias' mapped to 'testaliasedstring2'.") - self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Objectnick 'testalias' mapped to 'testaliasedstring3'.") + self.call(general.CmdNick(), "/account testalias = testaliasedstring2", "Account-nick 'testalias' mapped to 'testaliasedstring2'.") + self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Object-nick 'testalias' mapped to 'testaliasedstring3'.") self.assertEqual(u"testaliasedstring1", self.char1.nicks.get("testalias")) self.assertEqual(None, self.char1.nicks.get("testalias", category="account")) self.assertEqual(u"testaliasedstring2", self.char1.account.nicks.get("testalias", category="account")) diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index 3ba56d3410..78ecc0f4fb 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -689,7 +689,7 @@ class TestMail(CommandTest): self.call(mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "", "------------------------------------------------------------------------------| ID: From: Subject:", caller=self.account) - self.call(mail.CmdMail(), "2", "From: TestAccount2", caller=self.account) + self.call(mail.CmdMail(), "2", "------------------------------------------------------------------------------\nFrom: TestAccount2", caller=self.account) self.call(mail.CmdMail(), "/forward TestAccount2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.account) self.call(mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.account) self.call(mail.CmdMail(), "/delete 2", "Message 2 deleted", caller=self.account) From ebf0fcf0e38bca585ffdf6bf8eed4c32c8306abf Mon Sep 17 00:00:00 2001 From: Tehom Date: Tue, 24 Apr 2018 06:03:01 -0400 Subject: [PATCH 4/9] Call parse_ansi on each returned message before passing to regex to handle individually colored characters. --- evennia/commands/default/tests.py | 9 ++++++--- evennia/contrib/tests.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index a0492d6aab..a3be98984a 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -94,8 +94,11 @@ class CommandTest(EvenniaTest): # Get the first element of a tuple if msg received a tuple instead of a string stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg] if msg is not None: - returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg) - returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip() + # set our separator for returned messages based on parsing ansi or not + msg_sep = "|" if noansi else "||" + # Have to strip ansi for each returned message for the regex to handle it correctly + returned_msg = msg_sep.join(_RE.sub("", ansi.parse_ansi(mess, strip_ansi=noansi)) + for mess in stored_msg).strip() if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()): sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n" sep2 = "\n" + "=" * 30 + "Returned message" + "=" * 32 + "\n" @@ -166,7 +169,7 @@ class TestSystem(CommandTest): self.call(system.CmdPy(), "1+2", ">>> 1+2|3") def test_scripts(self): - self.call(system.CmdScripts(), "", "| dbref |") + self.call(system.CmdScripts(), "", "dbref ") def test_objects(self): self.call(system.CmdObjects(), "", "Object subtype totals") diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index 78ecc0f4fb..233327a264 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -688,8 +688,8 @@ class TestMail(CommandTest): "You have received a new @mail from TestAccount2(account 2)|You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2) - self.call(mail.CmdMail(), "", "------------------------------------------------------------------------------| ID: From: Subject:", caller=self.account) - self.call(mail.CmdMail(), "2", "------------------------------------------------------------------------------\nFrom: TestAccount2", caller=self.account) + self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.account) + self.call(mail.CmdMail(), "2", "From: TestAccount2", caller=self.account) self.call(mail.CmdMail(), "/forward TestAccount2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.account) self.call(mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.account) self.call(mail.CmdMail(), "/delete 2", "Message 2 deleted", caller=self.account) From 5335f217185d77a3110ff7227bb86b20f4595e21 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 25 Apr 2018 21:53:28 +0200 Subject: [PATCH 5/9] Update CODING_STYLE.md --- CODING_STYLE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 79488e94ec..460dfc15d5 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -97,15 +97,15 @@ def funcname(a, b, c, d=False, **kwargs): Args: a (str): This is a string argument that we can talk about over multiple lines. - b (int or str): Another argument - c (list): A list argument - d (bool, optional): An optional keyword argument + b (int or str): Another argument. + c (list): A list argument. + d (bool, optional): An optional keyword argument. Kwargs: - test (list): A test keyword + test (list): A test keyword. Returns: - e (str): The result of the function + e (str): The result of the function. Raises: RuntimeException: If there is a critical error, From 59d136f4053543fd59c2205a229c8a0682fdb909 Mon Sep 17 00:00:00 2001 From: Tehom Date: Thu, 10 May 2018 19:23:59 -0400 Subject: [PATCH 6/9] Add special handling for scripts when flushed from cache to avoid duplicate ExtendedLoopingCalls. --- evennia/scripts/scripts.py | 46 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index db6e9652cb..eafb87ee72 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -18,6 +18,17 @@ from future.utils import with_metaclass __all__ = ["DefaultScript", "DoNothing", "Store"] +FLUSHING_INSTANCES = False # whether we're in the process of flushing scripts from the cache +SCRIPT_FLUSH_TIMERS = {} # stores timers for scripts that are currently being flushed + + +def restart_scripts_after_flush(): + """After instances are flushed, validate scripts so they're not dead for a long period of time""" + global FLUSHING_INSTANCES + ScriptDB.objects.validate() + FLUSHING_INSTANCES = False + + class ExtendedLoopingCall(LoopingCall): """ LoopingCall that can start at a delay different @@ -278,6 +289,27 @@ class DefaultScript(ScriptBase): return max(0, self.db_repeats - task.callcount) return None + def at_idmapper_flush(self): + """If we're flushing this object, make sure the LoopingCall is gone too""" + ret = super(DefaultScript, self).at_idmapper_flush() + if ret: + try: + from twisted.internet import reactor + global FLUSHING_INSTANCES + # store the current timers for the _task and stop it to avoid duplicates after cache flush + paused_time = self.ndb._task.next_call_time() + callcount = self.ndb._task.callcount + self._stop_task() + SCRIPT_FLUSH_TIMERS[self.id] = (paused_time, callcount) + # here we ensure that the restart call only happens once, not once per script + if not FLUSHING_INSTANCES: + FLUSHING_INSTANCES = True + reactor.callLater(2, restart_scripts_after_flush) + except Exception: + import traceback + traceback.print_exc() + return ret + def start(self, force_restart=False): """ Called every time the script is started (for persistent @@ -294,9 +326,19 @@ class DefaultScript(ScriptBase): started or not. Used in counting. """ - if self.is_active and not force_restart: - # script already runs and should not be restarted. + # The script is already running, but make sure we have a _task if this is after a cache flush + if not self.ndb._task: + self.ndb._task = ExtendedLoopingCall(self._step_task) + try: + start_delay, callcount = SCRIPT_FLUSH_TIMERS[self.id] + del SCRIPT_FLUSH_TIMERS[self.id] + now = False + except (KeyError, ValueError, TypeError): + now = not self.db_start_delay + start_delay = None + callcount = 0 + self.ndb._task.start(self.db_interval, now=now, start_delay=start_delay, count_start=callcount) return 0 obj = self.obj From 9ee420458c8a4e6b9f0f3047280b75511a4a3ae5 Mon Sep 17 00:00:00 2001 From: Tehom Date: Thu, 10 May 2018 19:30:58 -0400 Subject: [PATCH 7/9] Fix paused tasks. --- evennia/scripts/scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index eafb87ee72..2cade33281 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -292,7 +292,7 @@ class DefaultScript(ScriptBase): def at_idmapper_flush(self): """If we're flushing this object, make sure the LoopingCall is gone too""" ret = super(DefaultScript, self).at_idmapper_flush() - if ret: + if ret and self.ndb._task: try: from twisted.internet import reactor global FLUSHING_INSTANCES From 058c65074e3769f2666c1c1f97c7d1704f469bd6 Mon Sep 17 00:00:00 2001 From: Tehom Date: Thu, 10 May 2018 19:40:58 -0400 Subject: [PATCH 8/9] Handle scripts with negative intervals. --- evennia/scripts/scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index 2cade33281..2b4bea3569 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -328,7 +328,7 @@ class DefaultScript(ScriptBase): """ if self.is_active and not force_restart: # The script is already running, but make sure we have a _task if this is after a cache flush - if not self.ndb._task: + if not self.ndb._task and self.db_interval >= 0: self.ndb._task = ExtendedLoopingCall(self._step_task) try: start_delay, callcount = SCRIPT_FLUSH_TIMERS[self.id] From 26205acc39bba538e794f14d0a2d01e3eb6c2552 Mon Sep 17 00:00:00 2001 From: Tehom Date: Tue, 29 May 2018 23:05:34 -0400 Subject: [PATCH 9/9] Fix __add__ in SaverList --- evennia/utils/dbserialize.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index 80203923e8..c08704be0e 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -237,10 +237,13 @@ class _SaverList(_SaverMutable, MutableSequence): self._data = list() @_save - def __add__(self, otherlist): + def __iadd__(self, otherlist): self._data = self._data.__add__(otherlist) return self._data + def __add__(self, otherlist): + return list(self._data) + otherlist + @_save def insert(self, index, value): self._data.insert(index, self._convert_mutables(value))