Python:メソッド呼び出しからスローされる可能性のある例外を知るにはどうすればよいですか?


89

Pythonコードを実行するときにどの例外が予想されるかを(コーディング時に)知る方法はありますか?どの例外タイプがスローされるかわからないため(そしてドキュメントを読むように言わないので、90%の確率で基本のExceptionクラスをキャッチすることになります。多くの場合、例外は深いところから伝播される可能性があります。ドキュメントが更新または修正されていない場合)。これをチェックするためのツールはありますか?(Pythonコードとライブラリを読むなど)?


2
Python <2.6では、サブクラスraiseだけでなく文字列も使用できることに注意してくださいBaseException。したがって、制御except Exceptionできないライブラリコードを呼び出す場合でも、文字列の例外をキャッチしないため、十分ではありません。他の人が指摘しているように、あなたはここで間違った木を吠えています。
ダニエルプライデン2009年

私はそれを知りませんでした。例外を除いて私は思った:..ほとんどすべてをキャッチします。
GabiMe 2009年

2
except ExceptionPython2.6以降で文字列例外をキャッチするために正常に機能します。
ジェフリーハリス

回答:


22

静的な型付けルールがないため、解決策は不正確になる可能性があると思います。

例外をチェックするツールを私は知りませんが、ニーズに合った独自のツールを思いつくことができます(静的分析で少し遊ぶ良い機会です)。

最初の試みとして、ASTを構築し、すべてのRaiseノードを検索してから、例外を発生させる一般的なパターンを理解しようとする関数を作成できます(コンストラクターを直接呼び出すなど)。

x次のプログラムにしましょう。

x = '''\
if f(x):
    raise IOError(errno.ENOENT, 'not found')
else:
    e = g(x)
    raise e
'''

compilerパッケージを使用してASTをビルドします。

tree = compiler.parse(x)

次に、Raiseビジタークラスを定義します。

class RaiseVisitor(object):
    def __init__(self):
        self.nodes = []
    def visitRaise(self, n):
        self.nodes.append(n)

そして、AST収集Raiseノードを歩きます。

v = RaiseVisitor()
compiler.walk(tree, v)

>>> print v.nodes
[
    Raise(
        CallFunc(
            Name('IOError'),
            [Getattr(Name('errno'), 'ENOENT'), Const('not found')],
            None, None),
        None, None),
    Raise(Name('e'), None, None),
]

コンパイラのシンボルテーブルを使用してシンボルを解決したり、データの依存関係を分析したりすることで続行できます。または、CallFunc(Name('IOError'), ...)「間違いなく上げることを意味するはずです」と推測することもできます。IOErrorこれは、迅速な実用的な結果にはまったく問題ありません:)


この興味深い答えをありがとう。なぜすべてのレイズノード以外のものを探す必要があるのか​​理解できませんでした。「コンパイラのシンボルテーブルを使用してシンボルを解決し、データの依存関係を分析する」必要があるのはなぜですか?例外を発生させる唯一の方法はra​​ise()によるものではありませんか?
GabiMe 2009年

1
与えられたv.nodes値を超えて、あなたは実際のものとは何か、と言うことはできませんName('IOError')Name('e')。あなたはそれらをどのような値(複数可)を知っていないIOErrore、彼らはいわゆる自由変数であるとして、を指すことができます。それらのバインディングコンテキストがわかっている場合でも(ここでシンボルテーブルが機能します)、何らかのデータ依存性分析を実行して、それらの正確な値を推測する必要があります(これはPythonでは難しいはずです)。
Andrey Vlasovskikh 2009年

実用的な半自動化されたソリューションを探している['IOError(errno.ENOENT, "not found")', 'e']ので、ユーザーに表示されるリストは問題ありません。しかし、文字列で表される変数の値の実際のクラスを推測することはできません:)(再
投稿して

1
はい。この方法は賢いですが、実際には完全に網羅されているわけではありません。Pythonの動的な性質により、呼び出しているコードがのようなことを行うことは完全に可能です(明らかに悪い考えですが)exc_class = raw_input(); exec "raise " + exc_class。重要なのは、この種の静的分析は、Pythonのような動的言語では実際には不可能であるということです。
ダニエルプライデン2009年

7
ちなみに、find /path/to/library -name '*.py' | grep 'raise '同様の結果を得ることができます:)
Andrey Vlasovskikh 2009年

