Skip to content

Commit

Permalink
Repurpose IFuture (#431)
Browse files Browse the repository at this point in the history
* Repurpose IFuture

* Replace IterationFuture with CallFuture

* Update traits_futures/i_future.py

Co-authored-by: Poruri Sai Rahul <rporuri@enthought.com>

* Reflow text

Co-authored-by: Poruri Sai Rahul <rporuri@enthought.com>
  • Loading branch information
mdickinson and Poruri Sai Rahul committed Jul 19, 2021
1 parent 37c259f commit abeb975
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 149 deletions.
4 changes: 2 additions & 2 deletions docs/source/guide/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ of the new background task type:
.. |BaseFuture| replace:: :class:`~.BaseFuture`
.. |BaseTask| replace:: :class:`~.BaseTask`
.. |dispatch| replace:: :meth:`~.BaseFuture.dispatch`
.. |exception| replace:: :attr:`~traits_futures.i_future.IFuture.exception`
.. |exception| replace:: :attr:`~traits_futures.base_future.BaseFuture.exception`
.. |HasStrictTraits| replace:: :class:`~traits.has_traits.HasStrictTraits`
.. |IFuture| replace:: :class:`~.IFuture`
.. |ITaskSpecification| replace:: :class:`~.ITaskSpecification`
.. |result| replace:: :attr:`~traits_futures.i_future.IFuture.result`
.. |result| replace:: :attr:`~traits_futures.base_future.BaseFuture.result`
.. |submit| replace:: :meth:`~.submit`
.. |submit_call| replace:: :func:`~.submit_call`
.. |submit_iteration| replace:: :func:`~.submit_iteration`
Expand Down
4 changes: 2 additions & 2 deletions docs/source/guide/examples/interruptible_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
CANCELLED,
COMPLETED,
FAILED,
IFuture,
IterationFuture,
submit_iteration,
TraitsExecutor,
)
Expand All @@ -59,7 +59,7 @@ class InterruptibleTaskExample(HasStrictTraits):
traits_executor = Instance(TraitsExecutor)

#: The future object returned on task submission.
future = Instance(IFuture)
future = Instance(IterationFuture)

#: Number of points to use.
sample_count = Int(10 ** 8)
Expand Down
4 changes: 2 additions & 2 deletions docs/source/guide/examples/non_interruptible_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
Str,
)
from traits_futures.api import (
CallFuture,
CANCELLED,
COMPLETED,
FAILED,
IFuture,
submit_call,
TraitsExecutor,
)
Expand All @@ -57,7 +57,7 @@ class NonInterruptibleTaskExample(HasStrictTraits):
traits_executor = Instance(TraitsExecutor)

#: The future object returned on task submission.
future = Instance(IFuture)
future = Instance(CallFuture)

#: Number of points to use.
sample_count = Int(10 ** 8)
Expand Down
12 changes: 6 additions & 6 deletions docs/source/guide/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,12 @@ needed.
.. |STOPPING| replace:: :data:`~traits_futures.executor_states.STOPPING`
.. |STOPPED| replace:: :data:`~traits_futures.executor_states.STOPPED`

.. |cancel| replace:: :meth:`~traits_futures.i_future.IFuture.cancel`
.. |cancellable| replace:: :attr:`~traits_futures.i_future.IFuture.cancellable`
.. |done| replace:: :attr:`~traits_futures.i_future.IFuture.done`
.. |future_state| replace:: :attr:`~traits_futures.i_future.IFuture.state`
.. |result| replace:: :attr:`~traits_futures.i_future.IFuture.result`
.. |exception| replace:: :attr:`~traits_futures.i_future.IFuture.exception`
.. |cancel| replace:: :meth:`~traits_futures.base_future.BaseFuture.cancel`
.. |cancellable| replace:: :attr:`~traits_futures.base_future.BaseFuture.cancellable`
.. |done| replace:: :attr:`~traits_futures.base_future.BaseFuture.done`
.. |future_state| replace:: :attr:`~traits_futures.base_future.BaseFuture.state`
.. |result| replace:: :attr:`~traits_futures.base_future.BaseFuture.result`
.. |exception| replace:: :attr:`~traits_futures.base_future.BaseFuture.exception`

