从sys.exc_info()处理回溯对象时的适当注意和安全性

时间:2011-09-11 00:12:05

标签: python exception error-handling circular-reference

我知道sys.exc_info文档在处理回溯对象时要小心,但我仍然不确定某些情况下的安全性或不安全性。此外,文档说“警告:不要这样做!”,紧接着是“注意:实际上,它没问题”,这进一步让我感到困惑。

在任何情况下,文档和“Why is there a need to explicitly delete the sys.exc_info() traceback in Python?”(Alex Martelli的答案)似乎都暗示其唯一的局部变量引用分配给它们的导致问题的追溯值。

这给我留下了几个问题:

  1. 在这种情况下,“局部变量”究竟是什么意思?我正在努力学习术语,但是:这是指仅在函数中创建的变量,还是由函数参数创建的变量?那么范围内的其他变量呢,例如全局或自我?
  2. 闭包如何影响回溯的潜在循环引用?一般的想法是:一个闭包可以引用它的封闭函数可以的所有东西,所以带有对闭包的引用的回溯最终会引用很多。我正在努力想出一个更具体的例子,但是有一些组合:一个内部函数,返回sys.exc_info()的代码,以及某个范围内的昂贵的短寿命对象。
  3. 随意告诉我,我的结论或假设在哪里是错误的,因为我在写这篇文章时已多次将自己的理由和不相信我自己的陈述推理出来:)。

    虽然我想要回答我的具体例子,但我也要求提供关于如何在更深奥的情况下安全地处理追溯的一般建议,知识或战争故事(例如,你必须运行循环并且想要为了累积任何引发的异常,你必须生成一个新的线程,并且需要报告任何引发的异常,你必须创建闭包和回调,并且必须回复引发的异常等等。)

    示例1:执行错误处理的内部函数

    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
    

    Handled()闭包是否导致任何引发的异常引用error_queue并导致循环引用,因为error_queue也包含回溯?是从error_queue删除回溯(即,调用.get())足以消除循环引用?

    示例2:exc_info范围内的长生对象,或者返回exc_info

    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()
    

    AlphaSub()的引发异常是否引用expensive_object,并且,因为expensive_object被缓存,回溯永远不会消失?如果是这样,那么如何打破这样的循环?

    或者,exc_info包含Alpha堆栈帧,Alpha堆栈帧包含对exc_info的引用,从而产生循环引用。如果是这样,那么如何打破这样的循环?

1 个答案:

答案 0 :(得分:4)

  

在这种情况下,“局部变量”究竟是什么意思?我正在努力学习术语,但是:这是指仅在函数中创建的变量,还是由函数参数创建的变量?那么范围内的其他变量呢,比如全局或自我?

“局部变量”是在函数内创建的所有名称绑定。这包括任何函数参数和分配的任何变量。例如。在下面:

def func(fruwappah, qitzy=None):
    if fruwappah:
        fruit_cake = 'plain'
    else:
        fruit_cake = qitzy
    frosting = 'orange'

变量fruwappahqitzyfruit_cakefrosting都是本地的。哦,因为self在函数标题中(当它出现时,就在我的例子中;),它也是本地的。

  

闭包如何影响回溯的潜在循环引用?一般的想法是:一个闭包可以引用它的封闭函数可以的所有东西,所以带有对闭包的引用的回溯最终会引用很多。我正在努力想出一个更具体的例子,但是它的一些组合:一个内部函数,返回sys.exc_info()的代码,以及某个范围内的昂贵的短寿命对象。

作为与状态链接的答案:跟踪引用每个函数(及其变量)在异常发生时处于活动状态 。换句话说,是否涉及闭包是无关紧要的 - 分配给闭包(非本地),或者就此而言,全局变量将创建循环引用。

有两种基本方法可以解决这个问题:

  1. 定义一个在引发异常后将被称为的函数 - 它在回溯中不会有堆栈帧,所以当它结束所有变量时 - 包括回溯 - 会消失;或
  2. 完成后请确保del traceback_object
  3. 说了这么多,我还需要在我自己的代码中使用traceback对象 - Exception及其各种属性到目前为止已足够了。