Dive Into Python Python for experienced programmers |

## 3.10. Handling exceptions

Like many object-oriented languages, Python has exception handling via `try...except` blocks.

Python uses `try...except` to handle exceptions and `raise` to generate them. Java and C++ use `try...catch` to handle exceptions, and `throw` to generate them.

If you already know all about exceptions, you can skim this section. If you've been stuck programming in a lesser language that doesn't have exception handling, or you've been using a real language but not using exceptions, this section is very important.

Exceptions are everywhere in Python; virtually every module in the standard Python library uses them, and Python itself will raise them in lots of different circumstances. You've already seen them repeatedly throughout this book.

- Accessing a non-existent dictionary key will raise a `KeyError` exception.
- Searching a list for a non-existent value will raise a `ValueError` exception.
- Calling a non-existent method will raise an `AttributeError` exception.
- Referencing a non-existent variable will raise a `NameError` exception.
- Mixing datatypes without coercion will raise a `TypeError` exception.

In each of these cases, we were simply playing around in the Python IDE: an error occurred, the exception was printed (depending on your IDE, in an intentionally jarring shade of red), and that was that. This is called an unhandled exception; when the exception was raised, there was no code to explicitly notice it and deal with it, so it bubbled its way back to the default behavior built in to Python, which is to spit out some debugging information and give up. In the IDE, that's no big deal, but if that happened while your actual Python program was running, the entire program would come to a screeching halt.[6]

An exception doesn't have to be a complete program crash, though. Exceptions, when raised, can be handled. Sometimes an exception is really because you have a bug in your code (like accessing a variable that doesn't exist), but many times, an exception is something you can plan for. If you're opening a file, it might not exist; if you're connecting to a database, it might be unavailable, or you might not have the correct security credentials to access it. If you know a line of code may raise an exception, you should handle the exception using a `try...except` block.

Example 3.21. Opening a non-existent file

    >>> fsock = open("/notthere", "r")
    Traceback (innermost last):
      File "<interactive input>", line 1, in ?
    IOError: [Errno 2] No such file or directory: '/notthere'
    >>> 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

| Using the built-in `open` function, we can try to open a file for reading (more on `open` in the next section). But the file doesn't exist, so this raises the `IOError` exception. Since we haven't provided any explicit check for an `IOError` exception, Python just prints out some debugging information about what happened and then gives up.
| We're trying to open the same non-existent file, but this time we're doing it within a `try...except` block.
| When the `open` method raises an `IOError` exception, we're ready for it. The `except IOError:` line catches the exception and executes our own block of code, which in this case just prints a more pleasant error message.
| Once an exception has been handled, processing continues normally on the first line after the `try...except` block. Note that this line will always print, whether or not an exception occurs. If you really did have a file called `notthere` in your root directory, the call to `open` would succeed, the `except` clause would be ignored, and this line would still be executed.

Exceptions may seem unfriendly (after all, if you don't catch the exception, your entire program will crash), but consider the alternative. Would you rather get back an unusable file object to a non-existent file? You'd have to check its validity somehow anyway, and if you forgot, your program would give you strange errors somewhere down the line that you would have to trace back to the source. I'm sure you've done this; it's not fun. With exceptions, errors occur immediately, and you can handle them in a standard way at the source of the problem.

There are lots of other uses for exceptions besides handling actual error conditions. A common use in the standard Python library is to try to import a module, then check whether it worked. Importing a module that does not exist will raise an `ImportError` exception. You can use this to define multiple levels of functionality based on which modules are available at run-time, or to support multiple platforms (where platform-specific code is separated into different modules).

You can also define your own exceptions by creating a class that inherits from the built-in `Exception` class, and then raise your exceptions with the `raise` command. This is beyond the scope of this section, but see the further reading section if you're interested.

Example 3.22. Supporting platform-specific functionality

This code comes from the `getpass` module, a wrapper module for getting a password from the user. Getting a password is accomplished differently on UNIX, Windows, and Mac OS platforms, but this code encapsulates all of those differences.

      # 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

| `termios` is a UNIX-specific module that provides low-level control over the input terminal. If this module is not available (because it's not on your system, or your system doesn't support it), the import fails and Python raises an `ImportError`, which we catch.
| OK, we didn't have `termios`, so let's try `msvcrt`, which is a Windows-specific module that provides an API to lots of useful functions in the Microsoft Visual C++ runtime services. If this import fails, Python will raise an `ImportError`, which we catch.
| If the first two didn't work, we try to import a function from `EasyDialogs`, which is a Mac OS-specific module that provides functions to pop up dialogs of various types. Once again, if this import fails, Python will raise an `ImportError`, which we catch.
| None of these platform-specific modules is available (which is possible, since Python has been ported to lots of different platforms), so we have to fall back on a default password input function (which is defined elsewhere in the `getpass` module). Notice what we're doing here: we're assigning the function `default_getpass` to the variable `getpass`. If you read the official `getpass` documentation, it tells you that the `getpass` module defines a `getpass` function. This is how it does it: by binding `getpass` to the right function for your platform. Then when you call the `getpass` function, you're really calling a platform-specific function that this code has set up for you. You don't have to know or care what platform your code is running on; just call `getpass`, and it will always do the right thing.
| A `try...except` block can have an `else` clause, like an `if` statement. If no exception is raised during the `try` block, the `else` clause is executed afterwards. In this case, that means that the `from EasyDialogs import AskPassword` import worked, so we should bind `getpass` to the `AskPassword` function. Each of the other `try...except` blocks have similar `else` clauses to bind `getpass` to the appropriate function when we find an `import` that works.

Further reading

- Python Tutorial discusses defining and raising your own exceptions, and handling multiple exceptions at once.
- Python Library Reference summarizes all the built-in exceptions.
- Python Library Reference documents the getpass module.
- Python Library Reference documents the `traceback` module, which provides low-level access to exception attributes after an exception is raised.
- Python Reference Manual discusses the inner workings of the `try...except` block.

Footnotes

[6] [6] Or, as some marketroids would put it, your program would perform an illegal action. Whatever.