Due to a miscommunication between Elsevier Press and the Python Software Foundation, the first printing of this title used an unauthorized modification of the trademarked Python logo. We apologize to the PSF for this, and they have been understanding of our in-press status; in the 2nd and subsequent printing of this title, we shall use a cover design that has been approved as non-dilutive by the Python Software Foundation.

Help make the world a better place and make a secure donation to the Python Software Foundation today!

 

Sloppy Exception Handling

I’ve been going over a bunch of old code lately and picking out some awful habits of mine. Today I want to talk about one that unfortunately is also prevalent throughout the 1st edition of book: sloppy exception handling.

Let’s first look at a short (somewhat contrived) example to demonstrate a couple problems.

import maya.cmds as cmds
def make_nodes(node_type_name, amount):
    """
    Make the specified amount of the specified node.
    @param node_type_name: The name of the node type to create.
    @param amount: The number of nodes to make.
    """
    current_selection = cmds.ls(sl=True)
    try:
        for i in range(amount):
            cmds.createNode(node_type_name)
    except:
        raise
    try:
        cmds.select(current_selection)
    except:
        pass

The first problem here concerns the first try-except clause. Namely: why does it exist? If anything in the try block fails (either the loop or the createNode call), it will raise an exception. Wrapping this code in a try clause is useless in this case, because we don’t actually change any behavior if an exception happens to be raised. The exception is just tossed out to the calling context.

The second problem is that both of these try-except clauses use what I have grown to call Pokémon Exception Handling: they gotta catch ’em all! Unlike the first try-except clause, the second one at least has a point here. (For example, if the selection list is empty when this function is called, resetting the selection would ordinarily raise a TypeError.) However, the PEP style guide recommends against this type of bare except clause (and suggests minimally specifying Exception). Why?

Imagine this function did something more substantial to edit the scene. Suppose it possibly deletes some nodes as well as creates them. If you simply throw in a bare except clause, you may catch errors you do not actually want to, which can lead to sloppy programming. For example, while selecting an empty list throws a TypeError, trying to select an object that does not exist throws a ValueError. In this case, we want to silently trap and ignore the TypeError, but NOT the ValueError, as we anticipate the former, but not the latter. If we built a set of tests for this function, we might totally miss scenarios in which a ValueError is raised. In short, do not put in an except clause unless you anticipate a specific exception of some kind and plan to actually do something about it.

Finally, you should strive to try the minimum amount of code possible at any one time. It is possible some errors can be raised in multiple ways. Since the PEP discusses this point, I won’t go into detail, but you can imagine there may be some cases where you want to catch one TypeError, but not another, in which case they should not be wrapped in the same try clause.

I want to end this post by talking about the API command examples in the book. The book notes that when parsing the command’s arguments by constructing an MArgDatabase, simply raising the exception throws out a really non-descriptive RuntimeError. The book errantly advises that this just be passed over instead of raised, because the command engine would display information about the problem (at least when the command is invoked from MEL). But what about when the command is invoked from Python? Obviously we want it to throw some kind of exception, and probably not just a really low-level RuntimeError. The trouble here of course is that, because you cannot initialize an MArgDatabase or an MArgParser, you can’t really tell what exactly is wrong. In this case, the safest thing to do, in my view, is to raise a TypeError, which can occur with either an invalid flag, or with a flag argument of the wrong type. While you can’t tell the user exactly what is wrong, it is still better than raising the bult-in RuntimeError, and of course infinitely preferable to raising no exception at all!

~ by Adam on January 27, 2012.

3 Responses to “Sloppy Exception Handling”

  1. Thanks for that post, I had always worried that I wasn’t using try/excepts often enough because I felt an if statement sufficient in most cases but now I know why I never liked Pokemon ;P

  2. A few things:

    1- AFAIK a bare ‘raise’ statement will not change the traceback, so that statement is even more ineffective that you say.

    2- Catching on Exception (rather than a bare ‘except’) is mostly important so you do not catch SystemExit or KeyboardInterrupt, which inherit from BaseException and would not be caught by Exception. In fact for your example, I’d say catching ‘Exception’ is an acceptable thing to do. It would be more correct to be more specific but IMO it’s legitimate to say, ‘if this fails, I don’t care why.’ It’s rare to have that situation but I think restoring a selection is a possible instance of it.

  3.  De hecho no debo decir quue el videojuego sea bueno, pese
    a que esta chulo

    Hecha un vistazo y ademas navega por mi weblog; videojuegos para ninos gratis

Leave a Reply to Matt Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.