If it won't be simple, it simply won't be. [Hire me, source code] by Miki Tebeka, CEO, 353Solutions

Wednesday, March 14, 2012

Reading Avro Files Faster than Java

At work, we use a lot of Avro. One of the problems we faced was that the Python Avro package is very slow comparing to the Java one. The goal then was to write fastavro which is a subset of the avro package and should be at least as fast as Java. In this post I'll show how fastavro became faster than Java and also Python 3 compatible.

Going Fast
The Python avro package uses classes and properties heavily. This might allow for nice design but since function calls in Python are expensive it has a cost. The approach was to strip down most of the code in the avro package to one simple module, eliminating as many function calls as possible along the way and using only built in types. After some tweaking, fastavro was churning through the 10K records benchmark in about 2.6seconds (comparing to 13.9 seconds of the avro package). It was a nice speedup but the goal was to be as fast as Java (which was doing about 1.8sec).


Going Faster
Enter Cython. fastavro compiles the Python code without any specific Cython code. This way on machines that do not have a compiler users can still use fastavro. This complicated the build process a bit since now the C extension is generated using Cython in external Makefile. The code in fastavro first tries to import the C extension and if it fails imports the pure Python one.

This approach gave a 2x speedup (benchmark of 10K records done in 1.5seconds). Again, this is without any Cython specific code.

Python 3 Support
The initial Python 3 support was written on the first day of PyCon. However after hearing Robert Brewer's excellent talk I decided to take his advice and write a small compatibility layer (six was not used for various reasons).

As Robert said, this approach made fastavro better with strings, unicode and other things which were glossed over the 2.X only code. The build system was simplified a lot comparing to the one with the initial Python 3 support.

End Result
The end result is a package that reads Avro faster than Java and supports both Python 2 and Python 3. Using Cython and a little bit of work the was achieved without too much effort.

As usual, the code can be found on bitbucket.

Thursday, February 23, 2012

Super Simple Mocking

There are many mocking libraries for Python out there. Due to the dynamic nature of Python I find them an overkill. Below is a super simple mocking library that works for me.


Note that for some types (such as C extensions, objects with __slots__ ...) this will not work since they do not have a __dict__.

EDIT: Following HackerNews comments , I've changed the interface to mock(obj, **kw).

Sunday, February 05, 2012

Adding Key Navigation to Your Web Site

I don't like using the mouse and thankful for every web site that adds keyboard navigation (like Google Reader). Be nice to your users and add some yourself.

jQuery makes it super easy to add keyboard navigation to your site, below is a simple example adding keyboard navigation. "k/j" for up/down, "o" for opening an item and "?" for toggling help. JavaScript code starts at line 90.


Wednesday, January 11, 2012

fastavro with Cython

Added an optional step of compiling fastavro with Cython. Just doing that, with no Cython specific code reduced the time of processing 10K records from 2.9sec to 1.7sec. Not bad for that little work.

Also added a __main__.py so you can use fastavro to process Avro files:

  • python -m fastavro weather.avro # Dump records in JSON format
  • python -m fastavro --schema weather.avro # Dump schema

Friday, January 06, 2012

fastavro

Just released fastavro to PyPI. It has way less features than the official avro package, but according to my tests it's about 5 times faster.

Tuesday, December 27, 2011

How To Use A Chat To Amplify Your Team

A chat room is a great tool for any team. I've worked both in distributed and "local" team and in both cases a central chat room was one of the most effective tools we used. The chat room is the central location for updates, conversations, shared knowledge and more. It's also highly effective in many open source projects.


Starting is super easy, you can either set an internal server in your company with one of many open IRC/Jabber servers or try an external service (like Campfire, or HipChat).


The success of the chat room depends on the signal/noise ratio in the main room. Try to have every new conversation start in the main room, and only if it becomes to noisy move it to another room. 1/1 conversations are hiding information from the team and should be used only for personal communication.

Don't forget add services to the chat room. It's easy to add build notification, source commits, reminders and other smart bots. We use (Jenkins Jabber plugin) to get build notification. However be careful not to generate too much noise and tweak all these bots.

Logging and searching is another critical feature. Some services (like Campfire) provide it out of the box. If your chat server does not provide logging, it's pretty easy to add it with one of the many logging bots out there, and hooking search on top of the logs is pretty easy as well. (That's what we did with selenium.saucelabs.com which is a combination of Supybot and Omega).

