9

私は次のようにlintを実行しています:

$ python -m pylint.lint m2test.py

このコードで:

import M2Crypto
def f():
    M2Crypto.RSA.new_pub_key("").as_pem(cipher=None).split("\n")

lint出力は次のように終了します。

Exception AttributeError: '_shutdown' in <module 'threading' from '/usr/lib/python2.7/site-packages/M2Crypto-0.21.1-py2.7-linux-x86_64.egg/M2Crypto/threading.pyc'> ignored

このコードは実行時に正常に機能します(上記は実際には最小限のテストケースですが、フルバージョンは機能します)。例外は無視されますが、Bittenはこれを失敗と見なすため、このステップで停止します。

関数の定義の周りに「M2Crypto.threading.init()」/「M2Crypto.threading.cleanup()」を追加しようとしましたが、問題は解決しませんでした。

この問題の発生を防ぐにはどうすればよいですか?

Debian Lennyx86_64でM2Crypto0.21.1、pylint 0.24、Python 2.7(2.7.2も試してみました)を使用しています。

4

4 に答える 4

16

あなたが見ている例外は、同じ人によって書かれた、依存astngするツールキットであるパッケージ (おそらく「抽象構文ツリー、次世代」?)のバグによって引き起こされます。pylintついでに言うと、私は常に人々に、可能な場合pyflakesの代わりに使用することをお勧めしていますpylint。これは、迅速、単純、高速、予測可能であるためです。一方、pylint遅いだけでなく、正確に取得できるいくつかの種類の深い魔法を実行しようとしています。こういうトラブル。:)

PyPI の 2 つのパッケージは次のとおりです。

http://pypi.python.org/pypi/pylint

http://pypi.python.org/pypi/astng

そして、この問題は、レポートを生成するためにコードを実行しないため、必然的にコード内のバグでpylintあり、コード内のバグではないことに注意してください。ファイルなど)!あなたのコードは実行されないため、スレッド化や関数で呼び出しを保護するなどの注意を払っても、このエラーを防ぐことはできなかったでしょう — 他の理由で、これから調査しようとしている動作を変更するためにコード スニペットが発生した場合を除きます。pylintinit()cleanup()

それで、あなたの実際の例外に進みます。

私は実際に_shutdown前に聞いたことがありませんでした!Python 標準ライブラリをすばやく検索すると、その定義が示されましthreading.pyたが、どこからでも関数を呼び出すことはできませんでした。pythonrun.cPython C ソース コードを検索することによってのみ、インタープリターのシャットダウン中に関数が実際に呼び出されている場所を発見しました。

static void
wait_for_thread_shutdown(void)
{
    ...
    PyObject *threading = PyMapping_GetItemString(tstate->interp->modules,
                                                  "threading");
    if (threading == NULL) {
        /* threading not imported */
        PyErr_Clear();
        return;
    }
    result = PyObject_CallMethod(threading, "_shutdown", "");
    if (result == NULL) {
        PyErr_WriteUnraisable(threading);
    }
    ...
}

どうやらこれは、標準ライブラリ モジュールが必要とするある種のクリーンアップ関数でthreadingあり、確実に呼び出されるように Python インタープリター自体を特別なケースにしています。

上記のコードからわかるように、Python はthreading、プログラムの実行中にモジュールがまったくインポートされない場合でも、静かに問題なく処理します。しかし、threadingインポートされ、シャットダウン時にまだ存在する場合、インタープリターは_shutdown関数の内部を調べ、エラーメッセージを出力し、問題の原因であるゼロ以外の終了ステータスを返します。呼び出すことはできません。

threadingしたがって、モジュールが存在するのに、プログラムの調査が完了し、Python が終了する_shutdown時点でメソッドがない理由を発見する必要があります。pylintいくつかの楽器が必要です。終了時にモジュールがどのように見えるかを出力できpylintますか? 私たちはできる!モジュールは、最後のpylint/lint.py数行で、Run定義したクラスをインスタンス化することで「メイン プログラム」を実行します。

if __name__ == '__main__':
    Run(sys.argv[1:])

