56

How to handle all but one exception?

try:
    something
except <any Exception except for a NoChildException>:
    # handling

Something like this, except without destroying the original traceback:

try:
    something
except NoChildException:
    raise NoChildException
except Exception:
    # handling
wim
  • 338,267
  • 99
  • 616
  • 750
Ivan Vulović
  • 2,147
  • 6
  • 27
  • 43
  • 4
    Simple answer: Don't. It's really bad practice to catch all exceptions, as you will tend to catch ones you didn't mean to, obscuring errors. There are only a tiny number of legitimate cases for doing such a thing. – Gareth Latty Apr 20 '13 at 18:10
  • 2
    You seem to have answered your own question. Tell us why you are unsatisfied with what you have. – Robᵩ Apr 20 '13 at 18:15
  • 3
    @Robᵩ Not quite, his example will make a *new* exception, not re-raise the old one. – Gareth Latty Apr 20 '13 at 18:18
  • 5
    @Lattyware There is no problem with *catching* all exceptions - e.g. to add extra context then reraising. However, *swallowing* all exceptions (as this example does), is bad practice. – Tom Ferguson Sep 05 '14 at 14:36

5 Answers5

87

The answer is to simply do a bare raise:

try:
    ...
except NoChildException:
    # optionally, do some stuff here and then ...
    raise
except Exception:
    # handling

This will re-raise the last thrown exception, with original stack trace intact (even if it's been handled!).

wim
  • 338,267
  • 99
  • 616
  • 750
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 1
    i agree. but i get this: Fail: Your Patient.update method caught an exception of type ZeroDivisionError when it shouldn't have. You should never use bare `except` clauses in your code. Only catch raised NoChildExceptions. instead of this: Successfully ignored raised exception of type: ZeroDivisionError Successfully ignored raised exception of type: NameError Successfully ignored raised exception of type: AttributeError Successfully ignored raised exception of type: TypeError Successfully ignored raised exception of type: ValueError Successfully caught raised NoChildException Test Completed – Ivan Vulović Apr 20 '13 at 19:38
  • 1
    @IvanVulović So what you want is to *only* catch `NoChildException`s (which is the exact opposite of what you have asked), so just do `try: ... except NoChildException: ...`, no need for anything fancy. – Gareth Latty Apr 20 '13 at 20:13
  • 2
    Also: never `except e`/`raise e`. That swallows the stack trace. – Reinderien Sep 08 '14 at 04:41
2

New to Python ... but is not this a viable answer? I use it and apparently works.... and is linear.

try:
    something
except NoChildException:
    assert True
except Exception:
    # handling

E.g., I use this to get rid of (in certain situation useless) return exception FileExistsError from os.mkdir.

That is my code is:

try:
  os.mkdir(dbFileDir, mode=0o700)
except FileExistsError:
  assert True

and I simply accept as an abort to execution the fact that the dir is not somehow accessible.

dario1001
  • 21
  • 1
0

I'd offer this as an improvement on the accepted answer.

try:
    dosomestuff()
except MySpecialException:
    ttype, value, traceback = sys.exc_info()
    raise ttype, value, traceback
except Exception as e:
    mse = convert_to_myspecialexception_with_local_context(e, context)
    raise mse

This approach improves on the accepted answer by maintaining the original stacktrace when MySpecialException is caught, so when your top-level exception handler logs the exception you'll get a traceback that points to where the original exception was thrown.

0

You can do type checking on exceptions! Simply write

try:
    ...
except Exception as e:
    if type(e) == NoChildException:
        raise

It still includes the original stack trace.

Thomas Wagenaar
  • 6,489
  • 5
  • 30
  • 73
-1

I found a context in which catching all errors but one is not a bad thing, namely unit testing.

If I have a method:

def my_method():
   try:
      something()
   except IOError, e:
      handle_it()

Then it could plausibly have a unit test that looks like:

def test_my_method():
   try:
      my_module.my_method()
   except IOError, e:
      print "shouldn't see this error message"
      assert False
   except Exception, e:
      print "some other error message"
      assert False
   assert True

Because you have now detected that my_method just threw an unexpected exception.

coyot
  • 418
  • 1
  • 3
  • 12
  • 1
    Hmm, nah. If you don't expect any exception in the test, don't catch any. If you do expect it, use `with self.assertRaises` or `with pytest.raises` and assert outside the context manager. – wim Mar 15 '18 at 22:30
  • You are assuming pytest as the framework. Bad assumption. – coyot Mar 16 '18 at 02:06
  • No, I'm not. `self.assertRaises` is unittest (stdlib). And nosetests is now deprecated, pytest is probably the most common test framework. – wim Mar 16 '18 at 02:37
  • I think the folks at http://nose2.readthedocs.io/en/latest/ would be very surprised to hear of your claim of deprecation. Especially since Python doesn't deprecate third party libraries, which asks the question, "Deprecated by whom?" . Also, having not done a survey of Python developers, I would be very hesitant to claim which is the most popular, especially in mature products. And to go back to the original point of the test which you missed and which assertRaises doesn't solve, _is to test whether handle_it() handles the exception correctly_. – coyot Mar 16 '18 at 03:37
  • 1. I guess nose2 is a fork, because [nose](http://nose.readthedocs.io/en/latest/) is really deprecated according to the nose authors. Latest commit was [over 2 years ago](https://github.com/nose-devs/nose). 2. For your original point, it just doesn't make sense - if `my_module.my_method()` doesn't handle `IOError`, then it will blow up the stack until it gets caught by the test runner - *which will fail the test* - so there is no need to catch it and `assert False` inside the test. The test runner already does that job for you. So, that's useless to write into the test manually. Get it? – wim Mar 16 '18 at 04:09
  • 1
    FWIW, my point stands even for nose runner - there is `nose.tools.assert_raises` in the original and `nose2.tools.such.helper.assertRaises` in the newer project you've mentioned. – wim Mar 16 '18 at 04:17