3

我正在尝试从 sys.modules 中临时删除一个 python 模块,以便我可以将其作为测试用例的一部分导入(模拟出各种系统功能),然后再将其放回去。(是的,这有点疯狂,我可能最终会重组代码,现在我很好奇......)

我可以删除模块并重新导入它就好了,但是一旦完成,我似乎无法将其放回原始模块。(也许那是不可能的?)这是我写的一个测试用例来测试这个想法:

class Test(unittest.TestCase):

    def test_assumptions(self):
        import meta.common.fileutils as fu1
        del(sys.modules["meta.common.fileutils"])
        import meta.common.fileutils
        del(sys.modules["meta.common.fileutils"])
        sys.modules["meta.common.fileutils"] = fu1 # I hoped this would set the module back
        import meta.common.fileutils as fu2
        self.assertEqual(fu1, fu2)  # assert fails, fu2 is a new copy of module :-(

谁能提出为什么它可能会失败?

编辑,按照答案之一的建议使用 pop() 也失败了:

class Test(unittest.TestCase):

    def test_assumptions(self):
        import meta.common.fileutils as fu1
        orig = sys.modules.pop("meta.common.fileutils")
        import meta.common.fileutils
        del(sys.modules["meta.common.fileutils"])
        sys.modules["meta.common.fileutils"] = orig 
        import meta.common.fileutils as fu2
        self.assertEqual(fu1, orig) # passes
        self.assertEqual(fu2, orig) # fails
        self.assertEqual(fu1, fu2)  # fails
4

2 回答 2

3

在我看来,这里的问题与包裹有关。特别是,对于存在于包中的模块(例如meta.common),有两种方法可以访问它:通过sys.modules和通过父包的字典(例如meta.common.__dict__)。在我看来,这import meta.common.fileutils as fu2条线fu2的价值来自meta.common.__dict__,而不是来自sys.modules

所以解决方案:除了猴子补丁 sys.modules,你还应该猴子补丁父包。即,添加如下内容:

>>> import meta.common
>>> meta.common.fileutils = fu1

sys.modules["meta.common.fileutils"] = fu1在行前。

于 2013-05-10T20:36:56.807 回答
3

sys.modules结构实际​​上只是一个 Python dict。您可以从中删除模块,也可以将它们放回.

将原始模块对象存储在局部变量中,dict.pop()用于删除模块并返回它:

orig = sys.modules.pop('meta.common.fileutils')

然后,在恢复它时,只需将该对象放回sys.modules

sys.modules['meta.common.fileutils'] = orig
于 2013-05-10T20:10:16.123 回答