aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/python/src/_framework/foundation/future.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/src/_framework/foundation/future.py')
-rw-r--r--src/python/src/_framework/foundation/future.py232
1 files changed, 148 insertions, 84 deletions
diff --git a/src/python/src/_framework/foundation/future.py b/src/python/src/_framework/foundation/future.py
index f00c503257..bfc16fc1ea 100644
--- a/src/python/src/_framework/foundation/future.py
+++ b/src/python/src/_framework/foundation/future.py
@@ -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()