Goal:
Python uses try...except to handle exceptions and raise to generate them.
Java uses try...catch to handle exceptions, and throw to generate them.
Example 6.1. Opening a Non-Existent File
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [1]: fsock = open("/notthere","r")
---------------------------------------------------------------------------
IOError Traceback (most recent call last)
<ipython-input-1-12d3ba34a1ce> in <module>()
----> 1 fsock = open("/notthere","r")
IOError: [Errno 2] No such file or directory: '/notthere'
In [2]: try:
...: fsock = open("/notthere")
...: except IOError:
...: print "The file does not exist, exiting gracefully"
...: print "This line will always print"
...:
The file does not exist, exiting gracefully
This line will always print
|
A common use in the standard Python library is to try to import a module, and then check whether it worked. (You can use this to define multiple levels of functionality based on which modules are available at run-time, or to support multiple platforms)
Example 6.2 Supporting Platform-Specific Functionality
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | """
this example shows how to use an exception to support platform-specific functionality
this code comes from the getpass module, a wrapper module for getting a password from user
"""
# Bind the name getpass to the appropriate function
try:
import termios, TERMIOS
except ImportError:
try:
import msvcrt
except ImportError:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
getpass = AskPassword
else:
getpass = win_getpass
else:
getpass = unix_getpass
|
Key Func
open(file)
Example 6.3 Opening a File
In [1]: f = open("./Music/Taylor Swift - State of Grace.mp3","rb")
In [2]: f
Out[2]: <open file './Music/Taylor Swift - State of Grace.mp3', mode 'rb' at 0x9132808>
In [3]: f.mode
Out[3]: 'rb'
In [4]: f.name
Out[4]: './Music/Taylor Swift - State of Grace.mp3'
Python has a built-in function, open, for opening a file on disk.
The open method take up to three parameters: a filename, a mode, and a buffering parameter. Only the first one, filename, is required; the other two are optional. If not specific, the file is opened for reading in text mode.
Here you are opening the file for reading in binary mode.
Key Func
f.tell(), f.seek(...), f.read(...)
Example 6.4. Reading a File
In [5]: f
Out[5]: <open file './Music/Taylor Swift - State of Grace.mp3', mode 'rb' at 0x9132808>
In [6]: f.tell()
Out[6]: 0L
In [7]: f.seek(-128,2)
In [8]: f.tell()
Out[8]: 11934864L
In [9]: tagData = f.read(128)
In [10]: tagData
Out[10]: 'TAGState of Grace\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Taylor Swift\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00State of Grace - Single\x00\x00\x00\x00\x00\x00\x002012\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02'
In [11]: f.tell()
Out[11]: 11934992L
The seek method of a file object moves to another position in the open file.
The second parameter specifies what the first one means:
- 0 means move to an absolute position (counting from the start of the file)
- 1 means move to a relative position (counting from the current position)
- 2 means move to a position relative to the end of the file.
Since the MP3 tags you’re looking for are stored at the end of the file, you use 2 and tell the file object to move to a position 128 bytes from the end of the file.
Open files consume system resourcesm and depending on the file mode, other programs may not be able to access them. It’s important to close files as soon as you’re finished with them.
Example 6.5 Closing a File
In [12]: f
Out[12]: <open file './Music/Taylor Swift - State of Grace.mp3', mode 'rb' at 0x9132808>
In [13]: f.closed
Out[13]: False
In [15]: f.close()
In [16]: f
Out[16]: <closed file './Music/Taylor Swift - State of Grace.mp3', mode 'rb' at 0x9132808>
In [17]: f.closed
Out[17]: True
In [18]: f.seek(0)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-18-d0cb2b3c0dfb> in <module>()
----> 1 f.seek(0)
ValueError: I/O operation on closed file
In [19]: f.tell()
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-19-322ae27c0b94> in <module>()
----> 1 f.tell()
ValueError: I/O operation on closed file
In [20]: f.read()
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-20-bacd0e0f09a3> in <module>()
----> 1 f.read()
ValueError: I/O operation on closed file
In [21]: f.close()
Example 6.6 File Objects in MP3FileInfo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # this example shows how to safely open and read from a file and gracefully handle errors.
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128,2)
tagdata = fsock.read(128)
finally:
fsock.close()
.
.
.
except IOError:
pass
|
try...finally block:
- code in the finally block will always be executed, even if something in the try block raises an exception.
There are two basic file modes:
- “Append” (a) mode will add data to the end of the file.
- “write” (w) mode will overwrite the file.
Either mode will create the file automatically if it doesn’t already exist.
Example 6.7. Writing to Files
In [22]: logfile = open('test.log','w')
In [23]: logfile.write('test succeeded')
In [24]: logfile.close()
In [25]: print file('test.log').read()
test succeeded
In [26]: logfile = open('test.log','a')
In [27]: logfile.write('line 2')
In [28]: logfile.close()
In [29]: print file('test.log').read()
test succeededline 2
Most other languages don’t have a powerful list datatype like Python, so you end up doing a lot of manual work, specifying a start, end, and step to define a range of integers or character or other iteratable entities. But in Python, a for loop simply iterates over a lis, the same way list comprehensions work.
Example 6.8 Introducing the for loop
In [1]: li = ['a','b','e']
In [2]: for s in li:
...: print s
...:
a
b
e
In [3]: print "\n".join(li)
a
b
e
Example 6.9 Simple counters
In [5]: for i in range(5):
...: print i
...:
0
1
2
3
4
for loops are not just for simple counters. They can iterate through all kinds of things.
Example 6.10 Iterating Through a Dictionary
In [8]: import os
In [9]: for k,v in os.environ.items():
...: print "%s=%s" % (k,v)
...:
XAUTHORITY=/home/zeyuan_hu/.Xauthority
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LESSOPEN=| /usr/bin/lesspipe %s
LOGNAME=zeyuan_hu
USER=zeyuan_hu
[...snip...]
In [10]: print "\n".join(["%s=%s" % (k,v) for k,v in os.environ.items()])
XAUTHORITY=/home/zeyuan_hu/.Xauthority
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LESSOPEN=| /usr/bin/lesspipe %s
LOGNAME=zeyuan_hu
USER=zeyuan_hu
[...snip...]
Example 6.11 for loop in MP3FileInfo
1 2 3 4 5 6 7 8 9 10 11 12 | 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)}
.
.
.
if tagdata[:3] == "TAG":
for tag, (start, end, parseFunc) in self.tagDataMap.items():
self[tag] = parseFunc(tagdata[start:end])
|
tagDataMap is a class attribute that defines the tags you’re looking for in an MP3 file.
Modules, like everything else in Python, are objects. Once imported, you can always get a reference to a module through the global dictionary sys.modules.
Example 6.12 Introducing sys.modules
In [3]: import sys
In [4]: print '\n'.join(sys.modules.keys())
copy_reg
sre_compile
_sre
encodings
site
__builtin__
sys
__main__
encodings.encodings
abc
posixpath
_weakrefset
errno
re
os
Example 6.13 Using sys.modules
In [13]: import fileinfo
In [14]: print '\n'.join(sys.modules.keys())
copy_reg
fileinfo
sre_compile
_sre
encodings
site
__builtin__
sys
__main__
encodings.encodings
abc
posixpath
_weakrefset
errno
re
os
In [15]: fileinfo
Out[15]: <module 'fileinfo' from 'fileinfo.pyc'>
In [16]: sys.modules["fileinfo"]
Out[16]: <module 'fileinfo' from 'fileinfo.pyc'>
Example 6.14 The __module__ Class Attribute
In [17]: from fileinfo import MP3FileInfo
In [18]: MP3FileInfo.__module__
Out[18]: 'fileinfo'
In [19]: sys.modules[MP3FileInfo.__module__]
Out[19]: <module 'fileinfo' from 'fileinfo.pyc'>
Key Func
os.path.join(path, filename),
os.path.expanduser(pathname including "~")
path, filename = os.path.split(pathname)
filename, fileext = os.path.splitext(filename)
os.listdir(pathname)
os.path.isfile(pathname)
os.path.isdir(pathname)
os.getcwd()
os.path.normcase(file or folder)
glob module
Example 6.16 Constructing Pathnames
In [21]: import os
In [22]: os.path.join("../Music/","Taylor Swift - State of Grace.mp3")
Out[22]: '../Music/Taylor Swift - State of Grace.mp3'
In [23]: os.path.join("../Music","Taylor Swift - State of Grace.mp3")
Out[23]: '../Music/Taylor Swift - State of Grace.mp3'
In [24]: os.path.expanduser("~")
Out[24]: '/home/zeyuan_hu'
In [25]: os.path.join(os.path.expanduser("~"),"Python")
Out[25]: '/home/zeyuan_hu/Python'
# constructs a pathname out of one or more partial pathnames.
os.path.join(path, filename)
# expand a pathname that uses ~ to represent the current user's home directory
os.path.expanduser(path including "~")
Example 6.17 Splitting Pathnames
In [26]: os.path.split('../Music/Taylor Swift - State of Grace.mp3')
Out[26]: ('../Music', 'Taylor Swift - State of Grace.mp3')
In [27]: filepath, filename = os.path.split('../Music/Taylor Swift - State of Grace.mp3')
In [28]: filepath
Out[28]: '../Music'
In [29]: filename
Out[29]: 'Taylor Swift - State of Grace.mp3'
In [30]: shortname, extension = os.path.splitext(filename)
In [31]: shortname
Out[31]: 'Taylor Swift - State of Grace'
In [32]: extension
Out[32]: '.mp3'
# splits a full pathname and returns a tuple containing the path and filename.
# you can assign the return value of split function into a tuple of two variables.
path, filename = os.path.split(pathname)
# splits a filename and returns a tuple containing the filename and the file extension
filename, fileext = os.path.splitext(filename)
Example 6.18 Listing Directories
In [35]: os.path.expanduser("~")
Out[35]: '/home/zeyuan_hu'
In [36]: dirname = os.path.expanduser("~")
In [37]: os.listdir(dirname)
Out[37]:
['.pip','.adobe','.gconf','.gnome2','.xscreensaver','Dropbox',
'Music','.profile','py','Videos','.pki','.dropbox','.swp',
'.cache'.'.Xauthority','Documents','.vim', ... ]
In [38]: [f for f in os.listdir(dirname) if os.path.isfile(os.path.join(dirname,f))]
Out[38]:
['.dmrc', '.swp','.Xauthority','.pam_environment','.bash_history',
'.gksu.lock','.install4j','.viminfo','.pulse-cookie','examples.desktop',
'.vimrc~','.bashrc','.vimrc', ...]
In [39]: [f for f in os.listdir(dirname) if os.path.isdir(os.path.join(dirname,f))]
Out[39]:
['Downloads','.matplotlib','.pip','.gnome2','.java','Dropbox','Music',
'py','Videos','.dropbox','.cache','Documents','.vim','Desktop',
'Public','Ubuntu One','Templates','Pictures','.ssh', ... ]
# takes a pathname and returns a list of the contents of the directory
# note that listdir returns both files and folders, with no indication of which is which.
os.listdir(pathname)
# used to separate the files from the folders
# takes a pathname and returns 1 if the path represents a file, and 0 otherwise.
# also works with a partial path, relative to the current working directory.
os.path.isfile(pathname)
# get the current working directory
os.getcwd()
# used to get a list of the subdirectories within a directory
# takes a pathname and returns 1 if the path represents a directory, and 0 otherwise.
os.path.isdir(pathname)
Example 6.19 Listing Directories in fileinfo.py
1 2 3 4 5 | 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]
|
# normalize the case according to operating system defaults
os.path.normcase(file or folder)
Note
Whenever possible, you should use the functions in os and os.path for file, directory, and path manipulations. Those modules are wrappers for platform-specific modules, so function like os.path.split work on UNIX, Windows, Mac OS, and any other platform supported by Python.
Example 6.20 Listing Directories with glob
In [42]: os.listdir("/home/zeyuan_hu/Music")
Out[42]: ['a_time_long_forgotten_con.mp3', 'hellraiser.mp3','kairo.mp3',
'long_way_home1.mp3', 'sidewinder.mp3','spinning.mp3']
In [43]: import glob
In [44]: glob.glob("/home/zeyuan_hu/Music/*.mp3")
Out[44]: ["/home/zeyuan_hu/Music/a_time_long_forgotten_con.mp3",
"/home/zeyuan_hu/Music/hellraiser.mp3",
"/home/zeyuan_hu/Music/kairo.mp3",
"/home/zeyuan_hu/Music/long_way_home1.mp3",
"/home/zeyuan_hu/Music/sidewinder.mp3",
"/home/zeyuan_hu/Music/spinning.mp3"]
In [45]: glob.glob("/home/zeyuan_hu/Music/s*.mp3")
Out[45]: ["/home/zeyuan_hu/Music/sidewinder.mp3",
"/home/zeyuan_hu/Music/spinning.mp3"]
In [46]: glob.glob("/home/zeyuan_hu/*/*.mp3")
Example 6.21. listDirectory
1 2 3 4 5 6 7 8 9 10 11 12 | 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]
|
Python supports nest functions:
The nested function getFileInfoClass can be called only from the function in which it is defined, listDirectory.
Having constructed the name of the handler class that would handle this file, you check to see if that handler class actually exists in this module. If it does, you return the class, otherwise you return the base class FileInfo.
This is s a very important point: this function returns a class. Not an instance of a class, but the class itself.
Note
Note that listDirectory is completely generic. It doesn’t know ahead of time which types of files it will be getting, or which classes are defined that could potentially handle those files. It inspects the directory for the files to process, and then introspects its own module to see what special handler classes (like MP3FileInfo) are defined.
You can extend this program to handle other types of files simply by defining an appropriately-named class: HTMLFileInfo for HTML files, DOCFileInfo for Word .doc files., and so forth.
listDirectory will handle them all, without modification, by handing off the real work to the appropriate classes and collating the results.