Skip to content

Commit

Permalink
ITaskSpecification change: 'send' expects a single argument (#364)
Browse files Browse the repository at this point in the history
* Change the 'send' callable to accept a single object

* Revert unrelated spacing change

* Revert spurious spacing change

* Rework confusing docstring
  • Loading branch information
mdickinson committed Jul 5, 2021
1 parent 8331a1b commit b93d74f
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 37 deletions.
7 changes: 7 additions & 0 deletions docs/source/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ Backwards-incompatible changes
The following backwards-incompatible changes may affect advanced users
of Traits Futures.

* The ``send`` callable passed to the background task now expects a single
Python object as an argument, rather than accepting a message type and
a message argument as separate arguments. Existing uses of the form
``send(message_type, message_args)`` will need to be changed to
``send((message_type, message_args))``. This affects those writing their
own background task types, but does not affect users of the existing
background task types.
* The ``state`` trait of the ``~.TraitsExecutor`` is now read-only;
previously, it was writable.

Expand Down
24 changes: 12 additions & 12 deletions docs/source/guide/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ of type ``"progress"`` to report progress, while the background iteration task
sends messages of type ``"generated"``.

If none of the standard task types meets your needs, it's possible to write
your own background task type, that sends whatever message types you like. This
your own background task type, that sends whatever messages you like. This
section describes how to do this in detail.

To create your own task type, you'll need three ingredients:
Expand Down Expand Up @@ -54,10 +54,13 @@ is accompanied by the corresponding number.
Message types
~~~~~~~~~~~~~

In general, a message sent from the background to the foreground has two parts:
a message type, and an optional message argument. The message type should be a
string, while the message argument can be any Python object (though it should
usually be pickleable and immutable).
In general, the message sent from the background task to the future can be any
Python object, and the future can interpret the sent object in any way that it
likes. However, the |BaseFuture| base class that we'll use below provides a
default dispatcher for messages, and that dispatcher expects those messages to
have the form ``(message_type, message_args)``. Here the message type should be
a string that's valid as a Python identifier, while the message argument can be
any Python object (though it should usually be pickleable and immutable).

We first define named constants representing our three message types. This
isn't strictly necessary, but it makes the code cleaner.
Expand All @@ -66,11 +69,6 @@ isn't strictly necessary, but it makes the code cleaner.
:start-after: start message types
:end-before: end message types

Note that the message types are all strings. Ideally, those strings should be
valid Python identifiers, since (as we'll see later) the default message
dispatch mechanism uses these strings directly in the corresponding message
handler names.

The background callable
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -99,13 +97,15 @@ 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 does a couple of things:
implementation of this method expects incoming messages to have the
form ``(message_type, message_arg)``, and does a couple of things:

- 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 some
The |task_sent| 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
``_process_fizz``, ``_process_buzz`` and ``_process_fizz_buzz`` to handle
Expand Down
16 changes: 7 additions & 9 deletions docs/source/guide/examples/fizz_buzz_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ def fizz_buzz(send, cancelled):
Parameters
----------
send : callable(message_type, message_argument) -> None
Callable accepting two arguments: a message type (a string) as the
first argument, and the message argument (if any) as the optional
second argument. The message argument should be pickleable, and
preferably immutable (or at least, not intended to be mutated). It
should return nothing.
send : callable(object) -> None
Callable accepting the message to be sent, and returning nothing. The
message argument should be pickleable, and preferably immutable (or at
least, not intended to be mutated).
cancelled : callable
Callable accepting no arguments and returning a boolean result. It
returns ``True`` if cancellation has been requested, and ``False``
Expand All @@ -55,11 +53,11 @@ def fizz_buzz(send, cancelled):
n_is_multiple_of_5 = n % 5 == 0

if n_is_multiple_of_3 and n_is_multiple_of_5:
send(FIZZ_BUZZ, n)
send((FIZZ_BUZZ, n))
elif n_is_multiple_of_3:
send(FIZZ, n)
send((FIZZ, n))
elif n_is_multiple_of_5:
send(BUZZ, n)
send((BUZZ, n))

time.sleep(1.0)
n += 1
Expand Down
2 changes: 1 addition & 1 deletion traits_futures/background_iteration.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __call__(self, send, cancelled):
# exception carries that value. Return it.
return e.value

send(GENERATED, result)
send((GENERATED, result))
# Don't keep a reference around until the next iteration.
del result

Expand Down
2 changes: 1 addition & 1 deletion traits_futures/background_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def report(self, progress_info):
"""
if self.cancelled():
raise ProgressCancelled("Task was cancelled")
self.send(PROGRESS, progress_info)
self.send((PROGRESS, progress_info))


class ProgressBackgroundTask:
Expand Down
11 changes: 4 additions & 7 deletions traits_futures/i_task_specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,10 @@ def background_task(self):
by the callable, will be recorded in the corresponding future.
The ``send`` argument can be used by the background task to send
messages back to the main thread of execution. It's a callable that can
be used either in the form ``send(message_type)`` or in the form
``send(message_type, message_args)``. Here ``message_type`` is a simple
constant (typically a string), and ``message_args`` is a single Python
object containing optional arguments for the message. The arguments to
``send`` should typically be both immutable and pickleable. ``send``
returns no useful result.
messages back to the main thread of execution. It's a callable that
should be called as ``send(message)``, where ``message`` is an
arbitrary Python object. The argument to ``send`` should typically be
both immutable and pickleable. ``send`` returns no useful result.
The ``cancelled`` argument may be used by the background task to check
whether cancellation has been requested. When called with no arguments,
Expand Down
11 changes: 4 additions & 7 deletions traits_futures/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,13 @@ def __call__(self):
logger.exception("Unexpected exception in background task.")
raise

def _send_custom_message(self, message_type, message_args=None):
def _send_custom_message(self, message):
"""
Send a custom message from the background task to the future.
Parameters
----------
message_type : str
The message type.
message_args : object, optional
Any arguments providing additional information for the message.
If not given, ``None`` is passed.
message : object
The message to be sent.
"""
self._sender.send((SENT, (message_type, message_args)))
self._sender.send((SENT, message))

0 comments on commit b93d74f

Please sign in to comment.