diff --git a/evennia/__init__.py b/evennia/__init__.py index 679825bd70..b65e1edf9b 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -46,6 +46,7 @@ Command = None CmdSet = None default_cmds = None syscmdkeys = None +InterruptCommand = None # search functions search_object = None @@ -119,7 +120,7 @@ def _init(): global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter global DefaultRoom, DefaultExit, DefaultChannel, DefaultScript global ObjectDB, PlayerDB, ScriptDB, ChannelDB, Msg - global Command, CmdSet, default_cmds, syscmdkeys + global Command, CmdSet, default_cmds, syscmdkeys, InterruptCommand global search_object, search_script, search_player, search_channel, search_help, search_tag global create_object, create_script, create_player, create_channel, create_message, create_help_entry global settings,lockfuncs, logger, utils, gametime, ansi, spawn, managers @@ -142,7 +143,7 @@ def _init(): from .comms.models import Msg # commands - from .commands.command import Command + from .commands.command import Command, InterruptCommand from .commands.cmdset import CmdSet # search functions diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index 433c962b29..64336111a2 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() @@ -605,6 +606,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) @@ -752,4 +756,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 fc053b313a..b4d0f4554c 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 @@ -91,6 +92,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 @@ -328,3 +331,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, "")