diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index a24f46d449..63c08962b8 100644 --- a/evennia/commands/cmdhandler.py +++ b/evennia/commands/cmdhandler.py @@ -43,6 +43,7 @@ from twisted.internet import reactor from twisted.internet.task import deferLater from twisted.internet.defer import inlineCallbacks, returnValue from django.conf import settings +from evennia.commands.command import InterruptCommand from evennia.comms.channelhandler import CHANNELHANDLER from evennia.utils import logger, utils from evennia.utils.utils import string_suggestions, to_unicode @@ -51,7 +52,7 @@ from django.utils.translation import ugettext as _ _IN_GAME_ERRORS = settings.IN_GAME_ERRORS -__all__ = ("cmdhandler",) +__all__ = ("cmdhandler", "InterruptCommand") _GA = object.__getattribute__ _CMDSET_MERGE_CACHE = WeakValueDictionary() @@ -601,6 +602,9 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess # return result to the deferred returnValue(ret) + except InterruptCommand: + # Do nothing, clean exit + pass except Exception: _msg_err(caller, _ERROR_UNTRAPPED) raise ErrorReported(raw_string) @@ -748,4 +752,3 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess except Exception: # This catches exceptions in cmdhandler exceptions themselves _msg_err(error_to, _ERROR_CMDHANDLER) - diff --git a/evennia/commands/command.py b/evennia/commands/command.py index 6a6025616c..c5d1515d83 100644 --- a/evennia/commands/command.py +++ b/evennia/commands/command.py @@ -451,3 +451,10 @@ class Command(with_metaclass(CommandMeta, object)): """ return self.__doc__ + + +class InterruptCommand(Exception): + + """Cleanly interrupt a command.""" + + pass diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 39f2f8841f..e6a6f36590 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -20,6 +20,7 @@ from mock import Mock from evennia.commands.default.cmdset_character import CharacterCmdSet from evennia.utils.test_resources import EvenniaTest from evennia.commands.default import help, general, system, admin, player, building, batchprocess, comms +from evennia.commands.command import Command, InterruptCommand from evennia.utils import ansi, utils from evennia.server.sessionhandler import SESSIONS @@ -89,6 +90,8 @@ class CommandTest(EvenniaTest): else: returned_msg = "\n".join(stored_msg) returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip() + except InterruptCommand: + pass finally: receiver.msg = old_msg @@ -326,3 +329,19 @@ class TestBatchProcess(CommandTest): self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batchcommand processor Automatic mode for example_batch_cmds") # we make sure to delete the button again here to stop the running reactor self.call(building.CmdDestroy(), "button", "button was destroyed.") + +class CmdInterrupt(Command): + + key = "interrupt" + + def parse(self): + raise InterruptCommand + + def func(self): + self.msg("in func") + + +class TestInterruptCommand(CommandTest): + def test_interrupt_command(self): + ret = self.call(CmdInterrupt(), "") + self.assertEqual(ret, "")