Headless Error Handling Python Tutorial




Welcome to part 23 of the intermediate Python programming tutorial series. In this tutorial, we're going to cover how to handle for errors that occur in a headless/automated program that you're not constantly monitoring.

In our previous tutorial, we covered logging with Python, which is very useful, but, if you do hit errors and just return the str(e) of that error, you are likely to find it's not enough useful information, especially if your program is large. If it's a small program, you can probably find the variable it's talking about, like when you get a NameError: a is not defined, maybe you only define and reference this a once, so it's easy to immediately know where things went wrong. What if you get an error and have many references to a? It might not be a NameError either, it could be anything. With logging as we have it right now, we'd have no clue where to begin. What if your program is 100,000 lines long?! Yikes! So let's dig into how we can start to solve this.

Let's start with something simple, like:

try:
    a+b
except Exception as e:
    print(str(e))

Here, we get name 'a' is not defined. Luckily for us, we can actually use the sys module to access more of the exception's information with sys.exc_info, like so:

import sys

try:
    a+b
except:
    print(sys.exc_info()[0])
    print(sys.exc_info()[1])
    print(sys.exc_info()[2].tb_lineno)
    print('Error: {}. {}, line: {}'.format(sys.exc_info()[0],
                                         sys.exc_info()[1],
                                         sys.exc_info()[2].tb_lineno))

In this case, you will see:

<class 'NameError'>
name 'a' is not defined
4
Error: <class 'NameError'>. name 'a' is not defined, line: 4

Notice that we've simply sliced the values of sys.exc_info(), and we didn't unpack them to variables. As per a StackOverflow comment I read, it would be unwise to unpack them, just in case you get an exception within the exception itself, and you would wind up with a circular reference that never gets garbage collected. Above my head, but apparently that's a thing.

Now, we can combine this with logging, and do something like:

import sys
import logging

def error_handling():
    return 'Error: {}. {}, line: {}'.format(sys.exc_info()[0],
                                         sys.exc_info()[1],
                                         sys.exc_info()[2].tb_lineno)
try:
    a+b
except:
    logging.error(error_handling())

Output:

ERROR:root:Error: <class 'NameError'>. name 'a' is not defined, line: 9

Obviously, you can also configure logging to instead log this to a file. See the logging tutorial in this series for more information there.

The next tutorial:





  • Intermediate Python Programming introduction
  • String Concatenation and Formatting Intermediate Python Tutorial part 2
  • Argparse for CLI Intermediate Python Tutorial part 3
  • List comprehension and generator expressions Intermediate Python Tutorial part 4
  • More on list comprehension and generators Intermediate Python Tutorial part 5
  • Timeit Module Intermediate Python Tutorial part 6
  • Enumerate Intermediate Python Tutorial part 7
  • Python's Zip function
  • More on Generators with Python
  • Multiprocessing with Python intro
  • Getting Values from Multiprocessing Processes
  • Multiprocessing Spider Example
  • Introduction to Object Oriented Programming
  • Creating Environment for our Object with PyGame
  • Many Blobs - Object Oriented Programming
  • Blob Class and Modularity - Object Oriented Programming
  • Inheritance - Object Oriented Programming
  • Decorators in Python Tutorial
  • Operator Overloading Python Tutorial
  • Detecting Collisions in our Game Python Tutorial
  • Special Methods, OOP, and Iteration Python Tutorial
  • Logging Python Tutorial
  • Headless Error Handling Python Tutorial
  • __str__ and __repr_ in Python 3
  • Args and Kwargs
  • Asyncio Basics - Asynchronous programming with coroutines