2016-07-14 4 views
3

リードテーブルでドキュメントをコンパイルするには、モジュールh5pyをモックする必要があります。モックモジュールとサブクラス(TypeError:メタクラスベースを呼び出すときのエラー)

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のドキュメントではかなり一般的だと思います。コミュニティが答えを得ることは有益でしょう。

+0

あなたには、 'Mock'を使用していますPython 2、Python 3の 'MagicMock' Python 3 *では、あなたのサブクラスのインスタンスを作成しようとすると無限の再帰があります。あなたのサンプルはPython 2でしか動作しません。しかし、 '__getattr__'がより多くの' MagicMock'インスタンスを返すように*サブクラスを作成する必要はありません。 –

答えて

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デコレータの代わりに使用する新しいディスクリプタを作成して、またはのいずれかのインスタンスにバインドすることで回避できます。あなたのMockクラスのインスタンスでは、少なくともself._mock_nameという形式のコンテキストがあります。さらにカスタマイズすることなく、MagicMock直接を使ってPython 3では


は、基本クラスとして使用する場合作品:

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

が、これは実際に意図的でないと動作をサポート。クラスとサブクラスクラス名の文字列から「specced」されています

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