import contextlib import sys import logging log = logging.getLogger(__name__) @contextlib.contextmanager def nested(*managers): """ Like contextlib.nested but takes callables returning context managers, to avoid the major reason why contextlib.nested was deprecated. This version also logs any exceptions early, much like run_tasks, to ease debugging. TODO combine nested and run_tasks. """ exits = [] vars = [] exc = (None, None, None) try: for mgr_fn in managers: mgr = mgr_fn() exit = mgr.__exit__ enter = mgr.__enter__ vars.append(enter()) exits.append(exit) yield vars except Exception: log.exception('Saw exception from nested tasks') exc = sys.exc_info() finally: while exits: exit = exits.pop() try: if exit(*exc): exc = (None, None, None) except Exception: exc = sys.exc_info() if exc != (None, None, None): # Don't rely on sys.exc_info() still containing # the right information. Another exception may # have been raised and caught by an exit method raise exc[0], exc[1], exc[2]