MySQL for Python takes care of the nitty-gritty of
communication between your program and MySQL. As a result, handling
exceptions passed from MySQL is as straightforward as handling
exceptions passed from any other Python module.
Python exception-handling
Python error-handling uses a try...except...else code structure to handle exceptions. It then uses raise to generate the error.
while True:
try:
x = int(raw_input("Please enter a number: "))
break
except:
print "That is not a valid number. Please try again..."
While this is the textbook example for raising an error, there are a few points to keep in mind.
This sets up a loop with a condition that applies as long as there are no exceptions raised.
Python then tries to
execute whatever follows. If successful, the program terminates with
break. If not, an exception is registered, but not raised.
The use of except tells Python what to do
in the event of an exception. In this case it prints a message to the
screen, but it does not raise an exception. Instead, the while loop remains unbroken and another number is requested.
Catching an exception from MySQLdb
All exceptions in MySQL for Python are accessed as part of MySQLdb. Therefore, one cannot reference them directly. Using the fish database, execute the following code:
#!/usr/bin/env python
import MySQLdb
mydb = MySQLdb.connect(host ='localhost',
user = 'skipper',
passwd = 'secret',
db = 'fish'
cur = mydb.cursor()
# Note the use of '7a' instead of '7'
statement = """SELECT * FROM menu WHERE id=7a"""
try:
cur.execute(statement)
results = cur.fetchall()
print results
except Error:
print "An error has been passed."
The preceding code will return a NameError from Python itself. For Python to recognize the exception from MySQLdb, change each instance of Error to read MySQLdb.Error. The except clause then reads as follows:
except MySQLdb.Error:
print "An error has been passed."
The resulting output will be from the print statement.
An error has been passed.
Raising an error or a warning
An exception is only explicitly registered when raise is used. Instead of the print statement used in the except clause previously, we can raise an error and update the print statement with the following line of code:
Instead of the friendly statement about an error passing, we get a stack trace that ends as follows:
Remember that MySQLdb is a macro system for interfacing with _mysql_
and, subsequently, with the C API for MySQL. Any errors that pass from
MySQL come through each of those before reaching the Python interpreter
and your program.
Instead of raising an actual error, we can raise our own error message. After the MySQLdb.Error in the raise line, simply place your error message in parentheses and quotes.
raise MySQLdb.Error("An error has been passed. Please contact your system administrator.")
As shown here, the exact error message is customizable. If raise is simply appended to the preceding code as part of the except clause, the usual stack trace will be printed to stdout whenever the except clause is run. Note also that the flow of the program is interrupted whenever raise is executed.
The same process applies when raising a warning. Simply use MySQLdb.Warning and, if necessary, also use a suitable warning message.
Making exceptions less intimidating
For many users, program
exceptions are a sign on par with Armageddon and tend to elicit the
anxiety and mystery that accompany the usual view of that occasion. In
order to be more helpful to users and to help users be more helpful to
their IT support staff, it is good practice to give error messages that
are explanatory rather than merely cryptic. Consider the following two
error messages:
Exception: NameError in line 256 of someprogram.py.
The
value you passed is not of the correct format. The program needs an
integer value and you passed a character of the alphabet. Please contact
a member of IT staff if you need further clarification on this error
and tell them the error message is: "Unknown column '7a' in 'where clause' on line 256 of someprogram.py".
Admittedly, the first takes
up less space and takes less time to type. But it also is guaranteed to
compromise the usefulness of your program for the user and to increase
the number of phone calls to the IT helpdesk. While the second may be a
bit longer than necessary, the user and helpdesk will benefit from a
helpful message, regardless of its verbosity, more than an overly
technical and terse one.
To accomplish a user-friendly error message that nonetheless provides
the technical information necessary, catch the exception that Python
passes. Using the previous if...except loop, we can catch the error without the traceback as follows:
#!/usr/bin/env python
import MySQLdb
mydb = MySQLdb.connect(host ='localhost',
user = 'skipper',
passwd = 'secret',
db = 'fish')
cur = mydb.cursor()
statement = """SELECT * FROM menu WHERE id=7a"""
try:
cur.execute(statement)
results = cur.fetchall()
print results
except MySQLdb.Error, e:
print "An error has been passed. %s" %e
Now when the program is executed, we receive the following output:
An error has been passed. (1054, "Unknown column '7a' in 'where clause'")
This could easily be revised to similar wording as the second of the two error examples seen just now.