Package Products :: Package ZenUtils :: Module Driver |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.Driver

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  __doc__='''Driver.py
     12
     13  Run generators that produce Deferreds.
     14
     15  twisted.flow has something like this, except I cannot understand it.
     16
     17  $Id$
     18  '''
     19
     20  __version__ = "$Revision$"[11:-2]
     21
     22
     23  from twisted.internet import defer, reactor
     24  from twisted.python import failure
     25  from Products.ZenUtils.Exceptions import resolveException
     26
     27
     28 -class ShortCircuit:
     29 -    def __init__(self, value):
     30          self.value = value
     31
     32 -class Driver:
     33      "Walk an iterable that returns a deferred."
     34
     35 -    def __init__(self):
     36          self.defer = defer.Deferred()
     37          self.result = None
     38
     39 -    def drive(self, iterable):
     40          "Call the iterable and set up callbacks to finish"
     41          self.iter = iterable
     42          self._next()
     43          return self.defer
     44
     45 -    def _next(self):
     46          "Move on to the next iterable value"
     47          try:
     48              self.iter.next().addBoth(self._finish)
     49          except StopIteration:
     50              self.defer.callback(self.result)
     51          except ShortCircuit, ex:
     52              self.result = ex.value
     53              self.defer.callback(self.result)
     54          except Exception, ex:
     55              self.defer.errback(failure.Failure(ex))
     56
     57 -    def result(self):
     58          "Provide the result of the iterable as a value or exception"
     59          ex = self.result
     60          if isinstance(self.result, failure.Failure):
     61              ex = resolveException(self.result)
     62          if isinstance(ex, Exception):
     63              raise ex
     64          return self.result
     65      next = result                       # historical name for result
     66
     67 -    def finish(self, result):
     68          raise ShortCircuit(result)
     69
     70 -    def _finish(self, result):
     71          "Store the result of the last deferred for use in next()"
     72          self.result = result
     73          # prevent endless recursion
     74          reactor.callLater(0, self._next)
     75
     76 -def drive(callable):
     77      '''Typical use of Driver class:
     78
     79      def walk(driver):
     80          yield thing1()
     81          a = driver.next()
     82          print "Thing 1 is",
     83          yield thing2()
     84          b = driver.next()
     85          print "Thing 2 is",
     86          driver.finish(a + b)
     87
     88      drive(walk)
     89
     90      '''
     91      d = Driver()
     92      return d.drive(callable(d))
     93
     94
     95 -def driveLater(secs, callable):
     96      "Drive the callable at a later time"
     97      d = defer.Deferred()
     98      def driveAgain():
     99          drive(callable).chainDeferred(d)
    100      reactor.callLater(secs, driveAgain)
    101      return d
    102
    103
    104 -def test():
    105      lst = []
    106      def loop(d):
    107          for i in range(10):
    108              yield defer.succeed(i)
    109              lst.append(d.next())
    110      def final(v):
    111          assert lst[-1] == v
    112          assert lst == range(10)
    113          def unloop(d):
    114              yield defer.fail(ZeroDivisionError('hahaha'))
    115              d.next()
    116          def checkError(err):
    117              assert isinstance(err.value, ZeroDivisionError)
    118              reactor.stop()
    119          drive(unloop).addErrback(checkError)
    120      drive(loop).addCallback(final)
    121
    122
    123  if __name__ == '__main__':
    124      test()
    125      reactor.run()
    126