Introduction
To be notified or to be threaded that is the question!
As the package name may suggest, pyNotifier is a notifier/event scheduler abstraction written in python. It implements notification mechanisms for socket events (read or write) and timers. Additionally external event dispatchers may be called during an idle period.
Applications using such an notifier mechanism implemented by pyNotifier have a specific software architecture. These applications are interactive, meaning that almost all action is only done because an event occured that was watched by the application. For example a server appliciation has to react if there is data waiting on a socket or the socket is again ready to write data to the network. Some applications may need to act in a recurrently interval of time. Another type of interaction can be found in applications having a graphical user interface.
Most applications of the described type do not need threads. Threading is a mechanism that may help to solve some problems, but in most cases it causes more problems than it solves. Threads are very often used to process two or more tasks in parallel. The belief that threads are the solution to this problem is a fallacy. The only solution to this prolbem is the existence of several CPUs. A quasi parallel order of events may be solved by using threads, but can also be solved by using a notifier. When using threads the critical sections of the programs must be locked by using adequate algorithm as the thread scheduler may interrupt a function at any point. By using a notifier this problems are irrelevant as a notifier just schedules the next event, when the handler of the previous event is finished, i.e. there are no critcal sections that have to be protected. Being able to forego on any kind of protection for critical sections reduces the length and increases the clarity of the source code.
Application programmers who want to use a notifier/event scheduler based architecture for their software may run into one problem when trying to implement a graphical user interface for the X11 Window System. As the X11 window system itself uses a event based architecture all known widget set implementations are also based on such mechanisms. Examples for such widget sets are GTK+, Qt or wxWindows. The problem is that these software packages implement their own notifier/event schedulers. To solve this problem pyNotifier provides the possibility to wrap the notifier of a selected widget set, so that applications using the pyNotifier API may still be able to use a widget set without the need to convert their code to the notifier API of the widget set. Currently wrappers for the previously listed widget sets are available.
API
An application using pyNotifier has to initialise the notifier, i.e. it has to choose which notifier implementation should be used. This is done by calling the 'init' function
def init( type = GENERIC ):
If no argument is given to the 'init' function the internal implementation of pyNotifier is used. Other possible choices for the 'type' argument are GTK, QT and WX (current support for wxWindows is not up-to-date).
Sockets
To get notified when a specific event occurs the application has to register its interest for this event. For sockets and files this is done with the 'addSocket' function'.
def addSocket( id, method, condition = IO_IN )
The 'id' argument may be a socket or file object or a file descriptor that can be retrieved by calling the 'fileno()' member function of these objects. The second argument 'method' has to be a callable python object that is invoked by the notifier if the registered event has occured. The function is invoked with the 'id' as an argument. Instead of a normal function the [@callbacks 'Callback'] object provided by pyNotifier may be usful at this point.
To remove a registered socket or file from the notifier the 'removeSocket' function has to be invoked. The 'id' is the socket or file object or the file descriptor given to 'addSocket' and the optional argument 'condition' may be set to IO_IN or IO_OUT depending on the prevuoisly registered event.
def removeSocket( id, condition = IO_IN )
Another way to achieve the removal of a socket or file object from the notifier is to return False in the callback function. If a callback function returns False or nothing it is removed at the application is never again triggered if this specific event occurs.
Timer
pyNotifier supports just one type of timer. If a timer is registered for a given interval of time the application is recurrently triggered when the timer expires. To register a timer the 'addTimer' function has to be invoked. The first argument 'interval' must be specified in milliseconds. 'method' is the callback function that is invoked by the notifier without any argument when the timer expires.
def addTimer( interval, method ) -> unique timer identifier
To implement a one-shot timer that is just triggered once and never again the application can use the return value of its callback function for this timer and return False or None. In this case the notifier automatically removes the timer. Another way to remove a timer is to call the method 'removeTimer'.
def removeTimer( id )
The 'id' argument is the unique timer identifier returned by 'addTimer'.
External Dispatcher
All already described tasks of an applications are scheduled by events that have occured on sockets or files or by pre-defined recurrently time intervals. But some applications may also have some tasks that may not need to be scheduled by any events or an exact timing. These tasks should be repeated quite often, if there is some time to do it.
pyNotifier provides the feature to add so called external dispatchers. These dispatchers are functions that will be invoked in each notifier step after all timers and sockets were checked. To add a dispatcher function to the notifier main loop the function 'addDispatcher' is provided. The only argument to this function is the callback methodthat will be called.
def addDispatcher( method )
To remove such a dispatcher function from the notifier main loop 'removeDispatcher' is used with the call back method as the only argument.
def removeDispatcher( method )
Callbacks
pyNotifier provides a class 'Callback' that can be used as a callback function that is passed to the 'addSocket' and 'addTimer' function. This class provides the possibility to pass more than the specified arguments to the callback functions. For example the socket callback function is called with one single argument. To pass some state information to the callback function the 'Callback' object may be used. A default callback function for a socket event would look like 'socket1'.
import notifier
...
notifier.addSocket( fd, socket1 )
...
def socket1( fd ):
print 'data received on socket', fd'fd' is the 'id' given to 'addSocket'. To pass some state information to the callback function it can be done as shown in the following example.
import notifier
...
notifier.addSocket( fd, notifier.Callback( arg1, arg2 ) )
...
def socket1( fd, arg1, arg2 ):
print 'data received on socket', fd
print 'additional state information', arg1, arg2The arguments given to the Callback object are appended to the original list of arguments for the callback function. The argument list to the Callback object can be of any length.
Example
The following example will demonstrate the most important features of pyNotifier. More examples can be found in the latest release of pyNotifier in the examples sub-directory.
import sys
import notifier
def another_minute():
print "another minute is elapsed"
# callback should be invoked again
return True
def first_10_secs( secs ):
print "the first %d secs are elapsed" % secs
# tis shoudl be a one-shot timer
return False
def standard_in( in ):
print "someone entered some data on stdin"
print in.read( 80 )
# still want to watch it
return True
if __name__ == "__main__":
notifier.init( notifier.GENERIC )
notifier.addTimer( 60000, another_minute )
notifier.addTimer( 10000, notifier.Callback( first_10_secs, 10 ) )
notifier.addSocket( sys.stdin, standard_in )
notifier.loop()Any comments or questions can be send to
