Mock modules and subclasses (TypeError: Error when calling the metaclass bases)
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)
模拟 h5py
和 h5py.File
的正确方法是什么?
在我看来,这个问题对于带有 readthedocs 的文档来说非常普遍,其中某些模块必须被模拟。社区得到答案会很有用。
你不能真正使用Mock
实例来充当classes;它在 Python 2 上严重失败,并且在 Python 3 上工作只是偶然(见下文)。
如果你想让它们在 [=56= 中工作,你必须 return Mock
class ]层次结构:
>>> class A(Mock): # note, not called!
... pass
...
>>> class B(A):
... pass
...
>>> B
<class '__main__.B'>
>>> B()
<B id='4394742480'>
如果您根本无法导入 h5py ,这意味着您需要手动更新 classes 列表,其中 return class 而不是实例:
_classnames = {
'File',
# ...
}
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return Mock if name in _classnames else Mock()
这不是万无一失的;无法在 class 方法中检测父实例,因此 h5py.File().File
会导致另一个 'class' 被 returned 即使在实际实现中是一些其他对象代替。您可以 部分 通过创建一个新的描述符来解决这个问题,而不是 classmethod
装饰器,该装饰器将绑定到 class 或 给一个实例(如果有的话);这样你至少会在 Mock
class.
的实例上有一个 self._mock_name
形式的上下文
在 Python 3 中,直接使用 MagicMock
作为基础 class:
无需进一步自定义即可
>>> from unittest.mock import MagicMock
>>> h5py = MagicMock()
>>> class A(h5py.File): pass
...
>>> class B(A): pass
...
但这并不是真正有意和受支持的行为; classes 和 subclasses 是 'specced' 来自 classname string:
>>> 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
要在 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 的文档来说非常普遍,其中某些模块必须被模拟。社区得到答案会很有用。
你不能真正使用Mock
实例来充当classes;它在 Python 2 上严重失败,并且在 Python 3 上工作只是偶然(见下文)。
如果你想让它们在 [=56= 中工作,你必须 return Mock
class ]层次结构:
>>> class A(Mock): # note, not called!
... pass
...
>>> class B(A):
... pass
...
>>> B
<class '__main__.B'>
>>> B()
<B id='4394742480'>
如果您根本无法导入 h5py ,这意味着您需要手动更新 classes 列表,其中 return class 而不是实例:
_classnames = {
'File',
# ...
}
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return Mock if name in _classnames else Mock()
这不是万无一失的;无法在 class 方法中检测父实例,因此 h5py.File().File
会导致另一个 'class' 被 returned 即使在实际实现中是一些其他对象代替。您可以 部分 通过创建一个新的描述符来解决这个问题,而不是 classmethod
装饰器,该装饰器将绑定到 class 或 给一个实例(如果有的话);这样你至少会在 Mock
class.
self._mock_name
形式的上下文
在 Python 3 中,直接使用 MagicMock
作为基础 class:
>>> from unittest.mock import MagicMock
>>> h5py = MagicMock()
>>> class A(h5py.File): pass
...
>>> class B(A): pass
...
但这并不是真正有意和受支持的行为; classes 和 subclasses 是 'specced' 来自 classname string:
>>> 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