6

I just converted all my unit test data from JSON to YAML, and now an exception is raised somewhere in my code. More specifically, this is printed traceback:

Traceback (most recent call last):
  File "tests/test_addrtools.py", line 95, in test_validate_correctable_addresses
    self.assertTrue(self.validator(addr), msg)
  File "/Users/tomas/Dropbox/Broadnet/broadpy/lib/broadpy/addrtools.py", line 608, in __call__
    self.validate(addr)
  File "/Users/tomas/Dropbox/Broadnet/broadpy/lib/broadpy/addrtools.py", line 692, in validate
    if self._correction_citytypo(addr): return
  File "/Users/tomas/Dropbox/Broadnet/broadpy/lib/broadpy/addrtools.py", line 943, in _correction_citytypo
    ratio = lev_ratio(old_city, city)
TypeError: ratio expected two Strings or two Unicodes

Now, the file "addrtools.py" on line 943 contains the answer to my problem. I want to see the type and values of old_city and city in the scope where the exception is raised. I have this sort of issue all the time, and a quick and painless method of using pdb to inspect the locals in the scope where the exception is raised would save me tons of time in the future.


I did try the solution posted in the answer to this question, but the post-mortem function places me in python2.7/unittest/main.py(231)runTests() which doesn't help me a whole lot. I guess this is because the exception is caught and re-raised from the unittest code.

Community
  • 1
  • 1
Hubro
  • 56,214
  • 69
  • 228
  • 381
  • You can not modify these files directly? This debug thingy should go there directly, not in the unittest. – Michael Oct 02 '12 at 12:22
  • I don't want to modify the code or the unittest, I just want to inspect the error from the command line using `pdb`. – Hubro Oct 02 '12 at 12:25
  • Well, you can revert it a second later. It's not meant to persist there. If that's also not an option, I'm afraid I can't help. – Michael Oct 02 '12 at 12:32

2 Answers2

4

Wrap it with that:

def debug_on(*exceptions):
    if not exceptions:
        exceptions = (AssertionError, )
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                pdb.post_mortem(sys.exc_info()[2])
        return wrapper
    return decorator

Example:

@debug_on(TypeError)
def buggy_function()
    ....
    raise TypeError
Michael
  • 7,316
  • 1
  • 37
  • 63
  • 1
    Isn't there a solution where I don't have to modify my code to perform the debugging? – Hubro Oct 02 '12 at 12:59
  • Have you already had it run in a debugger? Setting breakpoints and then single stepping through your code. – Michael Oct 02 '12 at 13:01
  • Setting a break-point on the interesting line is not a problem, but when I'm running unit tests using a data file of tens, maybe hundreds, of scenarios, it gets kind of tedious to wait for the scenario that finally triggers the exception. Isn't there a way to just break when the exception is thrown? – Hubro Oct 02 '12 at 14:09
  • No chance to split the unittests in smaller chunks to run separately? – Michael Oct 02 '12 at 14:49
  • I guess I could replace `python -m unittest discover` with my own test runner script, where I utilize your solution. I'll look into it. – Hubro Oct 02 '12 at 16:57
  • `unittest.main(verbosity=2) in the main clause at the end of every file and you hopefully can run them separately, too. Path could make trouble, but you can normally fix that, when you run the script from project source like `python tests/test_bla.py`, instead of from their directory directly. – Michael Oct 02 '12 at 17:04
1

The unittest superset nose has an option that drops you to pdb when a test fails, if it's okay for you to use nose as your test runner:

--pdb                 Drop into debugger on errors
--pdb-failures        Drop into debugger on failures
Hubro
  • 56,214
  • 69
  • 228
  • 381
AKX
  • 152,115
  • 15
  • 115
  • 172