Python's assert is a weak debugging tool

October 29, 2006

Here's something (perhaps obvious) that recent experience has taught me:

Python's assert is a pretty bad debugging tool.

The problem is that a failed assert gives you almost no information, just that it did fail. Almost all the time, you need to know something about what the bad values are in order to actually debug the problem, so your first step is to transform 'assert condition' into something like:

if not condition:
  print <stuff>
  assert condition

(The exception that proves the rule are assertions added just to see if a particular condition was as impossible as you thought; there, once you know it's not impossible you're going to be adding code to handle it properly.)

One quick fix is to start using the two-expression form of assert, which at least lets you bundle a useful message (complete, hopefully, with some state information) with the assertion failure. But it's difficult to predict in advance just what information you're going to need to debug something that you thought was impossible when you wrote the code.

(Writing these entries is educational, as it forces me to actually do careful research so that I don't write something truly stupid instead of just relying on my memories. In other words, I didn't know that assert could also be given a message to assert with until just now.)

I consider this especially annoying in assert's case because it is a part of the language. As a language builtin, it could break the normal rules constraining functions in order to be more useful and do clever things like print information about the variables involved in the failing expression.

It's possible to do your own version of assert, or to use a traceback hook in order to make it smarter; possible things to do include dumping local variables and entering the debugger. So far I haven't tried to build anything like this myself, although the automatic variable dumping code would make an interesting exercise in playing around with deep Python introspection.


Comments on this page:

By Dan.Astoorian at 2006-10-31 12:21:16:

Almost all the time, you need to know something about what the bad values are in order to actually debug the problem, so your first step is to transform 'assert condition' into something like:

  if not condition:
    print <stuff>
    assert condition

I'd have replaced that last line with "raise AssertionError", myself--no need to re-evaluate the condition that's already failed. (Besides, if sufficiently impossible things are happening that the condition failed the first time, one may not want to take the chance that condition might somehow stop being false between the if and the assert, resulting in the exception never being raised.)

Then again, one could also just have replaced the block with something more like assert condition, <stuff> to provide the relevant details; .e.g., assert a==b, "a=%s, b=%s" % (`a`, `b`) , which, though clumsy, is probably no more cumbersome than breaking out the same information into an if block.

(The exception that proves the rule are assertions added just to see if a particular condition was as impossible as you thought; there, once you know it's not impossible you're going to be adding code to handle it properly.)

I can't imagine for what other purpose one would use the one-expression form of assert in the first place.

Such assertions may be thought of not so much a debugging tool as a defense mechanism. You don't expect the assertion to fail: if you did, you'd have written code to handle the condition instead of just declaring "this condition had better be true; and if it's not, the running procedure/program is better off dead so it doesn't do any further damage." It's somewhat analagous to the difference in RAM technology between error detection (which is cheaper) versus error correction (which is more complicated).

This brings to mind Steinbach's Guideline for Systems Programming: "Never test for an error condition you don't know how to handle."

--Dan

By cks at 2006-10-31 15:22:00:

The best short way I can put it is that I've used two sorts of asserts about impossible events. One of them is 'I don't see how this can possibly happen but if it does the code will blow up so I want to stop it right here'. The other is 'I don't think I will ever be handed this and I'm not going to bother handling it now, but if I'm wrong, I want to know about it'.

In the first case, if the assertion goes off I most likely have a bug that I need to track down and fix, and for that I am going to immediately need debugging stuff (and I am possibly foolish for not using the two argument form of assert, except that I blush to admit that I didn't know about it or hadn't remembered it until recently, plus I have to guess right about what information I need to figure out why this happened). In the second case, I just need to write the code to actually handle the case.

Written on 29 October 2006.
« Weekly spam summary on October 28th, 2006
First irritations with Fedora Core 6 »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sun Oct 29 21:53:15 2006
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.