Search code examples
pythonexceptionindex-error

How can I find out which index is out of range?


In the event of an IndexError, is there a way to tell which object on a line is 'out of range'?

Consider this code:

a = [1,2,3]
b = [1,2,3]

x, y = get_values_from_somewhere()

try:
   a[x] = b[y]
except IndexError as e:
   ....

In the event that x or y is too large and IndexError gets caught, I would like to know which of a or b is out of range (so I can perform different actions in the except block).

Clearly I could compare x and y to len(a) and len(b) respectively, but I am curious if there is another way of doing it using IndexError.


Solution

  • A more robust approach would be to tap into the traceback object returned by sys.exc_info(), extract the code indicated by the file name and line number from the frame, use ast.parse to parse the line, subclass ast.NodeVisitor to find all the Subscript nodes, unparse (with astunparse) and eval the nodes with the frame's global and local variables to see which of the nodes causes exception, and print the offending expression:

    import sys
    import linecache
    import ast
    import astunparse
    
    def find_index_error():
        tb = sys.exc_info()[2]
        frame = tb.tb_frame
        lineno = tb.tb_lineno
        filename = frame.f_code.co_filename
        line = linecache.getline(filename, lineno, frame.f_globals)
        class find_index_error_node(ast.NodeVisitor):
            def visit_Subscript(self, node):
                expr = astunparse.unparse(node).strip()
                try:
                    eval(expr, frame.f_globals, frame.f_locals)
                except IndexError:
                    print("%s causes IndexError" % expr)
        find_index_error_node().visit(ast.parse(line.strip(), filename))
    
    a = [1,2,3]
    b = [1,2,3]
    x, y = 1, 2
    def f():
        return 3
    try:
        a[f() - 1] = b[f() + y] + a[x + 1] # can you guess which of them causes IndexError?
    except IndexError:
        find_index_error()
    

    This outputs:

    b[(f() + y)] causes IndexError