[Zope3-checkins] SVN: Zope3/branches/srichter-twisted-integration/ First attempt at a twisted-based scheduler. It seems to work well. :-)

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Apr 28 13:53:55 EDT 2005


Log message for revision 30213:
  First attempt at a twisted-based scheduler. It seems to work well. :-)
  

Changed:
  A   Zope3/branches/srichter-twisted-integration/package-includes/scheduler-configure.zcml
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/README.txt
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/__init__.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/configure.zcml
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/cron.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/interfaces.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/loop.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/manager.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/scheduler-configure.zcml
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/task.py
  A   Zope3/branches/srichter-twisted-integration/src/scheduler/tests.py

-=-
Added: Zope3/branches/srichter-twisted-integration/package-includes/scheduler-configure.zcml
===================================================================
--- Zope3/branches/srichter-twisted-integration/package-includes/scheduler-configure.zcml	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/package-includes/scheduler-configure.zcml	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1 @@
+<include package="scheduler" />
\ No newline at end of file


Property changes on: Zope3/branches/srichter-twisted-integration/package-includes/scheduler-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/README.txt
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/README.txt	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/README.txt	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,169 @@
+==================
+The Task Scheduler
+==================
+
+
+  >>> def callable(*args, **kw):
+  ...     print 'I have been called.'
+
+
+The API
+-------
+
+From a high-level point of view, the developer starts out by developing their
+own or us an existing task component:
+
+  >>> from scheduler import task
+  >>> from twisted.internet import reactor
+
+  >>> class MyTask(task.Task):
+  ...
+  ...     def computeDelayToNextCall(self):
+  ...         return 88
+
+  >>> mytask = MyTask(callable, ('arg1', 'arg2'), {'kw1': 1, 'kw2': 2})
+
+As you can see, you only need to implement the ``computeDelayToNextCall()``
+method that used by the rescheduling mechism to determine the next time of
+execution. It is up to the developer to compute the delay, which is specified
+in seconds. Based on the system you are running on, a float for the delay
+allows you to specify sub-second intervals.
+
+So, now we have a task, but it has not been started:
+
+  >>> mytask.running
+  False
+  >>> mytask.count
+
+Now that you have a task component, we register it as a utility providing
+``ITask``.
+
+  >>> from zope.app.testing import ztapi
+  >>> from scheduler import interfaces
+  >>> ztapi.provideUtility(interfaces.ITask, mytask, 'MyTask')
+
+When Zope is started, an event listener will start all tasks. 
+
+  >>> from scheduler import manager
+  >>> manager.startAllTasks(None)
+
+  >>> mytask.running
+  True
+  >>> mytask.count
+  0
+
+At this time the first task execution should also be scheduled:
+
+  >>> from twisted.internet import reactor
+  >>> delayedCall = reactor.getDelayedCalls()[-1]
+
+  >>> delayedCall.func
+  MyTask(callable, *('arg1', 'arg2'), **{'kw1': 1, 'kw2': 2})
+
+After the time passes (we just do not have time to wait 88 seconds ;-), the
+task will be executed:
+
+  >>> mytask()
+  I have been called.
+
+If you want to stop a task, then you simply call ``stop()``:
+
+  >>> mytask.stop()
+  >>> mytask.running
+  False
+
+For more documentation and information on a task's features, see Twisted's
+``LoopingCall`` class, which is used as a base class for ``Task``.
+
+
+Loop Task
+---------
+
+A looping task is a task that gets executed in evenly-spaced time
+intervals. The interval can be specified in the final argument of the
+constructor:
+
+  >>> from scheduler import loop
+  >>> looptask = loop.LoopTask(callable, interval=30)
+
+The interval is specified in seconds, so in our case it is 30 seconds. The
+time interval is publically available
+
+  >>> looptask.timeInterval
+  30
+
+And when the delay is calculated, it should simply return the time interval:
+
+  >>> looptask.computeDelayToNextCall()
+  30
+
+
+Cron Task
+---------
+
+The cron-based task allows descriptions of scheduled times via a crontab-like
+format, where one can specify lists of minutes, hours, days, weekdays and
+months. To properly test the calculation of the delay, we have to overridde
+the time-module's ``time()`` function to return a constant time (in seconds).
+
+  >>> import time
+  >>> orig_time = time.time
+  >>> time.time = lambda : time.mktime((2005, 1, 1, 0, 0, 0, 5, 1, 0))
+
+We can now create a task.
+
+  >>> from scheduler import cron
+  >>> crontask = cron.CronTask(callable, (), {})
+  
+This corresponds to the crontab entry::
+
+  * * * * * *
+
+so that
+
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 1, 0, 1, 0, 5, 1, 0)
+
+Here are a couple more examples:
+
+  # run five minutes after midnight, every day
+  # 5 0 * * *
+  >>> crontask = cron.CronTask(callable, (), {}, minute=(5,), hour=(0,))
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 1, 0, 5, 0, 5, 1, 0)
+
+  # run at 2:15pm on the first of every month
+  # 15 14 1 * *
+  >>> crontask = cron.CronTask(
+  ...     callable, (), {}, 
+  ...     minute=(5,), hour=(0,), dayOfMonth=(1,))
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 1, 0, 5, 0, 5, 1, 0)
+
+  # run at 10 pm on weekdays
+  0 22 * * 1-5
+  >>> crontask = cron.CronTask(
+  ...     callable, (), {}, 
+  ...     minute=(0,), hour=(22,), dayOfWeek=(1, 2, 3, 4, 5))
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 1, 22, 0, 0, 5, 1, 0)
+
+  # run 23 minutes after midn, 2am, 4am ..., everyday
+  # 23 0-23/2 * * *
+  >>> crontask = cron.CronTask(
+  ...     callable, (), {}, minute=(23,), hour=xrange(0, 24, 2))
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 1, 0, 23, 0, 5, 1, 0)
+
+  # run at 5 after 4 every sunday
+  5 4 * * sun
+  >>> crontask = cron.CronTask(
+  ...     callable, (), {}, 
+  ...     minute=(5,), hour=(4,), dayOfWeek=(0,))
+  >>> time.localtime(crontask.computeDelayToNextCall())
+  (2005, 1, 3, 4, 5, 0, 0, 3, 0)
+
+
+Finally, we need to cleanup after ourselves.
+
+  >>> time.time = orig_time
\ No newline at end of file


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/__init__.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/__init__.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/__init__.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1 @@
+# Make a package


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/configure.zcml
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/configure.zcml	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/configure.zcml	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,25 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:apidoc="http://namespaces.zope.org/apidoc"
+   xmlns:zcml="http://namespaces.zope.org/zcml"
+   i18n_domain="zope"
+   >
+
+  <!-- Register event listener for  -->
+  <subscriber
+      handler=".manager.startAllTasks"
+      for="zope.app.appsetup.interfaces.IProcessStartingEvent"
+      />
+
+  <!-- Register scheduler package with API Doc Tool -->
+  <apidoc:rootModule module="scheduler" zcml:condition="have apidoc" />
+
+  <!-- Register the documentation as a chapter in the API doc book -->
+  <apidoc:bookchapter 
+      id="scheduler"
+      title="The Task Scheduler"
+      doc_path="README.txt"
+      zcml:condition="have apidoc"
+      />
+
+</configure>


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/cron.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/cron.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/cron.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Cron-like Scheduler Task
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import time
+import zope.interface
+from twisted.internet import reactor
+from scheduler import interfaces, task
+
+class CronTask(task.Task):
+
+    zope.interface.implements(interfaces.ICronTask)
+
+    # Set a four year timeout to find the next scheduled time
+    timeout = 4*60*60*24*365
+
+    def __init__(self, callable, arguments=(), keywords={},
+                 minute=(), hour=(), dayOfMonth=(), month=(), dayOfWeek=()):
+        super(CronTask, self).__init__(callable, arguments, keywords)
+
+        # See scheduler.interfaces.ICronTask
+        self.minute = minute
+        self.hour = hour
+        self.dayOfMonth = dayOfMonth
+        self.month = month
+        self.dayOfWeek = dayOfWeek
+
+
+    def computeDelayToNextCall(self):
+        """See scheduler.interfaces.ITask"""
+        now = next = time.time()
+        while next <= now+self.timeout:
+            next += 60
+            fields = time.localtime(next)
+
+            if ((self.month and fields[1] not in self.month) or
+                (self.dayOfMonth and fields[2] not in self.dayOfMonth) or
+                (self.dayOfWeek and fields[6] % 7 not in self.dayOfWeek) or
+                (self.hour and fields[3] not in self.hour) or
+                (self.minute and fields[4] not in self.minute)):
+
+                continue
+
+            return next
+
+        raise SomeError


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/cron.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/interfaces.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/interfaces.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/interfaces.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,116 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Task Scheduler Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.schema
+from zope.interface import Interface, Attribute
+
+
+class ITask(Interface):
+    """A task that is executed at a particular time."""
+
+    # These names were chosen to match Twisted's LoopingCall class.
+    f = Attribute("The callable that is executes at the scheduled time.")
+
+    a = zope.schema.Tuple(
+        title=u"Arguments",
+        description=u"Arguments (tuple) for the callable.")
+
+    kw = zope.schema.Dict(
+        title=u"Keywords",
+        description=u"Keywords (dict) for the callable.")
+
+    starttime = zope.schema.Float(
+        title=u"Start Time",
+        description=u"Time in seconds at which the task will be first "
+                    u"scheduled.")
+
+    running = zope.schema.Bool(
+        title=u"Is Running",
+        description=u"Tells you whether a task is running or not.",
+        default=False)
+
+    def start():
+        """Start to schedule the task."""
+
+    def stop():
+        """Stop running the task."""
+        
+    def computeDelayToNextCall():
+        """Calculate the delay to the next execution of this task.
+
+        The returned value should be a float or integer that specifies the
+        time in seconds. Note that not all operating systems support floating
+        points.
+        """
+
+    def __call__():
+        """Execute the task and reschedule it."""
+
+
+class ITaskStatistic(Interface):
+    """Keeps track of statistical information about the task."""
+
+    count = zope.schema.Int(
+        title=u"Execution Counter",
+        description=u"Specifies how many times the callable has been executed,"
+                    u"since the task was started.",
+        default=0)
+
+
+class ILoopTask(ITask):
+    """A task that is executed at a particular interval."""
+
+    timeInterval = zope.schema.Int(
+        title=u"Interval",
+        description=u"Interval between calls in seconds",
+        default=60)
+
+
+class ICronTask(ITask):
+    """A special task that schedules the job based on cron-like information."""
+
+    minute = zope.schema.Tuple(
+        title=u"Minute",
+        description=u"The minute (list) to run the task.",
+        value_type=zope.schema.Int(min=0, max=59)
+        )
+
+    hour = zope.schema.Tuple(
+        title=u"Hour",
+        description=u"The hour (list) to run the task.",
+        value_type=zope.schema.Int(min=0, max=23)
+        )
+
+    dayOfMonth = zope.schema.Tuple(
+        title=u"Day of Month",
+        description=u"The day of month (list) to run the task.",
+        value_type=zope.schema.Int(min=1, max=31)
+        )
+
+    month = zope.schema.Tuple(
+        title=u"Month",
+        description=u"The month (list) to run the task.",
+        value_type=zope.schema.Int(min=1, max=12)
+        )
+
+    dayOfWeek = zope.schema.Tuple(
+        title=u"Day of Week",
+        description=u"The day of week (list) to run the task.",
+        value_type=zope.schema.Int(min=0, max=7)
+        )


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/interfaces.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/loop.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/loop.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/loop.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Simple Loop Scheduler Task
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+from scheduler import interfaces, task
+
+class LoopTask(task.Task):
+
+    zope.interface.implements(interfaces.ILoopTask)
+
+    def __init__(self, callable, arguments=(), keywords={}, interval=60):
+        super(LoopTask, self).__init__(callable, arguments, keywords)
+
+        # See scheduler.interfaces.ILoopTask
+        self.timeInterval = interval
+
+    def computeDelayToNextCall(self):
+        """See scheduler.interfaces.ITask"""
+        return self.timeInterval


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/loop.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/manager.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/manager.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/manager.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Task Management objects for Zope 3
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+from zope.app import zapi
+from scheduler import interfaces
+
+def startAllTasks(event):
+    for name, task in zapi.getUtilitiesFor(interfaces.ITask):
+        task.start()
+
+def stopAllTasks(event):
+    for name, task in zapi.getUtilitiesFor(interfaces.ITask):
+        task.stop()
+    


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/manager.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/scheduler-configure.zcml
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/scheduler-configure.zcml	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/scheduler-configure.zcml	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1 @@
+<include package="scheduler" />
\ No newline at end of file


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/scheduler-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/task.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/task.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/task.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Scheduler Task
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import time
+import zope.interface
+from twisted.internet import defer, task, reactor
+from twisted.python import reflect
+
+from scheduler import interfaces
+
+class Task(task.LoopingCall):
+
+    zope.interface.implements(interfaces.ITask, interfaces.ITaskStatistic)
+
+    def __init__(self, callable, arguments=(), keywords={}):
+        task.LoopingCall.__init__(self, callable, *arguments, **keywords)
+
+
+    def start(self, now=False):
+        """See scheduler.interfaces.ITask"""
+        # The LoopingCall implementation sucks, so we make it better. :) The
+        # interval can be anything, since we ignore it anyways.
+        task.LoopingCall.start(self, 0, now)
+
+    def computeDelayToNextCall(self):
+        """See scheduler.interfaces.ITask"""
+        raise NotImplemented, \
+              'Please implement `computeDelayToNextCall()` in a sub-class.'
+
+
+    def _reschedule(self):
+        """Schedule the next task execution."""
+        delay = self.computeDelayToNextCall()
+
+        if delay == 0:
+            self.call = reactor.callLater(0, self)
+        else:
+            self.call = reactor.callLater(delay, self)
+
+    def __repr__(self):
+        if hasattr(self.f, 'func_name'):
+            func = self.f.func_name
+            if hasattr(self.f, 'im_class'):
+                func = self.f.im_class.__name__ + '.' + func
+        else:
+            func = reflect.safe_repr(self.f)
+
+        return '%s(%s, *%s, **%s)' % (
+            self.__class__.__name__, func, reflect.safe_repr(self.a),
+            reflect.safe_repr(self.kw))


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/task.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/srichter-twisted-integration/src/scheduler/tests.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/scheduler/tests.py	2005-04-28 17:53:11 UTC (rev 30212)
+++ Zope3/branches/srichter-twisted-integration/src/scheduler/tests.py	2005-04-28 17:53:55 UTC (rev 30213)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for the Preferences System
+
+$Id: tests.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+import unittest
+from zope.testing import doctest, doctestunit
+from zope.app.testing import placelesssetup
+
+def isSameTime(time1, time2, error=1):
+    """Check whether the time is the same within a range +/- error."""
+    if time1-error < time2 and time1+error > time2:
+        return True
+    return False
+
+def setUp(test):
+    placelesssetup.setUp(test)
+
+def tearDown(test):
+    placelesssetup.tearDown(test)
+    # Let's remove all those delayed calls from the reactor
+    from twisted.internet import reactor
+    reactor._newTimedCalls = []
+    
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint,
+                                    'isSameTime': isSameTime},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(default='test_suite')


Property changes on: Zope3/branches/srichter-twisted-integration/src/scheduler/tests.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list