【发布时间】:2016-01-29 04:45:34
【问题描述】:
我有一个在 Django 服务上运行的大型 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
...
在 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(),为每次使用创建单独的实例? -
测试代码访问上下文管理器单例上的“全局值”,以了解是否应用测试。这不在 with 语句的直接上下文中。测试方法中有
if override_tests.overriding: return True,因此它们被覆盖。如果有多个上下文管理器实例,我不能这样做。 -
任何依赖于改变全局状态的东西都会遇到线程问题。听起来您可能需要重新考虑这里的整个结构。通过检查那种全局标志来改变它们的行为的函数不是很健壮。例如,您可以将所有测试函数放入一个类中,以便每个实例存储自己的覆盖状态,然后为每个线程创建该类的新实例。
-
无法将测试集中到一个类中。我正在查看 threading.local() 但我还没有设法完成这项工作。
标签: python django multithreading python-2.7