1

私はCからPythonに書き直したモジュールをテストしていて、私のキャリアで見た奇妙なバグの1つを持っています。テストを伴う問題のあるPythonコードは、わずか3行です。

# MATCHERS is a list of compiled regular expression objects defined as a
# global in the top level of the module
print dir(MATCHERS[0])
# diff.Differ is a class implemented in C with thed Python API; zoneA/B Words are
# lists and CompareLines, isJunk, and SetStaticLine are functions
differ = diff.Differ(zoneAWords, zoneBWords, CompareLines, isJunk, SetStaticLine)
# Causes a TypeError
dir(MATCHERS[0])

これらの3行は、次の出力を生成します。

['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'findall', 'finditer', 'flags', 'groupindex', 'groups', 'match', 'pattern', 'scanner', 'search', 'split', 'sub', 'subn']
Traceback (most recent call last):
  File "Diff.py", line 1032, in <module>
    main()
  File "Diff.py", line 1019, in main
    diffs = CompareFiles(sys.argv[1], sys.argv[2])
  File "Diff.py", line 584, in CompareFiles
    print dir(MATCHERS[0])
TypeError: eqTest must be a function

コンパイルされた正規表現オブジェクトのdir()を出力すると、Cで記述されたオブジェクトが作成される前は正常に機能しますが、後でdir()を取得するだけでTypeErrorが発生します。私の知る限り、dir()がTypeErrorを引き起こす方法はありません。TypeErrorとそれに関連するメッセージは、オブジェクトのeqTestフィールド用にカスタマイズされたセッター関数の一部としての私のコードからのものですが、dir()の呼び出しは、もちろん、eqTestを何かに設定しようとはしません。前の行ですでに正常に設定されています。

これはすべて、Cコードで何かがうまくいかず、そのような奇妙な動作を引き起こしたと私に信じさせます。私の現在の仮説はバッファオーバーフローです。配列の境界を超えて書き込むと、これらのエラーの原因となっている何かが変更されました。(MATCHERS [0]に文字列を一致させるように要求すると、エラーメッセージなしでPythonがクラッシュします)私はしばらくの間初期化コードを調べていて、これを引き起こしている可能性のあるものが他にあるかどうかを確認しようと思っていました。

私のコードは長すぎてここに含めることができず、ここのPastebinにあります; 434行目以降はまだ実行されていないため、すべて無視できます。

4

1 に答える 1

2

問題は、の初期化が失敗したときにDiffer_init関数が返されないことである可能性があります。-1setup_differ

あなたのsetup_differ電話py_differ_set_eqTest

static int py_differ_set_eqTest(DIFFER* self, PyObject* value, void* closure)
{
    PyObject* tmp;

    TYPE_CHECK(PyFunction_Check, "eqTest must be a function");
    ASSIGN_MEMBER_NAME(self, value, eqTest);
    return 0;
}

TYPE_CHECKマクロの場所:

#define TYPE_CHECK(typeChecker, msg) if (value != Py_None && !typeChecker(value)) \
    {PyErr_SetString(PyExc_TypeError, msg); return -1;}

問題は、が成功したか失敗したsetup_differかをチェックせず、失敗した場合でも返品できることです。py_differ_set_eqTestDiffer_init0

これで、を呼び出すとdir、属性が正しく出力されますが、返される前に、おそらく例外をチェックし、TypeErrorによって発生したものを確認して発生さTYPE_CHECKせます。

これを修正するには、すべての関数が値を返す必要があります。これにより、例外が発生したかどうかを判断でき、失敗したDiffer_init場合は-1を返す必要があります。setup_differ

于 2013-01-29T19:57:02.353 に答える