[Checkins] SVN: lovely.remotetask/trunk/ merge of branch adamg-startlater

Adam Groszer agroszer at gmail.com
Tue Jan 29 09:40:08 EST 2008


Log message for revision 83297:
  merge of branch adamg-startlater
  - Some bugs smashed, improved tests.
  - Added ``startLater`` to ``TaskService.add``. See startlater.txt for more info.
    This facilitates to separate jobb add and start timepoints. (Not cron-like)

Changed:
  U   lovely.remotetask/trunk/CHANGES.txt
  U   lovely.remotetask/trunk/src/lovely/remotetask/README.txt
  U   lovely.remotetask/trunk/src/lovely/remotetask/browser/README.txt
  U   lovely.remotetask/trunk/src/lovely/remotetask/interfaces.py
  U   lovely.remotetask/trunk/src/lovely/remotetask/service.py
  A   lovely.remotetask/trunk/src/lovely/remotetask/startlater.txt
  U   lovely.remotetask/trunk/src/lovely/remotetask/testing.py
  U   lovely.remotetask/trunk/src/lovely/remotetask/tests.py

-=-
Modified: lovely.remotetask/trunk/CHANGES.txt
===================================================================
--- lovely.remotetask/trunk/CHANGES.txt	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/CHANGES.txt	2008-01-29 14:40:06 UTC (rev 83297)
@@ -2,6 +2,14 @@
 Changes for lovely.remotetask
 =============================
 
+2008/01/28 (new):
+=================
+
+- Some bugs smashed, improved tests.
+
+- Added ``startLater`` to ``TaskService.add``. See startlater.txt for more info.
+  This facilitates to separate jobb add and start timepoints. (Not cron-like)
+
 2007/12/?? (new):
 =================
 
@@ -114,4 +122,3 @@
 
  - added namespace declaration in lovely/__init__.py
  - allow to delay a job
-

Modified: lovely.remotetask/trunk/src/lovely/remotetask/README.txt
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/README.txt	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/README.txt	2008-01-29 14:40:06 UTC (rev 83297)
@@ -107,6 +107,8 @@
 The ``add()`` function schedules the task called "echo" to be executed with
 the specified arguments. The method returns a job id with which we can inquire
 about the job.
+By default the ``add()`` function adds and starts the job ASAP. Sometimes we need
+to have a jobid but not to start the job yet. See startlater.txt how.
 
   >>> service.getStatus(jobid)
   'queued'
@@ -720,3 +722,32 @@
      False
 
      >>> root_service.clean()
+
+
+Check Interfaces and stuff
+--------------------------
+
+  >>> from zope.interface.verify import verifyClass, verifyObject
+  >>> verifyClass(interfaces.ITaskService, remotetask.TaskService)
+  True
+  >>> verifyObject(interfaces.ITaskService, service)
+  True
+  >>> interfaces.ITaskService.providedBy(service)
+  True
+
+  >>> from lovely.remotetask.job import Job
+  >>> fakejob = Job(1, u'echo', {})
+  >>> verifyClass(interfaces.IJob, Job)
+  True
+  >>> verifyObject(interfaces.IJob, fakejob)
+  True
+  >>> interfaces.IJob.providedBy(fakejob)
+  True
+
+  >>> fakecronjob = CronJob(1, u'echo', {})
+  >>> verifyClass(interfaces.ICronJob, CronJob)
+  True
+  >>> verifyObject(interfaces.ICronJob, fakecronjob)
+  True
+  >>> interfaces.IJob.providedBy(fakecronjob)
+  True
\ No newline at end of file

Modified: lovely.remotetask/trunk/src/lovely/remotetask/browser/README.txt
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/browser/README.txt	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/browser/README.txt	2008-01-29 14:40:06 UTC (rev 83297)
@@ -128,7 +128,7 @@
   >>> 'Jobs were successfully cancelled.' in browser.contents
   True
 
-It is also possible cancle all jobs::
+It is also possible cancel all jobs::
 
   >>> browser.getControl('Cancel all', index=0).click()
   >>> 'All jobs cancelled' in browser.contents
@@ -163,7 +163,7 @@
   >>> from lovely.remotetask.interfaces import ITaskService
   >>> service = getRootFolder()['tasks']
 
-We add e job for a task which raises a ZeroDivisionError every time it is
+We add a job for a task which raises a ZeroDivisionError every time it is
 called.
 
   >>> jobid = service.add(u'exception')
@@ -249,3 +249,11 @@
 
   >>> service.getStatus(jobid)
   'error'
+
+
+Clenaup
+-------
+
+Allow the threads to exit:
+
+  >>> sleep(0.2)

Modified: lovely.remotetask/trunk/src/lovely/remotetask/interfaces.py
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/interfaces.py	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/interfaces.py	2008-01-29 14:40:06 UTC (rev 83297)
@@ -28,6 +28,8 @@
 COMPLETED = 'completed'
 DELAYED = 'delayed'
 CRONJOB = 'cronjob'
