The Main Loop

The plumbing within Kaa that orchestrates the main loop – dispatching callbacks triggered by events (such as activity on a file descriptor or timers) – is collectively referred to as “the notifier.” If you’re familiar with the Twisted framework, it is very similar to Twisted’s reactor.

This main loop facility depends on pyNotifier. A system-wide installation of pynotifier will be used if it exists, otherwise kaa will fallback to an internal version which supports integration with gtk and Twisted main loops. kaa.base fully wraps and enhances stock pyNotifier API with process, thread and signal handling and a wide variety of classes suitable for constructing callables used as callbacks.

To start the main loop, import kaa and call kaa.main.run(). The main loop (but not the program) will terminate when SIGTERM or SIGINT (ctrl-c) is received. The simplest Kaa program is therefore:

import kaa
kaa.main.run()

This program is effectively deaf and mute. Internally, it will go to sleep for 30 seconds, wake up briefly, sleep again, and so on. (30 is a happenstance of the internal pyNotifier implementation; 30 seconds is basically infinity as far as a computer is concerned.)

From the above basic shell, you can begin hooking functionality into the program via the rest of the Kaa API: timers, sockets, subprocesses, I/O channels, coroutines, threads, etc.

Main Loop API

kaa.main.run(threaded=False, daemon=True)

Start the main loop.

The main loop will continue to run until an exception is raised (and makes its way back up to the main loop without being caught). SystemExit and KeyboardInterrupt exceptions will cause the main loop to terminate, and execution will resume after run() was called.

Parameters:
  • threaded (bool) – if True, the Kaa mainloop will start in a new thread.
  • daemon – applies when threaded=True, and indicates if the thread running the main loop is a daemon thread. Daemon threads will not block the program from exiting when the main Python thread terminates.

Specifying threaded=True is useful if the main Python thread has been co-opted by another mainloop framework and you want to use Kaa in parallel. Another use-case would be using kaa from the interactive Python shell:

>>> import kaa, sys, time
>>> kaa.main.run(threaded=True)
>>> @kaa.threaded()
... def foo():
...     time.sleep(5)
...     return 'Background task finished\n'
...
>>> foo().connect(sys.stdout.write)
<Callable for <built-in method write of file object at 0xb7de5068>>
>>> Background task finished

Warning

Once the main loop has been started, do not fork using os.fork(). Doing so may cause peculiar interactions when using threads. To safely fork, use kaa.utils.fork(), which may be called whether the main loop has been started or not.

kaa.main.stop()

Stop the main loop and terminate all child processes and thread pools started via the Kaa API.

Any notifier callback can also cause the main loop to terminate by raising SystemExit.

kaa.main.loop(condition, timeout=None)

Executes the main loop until condition is met.

Parameters:
  • condition – a callable or object that is evaluated after each step of the loop. If it evaluates False, the loop terminates. (If condition is therefore True, the loop will run forever.)
  • timeout – number of seconds after which the loop will terminate (regardless of the condition).

This function may be called recursively, however two loops may not run in parallel threads.

Generally it is not necessary to call this function. You probably want to use kaa.main.run() instead.

Warning

Refer to the warning detailed in kaa.main.run().

kaa.main.is_running()

Return True if the main loop is currently running.

kaa.main.is_shutting_down()

Return True if the main loop is currently inside stop()

kaa.main.is_stopped()

Returns True if the main loop used to be running but is now shutdown.

This is useful for worker tasks running a thread that need to live for the life of the application, but are started before kaa.main.run() is called. These threads can loop until kaa.main.is_stopped()

kaa.main.step(*args, **kwargs)

Performs a single iteration of the main loop.

Warning

This function should almost certainly never be called directly. Use it at your own peril. (If you do use it, you must call init() first.)

kaa.main.init(module=None, reset=False, **options)

Initialize the Kaa main loop facilities.

Parameters:
  • module (str) –

    the main loop implementation to use.

    • generic: Native python-based main loop (default),
    • gtk: use pygtk’s main loop (automatically selected if the gtk module is imported);
    • twisted: Twisted main loop;
    • thread: Native python-based main loop in a separate thread with custom hooks (needs handler kwarg)
  • reset – discards any jobs queued by other threads; this is useful following a fork.
  • options – module-specific keyword arguments

This function must be called from the Python main thread.

Note

Normally it’s not necessary to expliticly invoke this function; calling loop() or run() will do it for you. However if you want to use a different main loop module than the default, or you begin using the Kaa API in a thread before the main loop is started, you will need to invoke init() first.

Main Loop Signals

Global Kaa signals are accessed via the kaa.signals Signals object. For example:

def shutdown_handler():
    print 'Shutting down'

kaa.signals['shutdown'].connect(shutdown_handler)

Importing other Kaa modules (such as those in kaa.input) may add specialized signals, however by default kaa.signals contains:

shutdown

Emitted when the Kaa main loop is shutting down, but before any subprocesses or threads are terminated.

def callback()

The callback takes no arguments.

exit

Emitted when the process exits. This differs from the shutdown signal in that the Python program may continue to run after shutdown emits and the mainloop terminates, whereas exit is invoked from an atexit handler.

def callback()

The callback takes no arguments.

step

Emitted after each iteration step of the main loop.

This signal is probably not suitable as an ‘idle worker’ because the interval between emissions may be as much as 30 seconds. However it is useful for functions which need to be called after notifier callbacks such as timers and IO monitors. One example use-case is rendering a canvas after notifier callbacks have manipulated objects upon it.

