我有一个运行在Django服务上的大型python应用程序。我需要关闭许可测试对于某些操作,所以我创造了这个情况管理器:使用上下文管理如何制作2.7 python上下文管理器线程安全
class OverrideTests(object):
def __init__(self):
self.override = 0
def __enter__(self):
self.override += 1
# noinspection PyUnusedLocal
def __exit__(self, exc_type, exc_val, exc_tb):
self.override -= 1
assert not self.override < 0
@property
def overriding(self):
return self.override > 0
override_tests = OverrideTests()
应用程序的各个部分,然后可以在此改变测试:
with override_tests:
do stuff
...
内做的东西,上述上下文管理器可以在不同的功能中多次使用。计数器的使用保持这种控制,并且似乎工作正常......直到线程卷入。
一旦涉及到线程,就会重新使用全局上下文管理器,因此测试可能会错误地被覆盖。
下面是一个简单的测试用例 - 这工作得很好,如果thread.start_new_thread(do_id,())
线被替换为简单do_it
但引人注目的失败,如下所示:
def stat(k, expected):
x = '.' if override_tests.overriding == expected else '*'
sys.stdout.write('{0}{1}'.format(k, x))
def do_it_inner():
with override_tests:
stat(2, True)
stat(3, True) # outer with context makes this true
def do_it():
with override_tests:
stat(1, True)
do_it_inner()
stat(4, False)
def do_it_lots(ntimes=10):
for i in range(ntimes):
thread.start_new_thread(do_it,())
我怎样才能让这种情况下管理器线程安全的,这样在每个Python线程,即使它可以重入也一直使用它?
您是否试过不创建单个上下文管理器,而是使用'with OverrideTests()',为每个用法创建一个单独的实例? – BrenBarn
测试代码访问上下文管理器单例中的'全局值'以知道是否应用测试。这不是在与声明的直接背景下。在测试方法中有'if override_tests.overriding:return True',以便它们被覆盖。如果有多个上下文管理器实例,我不能这么做。 –
任何依赖变异全局状态的东西都会遇到线程问题。听起来你可能需要重新思考这里的整个结构。具有通过检查这种全局标志来改变它们的行为的功能并不是非常健壮的。例如,您可以将所有测试函数放入一个类中,以便每个实例存储自己的覆盖状态,然后为每个线程创建该类的新实例。 – BrenBarn