+DELAYED = 'delayed'
+STARTLATER = 'start later'
 
 
 class ITask(interface.Interface):
@@ -74,20 +76,22 @@
             default = ITask,
             )
 
-    processor = schema.Field(
-            title = u'Processor',
-            description = u'A callable that processes queued jobs using '
-                          u'an infinite loop.',
-            )
+    #processor = schema.Field(
+    #        title = u'Processor',
+    #        description = u'A callable that processes queued jobs using '
+    #                      u'an infinite loop.',
+    #        )
 
     def getAvailableTasks():
         """Return a mapping of task name to the task."""
 
-    def add(task, input):
+    def add(task, input=None, startLater=False):
         """Add a new job for the specified task.
 
-        The task argument is a string specifying the task. The input are
-        arguments for the task.
+        * task argument is a string specifying the task.
+        * input are arguments for the task.
+        * startLater, if True job will be added (gets a jobid) but needs
+          to be started with startJob later
         """
 
     def addCronJob(task, input,
@@ -99,6 +103,10 @@
                   ):
         """Add a new cron job."""
 
+    def startJob(jobid):
+        """Start a job previously added job with add(..., startLater=True)
+        """
+
     def reschedule(jobid):
         """Rescheudle a cron job.
 
@@ -168,7 +176,7 @@
         title=u'Status',
         description=u'The current status of the job.',
         values=[QUEUED, PROCESSING, CANCELLED, ERROR,
-                COMPLETED, DELAYED, CRONJOB],
+                COMPLETED, DELAYED, CRONJOB, STARTLATER],
         required=True)
 
     input = schema.Object(
@@ -256,7 +264,7 @@
         The job must be rescheduled in the containing service.
         """
 