そこで私lint.pyはエディターで開きました — それぞれの小さなプロジェクトを Python 仮想環境にインストールすることの素晴らしい点の 1 つは、簡単な実験のためにサードパーティのコードに飛び込んで編集できることです — そしてprintRunクラスの__init__()メソッド:

    sys.path.pop(0)
    print "*****", sys.modules['threading'].__file__  # added by me!
    if exit:
        sys.exit(self.linter.msg_status)

コマンドを再実行しました:

python -m pylint.lint m2test.py

そして、モジュールの__file__文字列が出てきました:threading

***** /home/brandon/venv/lib/python2.7/site-packages/M2Crypto/threading.pyc

まあ、それを見てください。

これが問題です!

このパスによると、実際には、M2Crypto/threading.pyすべての通常の状況下で と呼ばれるだけのモジュールが存在するM2Crypto.threadingためsys.modules、次の名前でディクショナリに配置されます。

sys.modules['M2Crypto.threading']

しかし、どういうわけか、そのファイルはメインの Python モジュールとして読み込まれ、標準ライブラリにthreadingある公式モジュールを隠しています。threadingこのため、Python の終了ロジックは、標準ライブラリ_shutdown()関数が欠落していることを正確に訴えています。

これはどのように起こりますか?最上位モジュールはsys.path、その下のサブディレクトリではなく、 に明示的にリストされているパスにのみ表示できます。これは新たな疑問につながります:pylint実行中に…/M2Crypto/ディレクトリ自体がsys.pathトップレベルのモジュールを含んでいるかのように配置されるポイントはありますか? どれどれ!

M2Cryptoより多くのインストルメンテーションが必要です:名前に が含まれるディレクトリが に現れる瞬間を Python に教えてもらう必要がありますsys.path。それは本当に遅くなり__init__.pyますが、実行時にインポートされる最初のモジュールであるため、-m pylint.lintpylintsys.pathにtrace 関数を追加しましょう。初期化:

def install_tracer():
    import sys
    output = open('mytracer.out', 'w')
    def mytracer(frame, event, arg):
        broken = any(p.endswith('M2Crypto') for p in sys.path)
        output.write('{} {}:{} {}\n'.format(
                broken, frame.f_code.co_filename, frame.f_lineno, event))
        return mytracer
    sys.settrace(mytracer)

install_tracer()
del install_tracer

ここで私がいかに注意しているかに注意してください: モジュールの名前空間で名前を 1 つだけ定義し、それを慎重に削除して、pylintロードを続行する前にクリーンアップします! そして、トレース関数自体が必要とするすべてのリソース (つまり、sysモジュールとoutput開いているファイル) はinstall_tracer()クロージャーで利用できるため、外側からはpylintいつもとまったく同じように見えます。誰かがそれを内省しようとする場合に備えて、pylintそうかもしれません!

これにより、約 80 万行のファイルmytracer.outが生成され、それぞれ次のようになります。

False /home/brandon/venv/lib/python2.7/posixpath.py:118 call

ファイル名と行番号は実行中のコード行であり、インタープリターが実行のどの段階にあるかをFalse示します。sys.pathcall

それでsys.path、中毒になることはありますか?True最初の行またはFalse各行だけを見て、各値で始まる連続する行の数を見てみましょう。

$ awk '{print$1}' mytracer.out | uniq -c
 607997 False
   3173 True
   4558 False
  33217 True
   4304 False
  41699 True
   2953 False
 110503 True
  52575 False

わお!それは問題だ!一度に数千行の実行の場合、テストケースは ですTrue。これは、インタープリターが…/M2Crypto/パスM2Crypto上で実行されていることを意味します。を含む ディレクトリのみが…/M2Cryptoパス上にあるべきです。Falseファイルで最初に遷移するものを探していると、次のようTrueに表示されます。

False /home/brandon/venv/lib/python2.7/site-packages/logilab/astng/builder.py:132 line
False /home/brandon/venv/lib/python2.7/posixpath.py:118 call
...
False /home/brandon/venv/lib/python2.7/posixpath.py:124 line
False /home/brandon/venv/lib/python2.7/posixpath.py:124 return
True /home/brandon/venv/lib/python2.7/site-packages/logilab/astng/builder.py:133 line

ファイルの 132 行目と 133 行目を見るとbuilder.py、犯人が明らかになります。

