# Dive Into Python

Python for experienced programmers

Find:

## Chapter 3. An Object-Oriented Framework

## 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 string`s of the module, the classes, and the functions to get an overview of what this program 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`

If you have not already done so, you can download this and other examples used in this book.

    """Framework for getting filetype-specific metadata.

    Instantiate appropriate class with filename.  Returned object acts 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, v) for k, v in info.items()])

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

    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
    import sys
    from UserDict import UserDict

    def stripnulls(data):
        "strip whitespace and nulls"
        return data.replace("\00", "").strip()

    class FileInfo(UserDict):
        "store file metadata"
        def __init__(self, filename=None):
            UserDict.__init__(self)
            self["name"] = filename

    class MP3FileInfo(FileInfo):
        "store ID3v1.0 MP3 tags"
        tagDataMap = {"title"   : (  3,  33, stripnulls),
                      "artist"  : ( 33,  63, stripnulls),
                      "album"   : ( 63,  93, stripnulls),
                      "year"    : ( 93,  97, stripnulls),
                      "comment" : ( 97, 126, stripnulls),
                      "genre"   : (127, 128, ord)}

        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":
                    for tag, (start, end, parseFunc) in self.tagDataMap.items():
                        self[tag] = parseFunc(tagdata[start:end])
            except IOError:
                pass

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

    def listDirectory(directory, fileExtList):
        "get list of file info objects for files of particular extensions"
        fileList = [os.path.normcase(f) for f in os.listdir(directory)]
        fileList = [os.path.join(directory, f) for f in fileList \
                    if os.path.splitext(f)[1] in fileExtList]
        def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):
            "get file info class from filename extension"
            subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]
            return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
        return [getFileInfoClass(f)(f) for f in fileList]

    if __name__ == "__main__":
        for info in listDirectory("/music/_singles/", [".mp3"]):
            print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
            print

| 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