Djangoat is short for Django Auto Tester, but I often pronounce it “djan-goat”… This script does monitor Django project directory by inotify mechanism, run unit tests when file changed, and notify errors through Mumbles if tests failed. It depends on inotify and Mumbles, so runs on Linux only, Mac version comes later…

Python code:

?Download djangoat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import commands
import datetime
import dbus
import dbus.service
import os
import sys 
 
from dbus.mainloop.glib import DBusGMainLoop
 
from pyinotify import WatchManager, ThreadedNotifier, \
                      ProcessEvent, IN_CLOSE_WRITE, \
                      ExcludeFilter
 
FIREFOX_DBUS_INTERFACE = 'org.mozilla.firefox.DBus'
FIREFOX_DBUS_PATH = '/org/mozilla/firefox/DBus'
 
class FireFoxDBus(dbus.service.Object):
    def __init__(self, bus_name):
        dbus.service.Object.__init__(self, bus_name, FIREFOX_DBUS_PATH)
 
    @dbus.service.signal(dbus_interface=FIREFOX_DBUS_INTERFACE, signature='ss')
    def DownloadComplete(self, title, subject):
        pass
 
# Which type of files' change should be monitor
MONITOR_EXTENSIONS = ('.py', '.html')
 
class Watcher(ProcessEvent):
    def process_IN_CLOSE_WRITE(self, event):
        global cmd
        global firefox_dbus
 
        for extension in MONITOR_EXTENSIONS:
            if event.pathname.endswith(extension):
                start_time = datetime.datetime.now()
                print start_time
                output = commands.getoutput(cmd)
                print output
                # If test failed, call mumbles for notification
                if not output.endswith('OK'):
                    firefox_dbus.DownloadComplete(start_time.isoformat(), output)
 
    def process_default(self, event):
        pass
 
if __name__ == '__main__':
    if len(sys.argv) < 2:
        print 'Please specify a path for monitoring...'
        sys.exit()
 
    path = sys.argv[1]
    cmd = "python %s/manage.py test -v 0" % path
 
    # Exclude filter object
    excl_file = os.path.join(os.getcwd(), 'exclude.patterns')
    excl = ExcludeFilter({excl_file: ('excl_lst',)})
 
    # Add watch
    wm = WatchManager()
    notifier = ThreadedNotifier(wm, Watcher())
    wm.add_watch(path, IN_CLOSE_WRITE, rec=True, \
                 auto_add=True, exclude_filter=excl)
 
    # Set up an event loop
    dbus_loop = DBusGMainLoop()
    name = dbus.service.BusName(FIREFOX_DBUS_INTERFACE,
                                bus=dbus.SessionBus(mainloop=dbus_loop))
    firefox_dbus = FireFoxDBus(name)
 
    try:
        notifier.loop()
    except KeyboardInterrupt:
        print 'Djangoat shut down...'
    except Exception, ex:
        print 'Exception in Djangoat: %s' % (ex)
1
2
3
4
5
6
#  -*- mode: python; -*-
 
# Exclude pattens
excl_lst = ['^\.git/',
            '^\.svn/',
           ]

Screenshoot:
djangoat
Code repository:
http://git.lazytech.info/?p=daily-scripts.git

Tags: , , ,

CHM Reader is a great extension make Firefox support .chm file, project host on:http://sourceforge.net/projects/chmreader/

When I compiled it on my OSX 10.5.7 occurred following issue:

1
2
3
g++ -o components/mozCHMModule.os -c -fPIC -I/Users/duo/xulrunner-sdk/include -I/Users/duo/xulrunner-sdk/sdk/include components/mozCHMModule.cpp
/Users/duo/xulrunner-sdk/sdk/include/nsStringAPI.h:1053: error: size of array 'arg' is negative
scons: *** [components/mozCHMModule.os] Error 1

The line in nsStringAPI.h is PR_STATIC_ASSERT(sizeof(wchar_t) == 2), but 4-bytes is the default wide character size on macs, so we should add -fshort-wchar to compile flag options.

The other issue in link stage:

1
2
3
4
g++ -o platform/Darwin_x86-gcc3/components/libchm.dylib -dynamiclib components/chm_lib.os components/lzx.os components/mozCHMModule.os components/mozCHMFile.os components/mozCHMUnitInfo.os components/mozCHMInputStream.os -L/Users/duo/xulrunner-sdk/lib -L/Users/duo/xulrunner-sdk/sdk/lib -lxpcom -lxpcomglue_s -lnspr4 -lplds4 -lplc4
ld: file not found: @executable_path/libsmime3.dylib
collect2: ld returned 1 exit status
scons: *** [platform/Darwin_x86-gcc3/components/libchm.dylib] Error 1

Just add -Wl,-executable_path -Wl,/path/to/gecko/sdk/bin to link flag options.

