diff --git a/evennia/utils/persistent.py b/evennia/scripts/taskhandler.py similarity index 62% rename from evennia/utils/persistent.py rename to evennia/scripts/taskhandler.py index 5fdac2cef..0fc3bfe73 100644 --- a/evennia/utils/persistent.py +++ b/evennia/scripts/taskhandler.py @@ -1,7 +1,5 @@ """ -Module containing persistent features and the persistent differed -tasks, generated by utils.delay(persistent=True). - +Module containing the task handler for Evennia deferred tasks, persistent or not. """ from datetime import datetime, timedelta @@ -11,17 +9,20 @@ from evennia.server.models import ServerConfig from evennia.utils.logger import log_trace, log_err from evennia.utils.dbserialize import dbserialize, dbunserialize -PERSISTENT_TASKS = None +TASK_HANDLER = None -class PersistentTasks(object): +class TaskHandler(object): - """A light singleton wrapper allowing to access permanent tasks. + """ + 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. - Permament tasks are created by `utils.delay` when the `persistent` - keyword argument is set to True. Tasks are saved in ServerConfig. It's easier to access these tasks (should it be necessary) using - `evennia.utils.persistent.PERSISTSENT_TASKS`, which contains one - instance of this class, and use its `add and `remove` methods. + `evennia.scripts.taskhandler.TASK_HANDLER`, which contains one + instance of this class, and use its `add` and `remove` methods. """ @@ -88,56 +89,63 @@ class PersistentTasks(object): any (any): any additional positional arguments to send to the callback Kwargs: + persistent (bool, optional): persist the task (store it). any (any): additional keyword arguments to send to the callback - Note: - This doesn't create any delay at this time to call the task, - but the task is written in ServerConfig along with the - others. - """ - now = datetime.now() - delta = timedelta(seconds=timedelay) + persistent = kwargs.get("persistent", False) + if persistent: + del kwargs["persistent"] + now = datetime.now() + delta = timedelta(seconds=timedelay) - # Choose a free task_id - safe_args = [] - safe_kwargs = {} - used_ids = self.tasks.keys() - task_id = 1 - while task_id in used_ids: - task_id += 1 + # Choose a free task_id + safe_args = [] + safe_kwargs = {} + used_ids = self.tasks.keys() + task_id = 1 + while task_id in used_ids: + task_id += 1 - # Check that args and kwargs contain picklable information - for arg in args: - try: - dbserialize(arg) - except (TypeError, AttributeError): - logger.log_err("The positional argument {} cannot be " \ - "pickled and will not be present in the arguments " \ - "fed to the callback {}".format(arg, callback)) - else: - safe_args.append(arg) + # Check that args and kwargs contain picklable information + for arg in args: + try: + dbserialize(arg) + except (TypeError, AttributeError): + logger.log_err("The positional argument {} cannot be " \ + "pickled and will not be present in the arguments " \ + "fed to the callback {}".format(arg, callback)) + else: + safe_args.append(arg) - for key, value in kwargs.items(): - try: - dbserialize(value) - except (TypeError, AttributeError): - logger.log_err("The {} keyword argument {} cannot be " \ - "pickled and will not be present in the arguments " \ - "fed to the callback {}".format(key, value, callback)) - else: - safe_kwargs[key] = value + for key, value in kwargs.items(): + try: + dbserialize(value) + except (TypeError, AttributeError): + logger.log_err("The {} keyword argument {} cannot be " \ + "pickled and will not be present in the arguments " \ + "fed to the callback {}".format(key, value, callback)) + else: + safe_kwargs[key] = value - self.tasks[task_id] = (now + delta, callback, safe_args, safe_kwargs) - self.save() - return task_id + self.tasks[task_id] = (now + delta, callback, safe_args, safe_kwargs) + self.save() + callback = self.do_task + args = [task_id] + kwargs = {} + + return task.deferLater(reactor, timedelay, callback, *args, **kwargs) def remove(self, task_id): - """Remove a task without executing it. + """Remove a persistent task without executing it. Args: task_id (int): an existing task ID. + Note: + A non-persistent task doesn't have a task_id, it is not stored + in the TaskHandler. + """ del self.tasks[task_id] if task_id in self.to_save: @@ -179,4 +187,5 @@ class PersistentTasks(object): # Create the soft singleton -PERSISTENT_TASKS = PersistentTasks() +TASK_HANDLER = TaskHandler() + diff --git a/evennia/server/server.py b/evennia/server/server.py index 7c4bf4605..d6a47c208 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -455,10 +455,10 @@ class Evennia(object): # (this also starts any that didn't yet start) ScriptDB.objects.validate(init_mode=mode) - # start the persistent tasks - from evennia.utils.persistent import PERSISTENT_TASKS - PERSISTENT_TASKS.load() - PERSISTENT_TASKS.create_delays() + # start the task handler + from evennia.scripts.taskhandler import TASK_HANDLER + TASK_HANDLER.load() + TASK_HANDLER.create_delays() # delete the temporary setting ServerConfig.objects.conf("server_restart_mode", delete=True) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index a7f47ca55..ab67421ad 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -919,7 +919,7 @@ def uses_database(name="sqlite3"): return engine == "django.db.backends.%s" % name -_PERSISTENT_TASKS = None +_TASK_HANDLER = None def delay(timedelay, callback, *args, **kwargs): """ @@ -931,9 +931,9 @@ def delay(timedelay, callback, *args, **kwargs): arguments after `timedelay` seconds. args (any, optional): Will be used as arguments to callback Kwargs: - persistent (bool, optional): should make the delay persistent - over a reboot or reload - any (any): Will be used to call the callback. + persistent (bool, optional): should make the delay persistent + over a reboot or reload + any (any): Will be used to call the callback. Returns: deferred (deferred): Will fire fire with callback after @@ -943,6 +943,8 @@ def delay(timedelay, callback, *args, **kwargs): specified here. 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, assuming they can be. The callback will be executed even after @@ -950,19 +952,11 @@ def delay(timedelay, callback, *args, **kwargs): (and server down time). """ - global _PERSISTENT_TASKS - persistent = kwargs.get("persistent", False) - if persistent: - del kwargs["persistent"] - # Do some imports here to avoid circular import and speed things up - if _PERSISTENT_TASKS is None: - from evennia.utils.persistent import PERSISTENT_TASKS as _PERSISTENT_TASKS - task_id = _PERSISTENT_TASKS.add(timedelay, callback, *args, **kwargs) - callback = _PERSISTENT_TASKS.do_task - args = [task_id] - kwargs = {} - - return task.deferLater(reactor, timedelay, callback, *args, **kwargs) + global _TASK_HANDLER + # Do some imports here to avoid circular import and speed things up + if _TASK_HANDLER is None: + from evennia.scripts.taskhandler import TASK_HANDLER as _TASK_HANDLER + return _TASK_HANDLER.add(timedelay, callback, *args, **kwargs) _TYPECLASSMODELS = None