繁体   English   中英

模拟模块和子类(TypeError:调用元类库时出错)

[英]Mock modules and subclasses (TypeError: Error when calling the metaclass bases)

要编译readthedocs上的文档,必须模拟模块h5py。 我收到一个错误,可以使用这个简单的代码重现:

from __future__ import print_function
import sys

try:
    from unittest.mock import MagicMock
except ImportError:
    # Python 2
    from mock import Mock as MagicMock


class Mock(MagicMock):
    @classmethod
    def __getattr__(cls, name):
        return Mock()

sys.modules.update({'h5py': Mock()})

import h5py
print(h5py.File, type(h5py.File))


class A(h5py.File):
    pass

print(A, type(A))


class B(A):
    pass

该脚本的输出是:

<Mock id='140342061004112'> <class 'mock.mock.Mock'>
<Mock spec='str' id='140342061005584'> <class 'mock.mock.Mock'>
Traceback (most recent call last):
  File "problem_mock.py", line 32, in <module>
class B(A):
TypeError: Error when calling the metaclass bases
    str() takes at most 1 argument (3 given)

模拟h5pyh5py.File的正确方法是什么?

在我看来,这个问题对于使用readthedocs的文档非常普遍,其中一些模块必须被模拟。 社区有一个答案是有用的。

你不能真正使用Mock 实例作为 ; 它在Python 2上很难实现,并且只是偶然地在Python 3中工作(见下文)。

如果您希望它们在类层次结构中工作,则必须返回Mock 本身:

>>> class A(Mock):  # note, not called!
...     pass
...
>>> class B(A):
...     pass
...
>>> B
<class '__main__.B'>
>>> B()
<B id='4394742480'>

如果您无法导入h5py 可言 ,这意味着你需要保持在您返回类而不是一个实例类手动更新列表:

_classnames = {
    'File',
    # ...
}

class Mock(MagicMock):
    @classmethod
    def __getattr__(cls, name):
        return Mock if name in _classnames else Mock()

这不是万无一失的; 没有办法在classmethod中检测父实例,所以h5py.File().File会导致返回另一个'class',即使在实际的实现中是其他对象。 您可以通过创建一个新的描述符来代替classmethod装饰器来部分解决这个问题,该类classmethod装饰器将绑定到类实例(如果有的话); 这样你至少会在你的Mock类的实例上有self._mock_name形式的上下文。


在Python 3中,当用作基类时, 直接使用MagicMock而无需进一步定制:

>>> from unittest.mock import MagicMock
>>> h5py = MagicMock()
>>> class A(h5py.File): pass
...
>>> class B(A): pass
...

但这不是真正有意和支持的行为; 从类名字符串中“引用”类和子类:

>>> A
<MagicMock spec='str' id='4353980960'>
>>> B
<MagicMock spec='str' id='4354132344'>

由于实例不起作用,因此有各种各样的问题:

>>> A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mjpieters/Development/Library/buildout.python/parts/opt/lib/python3.5/unittest/mock.py", line 917, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/Users/mjpieters/Development/Library/buildout.python/parts/opt/lib/python3.5/unittest/mock.py", line 976, in _mock_call
    result = next(effect)
StopIteration

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM