Python exception handling - line number

PythonExceptionIndexing

Python Problem Overview


I'm using python to evaluate some measured data. Because of many possible results it is difficult to handle or possible combinations. Sometimes an error happens during the evaluation. It is usually an index error because I get out of range from measured data.

It is very difficult to find out on which place in code the problem happened. It would help a lot if I knew on which line the error was raised. If I use following code:

try:
    result = evaluateData(data)
except Exception, err:
    print ("Error: %s.\n" % str(err))

Unfortunately this only tells me that there is and index error. I would like to know more details about the exception (line in code, variable etc.) to find out what happened. Is it possible?

Thank you.

Python Solutions


Solution 1 - Python

Solution, printing filename, linenumber, line itself and exception descrpition:

import linecache
import sys

def PrintException():
    exc_type, exc_obj, tb = sys.exc_info()
    f = tb.tb_frame
    lineno = tb.tb_lineno
    filename = f.f_code.co_filename
    linecache.checkcache(filename)
    line = linecache.getline(filename, lineno, f.f_globals)
    print 'EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj)


try:
    print 1/0
except:
    PrintException()

Output:

EXCEPTION IN (D:/Projects/delme3.py, LINE 15 "print 1/0"): integer division or modulo by zero

Solution 2 - Python

To simply get the line number you can use sys, if you would like to have more, try the traceback module.

import sys    
try:
    [][2]
except IndexError:
    print("Error on line {}".format(sys.exc_info()[-1].tb_lineno))

prints:

Error on line 3

Example from the traceback module documentation:

import sys, traceback

def lumberjack():
    bright_side_of_death()

def bright_side_of_death():
    return tuple()[0]

try:
    lumberjack()
except IndexError:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print "*** print_tb:"
    traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
    print "*** print_exception:"
    traceback.print_exception(exc_type, exc_value, exc_traceback,
                              limit=2, file=sys.stdout)
    print "*** print_exc:"
    traceback.print_exc()
    print "*** format_exc, first and last line:"
    formatted_lines = traceback.format_exc().splitlines()
    print formatted_lines[0]
    print formatted_lines[-1]
    print "*** format_exception:"
    print repr(traceback.format_exception(exc_type, exc_value,
                                          exc_traceback))
    print "*** extract_tb:"
    print repr(traceback.extract_tb(exc_traceback))
    print "*** format_tb:"
    print repr(traceback.format_tb(exc_traceback))
    print "*** tb_lineno:", exc_traceback.tb_lineno

Solution 3 - Python

I use the traceback which is simple and robust:

import traceback

try:
    raise ValueError()
except:
    print(traceback.format_exc())

Out:

Traceback (most recent call last):
  File "catch.py", line 4, in <module>
    raise ValueError()
ValueError

Solution 4 - Python

The simplest way is just to use:

import traceback
try:
    <blah>
except IndexError:
    traceback.print_exc()

or if using logging:

import logging
try:
    <blah>
except IndexError as e:
    logging.exception(e)

Solution 5 - Python

Gives you file, lineno, and exception for the last item in the call stack

from sys import exc_info
from traceback import format_exception


def print_exception():
    etype, value, tb = exc_info()
    info, error = format_exception(etype, value, tb)[-2:]
    print(f'Exception in:\n{info}\n{error}')

try:
    1 / 0
except:
    print_exception()

prints

Exception in:
   File "file.py", line 12, in <module>
    1 / 0

ZeroDivisionError: division by zero

Solution 6 - Python

I would suggest using the python logging library, it has two useful methods that might help in this case.

  1. logging.findCaller()
  • findCaller(stack_info=False) - Reports just the line number for the previous caller leading to the exception raised
  • findCaller(stack_info=True) - Reports the line number & stack for the previous caller leading to the exception raised
  1. logging.logException()
  • Reports the line & stack within the try/except block that raised the exception

For more info checkout the api https://docs.python.org/3/library/logging.html

Solution 7 - Python

I always use this snippet

import sys, os

try:
    raise NotImplementedError("No error")
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print(exc_type, fname, exc_tb.tb_lineno)

for different views and possible issues you can refer https://stackoverflow.com/questions/1278705/when-i-catch-an-exception-how-do-i-get-the-type-file-and-line-number/1278740#1278740

Solution 8 - Python

