0

I have a try/except*n/finally block of code and I was wondering what the correct way to do the following is:

try:
    some_function()
except exceptions.ExceptionA as e:
    self.logger.error("Error")
    subject = 'error A'
    message = 'Check error A'
except exceptions.ExceptionB as b:
    self.logger.error("Error")
    subject = 'error A'
    message = 'Check error A'
finally:
    self.publish(subject, message)
    sys.exit()
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Mo.
  • 40,243
  • 37
  • 86
  • 131

2 Answers2

2

No, because finally is executed always, not just when an exception has been raised and handled. It'll be executed when there is no exception, or an exception was raised you do not handle, or you used return or used continue or break in a loop outside the try, etc.

If an exception has been handled, then names you bind in an except suite will remain set by the time the finally suite executes, but you need to set them to sentinel values first lest you want to avoid NameError or UnboundLocal exceptions in case no such exception was raised:

subject = message = None
try:
    some_function()
except exceptions.ExceptionA as e:
    self.logger.error("Error")
    subject = 'error A'
    message = 'Check error A'
except exceptions.ExceptionB as b:
    self.logger.error("Error")
    subject = 'error A'
    message = 'Check error A'
finally:
    # message and subject *can* be bound to None now
    self.publish(subject, message)
    sys.exit()

If you wanted to execute code only if an exception was raised and handled, you'll have to do so explicitly for each except suite. You cannot use finally for that case, not without additional checking (you could gate on subject is not None for example).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Ah yes, for some reason I forgot that finally is called even when there is no exception called! Thanks! – Mo. Mar 11 '15 at 21:01
1

Yes, you can use variables from except in finally blocks because variables in Python have function scope.

In [1]: try:
   ...:     raise ValueError()
   ...: except:
   ...:     x = 1
   ...: finally:
   ...:     print(x)
   ...:     
1

But you have to make sure the variable is initialized in all code paths.

try:
    pass
except:
    x = 1
finally:
    print(x) # NameError

Typically, you will initialize the variables first:

x = None
try:
    ...
except:
    x = ...
finally:
    ... safe to use x here ...
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • @MartijnPieters: I appreciate the thought, but you've just translated one piece of code you can't run into one other piece of code you can't run. I prefer the ellipses to comments, because the comments don't work in Python. – Dietrich Epp Mar 11 '15 at 21:15