Twisted min version upped to 15.2.1 due to a change in the LoopingCall infrastructure that Evennia relies on. This resolves #744 by updating the Evennia implementation accordingly.

This commit is contained in:
Griatch 2015-05-27 22:35:05 +02:00
parent ff4013a58a
commit 97e04ee710
6 changed files with 61 additions and 83 deletions

View file

@ -8,7 +8,6 @@ ability to run timers.
from twisted.internet.defer import Deferred, maybeDeferred from twisted.internet.defer import Deferred, maybeDeferred
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings
from evennia.typeclasses.models import TypeclassBase from evennia.typeclasses.models import TypeclassBase
from evennia.scripts.models import ScriptDB from evennia.scripts.models import ScriptDB
from evennia.scripts.manager import ScriptManager from evennia.scripts.manager import ScriptManager
@ -40,7 +39,7 @@ class ExtendedLoopingCall(LoopingCall):
interval (int): Repeat interval in seconds. interval (int): Repeat interval in seconds.
now (bool, optional): Whether to start immediately or after now (bool, optional): Whether to start immediately or after
`start_delay` seconds. `start_delay` seconds.
start_delay (bool: The number of seconds before starting. start_delay (int): The number of seconds before starting.
If None, wait interval seconds. Only valid if `now` is `False`. If None, wait interval seconds. Only valid if `now` is `False`.
It is used as a way to start with a variable start time It is used as a way to start with a variable start time
after a pause. after a pause.
@ -64,44 +63,40 @@ class ExtendedLoopingCall(LoopingCall):
"ExtendedLoopingCall.") "ExtendedLoopingCall.")
if interval < 0: if interval < 0:
raise ValueError, "interval must be >= 0" raise ValueError, "interval must be >= 0"
self.running = True self.running = True
d = self.deferred = Deferred() d = self.deferred = Deferred()
self.starttime = self.clock.seconds() self.starttime = self.clock.seconds()
self._expectNextCallAt = self.starttime
self.interval = interval self.interval = interval
self._runAtStart = now self._runAtStart = now
self.callcount = max(0, count_start) self.callcount = max(0, count_start)
self.start_delay = start_delay if start_delay is None else max(0, start_delay)
if now: if now:
# run immediately
self() self()
elif start_delay is not None and start_delay >= 0:
# start after some time: for this to work we need to
# trick _scheduleFrom by temporarily setting a different
# self.interval for it to check.
real_interval, self.interval = self.interval, start_delay
self._scheduleFrom(self.starttime)
# re-set the actual interval (this will be picked up
# next time it runs
self.interval = real_interval
else: else:
if start_delay is not None and start_delay >= 0: self._scheduleFrom(self.starttime)
# we set `start_delay` after the `_reschedule` call to make
# next_call_time() find it until next reschedule.
self.interval = start_delay
self._reschedule()
self.interval = interval
self.start_delay = start_delay
else:
self._reschedule()
return d return d
def __call__(self): def __call__(self):
""" """
Tick one step Tick one step. We update callcount (tracks number of calls) as
well as null start_delay (needed in order to correctly
estimate next_call_time at all times).
""" """
self.callcount += 1 self.callcount += 1
super(ExtendedLoopingCall, self).__call__()
def _reschedule(self):
"""
Handle call rescheduling including nulling `start_delay` and
stopping if number of repeats is reached.
"""
self.start_delay = None self.start_delay = None
super(ExtendedLoopingCall, self)._reschedule() super(ExtendedLoopingCall, self).__call__()
def force_repeat(self): def force_repeat(self):
""" """
@ -116,22 +111,24 @@ class ExtendedLoopingCall(LoopingCall):
"that was not running.") "that was not running.")
if self.call is not None: if self.call is not None:
self.call.cancel() self.call.cancel()
self._expectNextCallAt = self.clock.seconds() self.call.callback()
self() self()
def next_call_time(self): def next_call_time(self):
""" """
Get the next call time. Get the next call time. This also takes the eventual effect
of start_delay into account.
Returns: Returns:
next (int): The time in seconds until the next call. This next (int or None): The time in seconds until the next call. This
takes `start_delay` into account. Returns `None` if takes `start_delay` into account. Returns `None` if
the task is not running. the task is not running.
""" """
if self.running: if self.running:
currentTime = self.clock.seconds() total_runtime = self.clock.seconds() - self.starttime
return self._expectNextCallAt - currentTime interval = self.start_delay or self.interval
return interval - (total_runtime % self.interval)
return None return None
class ScriptBase(ScriptDB): class ScriptBase(ScriptDB):