23

処理する例外のみをキャッチする必要があります。

具体的なタイプですべての例外をキャッチするのはナンセンスです。あなたが特定の例外をキャッチする必要がありできるされますハンドルを。その他の例外については、「基本例外」をキャッチしてログに記録し(str()関数を使用)、プログラムを終了する(またはクラッシュした状況で適切な何かを実行する)汎用キャッチを作成できます。

本当にすべての例外を処理するつもりで、それらのどれも致命的ではないと確信している場合(たとえば、ある種のサンドボックス環境でコードを実行している場合)、一般的なBaseExceptionをキャッチするアプローチは目的に適合します。

使用しているライブラリのリファレンスではなく、言語の例外リファレンスにも関心があるかもしれません。

ライブラリ参照が非常に貧弱で、システムの例外をキャッチするときに独自の例外を再スローしない場合、唯一の有用なアプローチはテストを実行することです(文書化されていない場合は変更される可能性があるため、テストスイートに追加することもできます)。 。コードにとって重要なファイルを削除し、どの例外がスローされているかを確認してください。提供するデータが多すぎて、どのようなエラーが発生するかを確認してください。

とにかくテストを実行する必要があります。ソースコードによって例外を取得する方法が存在したとしても、それらのいずれかをどのように処理する必要があるかがわからないためです。「ファイルneedful.txtが見つかりません!」というエラーメッセージが表示される可能性があります。捕まえIndexErrorたら?テストだけがわかります。


27
確かに、しかし、何がスローされるかわからない場合、どの例外を処理する必要があるかをどのように決定できますか?
GabiMe 2009年

@ bugspy.net、この問題を反映するように私の答えを修正しました
P Shved

多分それはこれを見つけることができるソースコードアナライザーの時間ですか?開発するのはそれほど難しいことではないと思います
GabiMe 2009年

@ bugspy.net、私はそれがその時ではないかもしれない理由を強調しました。
p Shved

確かにあなたは正しい。ただし、開発中に、どのタイプの例外が発生する可能性があるかを知ることは、依然として興味深い場合があります。
hek2mgl 2015

11

この問題を解決するための正しいツールは、ユニットテストです。ユニットテストでは発生しない実際のコードによって発生する例外がある場合は、さらにユニットテストが必要です。

このことを考慮

def f(duck):
    try:
        duck.quack()
    except ??? could be anything

アヒルはどんなオブジェクトでもかまいません

明らかにAttributeError、アヒルにいんちきがないTypeError場合、アヒルにいんちきがある場合は呼び出すことができますが、呼び出すことはできません。あなたは何duck.quack()が上がるのか分かりませんが、多分DuckError何か

今、あなたがこのようなコードを持っていると仮定します

arr[i] = get_something_from_database()

それが発生した場合、それがIndexErrorarr [i]から来たのか、データベース関数の奥深くから来たのかわかりません。通常、例外が発生した場所はそれほど重要ではなく、何かがうまくいかず、あなたが望んでいたことが起こらなかったということです。

便利なテクニックは、このような例外をキャッチして再発生させることです。

except Exception as e
    #inspect e, decide what to do
    raise

あなたがそれを「リレイズ」しようとしているのなら、なぜそれを捕まえるのですか?
Tarnayカールマン

あなたはそれを再上げる必要はありません、それはコメントが示すことになっていたものです。
John La Rooy 2009年

2
例外をどこかに記録してから再発生させることもできます
John La Rooy 2009年

3
ユニットテストを書くことが答えだとは思いません。問題は「どの例外が予想されるかをどうやって見つけることができるか」であり、単体テストを書くことはこれを見つけるのに役立ちません。実際、単体テストを作成するには、どの例外が予想されるかをすでに知っている必要があるため、正しい単体テストを作成するには、元の質問にも回答する必要があります。
BrunoRanschaert19年

6

これまで誰も説明していませんでしたが、100%正確な例外の完全なリストを作成できない理由は、コメントする価値があると思いました。その理由の1つは、ファーストクラスの関数です。次のような関数があるとしましょう。

