Foundations of Python Network Programming

(WallPaper) #1
Chapter 5 ■ Network Data aND Network errors

91

This code assumes, of course, that DestinationError will only ever be wrapping OSError descendants like
socket.error. Otherwise, the str() method would have to be more complicated to handle the case where
the cause exception holds its textual information in an attribute other than strerror. But this at least illustrates the
pattern. The caller who catches a DestinationError can then examine its cause to learn about the network error
behind the semantically richer exception they have actually caught.


Catching and Reporting Network Exceptions

There are two basic approaches to catching exceptions: granular exception handlers and blanket exception handlers.
The granular approach to exceptions is to wrap a try...except clause around every single network call that
you ever make and print out a pithy error message in its place. While suitable for short programs, this can become
repetitive in long programs without necessarily providing much more information for the user. When you wrap
the hundredth network operation in your program with yet another try...except and specific error message, ask
yourself whether you are really providing more information.
The other approach is using blanket exception handlers. This involves stepping back from your code and
identifying big regions that do specific things, like these:


•    “This whole routine is about connecting to the license server.”

•    “All of the socket operations in this function are fetching a response from the database.”

•    “This last part is all cleanup and shutdown code.”

Then the outer parts of your program—the ones that collect input, command-line arguments, and configuration
settings and then set big operations in motion—can wrap those big operations with handlers like the following:


import sys


...


try:
deliver_updated_keyfiles(...)
except (socket.error, socket.gaierror) as e:
print('cannot deliver remote keyfiles: {}'.format(e), file=sys.stderr)
exit(1)


Better yet, have your code raise an error of your own devising that indicates an error that specifically needs to halt
the program and print error output for the user.


except:
FatalError('cannot send replies: {}'.format(e))


Then, at the very top level of your program, catch all the FatalError exceptions that you throw and print the
error messages out there. That way, when the day comes that you want to add a command-line option that sends fatal
errors to the system error logs instead of to the screen, you have to adjust only one piece of code instead of a dozen!
There is one final reason that might dictate where you add an exception handler to your network program: you
might want intelligently to retry an operation that failed. In long-running programs, this is common. Imagine a utility
that periodically sent out e-mails with its status. If it suddenly cannot send them successfully, then it probably does
not want to shut down for what might be just a transient error. Instead, the e-mail thread might log the error, wait
several minutes, and try again.

Free download pdf