3

要在 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)

h5py模拟和的正确方法是什么h5py.File

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

4

1 回答 1

1

您不能真正使用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()

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


在 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
于 2016-07-18T13:34:17.500 回答