How can I add context to an exception in Python
Question: I would like to add context to an exception like this:

    
    
    def process(vals):
        for key in vals:
            try:
                do_something(vals[key])
            except Exception as ex:  # base class. Not sure what to expect.
                raise # with context regarding the key that was being processed.
    

I found a way that is uncharacteristically long winded for Python. Is there a
better way than this?

    
    
    try:
        do_something(vals[key])
    except Exception as ex:
        args = list(ex.args)
        if len(args) > 1:
            args[0] = "{}: {}".format(key, args[0])
            ex.args = tuple(args)
        raise # Will re-trhow ValueError with new args[0]
    


Answer: The first item in `ex.args` is always the message -- if there is any. (Note
for some exceptions, such as the one raised by `assert False`, `ex.args` is an
empty tuple.)

I don't know of a cleaner way to modify the message than reassigning a new
tuple to `ex.args`. (We can't modify the tuple since tuples are immutable).

The code below is similar to yours, except it constructs the tuple without
using an intermediate list, it handles the case when `ex.args` is empty, and
to make the code more readable, it hides the boilerplate inside a context
manager:

    
    
    import contextlib
    
    def process(val):
        with context(val):
            do_something(val)
    
    def do_something(val):
        # assert False
        return 1/val
    
    @contextlib.contextmanager
    def context(msg):
        try:
            yield
        except Exception as ex:
            msg = '{}: {}'.format(msg, ex.args[0]) if ex.args else str(msg)
            ex.args = (msg,) + ex.args[1:]
            raise
    
    process(0)
    

yields a stack trace with this as the final message:

    
    
    ZeroDivisionError: 0: division by zero
    

