Python: module rox.tasks

rox.tasks | index

`The tasks module provides a simple light-weight alternative to threads.
When you have a long-running job you will want to run it in the background,
while the user does other things. There are four ways to do this:
- Use a new thread for each task.
- Use callbacks from an idle handler.
- Use a recursive mainloop.
- Use this module.
Using threads causes a number of problems. Some builds of pygtk/python don't
support them, they introduce race conditions, often lead to many subtle
bugs, and they require lots of resources (you probably wouldn't want 10,000
threads running at once). In particular, two threads can run at exactly the
same time (perhaps on different processors), so you have to be really careful
that they don't both try to update the same variable at the same time. This
requires lots of messy locking, which is hard to get right.
Callbacks work within a single thread. For example, you open a dialog box and
then tell the system to call one function if it's closed, and another if the
user clicks OK, etc. The function that opened the box then returns, and the
system calls one of the given callback functions later. Callbacks only
execute one at a time, so you don't have to worry about race conditions.
However, they are often very awkward to program with, because you have to
save state somewhere and then pass it to the functions when they're called.
A recursive mainloop only works with nested tasks (you can create a
sub-task, but the main task can't continue until the sub-task has
finished). We use these for, eg, rox.alert() boxes since you don't
normally want to do anything else until the box is closed, but it is not
appropriate for long-running jobs.
Tasks use python's generator API to provide a more pleasant interface to
callbacks. See the Task class (below) for more information.`

Classes
`` |  | Blocker IdleBlocker InputBlocker OutputBlocker TimeoutBlocker Task
class Blocker
`` | `A Blocker object starts life with 'happened = False'. Tasks can ask to be suspended until 'happened = True'. The value is changed by a call to trigger(). Example: kettle_boiled = tasks.Blocker() def make_tea(): print "Get cup" print "Add tea leaves" yield kettle_boiled; print "Pour water into cup" print "Brew..." yield tasks.TimeoutBlocker(120) print "Add milk" print "Ready!" tasks.Task(make_tea()) # elsewhere, later... print "Kettle boiled!" kettle_boiled.trigger() You can also yield a list of Blockers. Your function will resume after any one of them is triggered. Use blocker.happened to find out which one(s). Yielding a Blocker that has already happened is the same as yielding None (gives any other Tasks a chance to run, and then continues).`
| Methods defined here: __init__(self) add_task(self, task) `Called by the schedular when a Task yields this Blocker. If you override this method, be sure to still call this method with Blocker.add_task(self)!` remove_task(self, task) `Called by the schedular when a Task that was waiting for this blocker is resumed.` trigger(self) `The event has happened. Note that this cannot be undone; instead, create a new Blocker to handle the next occurance of the event.`
class IdleBlocker(Blocker)
`` | `An IdleBlocker blocks until a task starts waiting on it, then immediately triggers. An instance of this class is used internally when a Task yields None.`
| Methods defined here: add_task(self, task) `Also calls trigger.`
class InputBlocker(Blocker)
`` | `Triggers when os.read(stream) would not block.`
| Methods defined here: __init__(self, stream) add_task(self, task) remove_task(self, task)
class OutputBlocker(Blocker)
`` | `Triggers when os.write(stream) would not block.`
| Methods defined here: __init__(self, stream) add_task(self, task) remove_task(self, task)
class Task
`` | `Create a new Task when you have some long running function to run in the background, but which needs to do work in 'chunks'. Example (the first line is needed to enable the 'yield' keyword in python 2.2): from __future__ import generators from rox import tasks def my_task(start): for x in range(start, start + 5): print "x =", x yield None tasks.Task(my_task(0)) tasks.Task(my_task(10)) rox.mainloop() Yielding None gives up control of the processor to another Task, causing the sequence printed to be interleaved. You can also yield a Blocker (or a list of Blockers) if you want to wait for some particular event before resuming (see the Blocker class for details).`
| Methods defined here: __init__(self, iterator, name=None) `Call iterator.next() from a glib idle function. This function can yield Blocker() objects to suspend processing while waiting for events. name is used only for debugging.`
class TimeoutBlocker(Blocker)
`` | `Triggers after a set number of seconds. rox.toplevel_ref/unref are called to prevent the app quitting while a TimeoutBlocker is running.`
| Methods defined here: __init__(self, timeout) `Trigger after 'timeout' seconds (may be a fraction).`