title: Proper care and safety when dealing with traceback objects from sys.exc_info()
body: <p>I'm aware that the <a href="http://docs.python.org/library/sys.html#sys.exc_info" rel="nofollow noreferrer">sys.exc_info</a> documentation says to take care when dealing with traceback objects, but am still uncertain of how safe or unsafe some cases are.  Additionally, the documentation says "Warning: Don't do this!", followed immediately by "Note: Actually, its ok", which further confuses me.</p>
  
  <p>In any case, the docs and "<a href="https://stackoverflow.com/questions/1658293/why-is-there-a-need-to-explicitly-delete-the-sys-exc-info-traceback-in-python">Why is there a need to explicitly delete the sys.exc_info() traceback in Python?</a>" (Alex Martelli's answer), seem to imply its only local variables that reference the traceback value assigned to them that cause a problem.</p>
  
  <p>This leaves me with a few questions:</p>
  
  <ol>
  <li>What, exactly, does "local variable" mean in this context?  I'm struggling for terminology, but: does this mean only variables created in the function, or also variable created by the function parameters?  What about other variables in scope, e.g, globals or self?</li>
  <li>How do closures affect the potential circular references of the traceback?  The general thought being: a closure can reference everything its enclosing function can, so a traceback with a reference to a closure can end up referencing quite a bit.  I'm struggling to come up with a more concrete example, but some combination of: an inner function, code that returns sys.exc_info(), with expensive-short-lived-objects in scope somewhere.</li>
  </ol>
  
  <p>Feel free to tell me where my conclusions or assumptions are wrong, as I've reasoned myself into belief and non-belief of my own statements several times as I've written this :).</p>
  
  <p>While I'd like answers to my specific examples, I'm also asking for general advice, knowledge, or war stories on how to safely deal with tracebacks in more esoteric situations (e.g, you have to run a loop and want to accumulate any raised exceptions, you have to spawn a new thread and need to report any of its raised exceptions, you have to create closures and callbacks and have to communicate back raised exceptions, etc).</p>
  
  <p><strong>Example 1: an inner function that does error handling</strong></p>
  
  <pre><code>def DoWebRequest():
    thread, error_queue = CreateThread(ErrorRaisingFunc)
    thread.start()
    thread.join()
    if not error_queue.empty():
      # Purposefully not calling error_queue.get() for illustrative purposes
      print 'error!'
  
  def CreateThread(func):
    error_queue = Queue.Queue()
    def Handled():
      try:
        func()
      except Exception:
        error_queue.put(sys.exc_info())
    thread = threading.Thread(target=Handled)
    return thread, error_queue
  </code></pre>
  
  <p>Does the <code>Handled()</code> closure cause any raised exception to reference <code>error_queue</code> and result in a circular reference because <code>error_queue</code> also contains the traceback?  Is removing the traceback from <code>error_queue</code> (i.e., calling <code>.get()</code>) enough to eliminate the circular-reference?</p>
  
  <p><strong>Example 2: a long lived object in scope of exc_info, or returning exc_info</strong></p>
  
  <pre><code>long_lived_cache = {}
  
  def Alpha(key):
    expensive_object = long_lived_cache.get(key)
    if not expensive_object:
      expensive_object = ComputeExpensiveObject()
      long_lived_cache[key] = expensive_object
  
    exc_info = AlphaSub(expensive_object)
    if exc_info:
      print 'error!', exc_info
  
  def AlphaSub(expensive_object):
    try:
      ErrorRaisingFunc(expensive_object)
      return None
    except Exception:
      return sys.exc_info()
  </code></pre>
  
  <p>Does the raised exception of <code>AlphaSub()</code> have a reference to <code>expensive_object</code>, and, because <code>expensive_object</code> is cached, the traceback never goes away?  If so, how does one break such a cycle?</p>
  
  <p>Alternatively, <code>exc_info</code> contains the <code>Alpha</code> stack frame, and the <code>Alpha</code> stack frame contains the reference to <code>exc_info</code>, resulting in a circular reference.  If so, how does one break such a cycle?</p>