task handler update
updating taskhandler.py before updating unit tests. All evennia unit tests pass.
This commit is contained in:
parent
3cc14e2e4c
commit
84193dd9a7
2 changed files with 224 additions and 293 deletions
|
|
@ -15,9 +15,7 @@ TASK_HANDLER = None
|
||||||
|
|
||||||
|
|
||||||
def handle_error(*args, **kwargs):
|
def handle_error(*args, **kwargs):
|
||||||
"""
|
"""Handle errors withing deferred objects."""
|
||||||
Handle errors withing deferred objects.
|
|
||||||
"""
|
|
||||||
for arg in args:
|
for arg in args:
|
||||||
# suppress cancel errors
|
# suppress cancel errors
|
||||||
if arg.type == DefCancelledError:
|
if arg.type == DefCancelledError:
|
||||||
|
|
@ -25,10 +23,165 @@ def handle_error(*args, **kwargs):
|
||||||
raise arg
|
raise arg
|
||||||
|
|
||||||
|
|
||||||
|
class TaskHandlerTask:
|
||||||
|
"""A object to represent a single TaskHandler task.
|
||||||
|
|
||||||
|
Instance Attributes:
|
||||||
|
task_id (int): the global id for this task
|
||||||
|
deferred (deferred): a reference to this task's deferred
|
||||||
|
Propert Attributes:
|
||||||
|
paused (bool): check if the deferral of a task has been paused.
|
||||||
|
called(self): A task attribute to check if the deferral of a task has been called.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
pause(): Pause the callback of a task.
|
||||||
|
unpause(): Process all callbacks made since pause() was called.
|
||||||
|
do_task(): Execute the task (call its callback).
|
||||||
|
call(): Call the callback of this task.
|
||||||
|
remove(): Remove a task without executing it.
|
||||||
|
cancel(): Stop a task from automatically executing.
|
||||||
|
active(): Check if a task is active (has not been called yet).
|
||||||
|
exists(): Check if a task exists.
|
||||||
|
get_id(): Returns the global id for this task. For use with
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, task_id):
|
||||||
|
self.task_id = task_id
|
||||||
|
self.deferred = TASK_HANDLER.get_deferred(task_id)
|
||||||
|
|
||||||
|
def get_deferred(self):
|
||||||
|
"""Return the instance of the deferred the task id is using.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An instance of a deferral or False if there is no task with the id.
|
||||||
|
None is returned if there is no deferral affiliated with this id.
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.get_deferred(self.task_id)
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
"""Pause the callback of a task.
|
||||||
|
To resume use TaskHandlerTask.unpause
|
||||||
|
"""
|
||||||
|
d = self.deferred
|
||||||
|
if d:
|
||||||
|
d.pause()
|
||||||
|
|
||||||
|
def unpause(self):
|
||||||
|
"""Process all callbacks made since pause() was called."""
|
||||||
|
d = self.deferred
|
||||||
|
if d:
|
||||||
|
d.unpause()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paused(self):
|
||||||
|
"""A task attribute to check if the deferral of a task has been paused.
|
||||||
|
|
||||||
|
This exists to mock usage of a twisted deferred object.
|
||||||
|
|
||||||
|
This will return None if the deferred object for the task does not
|
||||||
|
exist or if the task no longer exists.
|
||||||
|
"""
|
||||||
|
d = self.deferred
|
||||||
|
if d:
|
||||||
|
return d.paused
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def do_task(self):
|
||||||
|
"""Execute the task (call its callback).
|
||||||
|
If calling before timedelay cancel the deferral affliated to this task.
|
||||||
|
Remove the task from the dictionary of current tasks on a successful
|
||||||
|
callback.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool or any: Set to `False` if the task does not exist in task
|
||||||
|
handler. Otherwise it will be the return of the task's callback.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.do_task(self.task_id)
|
||||||
|
|
||||||
|
def call(self):
|
||||||
|
"""Call the callback of a task.
|
||||||
|
Leave the task unaffected otherwise.
|
||||||
|
This does not use the task's deferred instance.
|
||||||
|
The only requirement is that the task exist in task handler.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool or any: Set to `False` if the task does not exist in task
|
||||||
|
handler. Otherwise it will be the return of the task's callback.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.call_task(self.task_id)
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
"""Remove a task without executing it.
|
||||||
|
Deletes the instance of the task's deferral.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the removal completed successfully.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.remove(self.task_id)
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
"""Stop a task from automatically executing.
|
||||||
|
This will not remove the task.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the cancel completed successfully.
|
||||||
|
False if the cancel did not complete successfully.
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.cancel(self.task_id)
|
||||||
|
|
||||||
|
def active(self):
|
||||||
|
"""Check if a task is active (has not been called yet).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if a task is active (has not been called yet). False if
|
||||||
|
it is not (has been called) or if the task does not exist.
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.active(self.task_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def called(self):
|
||||||
|
"""
|
||||||
|
A task attribute to check if the deferral of a task has been called.
|
||||||
|
|
||||||
|
This exists to mock usage of a twisted deferred object.
|
||||||
|
It will not set to false if Task.call has been called.
|
||||||
|
|
||||||
|
"""
|
||||||
|
d = self.deferred
|
||||||
|
if d:
|
||||||
|
return d.called
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def exists(self):
|
||||||
|
"""Check if a task exists.
|
||||||
|
Most task handler methods check for existence for you.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: Tru the task exists False if it does not.
|
||||||
|
"""
|
||||||
|
return TASK_HANDLER.exists(self.task_id)
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
""" Returns the global id for this task. For use with
|
||||||
|
`evennia.scripts.taskhandler.TASK_HANDLER`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
task_id (int): global task id for this task.
|
||||||
|
"""
|
||||||
|
return self.task_id
|
||||||
|
|
||||||
|
|
||||||
class TaskHandler(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
|
When `utils.delay` is called, the task handler is used to create
|
||||||
the task.
|
the task.
|
||||||
|
|
@ -53,9 +206,8 @@ class TaskHandler(object):
|
||||||
def load(self):
|
def load(self):
|
||||||
"""Load from the ServerConfig.
|
"""Load from the ServerConfig.
|
||||||
|
|
||||||
Note:
|
This should be automatically called when Evennia starts.
|
||||||
This should be automatically called when Evennia starts.
|
It populates `self.tasks` according to the ServerConfig.
|
||||||
It populates `self.tasks` according to the ServerConfig.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
to_save = False
|
to_save = False
|
||||||
|
|
@ -76,7 +228,7 @@ class TaskHandler(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
callback = getattr(obj, method)
|
callback = getattr(obj, method)
|
||||||
self.tasks[task_id] = date, callback, args, kwargs, True, None
|
self.tasks[task_id] = (date, callback, args, kwargs, True, None)
|
||||||
|
|
||||||
if self.stale_timeout > 0: # cleanup stale tasks.
|
if self.stale_timeout > 0: # cleanup stale tasks.
|
||||||
self.clean_stale_tasks()
|
self.clean_stale_tasks()
|
||||||
|
|
@ -84,8 +236,7 @@ class TaskHandler(object):
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def clean_stale_tasks(self):
|
def clean_stale_tasks(self):
|
||||||
"""
|
"""remove uncalled but canceled from task handler.
|
||||||
remove uncalled but canceled from task handler.
|
|
||||||
|
|
||||||
By default this will not occur until a canceled task
|
By default this will not occur until a canceled task
|
||||||
has been uncalled for 60 second after the time it should have been called.
|
has been uncalled for 60 second after the time it should have been called.
|
||||||
|
|
@ -139,35 +290,30 @@ class TaskHandler(object):
|
||||||
ServerConfig.objects.conf("delayed_tasks", self.to_save)
|
ServerConfig.objects.conf("delayed_tasks", self.to_save)
|
||||||
|
|
||||||
def add(self, timedelay, callback, *args, **kwargs):
|
def add(self, timedelay, callback, *args, **kwargs):
|
||||||
"""Add a new persistent task in the configuration.
|
"""Add a new task.
|
||||||
|
|
||||||
|
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.
|
||||||
|
For persisten tasks do not use memory references in the callback
|
||||||
|
function or arguments. After a restart those memory references are no
|
||||||
|
longer accurate.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timedelay (int or float): time in sedconds before calling the callback.
|
timedelay (int or float): time in sedconds before calling the callback.
|
||||||
callback (function or instance method): the callback itself
|
callback (function or instance method): the callback itself
|
||||||
any (any): any additional positional arguments to send to the callback
|
any (any): any additional positional arguments to send to the callback
|
||||||
|
*args: positional arguments to pass to callback.
|
||||||
Keyword Args:
|
**kwargs: key word arguments to pass to callback.
|
||||||
persistent (bool, optional): persist the task (stores it).
|
persistent (bool, optional): persist the task (stores it).
|
||||||
Add will return the task's id for use with the global TASK_HANDLER.
|
persisten key and value is removed from kwargs it will
|
||||||
any (any): additional keyword arguments to send to the callback
|
not be passed to callback.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
task_id (int), the task's id intended for use with this class.
|
TaskHandlerTask: An object to represent a task.
|
||||||
False, if the task has completed before addition finishes.
|
Reference evennia.scripts.taskhandler.Task for complete details.
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
"""
|
||||||
# set the completion time
|
# set the completion time
|
||||||
# Only used on persistent tasks after a restart
|
# Only used on persistent tasks after a restart
|
||||||
|
|
@ -224,7 +370,7 @@ class TaskHandler(object):
|
||||||
d = deferLater(self.clock, timedelay, callback, *args, **kwargs)
|
d = deferLater(self.clock, timedelay, callback, *args, **kwargs)
|
||||||
d.addErrback(handle_error)
|
d.addErrback(handle_error)
|
||||||
|
|
||||||
# some tasks may complete before the deferal can be added
|
# some tasks may complete before the deferred can be added
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
task = self.tasks.get(task_id)
|
task = self.tasks.get(task_id)
|
||||||
task = list(task)
|
task = list(task)
|
||||||
|
|
@ -235,21 +381,18 @@ class TaskHandler(object):
|
||||||
return False
|
return False
|
||||||
if self.stale_timeout > 0:
|
if self.stale_timeout > 0:
|
||||||
self.clean_stale_tasks()
|
self.clean_stale_tasks()
|
||||||
return Task(task_id)
|
return TaskHandlerTask(task_id)
|
||||||
|
|
||||||
def exists(self, task_id):
|
def exists(self, task_id):
|
||||||
"""
|
"""Check if a task exists.
|
||||||
Test if a task exists.
|
Most task handler methods check for existence for you.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_id (int): an existing task ID.
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True (bool): if the task exists.
|
bool: Tru the task exists False if it does not.
|
||||||
False (bool): if the task does not exist.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
Most task handler methods check for existence for you.
|
|
||||||
"""
|
"""
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
return True
|
return True
|
||||||
|
|
@ -257,51 +400,37 @@ class TaskHandler(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def active(self, task_id):
|
def active(self, task_id):
|
||||||
"""
|
"""Check if a task is active (has not been called yet).
|
||||||
Check if a task is active (has not been called yet).
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_id (int): an existing task ID.
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True (bool): If a task is active (has not been called yet).
|
bool: True if a task is active (has not been called yet). False if
|
||||||
False (bool): if the task
|
it is not (has been called) or if the task does not exist.
|
||||||
is not active (has already been called),
|
|
||||||
does not exist
|
|
||||||
"""
|
"""
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
# if the task has not been run, cancel it
|
# if the task has not been run, cancel it
|
||||||
d = self.get_deferred(task_id)
|
deferred = self.get_deferred(task_id)
|
||||||
if d: # it is remotely possible for a task to not have a deferral
|
return not (deferred and deferred.called)
|
||||||
if d.called:
|
|
||||||
return False
|
|
||||||
else: # the callback has not been called yet.
|
|
||||||
return True
|
|
||||||
else: # this task has no deferral, and could not have been called
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def cancel(self, task_id):
|
def cancel(self, task_id):
|
||||||
"""
|
"""Stop a task from automatically executing.
|
||||||
Stop a task from automatically executing.
|
|
||||||
This will not remove the task.
|
This will not remove the task.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_id (int): an existing task ID.
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True (bool): if the removal completed successfully.
|
bool: True if the cancel completed successfully.
|
||||||
False (bool): if the task:
|
False if the cancel did not complete successfully.
|
||||||
does not exist,
|
|
||||||
has already run,
|
|
||||||
does not have a deferral instance created for the task.
|
|
||||||
None, if there was a raised exception
|
|
||||||
"""
|
"""
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
# if the task has not been run, cancel it
|
# if the task has not been run, cancel it
|
||||||
d = self.get_deferred(task_id)
|
d = self.get_deferred(task_id)
|
||||||
if d: # it is remotely possible for a task to not have a deferral
|
if d: # it is remotely possible for a task to not have a deferred
|
||||||
if d.called:
|
if d.called:
|
||||||
return False
|
return False
|
||||||
else: # the callback has not been called yet.
|
else: # the callback has not been called yet.
|
||||||
|
|
@ -313,17 +442,14 @@ class TaskHandler(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def remove(self, task_id):
|
def remove(self, task_id):
|
||||||
"""
|
"""Remove a task without executing it.
|
||||||
Remove a task without executing it.
|
|
||||||
Deletes the instance of the task's deferral.
|
Deletes the instance of the task's deferral.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_id (int): an existing task ID.
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True (bool): if the removal completed successfully or if the a
|
bool: True if the removal completed successfully.
|
||||||
task with the id does not exist.
|
|
||||||
None: if there was a raised exception
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
d = None
|
d = None
|
||||||
|
|
@ -341,33 +467,30 @@ class TaskHandler(object):
|
||||||
del d
|
del d
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def remove_all(self, save=True, cancel=True):
|
def clear(self, save=True, cancel=True):
|
||||||
"""
|
"""clear all tasks.
|
||||||
Remove all tasks.
|
|
||||||
By default tasks are canceled and removed from the database also.
|
By default tasks are canceled and removed from the database also.
|
||||||
|
|
||||||
Arguments:
|
Args:
|
||||||
save=True (bool): Should changes to persistent tasks be saved to database.
|
save=True (bool): Should changes to persistent tasks be saved to database.
|
||||||
cancel=True (bool): Cancel scheduled tasks before removing it from task handler.
|
cancel=True (bool): Cancel scheduled tasks before removing it from task handler.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True (bool): if the removal completed successfully.
|
True (bool): if the removal completed successfully.
|
||||||
"""
|
"""
|
||||||
tasks_ids = tuple(self.tasks.keys())
|
if self.tasks:
|
||||||
for task_id in tasks_ids:
|
for task_id in self.tasks.keys():
|
||||||
if cancel:
|
if cancel:
|
||||||
self.cancel(task_id)
|
self.cancel(task_id)
|
||||||
del self.tasks[task_id]
|
self.tasks = {}
|
||||||
tasks_ids = tuple(self.to_save.keys())
|
if self.to_save:
|
||||||
for task_id in tasks_ids:
|
self.to_save = {}
|
||||||
del self.to_save[task_id]
|
|
||||||
if save:
|
if save:
|
||||||
self.save()
|
self.save()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def call_task(self, task_id):
|
def call_task(self, task_id):
|
||||||
"""
|
"""Call the callback of a task.
|
||||||
Call the callback of a task.
|
|
||||||
Leave the task unaffected otherwise.
|
Leave the task unaffected otherwise.
|
||||||
This does not use the task's deferred instance.
|
This does not use the task's deferred instance.
|
||||||
The only requirement is that the task exist in task handler.
|
The only requirement is that the task exist in task handler.
|
||||||
|
|
@ -376,8 +499,9 @@ class TaskHandler(object):
|
||||||
task_id (int): an existing task ID.
|
task_id (int): an existing task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
False (bool): if the task does not exist in task handler.
|
bool or any: Set to `False` if the task does not exist in task
|
||||||
?: The return of the task's callback.
|
handler. Otherwise it will be the return of the task's callback.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
date, callback, args, kwargs, persistent, d = self.tasks.get(task_id)
|
date, callback, args, kwargs, persistent, d = self.tasks.get(task_id)
|
||||||
|
|
@ -386,8 +510,7 @@ class TaskHandler(object):
|
||||||
return callback(*args, **kwargs)
|
return callback(*args, **kwargs)
|
||||||
|
|
||||||
def do_task(self, task_id):
|
def do_task(self, task_id):
|
||||||
"""
|
"""Execute the task (call its callback).
|
||||||
Execute the task (call its callback).
|
|
||||||
If calling before timedelay cancel the deferral affliated to this task.
|
If calling before timedelay cancel the deferral affliated to this task.
|
||||||
Remove the task from the dictionary of current tasks on a successful
|
Remove the task from the dictionary of current tasks on a successful
|
||||||
callback.
|
callback.
|
||||||
|
|
@ -396,16 +519,8 @@ class TaskHandler(object):
|
||||||
task_id (int): a valid task ID.
|
task_id (int): a valid task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
False (bool): if the:
|
bool or any: Set to `False` if the task does not exist in task
|
||||||
task no longer exists,
|
handler. Otherwise it will be the return of the task's callback.
|
||||||
has no affliated instance of deferral
|
|
||||||
The return of the callback passed on task creation.
|
|
||||||
This makes it possible for the callback to also return False
|
|
||||||
None: if there was a raised exception
|
|
||||||
|
|
||||||
Note:
|
|
||||||
On a successful call the task will be removed from the dictionary
|
|
||||||
of current tasks.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
callback_return = False
|
callback_return = False
|
||||||
|
|
@ -430,214 +545,30 @@ class TaskHandler(object):
|
||||||
task_id (int): a valid task ID.
|
task_id (int): a valid task ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An instance of a deferral or False if there is no task with the id.
|
deffered or None: An instance of a deferred or None if there is no
|
||||||
None is returned if there is no deferral affiliated with this id.
|
task with the id. None is returned if there is no deferral
|
||||||
|
affiliated with this id.
|
||||||
"""
|
"""
|
||||||
if task_id in self.tasks:
|
if task_id in self.tasks:
|
||||||
return self.tasks[task_id][5]
|
return self.tasks[task_id][5]
|
||||||
else:
|
else:
|
||||||
return False
|
return None
|
||||||
|
|
||||||
def create_delays(self):
|
def create_delays(self):
|
||||||
"""Create the delayed tasks for the persistent tasks.
|
"""Create the delayed tasks for the persistent tasks.
|
||||||
|
This method should be automatically called when Evennia starts.
|
||||||
Note:
|
|
||||||
This method should be automatically called when Evennia starts.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
for task_id, (date, callbac, args, kwargs, _, _) in self.tasks.items():
|
for task_id, (date, callback, args, kwargs, _, _) in self.tasks.items():
|
||||||
self.tasks[task_id] = date, callbac, args, kwargs, True, None
|
self.tasks[task_id] = date, callback, args, kwargs, True, None
|
||||||
seconds = max(0, (date - now).total_seconds())
|
seconds = max(0, (date - now).total_seconds())
|
||||||
d = deferLater(self.clock, seconds, self.do_task, task_id)
|
d = deferLater(self.clock, seconds, self.do_task, task_id)
|
||||||
d.addErrback(handle_error)
|
d.addErrback(handle_error)
|
||||||
# some tasks may complete before the deferal can be added
|
# some tasks may complete before the deferred can be added
|
||||||
if self.tasks.get(task_id, False):
|
if self.tasks.get(task_id, False):
|
||||||
self.tasks[task_id] = date, callbac, args, kwargs, True, d
|
self.tasks[task_id] = date, callback, args, kwargs, True, d
|
||||||
|
|
||||||
|
|
||||||
# Create the soft singleton
|
# Create the soft singleton
|
||||||
TASK_HANDLER = TaskHandler()
|
TASK_HANDLER = TaskHandler()
|
||||||
|
|
||||||
|
|
||||||
class Task:
|
|
||||||
"""
|
|
||||||
A object to represent a single TaskHandler task.
|
|
||||||
|
|
||||||
Instance Attributes:
|
|
||||||
task_id (int): the global id for this task
|
|
||||||
deferred (deferred): a reference to this task's deferred
|
|
||||||
Propert Attributes:
|
|
||||||
paused (bool): check if the deferral of a task has been paused.
|
|
||||||
called(self): A task attribute to check if the deferral of a task has been called.
|
|
||||||
|
|
||||||
Methods:
|
|
||||||
pause(): Pause the callback of a task.
|
|
||||||
unpause(): Process all callbacks made since pause() was called.
|
|
||||||
do_task(): Execute the task (call its callback).
|
|
||||||
call(): Call the callback of this task.
|
|
||||||
remove(): Remove a task without executing it.
|
|
||||||
cancel(): Stop a task from automatically executing.
|
|
||||||
active(): Check if a task is active (has not been called yet).
|
|
||||||
exists(): Check if a task exists.
|
|
||||||
get_id(): Returns the global id for this task. For use with
|
|
||||||
"""
|
|
||||||
def __init__(self, task_id):
|
|
||||||
self.task_id = task_id
|
|
||||||
self.deferred = TASK_HANDLER.get_deferred(task_id)
|
|
||||||
|
|
||||||
def get_deferred(self):
|
|
||||||
"""
|
|
||||||
Return the instance of the deferred the task id is using.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
An instance of a deferral or False if there is no task with the id.
|
|
||||||
None is returned if there is no deferral affiliated with this id.
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.get_deferred(self.task_id)
|
|
||||||
|
|
||||||
def pause(self):
|
|
||||||
"""
|
|
||||||
Pause the callback of a task.
|
|
||||||
To resume use Task.unpause
|
|
||||||
"""
|
|
||||||
d = self.deferred
|
|
||||||
if d:
|
|
||||||
d.pause()
|
|
||||||
|
|
||||||
def unpause(self):
|
|
||||||
"""
|
|
||||||
Process all callbacks made since pause() was called.
|
|
||||||
"""
|
|
||||||
d = self.deferred
|
|
||||||
if d:
|
|
||||||
d.unpause()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def paused(self):
|
|
||||||
"""
|
|
||||||
A task attribute to check if the deferral of a task has been paused.
|
|
||||||
|
|
||||||
This exists to mock usage of a twisted deferred object.
|
|
||||||
|
|
||||||
This will return None if the deferred object for the task does not
|
|
||||||
exist or if the task no longer exists.
|
|
||||||
"""
|
|
||||||
d = self.deferred
|
|
||||||
if d:
|
|
||||||
return d.paused
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def do_task(self):
|
|
||||||
"""
|
|
||||||
Execute the task (call its callback).
|
|
||||||
If calling before timedelay cancel the deferral affliated to this task.
|
|
||||||
Remove the task from the dictionary of current tasks on a successful
|
|
||||||
callback.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
False (bool): if the:
|
|
||||||
task no longer exists,
|
|
||||||
has no affliated instance of deferral
|
|
||||||
The return of the callback passed on task creation.
|
|
||||||
This makes it possible for the callback to also return False
|
|
||||||
None: if there was a raised exception
|
|
||||||
|
|
||||||
Note:
|
|
||||||
On a successful call the task will be removed from the dictionary
|
|
||||||
of current tasks.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.do_task(self.task_id)
|
|
||||||
|
|
||||||
def call(self):
|
|
||||||
"""
|
|
||||||
Call the callback of this task.
|
|
||||||
Leave the task unaffected otherwise.
|
|
||||||
This does not use the task's deferred instance.
|
|
||||||
The only requirement is that the task exist in task handler.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
False (bool): if the task does not exist in task handler.
|
|
||||||
?: The return of the task's callback.
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.call_task(self.task_id)
|
|
||||||
|
|
||||||
def remove(self):
|
|
||||||
"""
|
|
||||||
Remove a task without executing it.
|
|
||||||
Deletes the instance of the task's deferral.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True (bool): if the removal completed successfully or if the a
|
|
||||||
task with the id does not exist.
|
|
||||||
None: if there was a raised exception
|
|
||||||
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.remove(self.task_id)
|
|
||||||
|
|
||||||
def cancel(self):
|
|
||||||
"""
|
|
||||||
Stop a task from automatically executing.
|
|
||||||
This will not remove the task.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True (bool): if the removal completed successfully.
|
|
||||||
False (bool): if the task:
|
|
||||||
does not exist,
|
|
||||||
has already run,
|
|
||||||
does not have a deferral instance created for the task.
|
|
||||||
None, if there was a raised exception
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.cancel(self.task_id)
|
|
||||||
|
|
||||||
def active(self):
|
|
||||||
"""
|
|
||||||
Check if a task is active (has not been called yet).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True (bool): If a task is active (has not been called yet).
|
|
||||||
False (bool): if the task
|
|
||||||
is not active (has already been called),
|
|
||||||
does not exist
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.active(self.task_id)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def called(self):
|
|
||||||
"""
|
|
||||||
A task attribute to check if the deferral of a task has been called.
|
|
||||||
|
|
||||||
This exists to mock usage of a twisted deferred object.
|
|
||||||
It will not set to false if Task.call has been called.
|
|
||||||
|
|
||||||
"""
|
|
||||||
d = self.deferred
|
|
||||||
if d:
|
|
||||||
return d.called
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
"""
|
|
||||||
Test if a task exists.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True (bool): if the task exists.
|
|
||||||
False (bool): if the task does not exist.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
Most task handler methods check for existence for you.
|
|
||||||
"""
|
|
||||||
return TASK_HANDLER.exists(self.task_id)
|
|
||||||
|
|
||||||
def get_id(self):
|
|
||||||
"""
|
|
||||||
Returns the global id for this task. For use with
|
|
||||||
`evennia.scripts.taskhandler.TASK_HANDLER`.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
task_id (int): global task id for this task.
|
|
||||||
"""
|
|
||||||
return self.task_id
|
|
||||||
|
|
|
||||||
|
|
@ -463,23 +463,23 @@ class TestDelay(EvenniaTest):
|
||||||
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
|
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
|
||||||
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
|
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
|
||||||
self.assertEqual(self.char1.ndb.dummy_var, False)
|
self.assertEqual(self.char1.ndb.dummy_var, False)
|
||||||
_TASK_HANDLER.remove_all()
|
_TASK_HANDLER.clear()
|
||||||
self.char1.ndb.dummy_var = False
|
self.char1.ndb.dummy_var = False
|
||||||
_TASK_HANDLER._now = False
|
_TASK_HANDLER._now = False
|
||||||
# replicate a restart
|
# replicate a restart
|
||||||
_TASK_HANDLER.remove_all()
|
_TASK_HANDLER.clear()
|
||||||
_TASK_HANDLER.save()
|
_TASK_HANDLER.save()
|
||||||
self.assertFalse(_TASK_HANDLER.tasks)
|
self.assertFalse(_TASK_HANDLER.tasks)
|
||||||
self.assertFalse(_TASK_HANDLER.to_save)
|
self.assertFalse(_TASK_HANDLER.to_save)
|
||||||
# create a persistent task.
|
# create a persistent task.
|
||||||
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
|
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
|
||||||
_TASK_HANDLER.save()
|
_TASK_HANDLER.save()
|
||||||
_TASK_HANDLER.remove_all(False) # remove all tasks, do not save this change.
|
_TASK_HANDLER.clear(False) # remove all tasks, do not save this change.
|
||||||
_TASK_HANDLER.clock.advance(timedelay) # advance twisted reactor time past callback time
|
_TASK_HANDLER.clock.advance(timedelay) # advance twisted reactor time past callback time
|
||||||
self.assertEqual(self.char1.ndb.dummy_var, False) # task has not run
|
self.assertEqual(self.char1.ndb.dummy_var, False) # task has not run
|
||||||
_TASK_HANDLER.load()
|
_TASK_HANDLER.load()
|
||||||
_TASK_HANDLER.create_delays()
|
_TASK_HANDLER.create_delays()
|
||||||
_TASK_HANDLER.clock.advance(timedelay) # Clock must advance to trigger, even if past timedelay
|
_TASK_HANDLER.clock.advance(timedelay) # Clock must advance to trigger, even if past timedelay
|
||||||
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
|
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
|
||||||
_TASK_HANDLER.remove_all()
|
_TASK_HANDLER.clear()
|
||||||
self.char1.ndb.dummy_var = False
|
self.char1.ndb.dummy_var = False
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue