Friday, May 30, 2008

Jython: How to Instantiate Classes Written in Python Code

This week, as part of my GSoC project, I had to do some work related with zxJDBC, the very cool DBAPI <-> JDBC brige which is bundled with Jython. Among other things, I'm improving the default type mapping, adding support for converting java.sql.* instances to pythonic datetime.* ones.

As the type mapping is written inside a class written in Java, I was confronted to the problem of how to instantiate datetime.* objects, which (by now) Jython implements using pure python code. The answer was very simple: Just do by hand what Python always does when you write:

import datetime
datetime.date(year, month, day)

You know, the import statement is implemented by the __import__ builtin, foo.bar is getattr(foo, 'bar') and f(x) is f.__call__(x). Then, the following is equivalent to the previous snippet:

datetime = __import__('datetime')
getattr(datetime, 'date').__call__(year, month, day)

Which, translated to Java/Jython looks almost the same:

PyObject datetime = __builtin__.__import__('datetime')
datetime.__getattr__('date').__call__(Py.newInt(year),
Py.newInt(month),
Py.newInt(day))

Once you get the idea, not only instantiating, but doing anything with classes written with Jython from Java code looks like a piece of cake.

Hey!, How easiest could it be?

Note that this works if all the Jython machinery is in place. This will be the case if your Java code is being called (directly or indirectly) from python code being ran on Jython. As any Jython module, such as zxJDBC, is always used from python code, this is something to have in mind when writing them in Java.

Wednesday, May 28, 2008

Loading Rails Fixtures Without Deleting Existing Records

This blog post by choonkeat explain how to overcome the ruby limitation which forbids the use of the "class [name-of-the-class-to-monkeypatch]" statement inside methods.

Why would you need this?

I just used it to define a db:fixtures:insert task, which differs from db:fixtures:load on not removing existing records. I could have copied the Fixtures.create_fixtures method and remove the line which calls Fixtures#delete_existing_fixtures. But we all know that copy/paste have its problem. Monkey-patching Fixtures#delete_existing_fixtures to do nothing seemed like a less ugly solution, except that it could have unforeseen consequences. So I wanted to limit the effect of the monkey patching to last as little as possible. Here is the result:

# Mimics rails Fixtures.create_fixtures, without deleting existing records
# from the database.
#
# The implementation monkey-patches Fixtures.delete_existing_fixtures
# *temporarily*, restoring the original behaviours before exiting
def self.insert_fixtures(fixtures_directory, table_names, class_names = {})
::Fixtures.module_eval do
alias_method :original_delete_existing_fixtures,
:delete_existing_fixtures
def delete_existing_fixtures
end
end
::Fixtures.create_fixtures(fixtures_directory, table_names, class_names)
::Fixtures.module_eval do
alias_method :delete_existing_fixtures,
:original_delete_existing_fixtures
remove_method :original_delete_existing_fixtures
end


[By the way, looks like this monkey-patch -> call -> undo monkey-patch may be end being common idiom which could be better encapsulated on another method. Perhaps someone already did it...]

[Update: Here is what seems an elegant way to do it]

Finally, Here is the task:

namespace :db do
namespace :fixtures do
desc "Inserts fixtures into the current environment's database,
*without* deleting existing records (as db:fixtures:load does).
Insert specific fixtures using FIXTURES=x,y"

task :insert => :environment do
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'test', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
Utilities::Fixtures.insert_fixtures('test/fixtures', File.basename(fixture_file, '.*'))
end
end
end
end

Thursday, May 15, 2008

Ubuntu: Changing Swap Size Without Loosing Hibernation

Short recipe:

After resizing your swap partition (if you resized the swap partition instead of adding another one), use vol_id -u /dev/<device-name> to get the new partion UUID, and put it at the appropriate place on /etc/fstab and /etc/initramfs-tools/conf.d/resume. Then, run:

$ sudo dpkg-reconfigure initramfs-tools

Now, the not so long history:

When I made the changed on my partitions to go back to Linux, I made a mistake reserving only 1Gb of space for the swap partition. It sounded like a reasonable amount as I'd have a total of 3Gb of virtual memory, enough for my typical usage.

I forgot about hibernation, which, on Linux, uses the swap partition to store the main memory contents (unlike Windows, which have a separate file for hibernation). To be honest I didn't even considered hibernation when installing Ubuntu, it never worked in the past. But now it worked. At least when I wasn't using too much main memory. If i was, then it wouldn't fit in the swap file and the laptop couldn't hibernate.

GParted came to the rescue again, and my swap partition grew to a little more than 2Gb, which is the size of my main memory. Reboot on Ubuntu again and...no swap was recognized. WTF!?

After looking at the /etc/fstab I realized that the partitions are identified by an UUID rather than by their device name. So no /dev/sda5 (my swap partition) there. Just a bunch of hexadecimal digits. I thought of wiping that mess out and go back to my familiar devices names, but googling first was a better option. Then I found that I can easily know the new UUID of the partion using:

$ sudo vol_id -u /dev/sda5

And, what really saved me from another headache was discovering that, after a change of UUID you must update two files: the “classic” /etc/fstab and also /etc/initramfs-tools/conf.d/resume. And finally run:

$ sudo dpkg-reconfigure initramfs-tools


Not that straightforward as it could be, but I have no real complaints, as I now can hibernate my laptop whenever I want.

Sunday, May 11, 2008

Back to Linux

I'm back to Linux as my main OS. As you can infer from previous posts, I was using Windows on the past months. In fact, I was using Windows since I bought my Dell Laptop, a year and half ago. It wasn't just Windows: I used cygwin and coLinux inside my Windows XP to have a reasonable development environment too (except for Java, which feels OK for me on plain Windows). Somehow I survived running that all this time, forced in part because I had to use some Windows-only VPN-client software on my job.

Two weeks ago things changed. I did some backups, ran GParted (couldn't find my laptop's recovery disk, so my original plan of wiping out Windows and reinstalling everything couldn't go ahead) to shrink the NTFS partition, checked that everything was OK and installed Ubuntu Linux.

Choosing Ubuntu was a new thing: I started ten years ago with Redhat and changed to Debian “sid” five or six years ago (apt-get was such an important advantage on that times). But now I had little time to fight with my particular hardware configuration and heard that Ubuntu ran very good on Dell laptops. So I tried it and I'm not looking back.

The only thing that wasn't automatically configured was my broadcom wireless card. Hey, having to deal with only one driver issue looked very promising to me (but it will drive away people who expects everything running out of the box, anyway). Installing b43-fwcutter and following the instructions made me a happy user. But all that could be caused by me installing a non-final version of the distribution.

The overall impression is great. I'm even sticking to Gnome. Always used KDE in the past, but the default gnome environment seems very functional and I have nothing to say against it. And compiz is a great way to impress people :). Frankly, I miss konqueror versatility. But, considering that I would use Firefox as my main browser anyway (basically because it has great extensions; I don't like it eagerness for memory), that's no big deal.

Migrating things from my coLinux filesystem couldn't be easiest: just moving files, and everything (emacs, latex, gnucash, mercurial, svn) works.

Above all, having an unix-like environment is priceless.

If FlexBuilder 2 had a Linux version, it would be perfect. Perhaps too perfect to be real.