Another issue is presence, people need to make it clear when they are "in" or "out" of the room. In some cases when the server logs it's as easy as login/logout. In other cases just notify the team your are away (we use the IRC /me lunching a lot). Stating that it's acceptable to be "out" of the room allows people to close the chat when they need to be in the zone.

So go ahead, set a chat for you team. Soon you'll wonder how you managed before.

Thursday, December 15, 2011

Decimals Class Decorator

A friend at work had a problem where he wanted to make sure some attributes are always Decimal.

We considered using the excellent traits library, but it's an overkill and we didn't want to introduce another dependency. For me this looked like a job for properties but we wanted some way to generate the properties automatically. Lucky for us we now have class decorators (in the old days I'd probably use a metaclass).

Friday, December 02, 2011

branches

At work, our workflow involves JIRA and feature branches. We name the feature branches in the name of the issue they are solving.

However when doing hg branches it's hard to know what branch you want.
Below is a small script that annotates the output of hg branches with the issue description from JIRA (and also added the branch parent).

Friday, November 11, 2011

VirtualBox and USB

A little something I found out, writing it here in hope someone else will find it useful.

I'm on a Ubunbu system (11.10), and needed to connect to a USB device from an XP VM (wanted to sync and upgrade iPod touch, there is no iTunes for Linux). I couldn't connect the XP VM to any USB port (the USB menu was empty). The solution is to add yourself to the vboxusers group.

Open Users and Group, click on Manage Groups and double click on vboxusers and make sure your name is checked. After that logout and login, you should see the USB devices in the USB menu now.

Tuesday, November 01, 2011

Summarize tuples

Raymond asked "Group the list of tuples on a given field and sum or count selected other fields.", here's my solution using sqlite3. (original answer here).

Friday, October 28, 2011

Using dbus To Control Pidgin

I using Pidgin as my IM client, however after work I'd like to disconnect from our jabber server. Being the command like geek I am, I wrote the following. (Earlier version was written in bash using purple-remote .) You can learn more about Pidgin dbus interface here.

Saturday, October 15, 2011

Finding Module Version

A friend at work asked me how can he find which version of Python module is currently installed. The quick answer is to use pip:
     
        pip freeze | grep <module>


Friday, September 30, 2011

Old Berlios Projects

Seems like Berlios is closing down. I've placed a backup of my old projects here. Let me know if you like to take ownership on one/several of them.

Using Generators in nose Tests

Sometimes you have many tests that do exactly the same thing but with different data. nose provides an easy way to do that with tests that return generators.

Saturday, September 24, 2011

Feynman On The Importance Of Playing

I'm reading "Surely You're Joking, Mr. Feynman" (very good read).
What he says about burnout, playing and doing the things you love is priceless:

But when it came time to do some research. I couldn't get to work. I was a little tired; I was not interested; I couldn't do research! ...

And then I thought to myself, "You know, what they think of you is so fantastic, it's impossible to live up to it. You have no responsibility to live up to it!"... 

Then I had another thought; Physics disgusts me a little bit now, but I used to enjoy doing physics. Why did I enjoy it? I used to play with it. I used to do whatever I felt like doing - it didn't have to do with whether it was important for the development of nuclear physics...

So I get this new attitude ... I'm going to play with physics, whenever I want to, without worrying about any importance whatsoever.
Within a week I was in the cafeteria and some guy, fooling around, throws a plate in the air. ...

I had nothing to do, so I start to figure out the motion of the rotating plate...
And before I knew it (it was a very short time) I was "playing" - working, really - with the same old problem that I loved so much, that I had stopped working on when I went to Los Alamos; my thesis-type problems; all those old-fashioned wonderful things.
It was effortless. It was easy to play with these things. It was like uncorking a bottle: Everything flowed out effortlessly. ...

There was no importance to what I was doing, but ultimately there was. The diagrams and the whole business that I got the Nobel Prize for came from that piddling around with the wobbling plate.

Go out and play, I'm sure you'll make wonderful things.

EDIT: Thanks for HN for proof reading this. Also found a more complete excerpt here.

Thursday, September 08, 2011

Monday, August 29, 2011

"Top X" UI

Doing some experiments with data at work, one of the common tasks was "show top X items". However X was changing all the time. Finally I came out with a web based UI where you can set the top yourself. Hopefully someone else will find it useful.

Key points:
  • Using jQuery, jQuery UI and flot
  • Site is static, no backend needed
You can see a demo on word frequency of "Alice In Wonderland" here.


As usual, the code is in bitbucket.

Thursday, August 18, 2011

Crucible Command Line Client

At work, we use Crucible (which I mostly like). However we do reviews post commit (using feature branches). And creating a review from a patch is too much clicks to my taste. Hence crucible command line client. You can install it with easy_install crucible and then run it.

crucible has many options, but you can save many defaults in ~/.cruciblerc (see example at top of the crucible script).

Wednesday, July 13, 2011

logio

The logging module is nice, one think I find myself missing here and there is the ability to treat logs as file objects. This help them "play nice" with many modules.

Here is a quick hack to do that, at the bottom there's an example on how to create CSV log files (and then we can use TimedRotatingFileHandler to rotate the logs).

Thursday, July 07, 2011

PDB - The Movie

I did it again, this time showing how to work with PDB (the Python debugger).



Could it be the proximity to Hollywood that causes me to do these movies?

Saturday, June 25, 2011

ingress

One of the many cool features in Twisted is "manhole". It lets you connect to any running server and get a shell (over ssh or telnet) in the server environment. This is very helpful when debugging.

It's very easy to implement this using SocketServer which is in the standard library, this way even if you're not running under Twised, you can still have this feature. I've created ingress just for this (easy_install ingress), using it is super easy:
import ingress
ingress.install()

The code itself is not that complicated. (Could have been simpler if there was a way to override writing to stdout in code.InteractiveConsole).

Thursday, June 09, 2011

3 IDLE Tips - The Movie

I've managed to make a screencast, giving tips on how to use IDLE.
(Yeah, I know - it sucks, but that's how I learn ... ;)

Sunday, May 29, 2011

avrocat

After playing a bit with simpleavro, I've decided it'll be better done in the Unix tradition of "doing one thing well" and using pipes. Hence avrocat was born, a "cat" like utility for avro files.
usage: avrocat [-h] [-n COUNT] [-s SKIP] [-f {json,csv}] [--header]
               [--filter FILTER] [--schema]
               filename

`cat` for Avro files

positional arguments:
  filename              avro file (- for stdin)

optional arguments:
  -h, --help            show this help message and exit
  -n COUNT, --count COUNT
                        number of records to print
  -s SKIP, --skip SKIP  number of records to skip
  -f {json,csv}, --format {json,csv}
                        record format
  --header              print CSV header
  --filter FILTER       filter records (e.g. r['age']>1)
  --schema              print schema

EDIT: Seems like avrocat is finding itself into the "avro" package.

Saturday, May 28, 2011

Wednesday, May 18, 2011

Delicous Monkey

Sadly, the official Delicious Firefox addon does not support Firefox 4.
I couldn't find any other addon online the gives me keyboard shortcut for saving the current page in Delicious.
Lucky for me, there is GreaseMonkey. Below is a short script to have CTRL-M save the current page in Delicious.

Saturday, May 07, 2011

Convert PDF to JPEG


The wife needed a quick and dirty solution to convert PDF files to JPEG. Below is a quick UI (using Tkinter) about ImageMagic's "convert" utility.

Tuesday, April 05, 2011

A simpler named decorator

Everybody loves decorators. However there's is a difference if a decorator is called with or without () (as in @logged vs @logged("foo")). In the first case, the logged will be called with the function it decorates. In the second case, logged is called with a string and should return a function that accepts the function to be decorated. Below is a simple solution to support both cases in one decorator.

Saturday, March 26, 2011

A Whirlwind of Python


I gave a fast paced talk about Python at work, you can download it here. (Unzip and open index.html)

Inside the zip file are also the files used to create the talk. I've used s5 as framework and generated slides from Python source files using Pygments.

Thursday, March 03, 2011

Convert oggs to mp3 - the fast way

I've created a quick script to convert all .ogg files in a given directory to .mp3 (using oggdec and lame). However it was running too slow to my taste, a good excuse to play with multiprocessing.Pool. On a short list of 4 .ogg files the processing time went from 32 seconds to 12 seconds.

Wednesday, March 02, 2011

Adding XML support to ctags

I use ctags a lot with Vim. It let me jump to definitions quickly.

At work we have a lot of spring XML configuration files, and it was pretty easy to add support for XML to ctags. Just add the following to ~/.ctags.

--langdef=XML
--langmap=XML:.xml
--regex-XML=/id="([a-zA-Z0-9_]+)"/\1/d,definition/

Friday, February 18, 2011

AppEngine Work Environment

I'm doing a little AppEngine project (boy, my wife is one tough customer :). On the way I've developed some scripts to help me with my work flow (which is mostly coding with Vim and trying stuff on the REPL).

I have python2.5 (which is the version AppEngine uses) at /opt/python2.5 and I've "pip installed" pyflakes and ipython. The AppEngine SDK is at /opt/google_appengine.

gae.py


repl.sh


run-local.sh


check.sh


push.sh


pypath.sh

Monday, January 31, 2011

JSON decorator for CherryPy

#!/usr/bin/env python

from functools import wraps
import json
from cherrypy import response, expose

def jsonify(func):
    '''JSON decorator for CherryPy'''
    @wraps(func)
    def wrapper(*args, **kw):
        value = func(*args, **kw)
        response.headers["Content-Type"] = "application/json"
        return json.dumps(value)

    return wrapper

def example():
    from cherrypy import quickstart
    from datetime import datetime
    class Time:
        @expose
        @jsonify
        def index(self):
            now = datetime.now()
            return {
                "date" : now.strftime("%Y-%m-%d"),
                "time" : now.strftime("%H:%M"),
                "day" : now.strftime("%A"),
            }

    quickstart(Time())

if __name__ == "__main__":
    example()

$curl -i localhost:8080
HTTP/1.1 200 OK
Date: Mon, 31 Jan 2011 03:18:34 GMT
Content-Length: 56
Content-Type: application/json
Server: CherryPy/3.1.2

{"date": "2011-01-30", "day": "Sunday", "time": "19:18"}

Thursday, January 20, 2011

Having hg output svn diffs

In my current workplace we use Subversion as the main VCS. I use Mercurial on top of it to get easy feature branches. My problem was that we use svn patches in our review process, and mercurial (hg diff -r default) gave me different format patches. The solution (as suggested by durin42 on #mercurial) was to use hgsubversion.

After installing hgsubversion using pip (or easy_install), you need to add it to your ~/.hgrc:
    [extensions]
    hgsubversion =

Then you can create patches using "hg diff -svn -r default", I use the following genpatch script for that:
#!/bin/bash
# Generate svn style diff for current hg feature branch

branch=$(hg branch)
if [ -z $branch ]; then
    echo "error: not a mercurial repo" 1>&2
    exit 1
fi

if [ "$branch" == "default" ]; then
    echo "error: in default branch" 1>&2
    exit 1
fi

hg diff --svn -r default > ${branch}.patch

Sunday, January 16, 2011

gcalc - Command line interface to Google calculator

#!/usr/bin/env python
'''Command line interface to Google calculator
    gcalc 100f c -> 37.7777778 degrees Celsius
'''

# Idea taken from http://bit.ly/dVL4H3

import json
from urllib import urlopen
import re

def main(argv=None):
    import sys
    from optparse import OptionParser

    argv = argv or sys.argv

    parser = OptionParser("%prog FROM TO\n\t%prog 100f c")
    opts, args = parser.parse_args(argv[1:])
    if len(args) != 2:
        parser.error("wrong number of arguments") # Will exit

    url = "http://www.google.com/ig/calculator?q=%s=?%s" % tuple(args)
    try:
        # We decode to UTF-8 since Google sometimes return funny stuff
        result = urlopen(url).read().decode("utf-8", "ignore")
        # Convert to valid JSON: {foo: "1"} -> {"foo" : "1"}
        result = re.sub("([a-z]+):", '"\\1" :', result)
        result = json.loads(result)
    except (ValueError, IOError), e:
        raise SystemExit("error: %s" % e)

    if result["error"]:
        raise SystemExit("error: %s" % result["error"])

    print result["rhs"]

if __name__ == "__main__":
    main()

Friday, December 31, 2010

svncommit

#!/bin/bash
# Template for "svn commit"
# Add "export SVN_EDITOR=/path/to/this/file" to your .bashrc

# Fail one first error
set -e

filename=$1
editor=${EDITOR-vim}

mv $filename /tmp

# The template
cat << EOF > $filename
Bug # 
Reviewed-by:

EOF

# Add file list to template
cat /tmp/$filename >> $filename
mtime=$(stat -c %y $filename)
$editor $filename

# Restore old file so svn will see it didn't change
if [ "$(stat -c %y $filename)" == "$mtime" ]; then
    mv -f /tmp/$filename $filename
fi

Wednesday, November 03, 2010

Delays

Sorry for long pause.

I just stared a new job which is not in Python. My interested was also shifted to Clojure.

I'll try to post here more from time to time, but the frequency be lower for the seen future.

In the meanwhile, keep on hacking!

Tuesday, September 07, 2010

Getting last N items of iterator

islice allow you to get the first N items of an iterator, but since it doesn't accept negative indexes - you can't use it to get the last N. The solution is very simple - use deque.


Sunday, August 29, 2010

Configuring Ubuntu For Asus Eee

Lately I lost my main laptop (belonged to the workplace), my older one allows me to use her Eee for now. The Eee came with Windows 7 starter edition, which I grew tired of fast. I've decided to try Ubuntu on it. Trying not to cause too much damage, I used Wubi.

Installing the main distro went without a problem, and after a reboot I had default Gnome desktop up. What I'm describing next is my attempt to get as much screen real estate as possible. Note that I'm spending most of my time in Firefox and in the shell.

General Setup

  • Delete the bottom panel (right click and "Delete This Panel")
  • Auto hide the top panel (right click, "Properties" and "Autohide")
  • Install "Gnome Do"

Firefox Setup

  • Right click on the toolbar, "Customize..." and check "Use small icons"
  • Click on the "View" menu and leave only the navigation toolbar
  • Uncheck the "Status bar" in the "View" menu
  • Install the "Hide Menubar" Firefox addon (clicking on "ALT" will show the menu)
  • I use GMail and Google Reader, so "Better GMail" and "Better GReader" addons helped
  • Install "Adblock Plus" to get more content inside web pages
  • This is just a personal preference, but I think installing "Chromifox Extreme" theme freed up some space as well

Terminal Setup

In the "Edit" menu click on "Profile preferences". And on the "General" tab, uncheck "Show menubar by default on new terminals" (use SHIFT-F10 to get a context menu to enable it)

Two More Things

If your kids play webkinz, install Google Chrome. It works much better there (there's always a scroll bar they can use).

As you probably figured out, I am looking for a job right now. If you know someone who is hiring, please point them to my resume.

Wednesday, July 07, 2010

curl and couchdb - a love story

CouchDB API is JSON over HTTP. Which makes curl my default tool to play with the database.

Here's a small demo:

Friday, June 11, 2010

import_any

Import any file as a Python module. This is a nice big security risk, so beware ...

from types import ModuleType

def import_any(path, name=None):
    module = ModuleType(name or "dummy")
    execfile(path, globals(), module.__dict__)

    return module

Thursday, June 03, 2010

Tagging last good build in git

git (and mercurial) has a nice feature that you can "move" tags (using -f). At Sauce Labs we use that to have a track of our last good build and the last deployed revision.

In our continuous integration system, we mark the last good build in the last step which is executed only if the test suite passed.
tag-ok-build.sh
#!/bin/bash

# Error on 1'st failure
set -e

tag=last-green-build
revision=$(git rev-parse HEAD)

echo "Tagging $commit as $tag"
git tag -f $tag $commit
git pull origin master
git push --tags origin master


To deploy, we use the following script who also tags the last deployed version.
deploy.sh
#!/bin/bash
# Deploy, meaning sync from last successful build

tag=last-green-build

# Fetch get the latest changes from remote but does not update working
# directory, just the index
git fetch --tags

# Merge to the last successful bulid
git merge ${tag}

# Tag currently deployed revision
git tag -f deployed ${tag}
git push --tags

Saturday, May 15, 2010

couchnuke

A simple script to "nuke" a couchdb server, use with care!

#!/bin/bash

# Delete all databases on a couchdb server

# Exit on 1'st error
set -e

if [ $# -gt 1 ]; then
    echo "usage: $(basename $0) [DB_URL]"
    exit 1
fi

URL=${1-http://localhost:5984}

echo -n "Delete *ALL* databases on $URL? [y/n] "
read ans
if [ "$ans" != "y" ]; then
    exit
fi

for db in $(curl -s $URL/_all_dbs | tr '",[]' ' \n  ');
do
    echo "Deleting $db"
    curl -X DELETE $URL/$db
done

Monday, April 12, 2010

Run a temporary CouchDB

One nice solution to isolate tests, it to point the code to a temporary database. Below is a script to launch a new instance of CouchDB on a given port.

#!/bin/bash
# A script to run a temporary couchdb instance

if [ $# -ne 1 ]; then
    echo "usage: $(basename $0) PORT"
    exit 1
fi

port=$1

base=$(mktemp -d)
dbdir=$base/data
config=$base/config.ini
mkdir -p $dbdir

cat <<EOF > $config
[httpd]
port = $port

[couchdb]
database_dir = $dbdir
view_index_dir = $dbdir

[log]
file = ${base}/couch.log
EOF

trap "rm -fr $base" SIGINT SIGTERM

echo "couchdb directory is $base"
couchdb -a $config

Friday, April 09, 2010

Sourcing a shell script

Sometime you want to emulate the action of "source" in bash, settings some environment variables.
Here's one way to do it:

from subprocess import Popen, PIPE
from os import environ

def source(script, update=1):
    pipe = Popen(". %s; env" % script, stdout=PIPE, shell=True)
    data = pipe.communicate()[0]

    env = dict((line.split("=", 1) for line in data.splitlines()))
    if update:
        environ.update(env)

    return env

Thursday, March 25, 2010

Showing Solr Search Results Usign jQuery

Solr is a wonderful product. However it does not have a integrated search page (there's one in the admin section, but it's nothing you want to expose).

Luckily, Solr supports JSON results and even JSOP, which means we can do all the code for searching in our web page.

Note that I'm using the Solr demo that comes with the distribution, your search results might have different attributes depending on the schema.

<html>
    <head>
        <title>Solr Search</title>
    </head>
    <body>
        <h3>Solr Search</h3>

        Query: <input id="query" /> 
        <button id="search">Search</button>
        <hr />
        <div id="results">
        </div>
    </body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script>
        function on_data(data) {
            $('#results').empty();

            var docs = data.response.docs;
            $.each(docs, function(i, item) {
                $('#results').prepend($('<div>' + item.name + '</div>'));
            });

            var total = 'Found ' + docs.length + ' results';
            $('#results').prepend('<div>' + total + '</div>');
        }

        function on_search() {
            var query = $('#query').val();
            if (query.length == 0) {
                return;
            }

            var url='http://localhost:8983/solr/select/?wt=json&json.wrf=?&' +
                    'q=' + query;
            $.getJSON(url, on_data);
        }

        function on_ready() {
            $('#search').click(on_search);
            /* Hook enter to search */
            $('body').keypress(function(e) {
                if (e.keyCode == '13') {
                    on_search();
                }
            });
        }

        $(document).ready(on_ready);
    </script>
</html>  

To run the Solr demo do extract the distribution, then
cd example 
java -jar start.jar > /dev/null &
cd exampledocs 
./post.sh *.xml

Friday, March 12, 2010

Summarizer

Just a quick note that I've uploaded the code to "Text Summarizer" here.

Thursday, February 11, 2010

Parse HTTP Response

Steve commented (justly) that there is no Python library function to parse HTTP transportation. Here is one way to do it:

Thursday, February 04, 2010

Publish Buildbot builds to Twitter

Buildbot exposes a simple XMLRPC protocol. Using this we can write a small daemon that pools the build status and publish it somewhere (or do other interesting stuff with it). In our case we'll publish the results to twitter using its publishing API.
#!/usr/bin/env python
# Watch builds on buildbot and publish to twitter

from time import time, sleep
from xmlrpclib import ServerProxy
from urllib import urlopen

user, password = "tebeka", "SECRET_PASSWORD"
bbot_url = "http://buildbot.example.com/xmlrpc"
tweet_url = "http://%s:%s@twitter.com/statuses/update.xml" % (user, password)

def main():
proxy = ServerProxy(bbot_url)
last_time = time()

while 1:
now = time()
builds = proxy.getAllBuildsInInterval(last_time, now)
for build in builds:
builder, build, status = build[0], build[1], build[5]
status = "OK" if status == "success" else "BROKEN"
message = "[%s] build %s is %s" % (builder, build, status)
urlopen(tweet_url, "status=%s" % message)

last_time = now if builds else last_time
sleep(10)

if __name__ == "__main__":
main()

Friday, January 15, 2010

github-url

#!/bin/bash
# Show github URL for a given file

if [ $# -ne 1 ]; then
echo "usage: $(basename $0) FILENAME"
exit 1
fi

if [ ! -e $1 ]; then
echo "error: can't find $1"
exit 1
fi

if [ -d $1 ]; then
cd $1
dest=.
else
cd $(dirname $1)
dest=$(basename $1)
fi

url=$(git config remote.origin.url | cut -d: -f2)
if [ $? -ne 0 ]; then
echo "error: can't get remote url"
exit 1
fi
url=${url/.git/}

path=$(git ls-files --full-name $dest | head -1)
if [ $? -ne 0 ]; then
echo "error: git does not know about $1"
exit 1
fi

branch=$(git branch --no-color 2>/dev/null | grep '^* ' | sed 's/* //')
prefix="https://github.com/$url"
if [ "$dest" == "." ]; then
path=$(dirname $path)
url="${prefix}/tree/${branch}/$path"
else
url="${prefix}/blob/${branch}/$path"
fi

echo $url

Wednesday, December 30, 2009

Making TeamSpeak 3 work on Ubuntu

 TeamSpeak 3, you can download the Linux client from their site. The only problem is that currently (beta8) the client don't play nice with pulseaudio.

EDIT: Seems like beta-20 is working well with Ubuntu 10.4. Use the ts3client_runscript.sh (make sure you change directory to the TeamSpeak directory first).

After digging in the Ubuntu forums (can't find the exact post link), I came with the following solution:
  • Extract the client (cd /opt && sh ~/Downloads/TeamSpeak3-Client-linux_amd64-3.0.0-beta8.run)
  • Create the following script
#!/bin/bash

# Make teamspeak work on Ubuntu (basically disable pulseaudio before launching)

conf=$HOME/.pulse/client.conf
backup="${conf}.bak"

if [ -f "$conf" ]; then
    mv "$conf" "$backup"
fi
echo "autospawn = no" > "$conf"
pulseaudio --kill

cleanup() {
rm "$conf"
if [ -f "$backup" ]; then
        mv "$backup" "$conf"
fi
    pulseaudio -D
}

cd /opt/TeamSpeak3-Client-linux_amd64/
./ts3client_linux_amd64&
pid=$!
trap "kill -9 $pid; cleanup" SIGINT SIGTERM
wait $pid
cleanup
Good luck! (The other option is to use mumble.)

Tuesday, December 29, 2009

Multi VCS status

Since I use mercurial, git, subversion and sometimes bzr. I use the following script (called st) to find the status of the current directory.

#!/bin/bash

# Repository status

root=$PWD
while [ "$root" != "/" ];
do
if [ -d "$root/.hg" ]; then
hg status
break
fi
if [ -d "$root/.git" ]; then
git status --untracked-files=no
break
fi
if [ -d "$root/.svn" ]; then
svn status
break
fi
if [ -d "$root/.bzr" ]; then
bzr status
break
fi
root=$(dirname "$root")
done

if [ "$root" == "/" ]; then
echo "error: can't find a repository here"
exit 1
fi

Friday, December 11, 2009

Add Subversion files to git

I you need to start a git repository from a Subversion one, and you don't care about history. Then the following should be helpful.

#!/usr/bin/env python

# Add subversion repsotory to a git one
# Run (at top of svn repository)
# git init
# git-add-svn
# git ci -a -m "Initial import from svn $(svn info | grep Revision)"

__author__ = "Miki Tebeka <miki@mikitebeka.com>"

from os import walk
from os.path import join
from fnmatch import fnmatch
from itertools import ifilter
from subprocess import call
from sys import stdout
import re

is_vcs = re.compile("\.(git|svn)", re.I).search

def make_pred(exclude):
def is_ok(name):
return not any((fnmatch(name, ext) for ext in exclude))
return is_ok

def all_files():
for root, dirs, files in walk("."):
if is_vcs(root):
continue
for name in files:
yield join(root, name)

def git_add_svn(exclude):
pred = make_pred(exclude)
for filename in ifilter(pred, all_files()):
print filename
stdout.flush()
call(["git", "add", "-f", filename])

def main(argv=None):
import sys
from optparse import OptionParser

argv = argv or sys.argv

parser = OptionParser("%prog [options]")
parser.add_option("-e", "--exclude", dest="exclude", action="append",
help="extension to exclude")

opts, args = parser.parse_args(argv[1:])
if args:
parser.error("wrong number of arguments") # Will exit

git_add_svn(opts.exclude or [])


if __name__ == "__main__":
main()

Friday, December 04, 2009

Making GMail The Default Email Application On Ubuntu

  • Open System->Preferences->Preferred Applications
  • Change "Mail Reader" to "Custom"
  • Write bash -c 'to="%s"; gnome-open "https://mail.google.com/mail?view=cm&tf=0&to=${to#mailto:}"' in the "Command:" box

Saturday, November 07, 2009

Find Cell Number Carrier

#!/bin/bash

# Find the carrier for a cell number using whitepages

# Miki Tebeka <miki@mikitebeka.com>

if [ $# -ne 1 ]; then
echo "usage: $(basename $0) NUMBER"
exit 1
fi

# Fake Firefox
agent="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) "
agent="$agent Gecko/20061204 Firefox/2.0.0.1"

url="http://www.whitepages.com/carrier_lookup?"
url="${url}carrier=other&number_0=${1}&name_1=&number_1=&name_2=&number_2="
url="${url}&name_3=&number_3=&response=1"

curl -A "$agent" -L -s "$url" | \
grep -A1 carrier_result | tail -1 | \
sed -e 's/.*(\(.*\))/\1/' -e 's/^ \+//'

Tuesday, October 27, 2009

EnSalvage

I wrote a little utility to help a fried whose Entourage database got corrupted.

If anybody cares, here it is, you can even download a .app for Mac :)

Friday, October 23, 2009

binary

#!/bin/bash
# Show binary representation of a number

if [ $# -ne 1 ]; then
echo "usage: `basename $0` NUMBER" 1>&2
exit 1
fi

echo "obase=2; $1" | bc -l

Friday, October 09, 2009

strftime

#!/usr/bin/env python
'''Convert seconds since epoch to human readable time format.
Useful in cases where logs dump time information in epoch.

$ echo 1255099899 | strftime
10/09/2009 07:51
$
'''

from time import strftime, localtime
import re

def main(argv=None):
import sys
from optparse import OptionParser

argv = argv or sys.argv

default_format = "%m/%d/%Y %H:%M"
parser = OptionParser("%prog [options] [FILENAME]")
parser.add_option("--format", dest="format",
help="time format [%s]" % default_format, default=default_format)
parser.add_option("--doc", help="show strftime documentation",
dest="doc", action="store_true", default=0)
parser.add_option("--quote", help="quote result (e.g. '10/2/2009')",
dest="quote", default="")

opts, args = parser.parse_args(argv[1:])
if len(args) not in (0, 1):
parser.error("wrong number of arguments") # Will exit

if opts.doc:
import webbrowser
url = "http://docs.python.org/library/time.html#time.strftime"
webbrowser.open(url)
raise SystemExit

infile = args[0] if args else "-"
if infile == "-":
info = sys.stdin
else:
try:
info = open(infile)
except IOError, e:
raise SystemExit("error: can't open %s - %s" % (infile, e))

def callback(match):
t = localtime(float(match.group()))
human = strftime(opts.format, t)
if opts.quote:
human = human.replace(opts.quote, r"\\%s" % opts.quote)
human = opts.quote + human + opts.quote

return human

# We assume 1XXXXXXXXX (2001 +) dates, so we won't convert *everything*
print re.sub("1\d{9}(\.\d+)?", callback, info.read())


if __name__ == "__main__":
main()

Blog Archive