Thursday, July 8, 2010

Tab completion in the python interpreter

Since I program mostly in python, I spend a lot of time in the default python interpreter of my distro. I even use it as a replacement for bash quite often - mainly because of python's excellent string manipulation capabilities and well, I confess, also because bash's syntax always baffles me (especially the if statements). So whenever I get stuck with some bash code, I take the escape route of opening up the python interpreter.

But the default python interpreter that comes bundled with most linux distros (and also the OS X), lacks a killer feature of bash: tab completion. One can find this feature in some python interpreters like bpython, but they sometimes overdo it (popping up suggestions even when one doesn't want them, for example).

Anyway, I recently learned from this excellent blog post by Matthias Friedrich that it is REALLY easy to add tab completion in the python interpreter: all one needs to do is import rlcompleter and readline modules of python and assign a completer key (Tab is the most natural choice, of course). The blog post I linked above also describes how to set the PYTHONSTARTUP environment variable so that tab completion is enabled every time the interpreter is opened.

Since python's readline module uses the same GNU readline utility used by bash for tab-completion, it should be just as much fun, right? Well, that's not quite the case. Because tab completion imposes a severe restriction on you here: you cannot use the Tab key for indenting your code anymore. And using single space for python's code indentation is a very bad idea: trying to distinguish between different indentation levels in heavily nested code blocks is a trouble not worth taking even for sake of the benefits given by tab completion.

Anyway, I figured out a way to circumvent the problem by looking at these lines in the source code of the rlcompleter module (The file in question is /usr/lib/python2.6/ for Debian and Ubuntu): When one presses the completion key the function complete(self, text, state) defined in line#29 is called with the word being typed as it's second argument. I simply added an extra elif after the 42nd line asking it to return '\t' if the argument consists of only whitespaces: So, now I have all the goodness of tab completion without losing the ability of using tab for code indentation!

Tuesday, July 6, 2010

Weird rounding-off bug in WebKit

I ran into this weird problem while I was writing the css for my personal website. There is a navigation bar in the page with 7 links inside a <div> sitting side-by-side:

the page rendered like this in Firefox (and for certain viewport widths in chrome)

I specified the width of the first six links to be 14% and the that of the last one 16% in the css. All the paddings, margins and borders were set to zero. So, they seven links should take up the entire width of the containing <div>, right?

Well, in webkit based browsers (I checked in Google Chrome for linux and Safari in Mac OS X leopard), that sometimes just would not happen: the total width of the seven links would fall short of the total width of the containing <div> by just a few pixels and there would be an annoying narrow strip showing the background color of the <div>.

the pesky extra pixels in Chrome

More surprisingly, on resizing the browser window, sometimes the strip would go away and sometimes it would come back again. Firefox and opera didn't have any such problem rendering the page, though.

Now, I am no specialist in web designing. But still my common sense says that as long as 6*14+16 is 100, no such strip should be there. What the hell is going on?

Luckily, I found the answer while fiddling with the excellent Developer Tools of the Chrome browser. The widths computed by Chrome were:

ElementComputed widthIntended width
Container <div>1013-
First 6 <a>'s1411013*0.14=141.82
The last <a>1621013*0.16=162.08

Yes, webkit is taking the integer parts of the floats while computing the widths of the elements, instead of rounding off properly to the nearest integer. Therefore it's no wonder that for certain values of the width of the containing <div>, the sum of the widths of the child elements isn't adding up to the width of the parent.

I later found out that this bug has been reported in WebKit Bugzilla almost 5 years ago and hasn't been resolved yet. The bugzilla page doesn't even indicate that anybody ever tried to fix this crucial flaw capable of ruining the appearance of many webpages single-handedly.

Anyway, I wrote a few lines of javascript to make sure that the site renders correctly in chrome and safari and it's working just fine. It's really annoying though, being forced to use javascript to compensate for a rendering engine's shortcoming in this era of standards-compliant browsers. I shudder to imagine what I would encounter when I finally test the site in IE.

Sunday, May 30, 2010

Too obvious a solution

Last week I decided to give my ageing desktop computer a much needed overhaul: the ancient 2.66 GHz Intel Pentium 4 processor powering the machine had become obsolete long ago. Since the new Intel processors of Core ix family need motherboards of newer chipsets and since those motherboards support only DDR3 memory, I had to buy all those components, too. After a little bit of research, I chose:

To house all these fancy stuff, I went for a rather nice-looking CoolerMaster cabinet as well:

Assembling the components was a no-brainer, thanks to the tool-less design of the cabinet. So far, so good.

But the problem was, as usual, with the Windows 7 OS. Although it was already installed on the hard disk, it refused too start. Although Ubuntu was running without any such fuss, I was really in the mood for playing GTA IV. I bought that game almost two years ago but couldn't play simply because my computer lacked the required processing power. Now with the quad-code cpu, it was not a problem any more. But it appeared that Windows needed to be reinstalled.

Now, installing windows by booting from a DVD-ROM drive was not an option. What I needed was a bootable Windows 7 installer USB disk. A little bit of googling revealed that it is quite easy to create such a disk from a Windows 7 DVD : all one needs to do
is create an NTFS partition, copy the comtents of the DVD to the partition and (here's the catch) make the partition bootable using the diskpart software bundled with windows. The problem was that I didn't have any working Windows installation on any computer. On my laptop, Ubuntu was the only OS installed.

I hoped that unetbootin might help, but it appeared that it can only create bootable disks of linux and BSD distros, although the software itself is available for Windows also. Finding no other solution on the web, I did the first thing that came to my mind: I just enabled the boot flag on the partition containing the win 7 installation files using Gparted. And Voila, it worked!

Saturday, January 16, 2010

A lyric script for amarok 1.4 written in Python

I switched to amarok as my default music player after I came to know about the concept of amarok scripts - simple scripts interacting with amarok via DCOP and adding various functionalities to the player. There's a plethora of those scripts on the web: the best place to look for them is

However, my enthusiasm about amarok took a heavy blow pretty early - the version of amarok included in the distro I'm using (Ubuntu 9.10), turned out to be painfully sluggish and unresponsive. Unwilling to give up on the brilliant player on account of this single flaw, I decided to go for a previous version: I used an older version of amarok on either Ubuntu 8.10 or Debian Lenny for a very brief period and it was not at all sluggish back then. So I had to go through the usual chores: adding Jaunty and Intrepid repos in my sources.lst, updating apt (at this point synaptic showed a nasty error message complaining about its cache-limit not being big enough. A little googling later, I was able to increase
the cache-limit by editing /etc/apt/apt.conf.d/70debconf), and force-versioning the packages in Synaptic. And sure enough, the Intrepid version ran without any fuss.

After the new (old) amarok scanned and indexed my music collection, I promptly installed a couple of amarok scripts: Gnome-Multimedia-Keys, Amarok notify-osd(This one replaces the rather crude amarok osd's with the new default notification system of ubuntu). At this point I noticed that amarok has a separate category of scripts named 'lyrics'. All of them seem to be there for basically performing one simple job: fetch the lyrics of the song currently playing in amarok and show it under the 'Lyrics' tab in the left pane of the amarok window. But there was just one small problem: none of the included lyric scripts could display the lyrics of any song I'd listen to.

This really stumped me because whenever I google for the lyrics of any song, I generally find it in the first search result Google returns. I even found a lyric script which uses a google search, but all it did was simply render the google search results page onto the lyrics pane instead of showing the actual lyrics. Therefore I decided to write a lyric script myself. This script finds the lyrics of a song using the awesome python library for google search by Peteris Krumins. The first version of the script surely needs more polishing but at least does its job pretty nicely for me.

The script can be downloaded from here.

Tuesday, January 12, 2010

Living harmoniously with Ubuntu 9.10 and Nokia 5800 XpressMusic

As I mentioned in the previous post, I ran into some trouble syncing my new Nokia 5800 XpressMusic with my laptop running Ubuntu 9.10 Karmic Koala. Sure, the phone would work just fine in mass storage mode via the included data cable and let me transfer anything onto the SD card, but I was not satisfied with it. Apart from other issues, that way I'd still have to create the playlist manually in the phone's music player. And since the phone lacked the capability  to add all the contents of a folder to a playlist (It doesn't allow changing the order of songs in an existing playlist either - those guys at Finland seem to be rather aversive to the idea of playlists), I was not exactly thrilled at the prospect of having to add each song in the full discography of Poets of the Fall to a playlist.
         I needed a program which would not only transfer all the songs in a playlist in my computer to my phone but also create the appropriate playlist in the phone. At first I unsuccessfully tried my luck with Rhythmbox : it showed all the tracks on my phone but stubbornly refused to play any of them.
         But I got lucky the second time. I happened to start amarok - the music player I abandoned ever since Jaunty Jackalope due to its sluggish performance. After googling for amarok plugins to sync with Nokia symbian phones, I came to know about the existence of amarok scripts. Although I couldn't find any script meeting my requirements on the web, on reading some of the scripts I became aware of the powerful DCOP interface of amarok. To my great delight, I found out that it  gives one the freedom to control (and automate) every aspect of that brilliant music player and natively supports python scripts.
         So I chose the obvious solution. I  wrote the code myself in python. The actual procedure was very simple: ask the user to select the playlist he wants to sync and where he wants to copy them to, copy-paste the whole damn thing, create a playlist and be done with it. But  the user interaction part was problematic: the user has to specify long pathnames. Doing so at a  python raw_input prompt without tab-completion can be frustrating. I had to create a gui.
       The last time I tried to code some gui stuff in python, I decided to learn Tkinter. Needless to say, that time I created some of the ugliest possible widgets in the universe. I had no intension of making the same mistake again. But the idea of going through the whole elaborate affair of learning pyQt or pyGTK for creating some simple dialog boxes did not seem appealing to me. While facing this dilemma, I found another gem hidden in the lines of the amarokscripts I already had in my system: kdialog. This was just the perfect thing for my code: simple one-line commands to present pretty dialog boxes showing a message or a file browser where the user can select the paths by means of good old point and click.
       Well, here's another interesting piece of trivia: the temporary playlist amarok creates when you drag and drop music files to its main window is saved as an XML file, but the playlists created manually are saved in normal .m3u format. Unaware of the second fact, I ended up studying xml parsing in python. Although it was totally unnecessary, I learnt about a couple of very powerful python libraries: xml and urllib.
       So the process of syncing the Nokia 5800 with Ubuntu was not as simple as I thought it would be. But my chance encounter with DCOP and urllib turned out to be rather fortuitous as it enabled me to write my second amarok script. I'll write about it in another post someday.

Monday, January 11, 2010

Mixed feelings about my new Nokia 5800 XpressMusic

Well, I have to admit that the decision of buying this phone was a rather impulsive one. That sort of behaviour is certainly not characteristic of me. But the fact is that I haven't had any new gadget to fiddle with for quite a few month. Besides, the included freebies were really tempting: a Sennheiser headset and 100 songs downloadable from Nokia's new Ovi store. Although both of them turned out to be rather disappointing (I'll come to those issues later), I am quite satisfied with the phone.

         The large screen produces crisp colours. Excellent for watching videos. The touch based UI could be a bit better though, occasionally it can't understand whether I want to select something or just scroll through the page. Being an XpressMusic, it is adequately equipped to belt out good quality music. But the Sennheiser PMX-60 headset failed to live up to the expectation (The first thing I did after buying the phone was to check that one out, even before the phone itself!) and the high level of expectation is not to be blamed for that. The sound quality is not  actually bad, but it leaks sound A LOT. Likewise, it allows enough of outside noise to make me reach  for my Creative EP-630 noise isolation earphones ( listening to music on this Nokia/Creative combination is a real pleasure, by the way).
         The Ovi store was an unmitigated disaster. I knew that the website was IE only, but I planned to wiggle out of that one by installing IE on WINE. But lo and behold; the Finnish web designing geniuses did not stop at only making the website incompatible with all the decent browsers over there, but made no provision of directly downloading music from the site at all. The actual job must be done on a software specifically designed for that. And needless to say, that software is windows only. So the only option I have for redeeming that offer is to download the songs directly to my phone.
          To be fair, the phone itself leaves very little to be desired at its price point. Getting both Wi-fi and GPS at this price is really nice. Besides, Symbian being the most popular OS for mobile devices, an unnervingly huge collection of softwares are available on the web.
          But the biggest plus point of this phone is that the symbian platform is extremely python-friendly. The default symbian port of python (made by Nokia itself) even includes the option of setting up a serial connection to a linux box over bluetooth. This one turned out to be a major boredom buster for me, as it enabled me to tinker with the innards of the phone. They even provided a script to create .sis installers from a python script. I haven't checked it out yet but plan to do that as soon as I write any big enough program.
P.S. - I actually started to write the blog with the intention of describing the ordeal I had to go through for syncing the playlists on my computer with the phone, but unfortunately it is almost 7 a.m. So that story will have to wait (In case you are wondering what kind of errand I am up to at 7 a.m. in a January morning, I'm just hoping for a couple of hours of sleep before classes start).