On Sun, 02 Dec 2012 12:25:22 -0500, Roy Smith wrote: > This is kind of weird (Python 2.7.3): > > try: > print "hello" > except foo: > print "foo" > > prints "hello". The problem (IMHO) is that apparently the except clause > doesn't get evaluated until after some exception is caught. Which means > it never notices that foo is not defined until it's too late.
This is exactly the same as, well, everything in Python. Nothing is evaluated until needed. Consider this piece of legal Python code: Err = None if condition(x) > 100: Err = OneException elif another_condition(x): Err = AnotherException try: spam(a, b, c) except Err: recover() And consider that spam() may very well set the global Err to yet another value, or delete it altogether, before raising. How should Python check that Err is defined to an actual exception ahead of time? The names of exceptions are no different from any other names in Python. They're merely names, and they're looked up at runtime. If you want to boggle/confuse/annoy your friends, shadowing builtins can help: py> def spam(x): ... global UnicodeEncodeError ... UnicodeEncodeError = ZeroDivisionError ... return 1/x ... py> try: ... spam(0) ... except UnicodeEncodeError: ... print("Divided by zero") ... Divided by zero (By the way, if you ever do this by accident, you can recover with a simple "del UnicodeEncodeError" to restore access to the builtin.) As Terry has said, those sort of pre-runtime checks are the responsibility of pylint or pychecker or equivalent. Python is too dynamic to allow the sort of compile-time checks you can get from a Haskell, C or Pascal, so the Python compiler delegates responsibility to third-party applications. There's no point in making an exceptions for exceptions. > This just came up in some code, where I was trying to catch a very rare > exception. When the exception finally happened, I discovered that I had > a typo in the except clause (I had mis-spelled the name of the > exception). So, instead of getting some useful information, I got an > AttributeError :-( One of the nice features of Python 3: py> try: ... 1/0 ... except ZeroDividingError: ... pass ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 3, in <module> NameError: name 'ZeroDividingError' is not defined Very useful for debugging unexpected errors in except clauses. The downside is that until Python 3.3, there's no way to turn it off when you intentionally catch one exception and raise another in it's place. > Is this a bug, or intended behavior? It seems to me it would be much > more useful (if slightly more expensive) to evaluate the names of the > exceptions in the expect clause before running the try block. "Slightly" more expensive? Methinks you are underestimating the level of dynamism of Python. py> def pick_an_exception(): ... global x ... if 'x' in globals(): ... return TypeError ... x = None ... return NameError ... py> for i in range(3): ... try: ... print(x + 1) ... except pick_an_exception() as err: ... print("Caught", err) ... Caught name 'x' is not defined Caught unsupported operand type(s) for +: 'NoneType' and 'int' Caught unsupported operand type(s) for +: 'NoneType' and 'int' The except expression can be arbitrarily expensive, with arbitrary side- effects. -- Steven -- http://mail.python.org/mailman/listinfo/python-list