diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index 0b9b8d94af..c0ff71e766 100644 --- a/evennia/commands/cmdhandler.py +++ b/evennia/commands/cmdhandler.py @@ -39,6 +39,8 @@ from traceback import format_exc from itertools import chain from copy import copy import types +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.comms.channelhandler import CHANNELHANDLER @@ -128,6 +130,12 @@ _ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \ "reached for '{raw_string}' ({cmdclass})." +# delayed imports +_GET_INPUT = None + + +# helper functions + def _msg_err(receiver, stringtuple): """ Helper function for returning an error to the caller. @@ -152,12 +160,77 @@ def _msg_err(receiver, stringtuple): errmsg=stringtuple[1].strip(), timestamp=timestamp).strip()) + +def _progressive_cmd_run(cmd, generator, response=None): + """ + Progressively call the command that was given in argument. Used + when `yield` is present in the Command's `func()` method. + + Args: + cmd (Command): the command itself. + generator (GeneratorType): the generator describing the processing. + reponse (str, optional): the response to send to the generator. + + Raises: + ValueError: If the func call yields something not identifiable as a + time-delay or a string prompt. + + Note: + This function is responsible for executing the command, if + the func() method contains 'yield' instructions. The yielded + value will be accessible at each step and will affect the + process. If the value is a number, just delay the execution + of the command. If it's a string, wait for the user input. + + """ + global _GET_INPUT + if not _GET_INPUT: + from evennia.utils.evmenu import get_input as _GET_INPUT + + try: + if response is None: + value = generator.next() + else: + value = generator.send(response) + except StopIteration: + pass + else: + if isinstance(value, (int, float)): + utils.delay(value, _progressive_cmd_run, cmd, generator) + elif isinstance(value, basestring): + _GET_INPUT(cmd.caller, value, _process_input, cmd=cmd, generator=generator) + else: + raise ValueError("unknown type for a yielded value in command: {}".format(type(value))) + + +def _process_input(caller, prompt, result, cmd, generator): + """ + Specifically handle the get_input value to send to _progressive_cmd_run as + part of yielding from a Command's `func`. + + Args: + caller (Character, Player or Session): the caller. + prompt (basestring): The sent prompt. + result (basestring): The unprocessed answer. + cmd (Command): The command itself. + generator (GeneratorType): The generator. + + Returns: + result (bool): Always `False` (stop processing). + + """ + # We call it using a Twisted deferLater to make sure the input is properly closed. + deferLater(reactor, 0, _progressive_cmd_run, cmd, generator, response=result) + return False + + # custom Exceptions class NoCmdSets(Exception): "No cmdsets found. Critical error." pass + class ExecSystemCommand(Exception): "Run a system command" def __init__(self, syscmd, sysarg): @@ -676,55 +749,3 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess # This catches exceptions in cmdhandler exceptions themselves _msg_err(error_to, _ERROR_CMDHANDLER) -def _progressive_cmd_run(cmd, generator, response=None): - """ - Progressively call the command that was given in argument. - - Args: - cmd (Command): the command itself. - generator (GeneratorType): the generator describing the processing. - reponse (str, optional): the response to send to the generator. - - Note: - This function is responsible for executing the command, if - the func() method contains 'yield' instructions. The yielded - value will be accessible at each step and will affect the - process. If the value is a number, just delay the execution - of the command. If it's a string, wait for the user input. - - """ - try: - if response is None: - value = generator.next() - else: - value = generator.send(response) - except StopIteration: - pass - else: - if isinstance(value, (int, float)): - utils.delay(value, _progressive_cmd_run, cmd, generator) - elif isinstance(value, basestring): - from evennia.utils.evmenu import get_input - get_input(cmd.caller, value, _process_input, cmd=cmd, generator=generator) - else: - raise ValueError("unknown type for a yielded value in command: {}".format(type(value))) - -def _process_input(caller, prompt, result, cmd, generator): - """ - Specifically handle the get_input value to send to _progressive_cmd_run. - - Args: - caller (Character, Player or Session): the caller. - prompt (basestring): the sent prompt. - result (basestring): the unprocessed answer. - cmd (Command): the command itself. - generator (GeneratorType): the generator. - - Returns: - Always False (stop processing). - - """ - # We call it in a 'utils.delay()' to make sure the input is properly closed. - utils.delay(0, _progressive_cmd_run, cmd, generator, response=result) - - return False