View file

@ -264,7 +264,10 @@ class TickerHandler(object):
def restore(self): def restore(self):
""" """
Restore ticker_storage from database and re-initialize the handler from storage. This is triggered by the server at restart. Restore ticker_storage from database and re-initialize the
handler from storage. This is triggered by the server at
restart.
""" """
# load stored command instructions and use them to re-initialize handler # load stored command instructions and use them to re-initialize handler
ticker_storage = ServerConfig.objects.conf(key=self.save_name) ticker_storage = ServerConfig.objects.conf(key=self.save_name)
@ -353,7 +356,9 @@ class TickerHandler(object):
""" """
self.ticker_pool.stop(interval) self.ticker_pool.stop(interval)
if interval: if interval:
self.ticker_storage = dict((store_key, store_key) for store_key in self.ticker_storage if store_key[1] != interval) self.ticker_storage = dict((store_key, store_key)
for store_key in self.ticker_storage
if store_key[1] != interval)
else: else:
self.ticker_storage = {} self.ticker_storage = {}
self.save() self.save()

View file

@ -54,8 +54,9 @@ PORTAL_RESTART = None
SERVER_PY_FILE = None SERVER_PY_FILE = None
PORTAL_PY_FILE = None PORTAL_PY_FILE = None
PYTHON_MIN = '2.7' PYTHON_MIN = '2.7'
TWISTED_MIN = '12.0' TWISTED_MIN = '15.2.1'
DJANGO_MIN = '1.8' DJANGO_MIN = '1.8'
DJANGO_REC = '1.8' DJANGO_REC = '1.8'
@ -265,10 +266,10 @@ ERROR_PYTHON_VERSION = \
{python_min} or higher (but not 3.x). {python_min} or higher (but not 3.x).
""" """
WARNING_TWISTED_VERSION = \ ERROR_TWISTED_VERSION = \
""" """
WARNING: Twisted {tversion} found. Evennia recommends ERROR: Twisted {tversion} found. Evennia requires
v{twisted_min} or higher." version {twisted_min} or higher.
""" """
ERROR_NOTWISTED = \ ERROR_NOTWISTED = \
@ -334,6 +335,9 @@ def check_main_evennia_dependencies():
""" """
Checks and imports the Evennia dependencies. This must be done Checks and imports the Evennia dependencies. This must be done
already before the paths are set up. already before the paths are set up.
Returns:
not_error (bool): True if no dependency error was found.
""" """
error = False error = False
@ -347,7 +351,8 @@ def check_main_evennia_dependencies():
import twisted import twisted
tversion = twisted.version.short() tversion = twisted.version.short()
if tversion < TWISTED_MIN: if tversion < TWISTED_MIN:
print WARNING_TWISTED_VERSION.format(tversion=tversion, twisted_min=TWISTED_MIN) print ERROR_TWISTED_VERSION.format(tversion=tversion, twisted_min=TWISTED_MIN)
error = True
except ImportError: except ImportError:
print ERROR_NOTWISTED print ERROR_NOTWISTED
error = True error = True
@ -368,6 +373,8 @@ def check_main_evennia_dependencies():
error = True error = True
if error: if error:
sys.exit() sys.exit()
# return True/False if error was reported or not
return not error
def set_gamedir(path): def set_gamedir(path):

View file

