|  | @@ -27,146 +27,210 @@
 | 
	
		
			
				|  |  |  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
	
		
			
				|  |  |  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -"""The Future interface missing from Python's standard library.
 | 
	
		
			
				|  |  | +"""A Future interface.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Python's concurrent.futures library defines a Future class very much like the
 | 
	
		
			
				|  |  | -Future defined here, but since that class is concrete and without construction
 | 
	
		
			
				|  |  | -semantics it is only available within the concurrent.futures library itself.
 | 
	
		
			
				|  |  | -The Future class defined here is an entirely abstract interface that anyone may
 | 
	
		
			
				|  |  | +Python doesn't have a Future interface in its standard library. In the absence
 | 
	
		
			
				|  |  | +of such a standard, three separate, incompatible implementations
 | 
	
		
			
				|  |  | +(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This
 | 
	
		
			
				|  |  | +interface attempts to be as compatible as possible with
 | 
	
		
			
				|  |  | +concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor
 | 
	
		
			
				|  |  | +method.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Unlike the concrete and implemented Future classes listed above, the Future
 | 
	
		
			
				|  |  | +class defined in this module is an entirely abstract interface that anyone may
 | 
	
		
			
				|  |  |  implement and use.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +The one known incompatibility between this interface and the interface of
 | 
	
		
			
				|  |  | +concurrent.futures.Future is that this interface defines its own CancelledError
 | 
	
		
			
				|  |  | +and TimeoutError exceptions rather than raising the implementation-private
 | 
	
		
			
				|  |  | +concurrent.futures._base.CancelledError and the
 | 
	
		
			
				|  |  | +built-in-but-only-in-3.3-and-later TimeoutError.
 | 
	
		
			
				|  |  |  """
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import abc
 | 
	
		
			
				|  |  | -import collections
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -RETURNED = object()
 | 
	
		
			
				|  |  | -RAISED = object()
 | 
	
		
			
				|  |  | -ABORTED = object()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class Outcome(object):
 | 
	
		
			
				|  |  | -  """A sum type describing the outcome of some computation.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  Attributes:
 | 
	
		
			
				|  |  | -    category: One of RETURNED, RAISED, or ABORTED, respectively indicating
 | 
	
		
			
				|  |  | -      that the computation returned a value, raised an exception, or was
 | 
	
		
			
				|  |  | -      aborted.
 | 
	
		
			
				|  |  | -    return_value: The value returned by the computation. Must be present if
 | 
	
		
			
				|  |  | -      category is RETURNED.
 | 
	
		
			
				|  |  | -    exception: The exception raised by the computation. Must be present if
 | 
	
		
			
				|  |  | -      category is RAISED.
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -  __metaclass__ = abc.ABCMeta
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class TimeoutError(Exception):
 | 
	
		
			
				|  |  | +  """Indicates that a particular call timed out."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class _EasyOutcome(
 | 
	
		
			
				|  |  | -    collections.namedtuple('_EasyOutcome',
 | 
	
		
			
				|  |  | -                           ['category', 'return_value', 'exception']),
 | 
	
		
			
				|  |  | -    Outcome):
 | 
	
		
			
				|  |  | -  """A trivial implementation of Outcome."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -# All Outcomes describing abortion are indistinguishable so there might as well
 | 
	
		
			
				|  |  | -# be only one.
 | 
	
		
			
				|  |  | -_ABORTED_OUTCOME = _EasyOutcome(ABORTED, None, None)
 | 
	
		
			
				|  |  | +class CancelledError(Exception):
 | 
	
		
			
				|  |  | +  """Indicates that the computation underlying a Future was cancelled."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def aborted():
 | 
	
		
			
				|  |  | -  """Returns an Outcome indicating that a computation was aborted.
 | 
	
		
			
				|  |  | +class Future(object):
 | 
	
		
			
				|  |  | +  """A representation of a computation in another control flow.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Returns:
 | 
	
		
			
				|  |  | -    An Outcome indicating that a computation was aborted.
 | 
	
		
			
				|  |  | +  Computations represented by a Future may be yet to be begun, may be ongoing,
 | 
	
		
			
				|  |  | +  or may have already completed.
 | 
	
		
			
				|  |  |    """
 | 
	
		
			
				|  |  | -  return _ABORTED_OUTCOME
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -def raised(exception):
 | 
	
		
			
				|  |  | -  """Returns an Outcome indicating that a computation raised an exception.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  Args:
 | 
	
		
			
				|  |  | -    exception: The exception raised by the computation.
 | 
	
		
			
				|  |  | +  __metaclass__ = abc.ABCMeta
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Returns:
 | 
	
		
			
				|  |  | -    An Outcome indicating that a computation raised the given exception.
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -  return _EasyOutcome(RAISED, None, exception)
 | 
	
		
			
				|  |  | +  # NOTE(nathaniel): This isn't the return type that I would want to have if it
 | 
	
		
			
				|  |  | +  # were up to me. Were this interface being written from scratch, the return
 | 
	
		
			
				|  |  | +  # type of this method would probably be a sum type like:
 | 
	
		
			
				|  |  | +  #
 | 
	
		
			
				|  |  | +  # NOT_COMMENCED
 | 
	
		
			
				|  |  | +  # COMMENCED_AND_NOT_COMPLETED
 | 
	
		
			
				|  |  | +  # PARTIAL_RESULT<Partial_Result_Type>
 | 
	
		
			
				|  |  | +  # COMPLETED<Result_Type>
 | 
	
		
			
				|  |  | +  # UNCANCELLABLE
 | 
	
		
			
				|  |  | +  # NOT_IMMEDIATELY_DETERMINABLE
 | 
	
		
			
				|  |  | +  @abc.abstractmethod
 | 
	
		
			
				|  |  | +  def cancel(self):
 | 
	
		
			
				|  |  | +    """Attempts to cancel the computation.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    This method does not block.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def returned(value):
 | 
	
		
			
				|  |  | -  """Returns an Outcome indicating that a computation returned a value.
 | 
	
		
			
				|  |  | +    Returns:
 | 
	
		
			
				|  |  | +      True if the computation has not yet begun, will not be allowed to take
 | 
	
		
			
				|  |  | +        place, and determination of both was possible without blocking. False
 | 
	
		
			
				|  |  | +        under all other circumstances including but not limited to the
 | 
	
		
			
				|  |  | +        computation's already having begun, the computation's already having
 | 
	
		
			
				|  |  | +        finished, and the computation's having been scheduled for execution on a
 | 
	
		
			
				|  |  | +        remote system for which a determination of whether or not it commenced
 | 
	
		
			
				|  |  | +        before being cancelled cannot be made without blocking.
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +    raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Args:
 | 
	
		
			
				|  |  | -    value: The value returned by the computation.
 | 
	
		
			
				|  |  | +  # NOTE(nathaniel): Here too this isn't the return type that I'd want this
 | 
	
		
			
				|  |  | +  # method to have if it were up to me. I think I'd go with another sum type
 | 
	
		
			
				|  |  | +  # like:
 | 
	
		
			
				|  |  | +  #
 | 
	
		
			
				|  |  | +  # NOT_CANCELLED (this object's cancel method hasn't been called)
 | 
	
		
			
				|  |  | +  # NOT_COMMENCED
 | 
	
		
			
				|  |  | +  # COMMENCED_AND_NOT_COMPLETED
 | 
	
		
			
				|  |  | +  # PARTIAL_RESULT<Partial_Result_Type>
 | 
	
		
			
				|  |  | +  # COMPLETED<Result_Type>
 | 
	
		
			
				|  |  | +  # UNCANCELLABLE
 | 
	
		
			
				|  |  | +  # NOT_IMMEDIATELY_DETERMINABLE
 | 
	
		
			
				|  |  | +  #
 | 
	
		
			
				|  |  | +  # Notice how giving the cancel method the right semantics obviates most
 | 
	
		
			
				|  |  | +  # reasons for this method to exist.
 | 
	
		
			
				|  |  | +  @abc.abstractmethod
 | 
	
		
			
				|  |  | +  def cancelled(self):
 | 
	
		
			
				|  |  | +    """Describes whether the computation was cancelled.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Returns:
 | 
	
		
			
				|  |  | -    An Outcome indicating that a computation returned the given value.
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -  return _EasyOutcome(RETURNED, value, None)
 | 
	
		
			
				|  |  | +    This method does not block.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    Returns:
 | 
	
		
			
				|  |  | +      True if the computation was cancelled any time before its result became
 | 
	
		
			
				|  |  | +        immediately available. False under all other circumstances including but
 | 
	
		
			
				|  |  | +        not limited to this object's cancel method not having been called and
 | 
	
		
			
				|  |  | +        the computation's result having become immediately available.
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +    raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class Future(object):
 | 
	
		
			
				|  |  | -  """A representation of a computation happening in another control flow.
 | 
	
		
			
				|  |  | +  @abc.abstractmethod
 | 
	
		
			
				|  |  | +  def running(self):
 | 
	
		
			
				|  |  | +    """Describes whether the computation is taking place.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Computations represented by a Future may have already completed, may be
 | 
	
		
			
				|  |  | -  ongoing, or may be yet to be begun.
 | 
	
		
			
				|  |  | +    This method does not block.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Computations represented by a Future are considered uninterruptable; once
 | 
	
		
			
				|  |  | -  started they will be allowed to terminate either by returning or raising
 | 
	
		
			
				|  |  | -  an exception.
 | 
	
		
			
				|  |  | -  """
 | 
	
		
			
				|  |  | -  __metaclass__ = abc.ABCMeta
 | 
	
		
			
				|  |  | +    Returns:
 | 
	
		
			
				|  |  | +      True if the computation is scheduled to take place in the future or is
 | 
	
		
			
				|  |  | +        taking place now, or False if the computation took place in the past or
 | 
	
		
			
				|  |  | +        was cancelled.
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +    raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  # NOTE(nathaniel): These aren't quite the semantics I'd like here either. I
 | 
	
		
			
				|  |  | +  # would rather this only returned True in cases in which the underlying
 | 
	
		
			
				|  |  | +  # computation completed successfully. A computation's having been cancelled
 | 
	
		
			
				|  |  | +  # conflicts with considering that computation "done".
 | 
	
		
			
				|  |  |    @abc.abstractmethod
 | 
	
		
			
				|  |  | -  def cancel(self):
 | 
	
		
			
				|  |  | -    """Attempts to cancel the computation.
 | 
	
		
			
				|  |  | +  def done(self):
 | 
	
		
			
				|  |  | +    """Describes whether the computation has taken place.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    This method does not block.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Returns:
 | 
	
		
			
				|  |  | -      True if the computation will not be allowed to take place or False if
 | 
	
		
			
				|  |  | -        the computation has already taken place or is currently taking place.
 | 
	
		
			
				|  |  | +      True if the computation is known to have either completed or have been
 | 
	
		
			
				|  |  | +        unscheduled or interrupted. False if the computation may possibly be
 | 
	
		
			
				|  |  | +        executing or scheduled to execute later.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    @abc.abstractmethod
 | 
	
		
			
				|  |  | -  def cancelled(self):
 | 
	
		
			
				|  |  | -    """Describes whether the computation was cancelled.
 | 
	
		
			
				|  |  | +  def result(self, timeout=None):
 | 
	
		
			
				|  |  | +    """Accesses the outcome of the computation or raises its exception.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    This method may return immediately or may block.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Args:
 | 
	
		
			
				|  |  | +      timeout: The length of time in seconds to wait for the computation to
 | 
	
		
			
				|  |  | +        finish or be cancelled, or None if this method should block until the
 | 
	
		
			
				|  |  | +        computation has finished or is cancelled no matter how long that takes.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Returns:
 | 
	
		
			
				|  |  | -      True if the computation was cancelled and did not take place or False
 | 
	
		
			
				|  |  | -        if the computation took place, is taking place, or is scheduled to
 | 
	
		
			
				|  |  | -        take place in the future.
 | 
	
		
			
				|  |  | +      The return value of the computation.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Raises:
 | 
	
		
			
				|  |  | +      TimeoutError: If a timeout value is passed and the computation does not
 | 
	
		
			
				|  |  | +        terminate within the allotted time.
 | 
	
		
			
				|  |  | +      CancelledError: If the computation was cancelled.
 | 
	
		
			
				|  |  | +      Exception: If the computation raised an exception, this call will raise
 | 
	
		
			
				|  |  | +        the same exception.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    @abc.abstractmethod
 | 
	
		
			
				|  |  | -  def done(self):
 | 
	
		
			
				|  |  | -    """Describes whether the computation has taken place.
 | 
	
		
			
				|  |  | +  def exception(self, timeout=None):
 | 
	
		
			
				|  |  | +    """Return the exception raised by the computation.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    This method may return immediately or may block.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Args:
 | 
	
		
			
				|  |  | +      timeout: The length of time in seconds to wait for the computation to
 | 
	
		
			
				|  |  | +        terminate or be cancelled, or None if this method should block until
 | 
	
		
			
				|  |  | +        the computation is terminated or is cancelled no matter how long that
 | 
	
		
			
				|  |  | +        takes.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Returns:
 | 
	
		
			
				|  |  | -      True if the computation took place; False otherwise.
 | 
	
		
			
				|  |  | +      The exception raised by the computation, or None if the computation did
 | 
	
		
			
				|  |  | +        not raise an exception.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Raises:
 | 
	
		
			
				|  |  | +      TimeoutError: If a timeout value is passed and the computation does not
 | 
	
		
			
				|  |  | +        terminate within the allotted time.
 | 
	
		
			
				|  |  | +      CancelledError: If the computation was cancelled.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    @abc.abstractmethod
 | 
	
		
			
				|  |  | -  def outcome(self):
 | 
	
		
			
				|  |  | -    """Accesses the outcome of the computation.
 | 
	
		
			
				|  |  | +  def traceback(self, timeout=None):
 | 
	
		
			
				|  |  | +    """Access the traceback of the exception raised by the computation.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    If the computation has not yet completed, this method blocks until it has.
 | 
	
		
			
				|  |  | +    This method may return immediately or may block.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Args:
 | 
	
		
			
				|  |  | +      timeout: The length of time in seconds to wait for the computation to
 | 
	
		
			
				|  |  | +        terminate or be cancelled, or None if this method should block until
 | 
	
		
			
				|  |  | +        the computation is terminated or is cancelled no matter how long that
 | 
	
		
			
				|  |  | +        takes.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Returns:
 | 
	
		
			
				|  |  | -      An Outcome describing the outcome of the computation.
 | 
	
		
			
				|  |  | +      The traceback of the exception raised by the computation, or None if the
 | 
	
		
			
				|  |  | +        computation did not raise an exception.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Raises:
 | 
	
		
			
				|  |  | +      TimeoutError: If a timeout value is passed and the computation does not
 | 
	
		
			
				|  |  | +        terminate within the allotted time.
 | 
	
		
			
				|  |  | +      CancelledError: If the computation was cancelled.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      raise NotImplementedError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    @abc.abstractmethod
 | 
	
		
			
				|  |  | -  def add_done_callback(self, callback):
 | 
	
		
			
				|  |  | +  def add_done_callback(self, fn):
 | 
	
		
			
				|  |  |      """Adds a function to be called at completion of the computation.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    The callback will be passed an Outcome object describing the outcome of
 | 
	
		
			
				|  |  | +    The callback will be passed this Future object describing the outcome of
 | 
	
		
			
				|  |  |      the computation.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      If the computation has already completed, the callback will be called
 | 
	
		
			
				|  |  |      immediately.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Args:
 | 
	
		
			
				|  |  | -      callback: A callable taking an Outcome as its single parameter.
 | 
	
		
			
				|  |  | +      fn: A callable taking a this Future object as its single parameter.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      raise NotImplementedError()
 |