.. |CallFuture| replace:: :class:`~traits_futures.background_call.CallFuture`
.. |submit_call| replace:: :func:`~traits_futures.background_call.submit_call`
Expand Down
4 changes: 2 additions & 2 deletions traits_futures/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
Types of futures
----------------
- :class:`~.IFuture`
- :class:`~.CallFuture`
- :class:`~.IterationFuture`
- :class:`~.ProgressFuture`
Expand Down Expand Up @@ -62,6 +61,7 @@
- :class:`~.BaseFuture`
- :class:`~.BaseTask`
- :class:`~.IFuture`
- :class:`~.ITaskSpecification`
Parallelism contexts
Expand Down Expand Up @@ -117,7 +117,6 @@

__all__ = [
# Different types of Future
"IFuture",
"CallFuture",
"IterationFuture",
"ProgressFuture",
Expand All @@ -144,6 +143,7 @@
# Support for creating new task types
"BaseFuture",
"BaseTask",
"IFuture",
"ITaskSpecification",
# Contexts
"IParallelContext",
Expand Down
107 changes: 53 additions & 54 deletions traits_futures/base_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
HasRequiredTraits,
observe,
Property,
provides,
Str,
Tuple,
)
Expand Down Expand Up @@ -135,14 +134,66 @@ class _StateTransitionError(Exception):
"""


@provides(IFuture)
@IFuture.register
class BaseFuture(HasRequiredTraits):
"""
Convenience base class for the various flavours of Future.
"""

# IFuture interface #######################################################

def cancel(self):
"""
Request cancellation of the background task.
A task in ``WAITING`` or ``EXECUTING`` state will immediately be moved
to ``CANCELLING`` state. If the task is not in ``WAITING`` or
``EXECUTING`` state, this function does nothing.
.. versionchanged:: 0.3.0
This method no longer raises for a task that isn't cancellable.
In previous versions, :exc:`RuntimeError` was raised.
Returns
-------
cancelled : bool
True if the task was cancelled, False if the task was not
cancellable.
"""
if self.state in {WAITING, EXECUTING}:
self._user_cancelled()
logger.debug(f"{self} cancelled")
return True
else:
logger.debug(f"{self} not cancellable; state is {self.state}")
return False

def receive(self, message):
"""
Receive and process a message from the task associated to this future.
This method is primarily for use by the executors, but may also be of
use in testing.
Parameters
----------
message : object
The message received from the associated task.
Returns
-------
final : bool
True if the received message should be the last one ever received
from the paired task.
"""
message_type, message_arg = message
method_name = "_task_{}".format(message_type)
getattr(self, method_name)(message_arg)
return message_type in FINAL_MESSAGES

# BaseFuture interface ####################################################

#: The state of the background task, to the best of the knowledge of
#: this future. One of the six constants ``WAITING``, ``EXECUTING``,
#: ``COMPLETED``, ``FAILED``, ``CANCELLING`` or ``CANCELLED``.
Expand Down Expand Up @@ -219,58 +270,6 @@ def exception(self):
)
return self._exception

def cancel(self):
"""
Request cancellation of the background task.
A task in ``WAITING`` or ``EXECUTING`` state will immediately be moved
to ``CANCELLING`` state. If the task is not in ``WAITING`` or
``EXECUTING`` state, this function does nothing.
.. versionchanged:: 0.3.0
This method no longer raises for a task that isn't cancellable.
In previous versions, :exc:`RuntimeError` was raised.
Returns
-------
cancelled : bool
True if the task was cancelled, False if the task was not
cancellable.
"""
if self.cancellable:
self._user_cancelled()
logger.debug(f"{self} cancelled")
return True
else:
logger.debug(f"{self} not cancellable; state is {self.state}")
return False

def receive(self, message):
"""
Receive and process a message from the task associated to this future.
This method is primarily for use by the executors, but may also be of
use in testing.
Parameters
----------
message : object
The message received from the associated task.
Returns
-------
final : bool
True if the received message should be the last one ever received
from the paired task.
"""
message_type, message_arg = message
method_name = "_task_{}".format(message_type)
getattr(self, method_name)(message_arg)
return message_type in FINAL_MESSAGES

# BaseFuture interface ####################################################

def dispatch(self, message):
"""
Dispatch a message arriving from the associated BaseTask.
Expand Down
93 changes: 12 additions & 81 deletions traits_futures/i_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,101 +14,32 @@

