簡體   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