diff --git a/evennia/scripts/taskhandler.py b/evennia/scripts/taskhandler.py index 9506addef4..1fd8a211f1 100644 --- a/evennia/scripts/taskhandler.py +++ b/evennia/scripts/taskhandler.py @@ -19,8 +19,11 @@ class TaskHandler(object): A light singleton wrapper allowing to access permanent tasks. When `utils.delay` is called, the task handler is used to create - the task. If `utils.delay` is called with `persistent=True`, the - task handler stores the new task and saves. + the task. + If `utils.delay` is called with `persistent=True`. The + task handler creates the task. It saves the delay time, callback, + arguments and kwa to the database. Each of these variables are + serialized to do this. It's easier to access these tasks (should it be necessary) using `evennia.scripts.taskhandler.TASK_HANDLER`, which contains one @@ -101,9 +104,27 @@ class TaskHandler(object): any (any): any additional positional arguments to send to the callback Keyword Args: - persistent (bool, optional): persist the task (store it). + persistent (bool, optional): persist the task (stores it). + Add will return the task's id for use with the global TASK_HANDLER. any (any): additional keyword arguments to send to the callback + Returns: + twisted.internet.defer.Deferred instance of the deferred task + task_id (int), the task's id intended for use with this class. + + Notes: + This method has two return types. + An instance of twisted's Deferred class is standard. + If the persistent kwarg is truthy instead a task ID will be returned. + This task id can be used with task handler's do_task and remove methods. + + If the persistent kwarg is truthy: + The callback, args and values for kwarg will be serialized. Type + and attribute errors during the serialization will be logged, + but will not throw exceptions. + Do not use memory references in the callback function or arguments. + As those memory references will no longer acurately point to + the variable desired. """ persistent = kwargs.get("persistent", False) if persistent: diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index 67625bbfbb..b1a97c984d 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -6,13 +6,15 @@ TODO: Not nearly all utilities are covered yet. """ import os.path - import mock + from django.test import TestCase from datetime import datetime +from twisted.internet import task, reactor from evennia.utils.ansi import ANSIString from evennia.utils import utils +from evennia.utils.test_resources import EvenniaTest class TestIsIter(TestCase): @@ -292,3 +294,41 @@ class LatinifyTest(TestCase): byte_str = utils.to_bytes(self.example_str) result = utils.latinify(byte_str) self.assertEqual(result, self.expected_output) + + +_TASK_HANDLER = None + + +def dummy_func(obj): + """ + Used in TestDelay. + + A function that: + can be serialized + uses no memory references + uses evennia objects + """ + # get a reference of object + from evennia.objects.models import ObjectDB + obj = ObjectDB.objects.object_search(obj) + obj = obj[0] + # make changes to object + obj.ndb.dummy_var = 'dummy_func ran' + + +class TestDelay(EvenniaTest): + """ + Test utils.delay. + """ + + def test_delay(self): + # get a reference of TASK_HANDLER + global _TASK_HANDLER + if _TASK_HANDLER is None: + from evennia.scripts.taskhandler import TASK_HANDLER as _TASK_HANDLER + self.char1.ndb.dummy_var = False + # test a persistent deferral + task_id = utils.delay(1, dummy_func, self.char1.dbref, persistent=True) + _TASK_HANDLER.do_task(task_id) # run the deferred task + self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') + self.char1.ndb.dummy_var = False diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index b0abe2b9c7..105f9474a6 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -1022,7 +1022,7 @@ _TASK_HANDLER = None def delay(timedelay, callback, *args, **kwargs): """ - Delay the return of a value. + Delay the calling of a callback (function). Args: timedelay (int or float): The delay in seconds @@ -1040,15 +1040,26 @@ def delay(timedelay, callback, *args, **kwargs): commandhandler callback chain, the callback chain can be defined directly in the command body and don't need to be specified here. + Reference twisted.internet.defer.Deferred + if persistent kwarg is truthy: + task_id (int): the task's id intended for use with + evennia.scripts.taskhandler.TASK_HANDLER's do_task and remove methods. Note: The task handler (`evennia.scripts.taskhandler.TASK_HANDLER`) will be called for persistent or non-persistent tasks. If persistent is set to True, the callback, its arguments - and other keyword arguments will be saved in the database, + and other keyword arguments will be saved (serialized) in the database, assuming they can be. The callback will be executed even after a server restart/reload, taking into account the specified delay (and server down time). + Keep in mind that persistent tasks arguments and callback should not + use memory references. + If persistent is set to True the delay function will return an int + which is the task's id itended for use with TASK_HANDLER's do_task + and remove methods. + + All task's whose time delays have passed will be called on server startup. """ global _TASK_HANDLER