2

In python, whenever I use assertion and it is triggered, I only got an ugly information:

AssertionError

What should I do to automatically generate a message that explained the cause of the error, by parsing the AST of the condition argument?

e.g.: So

assert 2 == 3

causes:

AssertionError: 2 != 3
martineau
  • 119,623
  • 25
  • 170
  • 301
tribbloid
  • 4,026
  • 14
  • 64
  • 103

3 Answers3

0

If dependencies is an issue you can do it easily yourself. ie:

def assert_eq(x, y):
  if x != y:
    print('error: {} != {}'.format(x, y))
  assert x == y
Adam Van Prooyen
  • 891
  • 7
  • 17
0

An easy way to achieve your goal would be to use a wrapper function that takes the asserted condition as a string parameter, so that you can eval it and assert the result, while catching the AssertionError in a try block, so that you can re-raise the exception with the given string as a message:

def my_assert(condition):
    try:
        assert eval(condition)
    except AssertionError as e:
        e.args = condition,
        raise

so that:

my_assert('2 == 3')

would raise:

AssertionError: 2 == 3
blhsing
  • 91,368
  • 6
  • 71
  • 106
  • Needs a little more detail to show how it would work with x and y, I think. This function won't know those variables if called with "x == y"` – RemcoGerlich Jan 31 '19 at 22:51
  • It doesn't need to. The condition is passed in as a string and is `eval`'d directly so it's up to the Python compiler to interpret it. What this answer doesn't do is to raise the error with a condition opposite to the given one (so `2 == 3` is reported as `2 != 3`), which is what the OP states he/she wants it to but I don't think is necessary for his/her purpose. – blhsing Jan 31 '19 at 22:53
  • `eval` has security implications. Consider `ast.literal_eval` instead. See https://stackoverflow.com/a/15197698/2958070 – Ben Jan 31 '19 at 23:30
  • @Ben Unless all your assertions are for constants `ast.literal_eval` is not suited for the purpose since chances are variable names are involved in the given condition, which `ast.literal_eval` won't be able to resolve. Assertions are for development anyway and production code should be stripped of assertions, so security should no be a concern in the first place. – blhsing Jan 31 '19 at 23:50
  • @blhsing I didn't realize about variables screwing with `ast.literal_eval`, thanks. – Ben Feb 01 '19 at 00:13
0

Since AssertionError is a class, you can derive your own that does what you want. The tricky part is hooking it up so the interpreter will use it with assert statment.

Here's something that seems to work, but I don't know whether that will be the case when used in conjunction with jupyter notebook.

import builtins
import traceback


class MyAssertionError(builtins.AssertionError):
    def __init__(self, *args):
        super(MyAssertionError, self).__init__(*args)
        raw_tb = traceback.extract_stack()
        entries = traceback.format_list(raw_tb)

        # Remove the last two entries for the call to extract_stack(). Each
        # entry consists of single string with consisting of two lines, the
        # script file path then the line of source code making the call to this
        # function.
        del entries[-2:]
        self.lines = '\n'.join(entries)

    def __str__(self):
        return super(MyAssertionError, self).__str__() + '\n' + self.lines

builtins.AssertionError = MyAssertionError  # Replace builtin.


if __name__ == '__main__':

    assert 2 == 3
martineau
  • 119,623
  • 25
  • 170
  • 301