我正在尝试在其他人的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
在这里发生,我无法做出可靠发生的测试用例。我该怎么做才能进一步了解这个问题?
答案 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语句之前获取锁。