Skip to content

Commit

Permalink
Extract public 'dispatch' method from task_sent (#427)
Browse files Browse the repository at this point in the history
* Extract public 'dispatch' method from task_sent

* Update traits_futures/base_future.py
  • Loading branch information
mdickinson committed Jul 16, 2021
1 parent 1dd090f commit b437bad
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 28 deletions.
16 changes: 7 additions & 9 deletions docs/source/guide/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,13 @@ Now we define a dedicated future class ``FizzBuzzFuture`` for this background
task type. The most convenient way to do this is to inherit from the
|BaseFuture| class, which is a |HasStrictTraits| subclass that provides the
|IFuture| interface. Messages coming into the |BaseFuture| instance from the
background task are processed by the |task_sent| method. The default
implementation of this method expects incoming messages to have the
form ``(message_type, message_arg)``, and does a couple of things:
background task are processed by the |dispatch| method. The default
implementation of this method expects incoming messages to have the form
``(message_type, message_arg)``, and it converts each such message to a call to
a method named ``_process_<message_type>``, passing ``message_arg`` as an
argument.

- it dispatches the argument of each message to a method named
``_process_<message_type>``.
- it suppresses any messages that arrive after cancellation has been requested

The |task_sent| method can be safely overridden by subclasses if messages
The |dispatch| method can be safely overridden by subclasses if messages
do not have the form ``(message_type, message_arg)``, or if some
other dispatch mechanism is wanted. For this example, we use the default
dispatch mechanism, so all we need to do is to define methods
Expand Down Expand Up @@ -182,6 +180,7 @@ 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`
.. |HasStrictTraits| replace:: :class:`~traits.has_traits.HasStrictTraits`
.. |IFuture| replace:: :class:`~.IFuture`
Expand All @@ -191,5 +190,4 @@ of the new background task type:
.. |submit_call| replace:: :func:`~.submit_call`
.. |submit_iteration| replace:: :func:`~.submit_iteration`
.. |submit_progress| replace:: :func:`~.submit_progress`
.. |task_sent| replace:: :meth:`~.BaseFuture._task_sent`
.. |TraitsExecutor| replace:: :class:`~.TraitsExecutor`
48 changes: 29 additions & 19 deletions traits_futures/base_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ class BaseFuture(HasRequiredTraits):
Convenience base class for the various flavours of Future.
"""

# IFuture 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 @@ -257,16 +259,11 @@ def receive(self, message):
getattr(self, method_name)(message_arg)
return message_type in FINAL_MESSAGES

# Semi-private methods ####################################################

# These methods represent the state transitions in response to external
# events. They're used by the FutureWrapper, and are potentially useful for
# unit testing, but are not intended for use by the users of Traits
# Futures.
# BaseFuture interface ####################################################

def _task_sent(self, message):
def dispatch(self, message):
"""
Automate dispatch of different types of message.
Dispatch a message arriving from the associated BaseTask.
This is a convenience function, and may be safely overridden by
subclasses that want to use a different dispatch mechanism. For
Expand All @@ -275,27 +272,40 @@ def _task_sent(self, message):
that method. Subclasses then only need to provide the appropriate
``_process_<msgtype>`` methods.
If the future is already in ``CANCELLING`` state, no message is
dispatched.
Parameters
----------
message : object
Message sent by the background task. The default implementation of
this method expects the message to be in the form ``(message_type,
message_args)`` with ``message_type`` a string.
"""
message_type, message_arg = message
method_name = "_process_{}".format(message_type)
getattr(self, method_name)(message_arg)

# State transitions #######################################################

Internal state:
* _CANCELLING_AFTER_STARTED -> _CANCELLING_AFTER_STARTED
* EXECUTING -> EXECUTING
# These methods represent state transitions in response to external events.

def _task_sent(self, message):
"""
Automate dispatch of different types of message.
Delegates the actual work to the :meth:`dispatch` method,
which can be overridden by subclasses. Messages received after
cancellation are ignored.
Parameters
----------
message : tuple
Message from the background task, in the form (message_type,
message_args).
message : object
Message from the background task.
"""

if self._internal_state == _CANCELLING_AFTER_STARTED:
# Ignore messages that arrive after a cancellation request.
return
elif self._internal_state == EXECUTING:
message_type, message_arg = message
method_name = "_process_{}".format(message_type)
getattr(self, method_name)(message_arg)
self.dispatch(message)
else:
raise _StateTransitionError(
"Unexpected custom message in internal state {!r}".format(
Expand Down

0 comments on commit b437bad

Please sign in to comment.