130    # build astng representation
131    try:
132        sys.path.insert(0, dirname(path)) # XXX (syt) iirk
133        node = self.string_build(data, modname, path)
134    finally:
135        sys.path.pop(0)

コメントに注意してください。これは元のコードの一部であり、私が追加したものではありません! 明らかに、XXX (syt) iirkこのプログラマーの奇妙な母国語では、「このモジュールの親ディレクトリを配置して、誰かがサブモジュールを含むパッケージを強制的にイントロスペクトするたびに不思議なsys.pathことに壊れるようにする」というフレーズに対する感嘆符です。明らかに、それは非常にコンパクトな母国語です。:)pylintpylintthreading

sys.modulesの実際のインポートを監視するようにトレース モジュールを調整すると(演習は読者に任せます)、分析中に他の標準ライブラリ モジュールによってインポートされた が、無邪気にインポートしようとしたthreadingときに発生することがわかります。.SocketServerthreading

それでは、何が起こっているかを確認しましょう。

  1. pylint危険な魔法です。
  2. その魔法の一部として、あなたを見つけると、ディスク上で検索して解析し、名前空間から有効な名前または無効な名前をロードしているかどうかを予測import fooしようとします。foo.py
  3. [以下の私のコメントを参照してください。].split()の戻り値を呼び出すためRSA.as_pem()、はメソッドpylintをイントロスペクトしようとします。これにより、モジュールが使用され、モジュールが importを誘導する呼び出しを行います。as_pem()M2Crypto.BIOpylintthreading
  4. foo.pymoduleのロードの一部として、そのディレクトリが package 内にある場合でも、 onをpylint含むディレクトリをスローします。したがって、そのディレクトリ内のモジュールに、分析中に同じ名前の標準ライブラリ モジュールをシャドウする権限を与えます。foo.pysys.path
  5. Python が終了すると、 のメソッドを実行したいので、M2Crypto.threadingライブラリが所属する場所に座っていることに動揺します。threading_shutdown()threading

これはバグとして のpylint/のastng人々に報告する必要がありlogilab.orgます。私があなたを送ったと彼らに伝えてください。

これを行った後も使用し続けることにしpylintた場合、この場合、2 つの解決策があるようです: を呼び出すコードを検査しないかM2Crypto、インポート プロセスthreading中にインポートします。モジュールが興奮して代わりにスロットをつかもうとするに、スロットをつかむ機会を得ること。pylintimport threadingpylint/__init__.pysys.modules['threading'] pylintM2Crypto/threading.py

結論として、astngXXX (syt) irk の作者が最もよく言っていると思います。それはそう。

于 2011-09-14T02:06:07.333 に答える
3

これを追跡し、詳細な投稿をしてくれた Brandon Craig Rhodes に感謝します。

logilab-astng 0.23.0 がリリースされるまで、問題のある行を astng から削除しました。コードはhg リポジトリから入手できます。そして、これがOPのpbを修正することを確認できます。

于 2011-09-16T08:06:04.677 に答える
1

これはハックのように見えますが、うまくいくと思います。「as_pem()」の結果をコピーして分割します。

import M2Crypto
def f():
    M2Crypto.RSA.new_pub_key("").as_pem(cipher=None)[:].split("\n")

私はPython 2.6.7、M2Crypto 0.21.1、pylint 0.23を使用しています

于 2011-09-13T05:54:44.497 に答える
0

再現できませんでした (Ubuntu 11.04 64 ビットの pylint 0.24 および M2Crypto 0.21.1) が、2 つの提案:

スレッドを明示的に初期化します。

import M2Crypto
def f(): 
    M2Crypto.threading.init()
    M2Crypto.RSA.new_pub_key("").as_pem(cipher=None).split("\n")
    M2Crypto.threading.cleanup()

または、スレッド化せずに再コンパイルします。

m2crypto = Extension(name = 'M2Crypto.__m2crypto',
                 sources = ['SWIG/_m2crypto.i'],
                 extra_compile_args = ['-DTHREADING'],
                 #extra_link_args = ['-Wl,-search_paths_first'], # Uncomment to build Universal Mac binaries
                 )
于 2011-09-13T01:56:47.847 に答える