def callback()

The callback takes no arguments.

exception

Emitted when an uncaught exception has bubbled up to the main loop. This signal presents the last chance to handle it before the main loop will be aborted. (This also includes SystemExit and KeyboardInterrupt.)

def callback(tp, exc, tb)

The callback parameters correspond to sys.exc_info().

Param tp:the exception class
Param exc:the exception instance
Param tb:the traceback for this exception

If the callback returns False, the exception will be considered handled and the main loop will not terminate. Otherwise, it will.

Integration With Other Frameworks

Kaa can be made to play nicely with other main loop facilities. For example, you can write a pygtk application while still making use of Kaa’s convenient API.

GObject / GTK Integration

The generic mainloop is compatible with the GTK/Glib mainloop and kaa has a special handler to hook itself into the GTK/Glib mainloop. Kaa will use the GTK/Glib mainloop when gtk or gobject is imported once the mainloop is active. But it is possible to force the loop to use GTK/Glib by calling init:

import kaa
kaa.main.select_notifier('gtk')

This will the the GTK mainloop (the GTK mainloop is based on the glib mainloop but is a bit different). If you want the glib and not the GTK based mainloop add x11 = False to init.

If pyNotifier is installed it will be used to run the mainloop; usage of packages requiring pyNotifier and not kaa is possible.

A different approuch is to use the generic mainloop and start the gobject mainloop in a thread. This may be useful when one loop is extremly timing depended and it is a bad idea to block for even a short time. As an example, kaa.candy uses this to keep the gobject loop small and the animations alive even when the real mainloop is very busy.

kaa.gobject_set_threaded(self, mainloop=None)

Start the gobject mainloop in a thread. This function should always be used together with the generic mainloop. It is possible to jump between the gobject and the generic mainloop with the threaded decorator.

Parameters:mainloop – the mainloop object to use a mainloop based on gobject like the gstreamer or clutter mainloop. The object provided here must have a start and a stop function.

Note that callbacks from the gobject mainloop are called in that loop and not the kaa mainloop. Make sure you decorate the mainloop with the threaded decorator if necessary. For details about thread support see Thread Support. The threaded decorator can be used to force execution of a function in the gobject mainloop. Use kaa.GOBJECT as thread name:

import kaa

@kaa.threaded(kaa.MAINTHREAD)
def executed_in_kaa_mainloop():
    ...

@kaa.threaded(kaa.GOBJECT)
def executed_in_gobject_mainloop():
    ...

kaa.main.select_notifier('generic')
kaa.gobject_set_threaded()
kaa.main.run()

Twisted Integration

Kaa defines a Twisted reactor to integrate the Twisted mainloop into the kaa mainloop. After installing the reactor you can either run kaa.main.run() or reactor.run() to start the mainloop. Due to the internal design of Twisted you can not stop the mainloop from Twisted callbacks by calling sys.exit() or kaa.main.shutdown(), you need to call reactor.stop(). From kaa callbacks sys.exit() and kaa.main.stop() is supported:

# install special kaa reactor
import kaa.reactor
kaa.reactor.install()

# get reactor
from twisted.internet import reactor

# add callbacks to Twisted or kaa
# see test/twisted_in_kaa.py in the kaa.base package

# you can either call kaa.main.run() or reactor.run()
kaa.main.run()

The Twisted reactor will work with any kaa mainloop backend (generic and gtk).

There is also the reverse option putting the kaa mainloop into Twisted and let the Twisted reactor run. This is based on the thread mainloop described below and will not use an external pyNotifier installation:

# get reactor
from twisted.internet import reactor

import kaa
kaa.main.select_notifier('twisted')

# add callbacks to Twisted or kaa
# see test/kaa_in_twisted.py in the kaa.base package

# run Twisted mainloop
reactor.run()

Other mainloops

PyNotifier has wrappers for qt and wxwindows but they may not work as expected with other kaa modules. For that reasons they can not be selected. It is always possible to run the kaa mainloop in a thread but that also means that kaa modules and other parts of the code have a different idea what the mainloop is.

A different solution is the thread based mainloop in kaa. In this mode the kaa mainloop will run in an extra thread and will call a callback to the real mainloop that should be called from the real main thead. The other mainloop only needs to support a callback function that will be called from a thread and will execute the argument (a function without parameter) from the mainloop. An extra argument can be provided for a clean shutdown if the kaa mainloop whats to shut down the system. If not callback is provided, kaa.main.shutdown will be called.

The following example will integrate the kaa mainloop in the normal Twisted reactor. In this case the Twisted mainloop is running, kaa.main.run() should not be called:

# get reactor
from twisted.internet import reactor

# start thread based mainloop and add Twisted callback
import kaa
kaa.main.select_notifier('thread', handler = reactor.callFromThread,
                         shutdown = reactor.stop)

# add callbacks to Twisted or kaa
# see test/kaa_in_twisted.py in the kaa.base package

# run Twisted mainloop
reactor.run()

Note: the step signal will only be called every step the kaa mainloop does and does not affect steps the real mainloop does. Future version of kaa may fix that problem.

If you create a wrapper to use kaa with a different mainloop using this solution please send us an example so we can include support for that mainloop in the kaa distribution.

Table Of Contents

Previous topic

Signals and Callables

Next topic

Timers

This Page