How to handle AssertionError in Python and find out which line or statement it occurred on?
Asked Answered
H

3

102

I want to handle AssertionErrors both to hide unnecessary parts of the stack trace from the user and to print a message as to why the error occurred and what the user should do about it.

Is there any way to find out on which line or statement the assert failed within the except block?

try:
    assert True
    assert 7 == 7
    assert 1 == 2
    # many more statements like this
except AssertionError:
    print 'Houston, we have a problem.'
    print
    print 'An error occurred on line ???? in statement ???'
    exit(1)

I don't want to have to add this to every assert statement:

assert 7 == 7, "7 == 7"

because it repeats information.

Hyaena answered 20/7, 2012 at 21:34 Comment(5)
Two issues. First, if you are having trouble identifying where the exception is happening in your try..except, that's a sign your try..except block is too big. Second, the kind of thing meant to be caught by assert isn't something the user should ever see. If they see an AssertionError, the proper course of action is for them to contact the programmer and say "WTF?!".Warmonger
@John Y, you seem confused. You're saying AssertionErrors shouldn't be seen by the user, and then what the user should do when he sees one. It can't be both!Hyaena
BTW: Asserts should be about the structure of your code, that is, an assert should fail only if you have a bug in your software. They should not be used to check user input. You might consider using a different exception for this application.Cohune
@NedBatchelder Thanks, you are correct. I may reconsider using asserts here, but the procedure for getting the line code will still be useful.Hyaena
@NedBatchelder Why should asserts not be used for user input validation? What alternatives are recommended?Headwater
U
101

Use the traceback module:

import sys
import traceback

try:
    assert True
    assert 7 == 7
    assert 1 == 2
    # many more statements like this
except AssertionError:
    _, _, tb = sys.exc_info()
    traceback.print_tb(tb) # Fixed format
    tb_info = traceback.extract_tb(tb)
    filename, line, func, text = tb_info[-1]

    print('An error occurred on line {} in statement {}'.format(line, text))
    exit(1)
Unlatch answered 20/7, 2012 at 21:37 Comment(3)
How do I get the actual value instead of the line vars? e.g assert a == b I want to show the values of a and b. I tried sys.exc_info()[1] but it's emptyZygotene
@Zygotene Try assert a == b, (a, b), which will give you the values in sys.exc_info()[1].args[0] – or easier, name the AssertionError you catch ae, then it's in ae.args[0]. You can also create a helper method which does the actual assertion. This is what unittest.TestCase.assertEqual does.Unlatch
I took the nose approach here github.com/nose-devs/nose/blob/… to get the exact value. So I can keep the exception message meaning fullZygotene
G
52

The traceback module and sys.exc_info are overkill for tracking down the source of an exception. That's all in the default traceback. So instead of calling exit(1) just re-raise:

try:
    assert "birthday cake" == "ice cream cake", "Should've asked for pie"
except AssertionError:
    print 'Houston, we have a problem.'
    raise

Which gives the following output that includes the offending statement and line number:

Houston, we have a problem.
Traceback (most recent call last):
  File "/tmp/poop.py", line 2, in <module>
    assert "birthday cake" == "ice cream cake", "Should've asked for pie"
AssertionError: Should've asked for pie

Similarly the logging module makes it easy to log a traceback for any exception (including those which are caught and never re-raised):

import logging

try:
    assert False == True 
except AssertionError:
    logging.error("Nothing is real but I can't quit...", exc_info=True)
Gill answered 17/8, 2017 at 9:10 Comment(0)
G
1

A simpler way would be:

try:
    assert True, 'first'
    assert 7 == 7, 'second'
    assert 1 == 2, 'third'
    # many more statements like this
except AssertionError as e:
    print(f'Assertion failed at the {str(e)} line')
    exit(1)
Gillis answered 11/10, 2023 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.