import abc

from traits.api import Bool, Interface

from traits_futures.future_states import FutureState


class IFuture(Interface):
class IFuture(abc.ABC):
"""
Interface for futures returned by the executor.
"""

#: The state of the background task, to the best of the knowledge of
#: this future. One of the six constants ``WAITING``, ``EXECUTING``,
#: ``COMPLETED``, ``FAILED``, ``CANCELLING`` or ``CANCELLED``. Users
#: should treat this trait as read-only.
state = FutureState

#: True if cancellation of the background task can be requested,
#: else False. Cancellation of the background task can be requested
#: only if the ``state`` is one of ``WAITING`` or ``EXECUTING``. Users
#: should treat this trait as read-only.
cancellable = Bool()

#: True when communications from the background task are complete.
#: At that point, no further state changes can occur for this future.
#: This trait has value True if the ``state`` is one of ``COMPLETED``,
#: ``FAILED``, or ``CANCELLED``. It's safe to listen to this trait
#: for changes: it will always fire exactly once, and when it fires
#: it will be consistent with the ``state``. Users should treat this
#: trait as read-only.
done = Bool()

@property
@abc.abstractmethod
def result(self):
"""
Result of the background task.
This attribute is only available if the state of the future is
``COMPLETED``. If the future has not reached the ``COMPLETED`` state,
any attempt to access this attribute will raise an ``AttributeError``.
Returns
-------
result : object
The result obtained from the background task.
Raises
------
AttributeError
If the task is still executing, or was cancelled, or raised an
exception instead of returning a result.
"""

@property
@abc.abstractmethod
def exception(self):
"""
Information about any exception raised by the background task.
This attribute is only available if the state of this future is
``FAILED``. If the future has not reached the ``FAILED`` state, any
attempt to access this attribute will raise an ``AttributeError.``
Returns
-------
exc_info : tuple
Tuple containing exception information in string form:
(exception type, exception value, formatted traceback).
Raises
------
AttributeError
If the task is still executing, or was cancelled, or completed
without raising an exception.
"""
This interface can be used to implement new background task types. It
represents the knowledge that the executor needs to interact with futures.
"""

@abc.abstractmethod
def cancel(self):
"""
Request cancellation of the background task.
A task in ``WAITING`` or ``EXECUTING`` state will immediately be moved
to ``CANCELLING`` state. If the task is not in ``WAITING`` or
``EXECUTING`` state, this function does nothing.
.. versionchanged:: 0.3.0
This method no longer raises for a task that isn't cancellable.
In previous versions, :exc:`RuntimeError` was raised.
For a future that has not yet completed and has not previously been
cancelled, this method requests cancellation of the associated
background task and returns ``True``. For a future that has already
completed, or that has previously been cancelled, this method
does nothing, and returns ``False``.
Returns
-------
cancelled : bool
True if the task was cancelled, False if the task was not
cancellable.
True if the task was cancellable and this call requested
cancellation, False if the task was not cancellable (in which case
this call did nothing).
"""

@abc.abstractmethod
Expand Down

0 comments on commit abeb975

Please sign in to comment.