All the solutions answer the OPs problem, however, if one is holding onto a specific error instance and the last traceback stack won't do it they will not suffice —a corner scenario example given below.

In this case, the magic attribute __traceback__ of a raised exception instance may be used. This is a system traceback object (exposed as types.TracebackType, see Types) not the module. This traceback instance behaves just one like would expect (but it is always nice to check):

from collections import deque
from typing import (List, )
errors: List[Exception] = deque([], 5)
    
def get_raised(error_cls: type, msg: str) -> Exception:
# for debugging, an exception instance that is not raised
# ``__traceback__`` has ``None``
    try:
        raise error_cls(msg)
    except Exception as error:
        return error

error = get_raised(NameError, 'foo')
errors.append(error)

error = get_raised(ValueError, 'bar')
errors.append(error)

try:
    raise get_raised(TypeError, 'bar')
except Exception as error:
    errors.append(error)

Now, I can check out the first error's details:

import types

traceback = errors[0].__traceback__  # inadvisable name due to module
line_no: int = traceback.tb_lineno
frame: types.FrameType = traceback.tb_frame
previous: Union[type(None), types.TracebackType] = traceback.tb_next
filename: str = frame.f_code.co_filename

The traceback's previous is None for the second error despite a preceding error as expected, but for the third wherein an error is raised twice it is not.

This below is just a test which makes no sense contextually. A case were it is useful if when an exception is raised in a view of a webapp (a 500 status kind of incident) that gets caught and stored for the admit to inspect akin to Setry.io (but for free). Here is a minimal example where the home page / will raise an error, that gets caught and the route errors will list them. This is using Pyramid in a very concentrated way (multifile is way better) with no logging or authentication and the error logging could be better for the admin to inspect similar to Sentry.io.

from pyramid.config import Configurator
from waitress import serve
from collections import deque
# just for typehinting:
from pyramid.request import Request
from pyramid.traversal import DefaultRootFactory
from pyramid.router import Router
import types
from typing import (List, )


def home_view(context: DefaultRootFactory, request: Request) -> dict:
    raise NotImplementedError('I forgot to fill this')
    return {'status': 'ok'}  # never reached.

def caught_view(error: Exception, request: Request) -> dict:
    """
    Exception above is just type hinting.
    This is controlled by the context argument in 
    either the ``add_exception_view`` method of config,
    or the ``exception_view_config`` decorator factory (callable class)
    """
    # this below is a simplification as URLDecodeError is an attack (418)
    request.response.status = 500 
    config.registry.settings['error_buffer'].append(error)
    #logging.exception(error) # were it set up.
    #slack_admin(format_error(error))  # ditto
    return {'status': 'error',  'message': 'The server crashed!!'}

def format_error(error: Exception) -> str:
    traceback = error.__traceback__  # inadvisable name due to module
    frame: types.FrameType = traceback.tb_frame
    return f'{type(error).__name__}: {error}' +\
           f'at line {traceback.tb_lineno} in file {frame.f_code.co_filename}'

def error_view(context: DefaultRootFactory, request: Request) -> dict:
    print(request.registry.settings['error_buffer'])
    return {'status': 'ok', 
            'errors':list(map(format_error, request.registry.settings['error_buffer']))
           }
    
with Configurator(settings=dict()) as config:
    config.add_route('home', '/')
    config.add_route('errors', '/errors')
    config.add_view(home_view, route_name='home', renderer='json')
    config.add_view(error_view, route_name='errors', renderer='json')
    config.add_exception_view(caught_view, context=Exception, renderer='json')
    config.registry.settings['error_buffer']: List[Exception] = deque([], 5)  
    # not in config.registry.settings, not JSON serialisable
    # config.add_request_method
    app  : Router = config.make_wsgi_app()

port = 6969
serve(app, port=port)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeChView Question on Stackoverflow
Solution 1 - PythonApogentusView Answer on Stackoverflow
Solution 2 - PythonrootView Answer on Stackoverflow
Solution 3 - PythonBenyamin JafariView Answer on Stackoverflow
Solution 4 - PythonacowpyView Answer on Stackoverflow
Solution 5 - PythonreubanoView Answer on Stackoverflow
Solution 6 - PythongrandmaView Answer on Stackoverflow
Solution 7 - PythonGanesh KharadView Answer on Stackoverflow
Solution 8 - PythonMatteo FerlaView Answer on Stackoverflow