rtaft
rtaft

Reputation: 2348

How to raise an exception from within an 'with' block in python

I'm using sqlalchemy to create a transaction. I'm using with to take advantage of the auto commit and rollback:

with session.begin():
    do_stuff()

If an exception is raised, is there anyway to propagate that exception after the auto rollback?

Upvotes: 0

Views: 258

Answers (2)

chepner
chepner

Reputation: 530882

If any exception is raised during the body of a with statement, the __exit__ method is immediately called with that exception as an argument. It is up to __exit__ to then decide whether the exception has been properly handled (by returning a truthy value) or whether it should be re-raised after __exit__ completes (by returning a non-truthy value).

SessionTransaction.__exit__ only returns None, which indicates that any exception that may have been raised in the body of the with statement will be raised again.


Note that since the "default" return value of any function is None, the default behavior of __exit__ is to propagate any exceptions. No user-level code is expected to call __exit__ explicitly or look at its return value, so you really have to do out of your way to return a truthy value and suppress an expression.


Also note the description of the with statement's semantics:

The following code:

with EXPRESSION as TARGET:
    SUITE

is semantically equivalent to:

manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__ 
value = enter(manager)
hit_except = False

try:
    TARGET = value
    SUITE 
except:
    hit_except = True
    if not exit(manager, *sys.exc_info()):
        raise 
finally:
    if not hit_except:
        exit(manager, None, None, None)

The __exit__ method is called under one of two mutually exclusive conditions:

  1. An exception was raised, in which case it was called in the except clause. If it returns false, the caught exception is re-raised.
  2. An exception was not raised, in which case it is called in the finally block. hit_except is ensures that exit is not called twice if exit itself raises an exception in the except block.

Upvotes: 1

Barmar
Barmar

Reputation: 780723

You shouldn't need to do anything special.

Context managers created using with are roughly equivalent to:

try:
    context initialization
    do_stuff()
finally:
    contact cleanup

Unlike an except: clause, finally: doesn't block propagation of the exception. It executes the statements and then continues propagation.

Upvotes: 0

Related Questions