Friday, November 16, 2012

Context Managers Are Your Friend

Anytime there is built in support for functionality that reduces the potential for bugs (and number of lines of code), while simultaneously improving code legibility, that functionality should be used with reckless abandon. Similar to function decorators that can be used to elegantly wrap functions with additional functionality, context managers allow often repeated blocks of code to be elegantly written.

Let's take a common database transaction use-case as an example.
try:
    db.insert(some_pbject)
except:
    db.rollback()
else:
    db.commit()

... do some stuff ...

try:
    db.insert(some_other_pbject)
except:
    db.rollback()
else:
    db.commit()

As you can see, there is nothing special about the (pseudo) code above, as it simply is some code to insert some objects into a database. There isn't really anything wrong with above code, but using context managers we can do this in a much more elegant way:
from contextlib import contextmanager

@contextmanager
def transaction(db):
    try:
        yield
    except:
        db.rollback()
    else:
        db.commit()

with transaction(db):
    db.insert(some_pbject)

... do some stuff ...

with transaction(db):
    db.insert(some_other_pbject)

Ok, ok, that's about the same number of lines of code than the original, but now, every time you want to commit something to the database, you never need to write that try-except-else block to make sure that things are either committed or rolled back - you merely need to use the with transaction(db) statement and you're good to go! And since you only wrote the commit logic once, if there is a bug, it only needs to be fixed in one location. Similarly, if after you've written a bunch more code you decide that you want to log every time there is an exception, you simply have to add the logging code in the except block in your transaction function.

Now that you've gotten a taste of the power of context managers, you will probably want to know more about how they actually work. As there is a great explanation here and here, I'm not going to cover that in this post. You may also check out the Python docs on the @contextmanager.

Sunday, November 11, 2012

Create A Netflix-like Page for Your Movies

A while back I undertook the project of converting all my DVDs to files that I could store on a computer. Although having all movies in a single directory is very convenient for 'movie night', it's not great for browsing. Admittedly, there is no way to replicate the feeling of actually browsing DVDs sitting on a shelf, I decided to create a simple webpage with all of the movie cover images along the lines of what Netflix and Amazon have. Of course, the result isn't identical (nor did I feel like spending the time to make it absolutely perfect), but here is what it basically looks like (and yes, our movie collection is eclectic).


In case anyone wants to do the same thing with their movies, you can check out the script that I put together that generates this page. One big caveat, it assumes that your movie files are named 'nicely' and are of the form MOVIE_NAME (YEAR).avi (e.g. Battle Los Angeles (2011).avi). The year is optional, however. Once you have the script, you can run it as follows:
python moviepagemaker.py [movie_dir] [output_dir]
In case you don't feel like going over to github to take a look at the code, here it is in full. Before you complain, this isn't exactly the best code that I have ever written (and it was written while watching Drive on a Saturday night), but it should be good enough to get the job done.