Fix edge case in TaskHandler when un-pickleable callable supplied

This commit is contained in:
Griatch 2021-05-09 15:25:57 +02:00
parent cec566be79
commit bf4af8b208
2 changed files with 27 additions and 26 deletions

View file

@ -5,6 +5,7 @@ Module containing the task handler for Evennia deferred tasks, persistent or not
from datetime import datetime, timedelta
from twisted.internet import reactor
from pickle import PickleError
from twisted.internet.task import deferLater
from twisted.internet.defer import CancelledError as DefCancelledError
from evennia.server.models import ServerConfig
@ -287,18 +288,8 @@ class TaskHandler(object):
# Check if callback can be pickled. args and kwargs have been checked
safe_callback = None
try:
dbserialize(callback)
except (TypeError, AttributeError):
raise ValueError(
"the specified callback {} cannot be pickled. "
"It must be a top-level function in a module or an "
"instance method.".format(callback)
)
else:
safe_callback = callback
self.to_save[task_id] = dbserialize((date, safe_callback, args, kwargs))
self.to_save[task_id] = dbserialize((date, callback, args, kwargs))
ServerConfig.objects.conf("delayed_tasks", self.to_save)
def add(self, timedelay, callback, *args, **kwargs):
@ -318,8 +309,8 @@ class TaskHandler(object):
any (any): any additional positional arguments to send to the callback
*args: positional arguments to pass to callback.
**kwargs: keyword arguments to pass to callback.
persistent (bool, optional): persist the task (stores it).
persistent key and value is removed from kwargs it will
- persistent (bool, optional): persist the task (stores it).
Persistent key and value is removed from kwargs it will
not be passed to callback.
Returns:
@ -346,11 +337,22 @@ class TaskHandler(object):
safe_args = []
safe_kwargs = {}
# an unsaveable callback should immediately abort
try:
dbserialize(callback)
except (TypeError, AttributeError, PickleError):
raise ValueError(
"the specified callback {} cannot be pickled. "
"It must be a top-level function in a module or an "
"instance method.".format(callback)
)
return
# Check that args and kwargs contain picklable information
for arg in args:
try:
dbserialize(arg)
except (TypeError, AttributeError):
except (TypeError, AttributeError, PickleError):
log_err(
"The positional argument {} cannot be "
"pickled and will not be present in the arguments "
@ -362,7 +364,7 @@ class TaskHandler(object):
for key, value in kwargs.items():
try:
dbserialize(value)
except (TypeError, AttributeError):
except (TypeError, AttributeError, PickleError):
log_err(
"The {} keyword argument {} cannot be "
"pickled and will not be present in the arguments "

View file

@ -1030,20 +1030,19 @@ def delay(timedelay, callback, *args, **kwargs):
after `timedelay` seconds.
args (any, optional): Will be used as arguments to callback
Keyword Args:
persistent (bool, optional): should make the delay persistent
over a reboot or reload
persistent (bool, optional): Should make the delay persistent
over a reboot or reload. Defaults to False.
any (any): Will be used as keyword arguments to callback.
Returns:
deferred (deferred): Will fire with callback after
`timedelay` seconds. Note that if `timedelay()` is used in the
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.
deferred or int: If ``persistent`` kwarg is `False`, return deferred
that will fire with callback after `timedelay` seconds. Note that
if `timedelay()` is used in the 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 set, return the task's ID as an integer. This is
intended for use with ``evennia.scripts.taskhandler.TASK_HANDLER``
`.do_task` and `.remove` methods.
Note:
The task handler (`evennia.scripts.taskhandler.TASK_HANDLER`) will