logger.removeHandler(logger.handlers [0])如何抛出ValueError:list.remove(x):x不在列表中?

时间:2017-09-14 11:20:57

标签: python django list logging

我正在尝试在其他人的Python代码中调试异常,而且我不是Python的专家。代码尝试刷新并删除标准Python Logger上的所有处理程序:

def restart_logging(logger_id):
    logger = logging.getLogger(logger_id)
    while logger.handlers:
        handler = logger.handlers[0]
        handler.flush()
        logger.removeHandler(handler)
    init_logging(logger_id)

这引发了一个例外:

File "/usr/lib64/python2.6/logging/__init__.py", line 1208, in removeHandler
    self.handlers.remove(hdlr)
ValueError: list.remove(x): x not in list

我已经回顾了StackOverflow的其他“x not in list”问题,它们都适合这两个类别:

  • 多次删除同一项目(通常是因为for x in list: list.remove(x)而不是while list: list.remove(list[0])
  • 删除列表中从不存在的项目

我不知道这里是怎么适用的。

首先,使用while循环:当仍有处理程序时,取第一个(成功或存在IndexError),冲洗它,删除它。即使它删除了列表中的多个条目,如果列表仍然有条目,而不是for循环迭代(可能已删除)对象,它是一个while循环测试。

查看logging模块源,它只调用remove() 一次,甚至检查处理程序是否在列表之前致电remove()

def removeHandler(self, hdlr):
    """
    Remove the specified handler from this logger.
    """
    if hdlr in self.handlers:
        #hdlr.close()
        hdlr.acquire()
        try:
            self.handlers.remove(hdlr)
        finally:
            hdlr.release()

此代码作为Django Web应用程序的一部分执行。我可以理解,例如,它是否是一个Java J2EE应用程序,其中两个线程可以同时访问同一个列表,并且没有锁定来“获取第一个项目并将其删除”一个原子操作,因此两个线程都会看到相同的列表中的元素,但只有一个会删除它而另一个会失败,因为线程删除了第一个线程的“列表中的此项”和“从列表中删除此项”之间的元素。

但是,据我所知,Python没有并发性,并使用全局解释器锁来阻止一次发生多个事情。所以这不应该是可能的。

所以,我无法弄清楚为什么list.remove(x): x not in list在这里发生,我无法做出可靠发生的测试用例。我该怎么做才能进一步了解这个问题?

1 个答案:

答案 0 :(得分:1)

虽然单个操作可能是原子操作(以及您可能已注意到的list.remove等操作,但由于CPython的实现细节,这仅 ),removeHandler当然不是。在Python 2.6实现中(您非常有帮助地发布),上下文切换可以在if语句之后和锁定获取之前发生(假设这是hdlr.acquire()所做的)。因此,如果两个线程同时调用此函数,则保证在一个线程随后释放其对GIL的保持时引发异常,因为另一个线程贯穿整个事件完成(原始线程将从if内部继续,处理程序是仍然指向原始的,它将获取IO锁,然后尝试从列表中删除它,导致不必要的异常)。

在Python 2.7中,这已得到修复,removeHandler方法已更改为:

    def removeHandler(self, hdlr):
        """
        Remove the specified handler from this logger.
        """
        _acquireLock()
        try:
            if hdlr in self.handlers:
                self.handlers.remove(hdlr)
        finally:
            _releaseLock()

注意现在如何在if语句之前获取锁。