5

The behavior of sys.exc_info() is described in python docs and on SO and SO as:

  • within an except block or methods called by an except block, a triple describing the exception
  • elsewhere, a triple of None values

So why does this nosetest fail?

def test_lang_stack(self):
    try:
        self.assertEquals((None,None,None), sys.exc_info()) # no exception
        a = 5 / 0
    except:
        self.assertNotEquals((None,None,None), sys.exc_info())  # got exception
    else:
        self.fail('should not get here')
    #finally:
    #    self.assertEquals((None,None,None), sys.exc_info()) # already handled, right?
    self.assertEquals((None,None,None), sys.exc_info())  # already handled, right?

It fails at the last line. If you uncomment the finally block, it fails there instead.

I do see that if I put all this inside one method and call from a different method, then the calling method does not see the exception. The exc_info value seems to remain set to the end of the method where an exception is thrown, even after the exception is handled.

I'm using python2.7 on OSX.

Community
  • 1
  • 1
user634943
  • 101
  • 1
  • 4

2 Answers2

5

Before answering your question, I must explain about sys.exc_info() determines about latest/recent exception in your program:-

Your program is basically a series of function calls, with caller function calling called. Thus a call stack/execution stack is formed, where an entry is pushed for each function, being called. This entry is known as stack frame in python. Thus, when in your program, sys.exc_info() is invoked, it uses the following sequence to determining latest exception. It begins from the current stack frame, which is the function from where you are invoking sys.exc_info(). If the current stack frame is not handling/had not handled an exception, the information is taken from the calling stack frame, or its caller, and so on until a stack frame is found that is handling/had handled an exception . Here, “handling an exception” is defined as “executing or having executed an except clause.” For any stack frame, only information about the most recently handled exception is accessible.

Now, coming to your code,

def test_lang_stack(self):
    try:
        self.assertEquals((None,None,None), sys.exc_info()) # no exception
        a = 5 / 0
    except:
        self.assertNotEquals((None,None,None), sys.exc_info())  # got exception
    else:
        self.fail('should not get here')
    #finally:
    #    self.assertEquals((None,None,None), sys.exc_info()) # already handled, right?
    self.assertEquals((None,None,None), sys.exc_info())  # already handled, right?

As per the procedure explained above, sys.exc_info() will always find recent exception being handled in your function. So, It'll be never tuple of Nones, unless you call sys.exec_clear() explicitly.

I hope, it may help you.

Mangu Singh Rajpurohit
  • 10,806
  • 4
  • 68
  • 97
2

In both assertEquals() and assertNotEquals() you need to call:

sys.exc_clear()

This will clear things for the next error.

cforbish
  • 8,567
  • 3
  • 28
  • 32
  • I can manually clear the error like this, but the point is that the exc_info() call should distinguish between a context where I am handling an exception (in an except: block) vs a context where that exception is not being handled (everywhere else). I wouldn't want to have to explicitly add exc_clear() after every try/except statement throughout our project! – user634943 Jun 07 '13 at 16:09
  • I think the design is to only use sys.exc_info() in exception blocks. – cforbish Jun 10 '13 at 09:21
  • I agree with @user634943 that it would be great to have a way for exc_info() to return (None,None,None) if called outside of an except block. Unfortunately that does not happen magically and currently we do need to call exc_clear() manually... – snooze92 Sep 22 '14 at 10:13
  • Or @cforbish, is there a way to ask python whether a piece of code was code within an except block? – snooze92 Sep 22 '14 at 10:13