Welcome, guest | Sign In | My Account | Store | Cart

This is a recipe is often use for the mainline of my Python scripts. With this recipe your Python script will:

  • gracefully handle Ctrl+C (i.e. KeyboardInterrupt)
  • log an error (using the logging module) for uncaught exceptions, importantly with the file and line number in your Python code where the exception was raised
  • gracefully ignore a closed output pipe (common when the user pipes your script through less and terminates that)
  • if your script logger is enabled for DEBUG level logging, a full traceback will be shown for an uncaught exception

Presumptions:

  • you have a global log variable a la:

    import logging
    log = logging.setLevel("scriptname")
    
  • your script's entry point is a function def main(argv): ...

Python, 31 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
if __name__ == "__main__":
    try:
        retval = main(sys.argv)
    except KeyboardInterrupt:
        sys.exit(1)
    except SystemExit:
        raise
    except:
        import traceback, logging
        if not log.handlers and not logging.root.handlers:
            logging.basicConfig()
        skip_it = False
        exc_info = sys.exc_info()
        if hasattr(exc_info[0], "__name__"):
            exc_class, exc, tb = exc_info
            if isinstance(exc, IOError) and exc.args[0] == 32:
                # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`.
                skip_it = True
            if not skip_it:
                tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3]
                log.error("%s (%s:%s in %s)", exc_info[1], tb_path,
                    tb_lineno, tb_func)
        else:  # string exception
            log.error(exc_info[0])
        if not skip_it:
            if log.isEnabledFor(logging.DEBUG):
                print()
                traceback.print_exception(*exc_info)
            sys.exit(1)
    else:
        sys.exit(retval)

4 comments

Chris Jones 13 years, 10 months ago  # | flag

You have 1/0 which always produces an exception, by definition. What is the point of this?

Denis Barmenkov 13 years, 10 months ago  # | flag

Nine years old recipe looks more solid and prints stack trace along with variables values: http://code.activestate.com/recipes/52215/

Trent Mick 13 years, 10 months ago  # | flag

@chris: stupidity. I had put that in for testing. I'll remove it.

Trent Mick 13 years, 10 months ago  # | flag

@denis: thanks for the link. I'll probably try to incorporate that for a future rev.