Complete svn diff result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Index: components/SConscript
===================================================================
--- components/SConscript       (revision 114)
+++ components/SConscript       (working copy)
@@ -7,6 +7,8 @@
 
 libs = ['xpcom', 'xpcomglue_s', 'nspr4', 'plds4', 'plc4']
 
+linkflags = ''
+
 # We use firefox development files instead for geckosdk on FreeBSD
 if system() != 'FreeBSD':
        try:
@@ -38,7 +40,8 @@
     cpppath.append('/usr/include/nspr')
 
 elif system() == 'Darwin':
-    cxxflags = []
+    cxxflags = ['-fshort-wchar']
+    linkflags = ['-Wl,-executable_path', '-Wl,%s/bin' % geckosdk]
 
 elif system() == 'Windows':
     cxxflags = ['/D', 'WIN32', '/D', 'XP_WIN', '/nologo', '/MT', '/O2']
@@ -70,7 +73,7 @@
                '/usr/local/lib/firefox3/sdk/lib']
 
 env = Environment(CPPPATH = cpppath, LIBPATH = libpath, LIBS = libs,
-                  CXXFLAGS= cxxflags)
+                  CXXFLAGS= cxxflags, LINKFLAGS = linkflags)
 
 bxpt = Builder(
     action = 'xpidl -w -m typelib -Icomponents -I%s -I%s -e $TARGET $SOURCE' \

Tags: ,

The code extract from Ewrt Open-Source firmware, for my personal project purpose.
Project repository: http://git.lazytech.info/?p=aghttpd.git

Sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <signal.h>
 
#include "http.h"
 
static int caught_sigint = 0;
 
void handle_free ( GIOChannel *sock, http_request *h ) { 
    g_io_channel_unref( sock );
    http_request_free( h );
}
 
gboolean handle_write( GIOChannel *sock, GIOCondition cond, http_request *h ) { 
    gchar *response = "It Works!";
    guint r, n;
 
    n = strlen( response );
 
    http_add_header( h, "Content-Type", "text/html" );
    http_send_header( h, 200, "OK" );
 
    r = g_io_channel_write( h->sock, response, n, &n );
 
    if ( r != G_IO_ERROR_NONE ) {
        g_warning( "Serving response to %s failed: %m", h->peer_ip );
    }
 
    g_io_channel_close( sock );
    handle_free(sock, h);
 
    return FALSE;
}
 
gboolean handle_read( GIOChannel *sock, GIOCondition cond, http_request *h ) {
    ssize_t n = http_request_read( h );
 
    if ( http_request_ok( h ) ) {
        g_io_add_watch( sock, G_IO_OUT, (GIOFunc)handle_write, h );
        return FALSE;
    } else if ( 0 == n ) {
        handle_free( sock, h );
        return FALSE;
    } else {
        return TRUE;
    }
}
 
gboolean handle_accept( GIOChannel *sock, GIOCondition cond, gpointer data ) {
    struct sockaddr_in peer_addr;
    int peer_fd, n = sizeof( peer_addr );
    GIOChannel *peer;
    http_request *req;
 
    peer_fd = accept( g_io_channel_unix_get_fd(sock),
                      (struct sockaddr *)&peer_addr, &n );
    g_assert( peer_fd != -1 );
 
    peer = g_io_channel_unix_new( peer_fd );
    req  = http_request_new( peer );
    g_io_add_watch( peer, G_IO_IN, (GIOFunc)handle_read, req );
 
    return TRUE;
}
 
gboolean check_sigint ( GMainLoop *loop ) {
    if ( caught_sigint ) {
        g_message( "Caught SIGINT!" );
        g_main_quit( loop );
    }
    return TRUE;
}
 
void handle_sigint ( int sig ) {
    caught_sigint++;
}
 
int main (int argc, char **argv) {
    GMainLoop *loop;
    GIOChannel *sock;
 
    signal( SIGPIPE, SIG_IGN );
    signal( SIGINT, handle_sigint );
 
    loop = g_main_new( FALSE );
 
    sock = http_bind_socket( "0.0.0.0", 12345, 5 );
 
    g_io_add_watch( sock, G_IO_IN, (GIOFunc)handle_accept, NULL );
    g_timeout_add( 1000, (GSourceFunc)check_sigint, loop );
 
    g_message( "starting main loop" );
    g_main_run( loop );
    g_message( "exiting main loop" );
 
    return 0;
}

It take more lines number than “libevent webserver in 40 lines of c“…

And benchmarks on my virtual machine:
ab -c 1000 -n 10000 http://localhost:12345/

libevent: Requests per second: 1349.16 [#/sec] (mean)
aghttpd: Requests per second: 2255.85 [#/sec] (mean)

Tags: ,