source file: /home/buildslave/tahoe/edgy/build/src/allmydata/monitor.py
file stats: 40 lines, 40 executed: 100.0% covered
coverage versus previous test: 0 lines added, 0 lines removed
    1. 
    2. from zope.interface import Interface, implements
    3. from allmydata.util import observer
    4. 
    5. class IMonitor(Interface):
    6.     """I manage status, progress, and cancellation for long-running operations.
    7. 
    8.     Whoever initiates the operation should create a Monitor instance and pass
    9.     it into the code that implements the operation. That code should
   10.     periodically check in with the Monitor, perhaps after each major unit of
   11.     work has been completed, for two purposes.
   12. 
   13.     The first is to inform the Monitor about progress that has been made, so
   14.     that external observers can be reassured that the operation is proceeding
   15.     normally. If the operation has a well-known amount of work to perform,
   16.     this notification should reflect that, so that an ETA or 'percentage
   17.     complete' value can be derived.
   18. 
   19.     The second purpose is to check to see if the operation has been
   20.     cancelled. The impatient observer who no longer wants the operation to
   21.     continue will inform the Monitor; the next time the operation code checks
   22.     in, it should notice that the operation has been cancelled, and wrap
   23.     things up. The same monitor can be passed to multiple operations, all of
   24.     which may check for cancellation: this pattern may be simpler than having
   25.     the original caller keep track of subtasks and cancel them individually.
   26.     """
   27. 
   28.     # the following methods are provided for the operation code
   29. 
   30.     def is_cancelled(self):
   31.         """Returns True if the operation has been cancelled. If True,
   32.         operation code should stop creating new work, and attempt to stop any
   33.         work already in progress."""
   34. 
   35.     def raise_if_cancelled(self):
   36.         """Raise OperationCancelledError if the operation has been cancelled.
   37.         Operation code that has a robust error-handling path can simply call
   38.         this periodically."""
   39. 
   40.     def set_status(self, status):
   41.         """Sets the Monitor's 'status' object to an arbitrary value.
   42.         Different operations will store different sorts of status information
   43.         here. Operation code should use get+modify+set sequences to update
   44.         this."""
   45. 
   46.     def get_status(self):
   47.         """Return the status object. If the operation failed, this will be a
   48.         Failure instance."""
   49. 
   50.     def finish(self, status):
   51.         """Call this when the operation is done, successful or not. The
   52.         Monitor's lifetime is influenced by the completion of the operation
   53.         it is monitoring. The Monitor's 'status' value will be set with the
   54.         'status' argument, just as if it had been passed to set_status().
   55.         This value will be used to fire the Deferreds that are returned by
   56.         when_done().
   57. 
   58.         Operations that fire a Deferred when they finish should trigger this
   59.         with d.addBoth(monitor.finish)"""
   60. 
   61.     # the following methods are provided for the initiator of the operation
   62. 
   63.     def is_finished(self):
   64.         """Return a boolean, True if the operation is done (whether
   65.         successful or failed), False if it is still running."""
   66. 
   67.     def when_done(self):
   68.         """Return a Deferred that fires when the operation is complete. It
   69.         will fire with the operation status, the same value as returned by
   70.         get_status()."""
   71. 
   72.     def cancel(self):
   73.         """Cancel the operation as soon as possible. is_cancelled() will
   74.         start returning True after this is called."""
   75. 
   76.     #   get_status() is useful too, but it is operation-specific
   77. 
   78. class OperationCancelledError(Exception):
   79.     pass
   80. 
   81. class Monitor:
   82.     implements(IMonitor)
   83. 
   84.     def __init__(self):
   85.         self.cancelled = False
   86.         self.finished = False
   87.         self.status = None
   88.         self.observer = observer.OneShotObserverList()
   89. 
   90.     def is_cancelled(self):
   91.         return self.cancelled
   92. 
   93.     def raise_if_cancelled(self):
   94.         if self.cancelled:
   95.             raise OperationCancelledError()
   96. 
   97.     def is_finished(self):
   98.         return self.finished
   99. 
  100.     def when_done(self):
  101.         return self.observer.when_fired()
  102. 
  103.     def cancel(self):
  104.         self.cancelled = True
  105. 
  106.     def finish(self, status_or_failure):
  107.         self.set_status(status_or_failure)
  108.         self.finished = True
  109.         self.observer.fire(status_or_failure)
  110.         return status_or_failure
  111. 
  112.     def get_status(self):
  113.         return self.status
  114.     def set_status(self, status):
  115.         self.status = status