9

ここにドラゴンがいます。あなたは警告されました。

より良いテスト スイートを作成するのに役立つ新しいライブラリを作成することを考えています。これを行うための機能の 1 つは、テスト ランナーではなく、テスト対象のシステムで使用されているすべてのオブジェクトにテスト ダブル (モック オブジェクト、スタブ、フェイク、またはダミー)
があることを検証する機能です。)。テスターがライブ オブジェクトを必要とするため、テストの分離を減らす場合は、そのように明示的に指定する必要があります。

type()これを行う唯一の方法は、デフォルトのメタクラスである組み込み関数をオーバーライドすることです。
新しいデフォルト メタクラスは、test double レジストリ ディクショナリをチェックして、test double に置き換えられているかどうか、またはライブ オブジェクトが指定されているかどうかを確認します。

もちろん、これは Python 自体では不可能です。

>>> TypeError: can't set attributes of built-in/extension type 'type'

テスト スイート (およびおそらく Python) が実行される前に、Python のメタクラス ルックアップに介入する方法はありますか?
たぶんバイトコード操作を使用していますか?しかし、どのように正確に?

4

2 に答える 2

9

以下はお勧めできません。アイデアを実装すると、多くの問題やコーナーケースに遭遇しますが、Python 3.1 以降では、組み込みのフックをオーバーライドすることで、カスタムクラスの作成プロセスにフックできます。__build_class__

import builtins


_orig_build_class = builtins.__build_class__


class SomeMockingMeta(type):
    # whatever


def my_build_class(func, name, *bases, **kwargs):
    if not any(isinstance(b, type) for b in bases):
        # a 'regular' class, not a metaclass
        if 'metaclass' in kwargs:
            if not isinstance(kwargs['metaclass'], type):
                # the metaclass is a callable, but not a class
                orig_meta = kwargs.pop('metaclass')
                class HookedMeta(SomeMockingMeta):
                    def __new__(meta, name, bases, attrs):
                        return orig_meta(name, bases, attrs)
                kwargs['metaclass'] = HookedMeta
            else:
                # There already is a metaclass, insert ours and hope for the best
                class SubclassedMeta(SomeMockingMeta, kwargs['metaclass']):
                    pass
                kwargs['metaclass'] = SubclassedMeta
        else:
            kwargs['metaclass'] = SomeMockingMeta

    return _orig_build_class(func, name, *bases, **kwargs)


builtins.__build_class__ = my_build_class

これはカスタム クラスのみに限定されますが、非常に強力なフックを提供します。

3.1 より前のバージョンの Python では、フック クラスの作成を忘れることがあります。メタクラスが定義されていない場合、 Cbuild_class関数は C 型の値を直接使用し、モジュールから検索しないため、オーバーライドできません。type()__builtin__

于 2013-03-13T17:19:07.580 に答える
2

私はあなたのアイデアが好きですが、あなたは少しコースから外れていると思います。コードがクラスではなくライブラリ関数を呼び出す場合はどうなりますか?偽のtype()が呼び出されることはなく、そのライブラリ関数のモックに失敗したことを通知されることもありません。Djangoと実際のコードベースの両方にたくさんのユーティリティ関数があります。

Pythonソースへのパッチの形で必要なインタープリターレベルのサポートを作成することをお勧めします。または、PythonのCソースをいじるよりも、Python自体で記述されたPyPyのコードベースにそのようなフックを追加する方が簡単な場合があります。

Pythonインタープリターには、Pythonコードの任意の部分が他のコードの部分の実行をステップスルーし、各関数呼び出し、またはPythonの各行まで何を実行するかをチェックできるようにする包括的なツールセットが含まれていることに気付きました。必要に応じて実行されます。

sys.setprofileあなたのニーズには十分なはずです。これを使用すると、ターゲットプログラムによって行われるすべての関数呼び出しについて通知されるフック(コールバック)をインストールできます。これを使用してターゲットプログラムの動作を変更することはできませんが、「モックカバレッジ」メトリックを含む、それに関する統計を収集することはできます。

プロファイラーに関するPythonのドキュメントでは、に基づいて構築された多数のモジュールを紹介していますsys.setprofile。あなたはそれを効果的に使う方法を見るために彼らの情報源を研究することができます。

それでも不十分であることが判明した場合でもsys.settrace、ターゲットプログラムのすべての行をステップスルーし、その変数を検査して実行を変更できる、手間のかかるアプローチがあります。標準モジュールは、デバッグツールの標準セット(ブレークポイント、ステップイン、ステップオーバーなど)にbdb.py基づいて構築され、実装されています。これは、コマンドラインデバッガーや他のグラフィカルデバッガーによって使用されます。sys.settracepdb.py

これらの2つのフックがあれば、大丈夫です。

于 2013-03-14T22:31:13.640 に答える