From 97e04ee7103d94f6d4e4519ebe8b86d4fa335f7b Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 27 May 2015 22:35:05 +0200 Subject: [PATCH] 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. --- evennia/scripts/scripts.py | 53 ++++++++++++-------------- evennia/scripts/tickerhandler.py | 9 ++++- evennia/server/evennia_launcher.py | 17 ++++++--- evennia/utils/utils.py | 61 ++++++++---------------------- requirements.txt | 2 +- win_requirements.txt | 2 +- 6 files changed, 61 insertions(+), 83 deletions(-) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index 345f951845..6a8fe0f66c 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -8,7 +8,6 @@ ability to run timers. from twisted.internet.defer import Deferred, maybeDeferred from twisted.internet.task import LoopingCall from django.utils.translation import ugettext as _ -from django.conf import settings from evennia.typeclasses.models import TypeclassBase from evennia.scripts.models import ScriptDB from evennia.scripts.manager import ScriptManager @@ -40,7 +39,7 @@ class ExtendedLoopingCall(LoopingCall): interval (int): Repeat interval in seconds. now (bool, optional): Whether to start immediately or after `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`. It is used as a way to start with a variable start time after a pause. @@ -64,44 +63,40 @@ class ExtendedLoopingCall(LoopingCall): "ExtendedLoopingCall.") if interval < 0: raise ValueError, "interval must be >= 0" - self.running = True d = self.deferred = Deferred() self.starttime = self.clock.seconds() - self._expectNextCallAt = self.starttime self.interval = interval self._runAtStart = now self.callcount = max(0, count_start) + self.start_delay = start_delay if start_delay is None else max(0, start_delay) if now: + # run immediately 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: - if start_delay is not None and start_delay >= 0: - # 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() + self._scheduleFrom(self.starttime) return d 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 - 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 - super(ExtendedLoopingCall, self)._reschedule() + super(ExtendedLoopingCall, self).__call__() def force_repeat(self): """ @@ -116,22 +111,24 @@ class ExtendedLoopingCall(LoopingCall): "that was not running.") if self.call is not None: self.call.cancel() - self._expectNextCallAt = self.clock.seconds() + self.call.callback() 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: - 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 the task is not running. """ if self.running: - currentTime = self.clock.seconds() - return self._expectNextCallAt - currentTime + total_runtime = self.clock.seconds() - self.starttime + interval = self.start_delay or self.interval + return interval - (total_runtime % self.interval) return None class ScriptBase(ScriptDB): diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index 79b069cc42..fac6120f9f 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -264,7 +264,10 @@ class TickerHandler(object): 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 ticker_storage = ServerConfig.objects.conf(key=self.save_name) @@ -353,7 +356,9 @@ class TickerHandler(object): """ self.ticker_pool.stop(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: self.ticker_storage = {} self.save() diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 987667cba6..eab178b45d 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -54,8 +54,9 @@ PORTAL_RESTART = None SERVER_PY_FILE = None PORTAL_PY_FILE = None + PYTHON_MIN = '2.7' -TWISTED_MIN = '12.0' +TWISTED_MIN = '15.2.1' DJANGO_MIN = '1.8' DJANGO_REC = '1.8' @@ -265,10 +266,10 @@ ERROR_PYTHON_VERSION = \ {python_min} or higher (but not 3.x). """ -WARNING_TWISTED_VERSION = \ +ERROR_TWISTED_VERSION = \ """ - WARNING: Twisted {tversion} found. Evennia recommends - v{twisted_min} or higher." + ERROR: Twisted {tversion} found. Evennia requires + version {twisted_min} or higher. """ ERROR_NOTWISTED = \ @@ -334,6 +335,9 @@ def check_main_evennia_dependencies(): """ Checks and imports the Evennia dependencies. This must be done already before the paths are set up. + + Returns: + not_error (bool): True if no dependency error was found. """ error = False @@ -347,7 +351,8 @@ def check_main_evennia_dependencies(): import twisted tversion = twisted.version.short() 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: print ERROR_NOTWISTED error = True @@ -368,6 +373,8 @@ def check_main_evennia_dependencies(): error = True if error: sys.exit() + # return True/False if error was reported or not + return not error def set_gamedir(path): diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 7546b66997..a73f7e70fa 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -672,68 +672,37 @@ def run_async(to_execute, *args, **kwargs): 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. """ - # defining the requirements - python_min = '2.7' - twisted_min = '12.0' - django_min = '1.8' - django_rec = '1.8' + + # check main dependencies + from evennia.server.evennia_launcher import check_main_evennia_dependencies + not_error = check_main_evennia_dependencies() errstring = "" - no_error = True - - # 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 + # South is no longer used ... 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." - no_error = False + errstring += "\n ERROR: 'south' found in settings.INSTALLED_APPS. " \ + "\n South is no longer used. If this was added manually, remove it." + not_error = False # IRC support if settings.IRC_ENABLED: try: import twisted.words twisted.words # set to avoid debug info about not-used import except ImportError: - 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" - errstring += "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords." - no_error = False + errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it." \ + "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others" \ + "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords." + not_error = False errstring = errstring.strip() if errstring: mlen = max(len(line) for line in errstring.split("\n")) print "%s\n%s\n%s" % ("-"*mlen, errstring, '-'*mlen) - return no_error + return not_error def has_parent(basepath, obj): diff --git a/requirements.txt b/requirements.txt index 143ff786ba..f954de2a31 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # Evennia dependencies, for Linux/Mac platforms django >= 1.8, < 1.9 -twisted >= 12.0 +twisted >= 15.2.1 mock >= 1.0.1 pillow diff --git a/win_requirements.txt b/win_requirements.txt index 9133fffe03..2a41902909 100644 --- a/win_requirements.txt +++ b/win_requirements.txt @@ -4,6 +4,6 @@ pypiwin32 # general django >= 1.8, < 1.9 -twisted >= 12.0 +twisted >= 15.2.1 mock >= 1.0.1 pillow