【问题标题】:Python -- reloading module level objectsPython——重新加载模块级对象
【发布时间】:2012-07-23 19:03:12
【问题描述】:

我想做什么

我正在尝试编写单元测试来检查我的正则表达式是否正确构造:

# mediamanager/models.py

import re

from django.conf import settings

filetypes_re = {}
for key, exts in settings.MM_FILETYPES.items():
    filetypes_re[key] = re.compile(r'({})'.format('|'.join(exts)))

注意:实际上,我完全不知道为什么要编写这个单元测试,因为这段代码非常简单……但这不是重点。

如您所见,最终的正则表达式取决于用户可以设置的变量 settings.MM_FILETYPES。我需要测试特定的输入,对于这种情况,Django 提供了装饰器@override_settings,它可以临时覆盖设置值:

# mediamanager/tests.py

import unittest
from django.test.utils import override_settings
from mediamanager.models import (filetypes_re, …)  # import everything we want to test

class ModelsTestCase(unittest.TestCase):
    @override_settings(MM_FILETYPES={'image': ['jpg', 'png', 'gif'],
                                     'document': ['pdf', 'txt'],
                                     'audio': ['mp3', 'wav']})
    def test_filetype_re(self):
        filetypes_re_exp = {'image': '(jpg|png|gif)',
                            'document': '(pdf|txt)',
                            'audio': '(mp3|wav)'}

        for key, value in filetypes_re_exp.items():
            self.assertEqual(value, filetypes_re[key].pattern)

很遗憾,这个测试没有通过。在覆盖设置之前加载模块 mediamanager.models,因此使用旧设置编译 filetypes_re。我需要(以某种方式)重新加载它以使新设置生效。

问题

我以这种方式更改了单元测试:

@override_settings(MM_FILETYPES={'image': ['jpg', 'png', 'gif'],
                                 'document': ['pdf', 'txt'],
                                 'audio': ['mp3', 'wav']})
def test_filetype_re(self):
    import mediamanager.models  # obtaining module object from sys.modules have the same result
    reload(mediamanager.models)
    filetypes_re = mediamanager.models.filetypes_re
    filetypes_re_exp = {'image': '(jpg|png|gif)',
                        'document': '(pdf|txt)',
                        'audio': '(mp3|wav)'}

    for key, value in filetypes_re_exp.items():
        self.assertEqual(value, filetypes_re[key].pattern)

测试通过了。但可能是因为我从 mediamanager.models 模块导入了其他对象,这个测试用例中的其他测试失败了。不是全部,只有两个(这很奇怪)。 编辑:一点也不奇怪。只有在 test_filetyes_re 和 reload() 调用之后运行的测试才会失败。

问题

如何“重新加载”模块 mediamanager.models:

  1. filetypes_re 使用新设置进行评估?
  2. 从同一模型导入的所有其他对象不受影响?

我是否应该仅仅因为重新加载后变得不稳定而重写一段代码(我是指来自 mediamanager.models 的其他那些在重新加载后未通过测试的对象)?我读过一些关于重新加载模块的文章,通常这不是一个好主意。

有没有更好的方法来定义模块级对象,比如这个正则表达式,以使测试更容易?

【问题讨论】:

  • 你能解释一下为什么你需要重新加载模块吗?为什么要更改测试的第一个版本?
  • 那是因为用户自己定义了 MM_FILETYPES。因此我不知道我应该测试什么。我临时覆盖了这些设置(使用装饰器),但由于 mediamanager.models 已经加载,因此使用旧设置评估 filetypes_re。我需要重新加载他以使新设置生效。
  • 啊,好的-您需要清除一些东西-您问题中的第一块代码-来自您的settings.py?或者别的地方?其次,您的所有单元测试正在检查mediamanager.models.filetypes_re 是否符合您的预期。为什么? mediamanager 是您的应用吗?
  • 不,settings.py 无关紧要。每个设置都应该通过测试,这就是我使用 override_settings 装饰器的原因——创建已知输入。在这个unittest中,只有这个方法(test_filetypes_re)检查filetypes_re。其他方法检查来自 mediamanager.models 的其他对象,这就是这些测试失败的原因。模块重新加载后,从该模块导入的所有对象仍指向旧对象。
  • 什么旧的? mediamanager.models 是否发生了某种变化?

标签: python django unit-testing testing


【解决方案1】:

有几种方法。在我的脑海中浮现:

# mediamanager/models.py

import re

from django.conf import settings

def get_filetypes_re(mm_filetypes=settings.MM_FILETYPES):
    filetypes_re = {}
    for key, exts in settings.MM_FILETYPES.items():
    filetypes_re[key] = re.compile(r'({})'.format('|'.join(exts)))
    return filetypes_re 

和你的测试:

MM_FILETYPES={'image': ['jpg', 'png', 'gif'],
                        'document': ['pdf', 'txt'],
                        'audio': ['mp3', 'wav']})

def test_filetype_re(self):
    filetypes_re = mediamanager.models.get_filetypes_re(mm_filetypes=MM_FILETYPES)
    filetypes_re_exp = {'image': '(jpg|png|gif)',
                        'document': '(pdf|txt)',
                        'audio': '(mp3|wav)'}

    for key, value in filetypes_re_exp.items():
        self.assertEqual(value, filetypes_re[key].pattern)

【讨论】:

  • 这也让我想到了,但是:我正在编译这个正则表达式,所以我不必每次都重新编译它并节省一点时间。在我的模块之上还有更多的正则表达式。
  • 我真的怀疑您是否能够注意到您将节省的微秒。但如果你真的想进行微优化,你可以只记住函数的返回值。
  • 哦,仅供参考,python 缓存了最近的几百个编译的正则表达式——我怀疑即使是实现 memoization 所需的六行代码也会在执行时间上产生可衡量的差异。 stackoverflow.com/a/452143/473285
  • 在这种情况下,你的回答对我来说是可以接受的。我只是想在模块的顶部定义 filetypes_re = get_filetypes_re(),但是如果 python 在内部缓存 regexps,就没有必要了……谢谢!
  • 这比定义模块级变量要好:stackoverflow.com/a/1988826/473285
猜你喜欢
  • 2012-01-17
  • 1970-01-01
  • 2015-11-19
  • 2012-10-19
  • 1970-01-01
  • 2010-10-01
相关资源
最近更新 更多