Open Library and the beauty of Python

I’m currently working on a books site, so I recently was in the need to interact with the Open Library’s APIs, specifically to retrieve a book’s cover based on its ISBN. The API puts a limit on how many cover requests an IP can do when looking by ISBN, but not if you look by the OLID (Open Library book id), so I decided to see how hard it was to get a book’s OLID once I got its ISBN.

It’s useful every once in a while to recall how hard it was to accomplish certain things on certain cluttered and rigid languages, and how easy it is when you can use something as powerful as Python1. Well, I ended up with something like:

import urllib2
import json

URL = 'http://openlibrary.org/api/books?bibkeys=ISBN:{isbn}&format=json&jscmd=data'

def get_olid(isbn):

    response = urllib2.urlopen(URL.format(isbn=isbn))
    js_dict = json.loads(response.read())
    key = 'ISBN:{isbn}'.format(isbn=isbn)
    if js_dict.has_key(key):
        return js_dict[key]['identifiers']['openlibrary'][0]

But the real beauty of Python this time was not the clearness or succinctness of the code, nor how useful the libraries were, but rather how easy it was to figure out how to use those libraries. Just checked the function signatures on the online documentation (I could have used the help function, but I like the docs better), and started to play with them on the shell until I got the result I expected. It even saved me the time to read thoroughly the OL reference, since I could see in the shell the contents of the response they are sending me on each call.

1. Yes, there are no silver bullets, but there are some real good bullets and some real bad bullets for each job, and I find Python to be a great general purpose bullet :P.

Advertisements

Deploying a django project on nginx with gunicorn

Back when I started  working with django, one of the things that I found was sort of difficult to do was deploying a project, as suggested by the documentation, using apache and mod_wsgi (and previously with mod_python), at least when it came to serve several projects and its media at the same server. Doing all those things with nginx and gunicorn is shockingly easy.

To add a new site on nginx I just touch a new file in the /etc/nginx/sites-enabled, with the contents:

server {
    listen 80;
    server_name myserver.com;

    location / {
        proxy_pass http://127.0.0.1:8888;
    }
}

Now, supposing I want to have acces and error logs, and serve my site’s and the admin site’s media, I would add some lines to the server:

access_log /var/www/django/myproject/access.log;
error_log /var/www/django/myproject/error.log;

location /media {
    root /var/www/django/myproject;
}

location /admin_media {
    alias /usr/local/lib/python2.7/dist-packages/django/contrib/admin/media;
}

Where /media and /admin_media are the MEDIA_URL and ADMIN_MEDIA_PREFIX pointed by my settings.py. That is, I have a media folder in my project root directory and the admin media is served directly from the django installation.

To actually run my django site on http://127.0.0.1:8888 I create a gunicorn configuration file in my project root (where manage.py is located):

bind = "127.0.0.1:8888"
workers = 3

Assuming I named the file gunicorn.conf.py, gunicorn is run from the same directory doing:

gunicorn_django -c gunicorn.conf.py -D

And that’s it.

When the code is updated, there’s no need to restart nginx; instead, gunicorn is restarted (not so prettily) with:

kill -HUP <main gunicorn process id>

To serve multiple sites, just use differents ports (8888 in the example) in each one’s configuration file.