@ -672,68 +672,37 @@ def run_async(to_execute, *args, **kwargs):
def check_evennia_dependencies(): def check_evennia_dependencies():
""" """
Checks the versions of Evennia's dependencies. Checks the versions of Evennia's dependencies including making
some checks for runtime libraries
Returns False if a show-stopping version mismatch is found. Returns False if a show-stopping version mismatch is found.
""" """
# defining the requirements
python_min = '2.7' # check main dependencies
twisted_min = '12.0' from evennia.server.evennia_launcher import check_main_evennia_dependencies
django_min = '1.8' not_error = check_main_evennia_dependencies()
django_rec = '1.8'
errstring = "" errstring = ""
no_error = True # South is no longer used ...
# Python
pversion = ".".join(str(num) for num in sys.version_info if type(num) == int)
if pversion < python_min:
errstring += "\n ERROR: Python %s used. Evennia requires version %s or higher (but not 3.x)." % (pversion, python_min)
no_error = False
# Twisted
try:
import twisted
tversion = twisted.version.short()
if tversion < twisted_min:
errstring += "\n WARNING: Twisted %s found. Evennia recommends v%s or higher." % (twisted.version.short(), twisted_min)
except ImportError:
errstring += "\n ERROR: Twisted does not seem to be installed."
no_error = False
# Django
try:
import django
dversion = ".".join(str(num) for num in django.VERSION if type(num) == int)
dversion_main = ".".join(dversion.split(".")[:2]) # only the main version (1.5, not 1.5.4.0)
if dversion < django_min:
errstring += "\n ERROR: Django %s found. Evennia requires version %s or higher." % (dversion, django_min)
no_error = False
elif django_min <= dversion < django_rec:
errstring += "\n NOTE: Django %s found. This will work, but v%s is recommended for production." % (dversion, django_rec)
elif django_rec < dversion_main:
errstring += "\n NOTE: Django %s found. This is newer than Evennia's recommended version (v%s). It will"
errstring += "\n probably work, but may be new enough not to be fully tested yet. Report any issues." % (dversion, django_rec)
except ImportError:
errstring += "\n ERROR: Django does not seem to be installed."
no_error = False
# South
if 'south' in settings.INSTALLED_APPS: if 'south' in settings.INSTALLED_APPS:
errstring += "\n ERROR: 'south' found in settings.INSTALLED_APPS. South is no longer used. If this was added manually, remove it." errstring += "\n ERROR: 'south' found in settings.INSTALLED_APPS. " \
no_error = False "\n South is no longer used. If this was added manually, remove it."
not_error = False
# IRC support # IRC support
if settings.IRC_ENABLED: if settings.IRC_ENABLED:
try: try:
import twisted.words import twisted.words
twisted.words # set to avoid debug info about not-used import twisted.words # set to avoid debug info about not-used import
except ImportError: except ImportError:
errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it." errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it." \
errstring += "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others" "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others" \
errstring += "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords." "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords."
no_error = False not_error = False
errstring = errstring.strip() errstring = errstring.strip()
if errstring: if errstring:
mlen = max(len(line) for line in errstring.split("\n")) mlen = max(len(line) for line in errstring.split("\n"))
print "%s\n%s\n%s" % ("-"*mlen, errstring, '-'*mlen) print "%s\n%s\n%s" % ("-"*mlen, errstring, '-'*mlen)
return no_error return not_error
def has_parent(basepath, obj): def has_parent(basepath, obj):

View file

@ -1,6 +1,6 @@
# Evennia dependencies, for Linux/Mac platforms # Evennia dependencies, for Linux/Mac platforms
django >= 1.8, < 1.9 django >= 1.8, < 1.9
twisted >= 12.0 twisted >= 15.2.1
mock >= 1.0.1 mock >= 1.0.1
pillow pillow

View file

@ -4,6 +4,6 @@
pypiwin32 pypiwin32
# general # general
django >= 1.8, < 1.9 django >= 1.8, < 1.9
twisted >= 12.0 twisted >= 15.2.1
mock >= 1.0.1 mock >= 1.0.1
pillow pillow