-    def timeOfNextCall(self, now=None):
+    def timeOfNextCall(now=None):
         """Calculate the time for the next call of the job.
 
         now is a convenience parameter for testing.

Modified: lovely.remotetask/trunk/src/lovely/remotetask/service.py
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/service.py	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/service.py	2008-01-29 14:40:06 UTC (rev 83297)
@@ -64,7 +64,7 @@
         """See interfaces.ITaskService"""
         return dict(component.getUtilitiesFor(self.taskInterface))
 
-    def add(self, task, input=None):
+    def add(self, task, input=None, startLater=False):
         """See interfaces.ITaskService"""
         if task not in self.getAvailableTasks():
             raise ValueError('Task does not exist')
@@ -72,8 +72,11 @@
         self._counter += 1
         newjob = job.Job(jobid, task, input)
         self.jobs[jobid] = newjob
-        self._queue.put(newjob)
-        newjob.status = interfaces.QUEUED
+        if startLater:
+            newjob.status = interfaces.STARTLATER
+        else:
+            self._queue.put(newjob)
+            newjob.status = interfaces.QUEUED
         return jobid
 
     def addCronJob(self, task, input=None,
@@ -96,6 +99,14 @@
         self._scheduledQueue.put(newjob)
         return jobid
 
+    def startJob(self, jobid):
+        job = self.jobs[jobid]
+        if job.status == interfaces.STARTLATER:
+            self._queue.put(job)
+            job.status = interfaces.QUEUED
+            return True
+        return False
+
     def reschedule(self, jobid):
         self._scheduledQueue.put(self.jobs[jobid])
 
@@ -123,6 +134,7 @@
             job = self.jobs[jobid]
             if (   job.status == interfaces.CRONJOB
                 or job.status == interfaces.DELAYED
+                or job.status == interfaces.STARTLATER
                ):
                 job.status = interfaces.CANCELLED
 

Copied: lovely.remotetask/trunk/src/lovely/remotetask/startlater.txt (from rev 83284, lovely.remotetask/branches/adamg-startlater/src/lovely/remotetask/startlater.txt)
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/startlater.txt	                        (rev 0)
+++ lovely.remotetask/trunk/src/lovely/remotetask/startlater.txt	2008-01-29 14:40:06 UTC (rev 83297)
@@ -0,0 +1,129 @@
+=====================
+Remote Task Execution
+=====================
+
+Start later special
+~~~~~~~~~~~~~~~~~~~
+
+Usage
+_____
+
+  >>> STOP_SLEEP_TIME = 0.02
+
+Let's now start by creating a single service:
+
+  >>> from lovely import remotetask
+  >>> service = remotetask.TaskService()
+
+The object should be located, so it gets a name:
+
+  >>> from zope.app.folder import Folder
+  >>> site1 = Folder()
+  >>> root['site1'] = site1
+  >>> from zope.app.component.site import LocalSiteManager
+  >>> from zope.security.proxy import removeSecurityProxy
+  >>> sm = LocalSiteManager(removeSecurityProxy(site1))
+  >>> site1.setSiteManager(sm)
+
+  >>> sm['default']['testTaskService1'] = service
+  >>> service = sm['default']['testTaskService1'] # caution! proxy
+  >>> service.__name__
+  u'testTaskService1'
+  >>> service.__parent__ is sm['default']
+  True
+
+Let's register it under the name `TestTaskService1`:
+
+  >>> from zope import component
+  >>> from lovely.remotetask import interfaces
+  >>> sm = site1.getSiteManager()
+  >>> sm.registerUtility(service, interfaces.ITaskService,
+  ...                          name='TestTaskService1')
+
+Let's now define a task that simply echos an input string:
+
+  >>> def echo(input):
+  ...     return input
+
+  >>> import lovely.remotetask.task
+  >>> echoTask = remotetask.task.SimpleTask(echo)
+
+Let's now register the task as a utility:
+
+  >>> import zope.component
+  >>> zope.component.provideUtility(echoTask, name='echo')
+
+Since the service cannot instantaneously complete a task, incoming jobs are
+managed by a queue.
+More than that, sometimes we need to have a jobid and the job in place with
+all inputs and stuff, but start it later.
+Watch for the ``startLater=True``.
+
+  >>> jobid = service.add(u'echo', {'foo': 'bar'}, startLater=True)
+  >>> jobid
+  1
+
+The ``add()`` function schedules the task called "echo" to be executed with
+the specified arguments. The method returns a job id with which we can inquire
+about the job.
+
+  >>> service.getStatus(jobid)
+  'start later'
+
+Since the job has not been added to the queue, the status is set to
+"start later". Further, there is no result available yet:
+
+  >>> service.getResult(jobid) is None
+  True
+
+As long as the job is not being processed, it can be cancelled:
+
+  >>> service.cancel(jobid)
+  >>> service.getStatus(jobid)
+  'cancelled'
+
+Let's see how the starting later works.
+Add a job again:
+
+  >>> jobid = service.add(u'echo', {'foo': 'bar'}, startLater=True)
+  >>> jobid
+  2
+
+It's still in the status ``start later``:
+
+  >>> service.getStatus(jobid)
+  'start later'
+
+Do here some processing...
+Now start the job really:
+
+  >>> service.startJob(jobid)
+  True
+
+The status changed to ``queued``:
+
+  >>> service.getStatus(jobid)
+  'queued'
+
+Starting the job again won't work:
+
+  >>> service.startJob(jobid)
+  False
+
+From this point on the job will behave as it were added with ``add`` without
+the ``startLater=True`` parameter.
+
+  >>> service.process()
+
+  >>> service.getStatus(jobid)
+  'completed'
+
+  >>> service.getResult(jobid)
+  {'foo': 'bar'}
+  
+  >>> service.getError(jobid)
+  'None'
+
+  >>> service.stopProcessing()
+
+  >>> import time; time.sleep(STOP_SLEEP_TIME)

Modified: lovely.remotetask/trunk/src/lovely/remotetask/testing.py
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/testing.py	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/testing.py	2008-01-29 14:40:06 UTC (rev 83297)
@@ -104,17 +104,30 @@
         """See interfaces.ITaskService"""
         return dict(zope.component.getUtilitiesFor(self.taskInterface))
 
-    def add(self, task, input=None):
+    def add(self, task, input=None, startLater=False, jobClass=None):
         """See interfaces.ITaskService"""
         if task not in self.getAvailableTasks():
             raise ValueError('Task does not exist')
+        if jobClass == None:
+            jobClass = job.Job
         jobid = self._counter
         self._counter += 1
-        newjob = job.Job(jobid, task, input)
+        newjob = jobClass(jobid, task, input)
         self.jobs[jobid] = newjob
-        self._queue.put(newjob)
-        newjob.status = interfaces.QUEUED
+        if startLater:
+            newjob.status = interfaces.STARTLATER
+        else:
+            self._queue.put(newjob)
+            newjob.status = interfaces.QUEUED
         return jobid
+    
+    def startJob(self, jobid):
+        job = self.jobs[jobid]
+        if job.status == interfaces.STARTLATER:
+            self._queue.put(job)
+            job.status = interfaces.QUEUED
+            return True
+        return False
 
     def cancel(self, jobid):
         """See interfaces.ITaskService"""

Modified: lovely.remotetask/trunk/src/lovely/remotetask/tests.py
===================================================================
--- lovely.remotetask/trunk/src/lovely/remotetask/tests.py	2008-01-29 14:37:41 UTC (rev 83296)
+++ lovely.remotetask/trunk/src/lovely/remotetask/tests.py	2008-01-29 14:40:06 UTC (rev 83297)
@@ -54,6 +54,12 @@
                      |doctest.ELLIPSIS
                      |INTERPRET_FOOTNOTES
                      ),
+        DocFileSuite('startlater.txt',
+                     setUp=setUp,
+                     tearDown=tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE
+                     |doctest.ELLIPSIS
+                     ),
         DocFileSuite('processor.txt',
                      setUp=setUp,
                      tearDown=tearDown,



More information about the Checkins mailing list