diveintopython.org
Python for experienced programmers

 

Chapter 3. An Object-Oriented Framework

Table of Contents

3.1. Diving in

This chapter, and pretty much every chapter after this, deals with object-oriented Python programming. Remember when I said you should know an object-oriented language to read this book? Well, I wasn't kidding.

Here is a complete, working Python program. Read the doc strings to get an overview of what it does and how it works. As usual, don't worry about the stuff you don't understand; that's what the rest of the chapter is for.

Example 3.1. fileinfo.py

"""Framework for getting file information, including filetype-specific metadata.

Instantiate appropriate class with filename to get metadata.  Returned object
acts just like a dictionary, with key-value pairs for each piece of metadata.
    import fileinfo
    info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3")
    print "\\n".join(["%s=%s" % (k, info[k]) for k in info.keys()])

Or use listDirectory function to get info on all files in a directory.
    infoList = fileinfo.listDirectory("/music/ap/", [".mp3"])
    for info in infoList:
        ...

Framework can be extended by adding classes for particular file types, e.g.
HTMLFileInfo, MPGFileInfo, DOCFileInfo.  Each class is completely responsible for
parsing its files appropriately; see MP3FileInfo for example.
"""
import os
from UserDict import UserDict

class FileInfo(UserDict):
    "base class for file info"
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

class MP3FileInfo(FileInfo):
    "class for MP3 file info, including ID3v1.0 tags if found"
    def __normalize(self, data):
        "strip whitespace and nulls"
        return data.replace("\00", " ").strip()

    def __parse(self, filename):
        "parse ID3v1.0 tags from MP3 file"
        self.clear()
        try:
            fsock = open(filename, "rb", 0)
            try:
                fsock.seek(-128, 2)
                tagdata = fsock.read(128)
            finally:
                fsock.close()
            if tagdata[:3] == 'TAG':
                self["title"]   = self.__normalize(tagdata[3:33])
                self["artist"]  = self.__normalize(tagdata[33:63])
                self["album"]   = self.__normalize(tagdata[63:93])
                self["year"]    = self.__normalize(tagdata[93:97])
                self["comment"] = self.__normalize(tagdata[97:126])
                self["genre"]   = ord(tagdata[127])
        except IOError:
            pass

    def __setitem__(self, key, item):
        if key == "name" and item:
            self.__parse(item)
        FileInfo.__setitem__(self, key, item)

def __getFileInfoObjectClass(filename):
    "get file info class from filename extension, returns class"
    subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]
    if globals().has_key(subclass):
        return eval(subclass)
    else:
        return FileInfo

def listDirectory(directory, fileExtList):
    "return list of file info objects for files of particular extensions"
    fileExtList = [ext.upper() for ext in fileExtList]
    fileList = [os.path.join(directory, f) for f in os.listdir(directory) \
                if os.path.splitext(f)[1].upper() in fileExtList]
    return [__getFileInfoObjectClass(f)(f) for f in fileList]

if __name__ == "__main__":
    for info in listDirectory("/music/_singles/", [".mp3"]): 1
        for key, value in info.items():
            print "%s=%s" % (key, value)
        print
1 This program's output depends on the files on your hard drive. To get meaningful output, you'll have to change the directory path to point to a directory of MP3 files on your own machine.

Example 3.2. Output of fileinfo.py

This was the output I got on my machine. Your output will be different, unless, by some startling coincidence, you share my exact taste in music.

album=
artist=Ghost in the Machine
title=A Time Long Forgotten (Concept
genre=31
name=/music/_singles/a_time_long_forgotten_con.mp3
year=1999
comment=http://mp3.com/ghostmachine

album=Rave Mix
artist=***DJ MARY-JANE***
title=HELLRAISER****Trance from Hell
genre=31
name=/music/_singles/hellraiser.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Rave Mix
artist=***DJ MARY-JANE***
title=KAIRO****THE BEST GOA
genre=31
name=/music/_singles/kairo.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Journeys
artist=Masters of Balance
title=Long Way Home
genre=31
name=/music/_singles/long_way_home1.mp3
year=2000
comment=http://mp3.com/MastersofBalan

album=
artist=The Cynic Project
title=Sidewinder
genre=18
name=/music/_singles/sidewinder.mp3
year=2000
comment=http://mp3.com/cynicproject

album=Digitosis@128k
artist=VXpanded
title=Spinning
genre=255
name=/music/_singles/spinning.mp3
year=2000
comment=http://mp3.com/artists/95/vxp