def apl(f,arg):
   return f(arg)

これでapl、発生する例外をf発生させることができます。コアライブラリにはそのような関数は多くありませんが、カスタムフィルタ、map、reduceなどでリスト内包表記を使用するものはすべて影響を受けます。

ここでの「重大な」情報源は、ドキュメントとソースアナライザーだけです。彼らができないことを覚えておいてください。


5

ソケットを使用しているときにこれに遭遇しました。発生するすべてのエラー条件を調べたいと思いました(エラーを作成してどのソケットを使用するかを理解するのではなく、簡潔なリストが必要でした)。最終的に、「raise」に対して「/usr/lib64/python2.4/test/test_socket.py」をgrepすることになりました。

$ grep raise test_socket.py
Any exceptions raised by the clients during their tests
        raise TypeError, "test_func must be a callable function"
    raise NotImplementedError, "clientSetUp must be implemented."
    def raise_error(*args, **kwargs):
        raise socket.error
    def raise_herror(*args, **kwargs):
        raise socket.herror
    def raise_gaierror(*args, **kwargs):
        raise socket.gaierror
    self.failUnlessRaises(socket.error, raise_error,
    self.failUnlessRaises(socket.error, raise_herror,
    self.failUnlessRaises(socket.error, raise_gaierror,
        raise socket.error
    # Check that setting it to an invalid value raises ValueError
    # Check that setting it to an invalid type raises TypeError
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,

これはかなり簡潔なエラーのリストです。もちろん、これはケースバイケースでのみ機能し、テストが正確であるかどうかに依存します(通常は正確です)。それ以外の場合は、ほとんどすべての例外をキャッチし、ログに記録して分析し、それらを処理する方法を理解する必要があります(単体テストではそれほど難しくありません)。


4
これは私の主張を強化します。grepまたはソースアナライザーを使用して非常に基本的なもの(たとえば、Javaでは初日から存在していました。冗長性が良いこともあります。Javaは冗長です)を処理する必要がある場合、Pythonでの例外処理は非常に問題があります。しかし、少なくとも厄介な驚きはありません)
GabiMe 2009年

@GabiMe、この機能(または一般的な静的型付け)がすべてのバグを防ぐための特効薬とは異なります。Javaは厄介な驚きに満ちています。そのため、日食は​​定期的にクラッシュします。
John La Rooy 2013

2

私が有益だと思った2つの方法があります。1つ目は、iPythonでコードを実行すると、例外タイプが表示されます。

n = 2
str = 'me '
str + 2
TypeError: unsupported operand type(s) for +: 'int' and 'str'

2番目の方法では、キャッチしすぎた場合に解決し、時間の経過とともに改善します。tryコードに式を含めて、キャッチしexcept Exception as errます。どの例外がスローされたかを知るのに十分なデータを出力します。例外がスローされると、より正確なexcept句を追加してコードを改善します。関連するすべての例外をキャッチしたと感じたら、すべてを含む例外を削除します。それはプログラミングエラーを飲み込むので、とにかくするのは良いことです。

try:
   so something
except Exception as err:
   print "Some message"
   print err.__class__
   print err
   exit(1)

1

通常、例外をキャッチする必要があるのは、数行のコードだけです。main関数全体をtry except句に入れたくないでしょう。数行ごとに、どのような種類の例外が発生する可能性があるかを常に確認する必要があります(または簡単に確認できます)。

ドキュメントには、組み込みの例外の完全なリストがあります。予期しない例外を除外しようとしないでください。呼び出し元のコードで処理/予期される可能性があります。

編集:何がスローされるかは、明らかにあなたがしていることに依存します!シーケンスのランダム要素へのアクセスIndexErrorKeyError、dictのランダム要素:など。

IDLEでこれらの数行を実行して、例外を発生させてください。しかし、当然、ユニットテストの方が優れたソリューションになります。


1
これは私の簡単な質問に答えていません。例外処理を設計する方法や、いつ、どのようにキャッチするかについては質問しません。何が投げられるかを見つける方法を尋ねる
GabiMe 2009年

1
@ bugspy.net:あなたが求めることをすることは不可能であり、これは完全に有効な回避策です。
ダニエルプライデン2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.