Friday, January 28, 2011

 

Python exception madness - err, Python namespace madness

Riddle me this: What the heck?

a.py:
import b

class MyException(Exception):
   pass

if __name__ == '__main__':
   try:
      b.evil()
   except MyException:
      print 'Caught my exception'
   except Exception as e:
      print 'Caught something else: ' + repr(type(e))
b.py:
from a import MyException

def evil():
   print 'Raising my exception'
   raise MyException('My exception')
Output:
$ python a.py
Raising my exception
Caught something else:
<class 'a.myexception'>

What?! Looks like an oddity of the Python namespace. Note the class being listed as 'a.myexception' - it's as though the imported exception class isn't being recognized as being equal to the class defined just a few lines above. If you put the exception class in c.py, it works.

I'm guessing this is documented behavior of the package import mechanism... but it was certainly unexpected behavior to me.

Edit:

My good friend Rick suggested this was because of circular imports, but I suspected it was because a was both __main__ and a. So I did this:
run.py:
import a

a.test()
a.py:
import b

class MyException(Exception):
   pass

def test():
   try:
      b.evil()
   except MyException:
      print 'Caught my exception'
   except Exception as e:
      print 'Caught something else: ' + repr(type(e))
b.py:
import a

def evil():
   print 'Raising my exception'
   raise a.MyException('My exception')
output:
$ python run.py 
Raising my exception
Caught my exception
To distill a lesson from all this, it's: don't put exception classes in any Python scripts run directly... Or maybe don't import a Python script that's run directly from another Python script.

Rereading all this, I think in the first case that a might actually have been read in twice - once as __name__=='__main__' and once as __name__=='a'. In that case, it seems that __main__.MyException is not a.MyException - which leads to the exception handler failing.

If this is one of the reasons people recommend against circular imports, I'm inclined to agree - I spent way too long troubleshooting a very vague issue that seemed like an interpreter bug at first.

This page is powered by Blogger. Isn't yours?