Pythonトレースバックを理解する

Pythonトレースバックを理解する

Pythonは、コードで例外が発生したときに*トレースバック*を出力します。 トレースバックの出力は、初めて表示する場合や、何を伝えているのかわからない場合、少々圧倒されます。 しかし、Pythonトレースバックには、コードで発生した例外の原因を診断および修正するのに役立つ豊富な情報があります。 Pythonトレースバックが提供する情報を理解することは、優れたPythonプログラマーになるために不可欠です。

このチュートリアルが終了すると、次のことができるようになります。

  • 表示される次のトレースバックを理解する

  • より一般的なトレースバックのいくつかを認識する

  • 例外を処理しながらトレースバックを正常に記録します

*無料ボーナス:*リンク:[ここをクリックして無料のPythonチートシートを入手してください]データ型、辞書、リスト、Python関数の操作など、Python 3の基本を示します。

Pythonトレースバックとは何ですか?

トレースバックは、特定の時点でコード内で行われた関数呼び出しを含むレポートです。 トレースバックは、 stack tracestack tracebackbacktrace などの名前で知られています。 Pythonでは、使用される用語は traceback です。

プログラムで例外が発生すると、Pythonは現在のトレースバックを出力して、何が問題なのかを知るのに役立ちます。 以下に、この状況を説明する例を示します。

# example.py
def greet(someone):
    print('Hello, ' + someon)

greet('Chad')

ここでは、パラメーター `+ someone `で ` greet()`が呼び出されます。 ただし、 ` greet()`では、その変数名は使用されません。 代わりに、 ` print()`呼び出しで ` someon +`としてつづりが間違っています。

*注意:*このチュートリアルは、Pythonの例外を理解していることを前提としています。 慣れていない、または単に復習したい場合は、https://realpython.com/python-exceptions/[Python Exceptions:An Introduction]をご覧ください。

このプログラムを実行すると、次のトレースバックが表示されます。

$ python example.py
Traceback (most recent call last):
  File "/path/to/example.py", line 4, in <module>
    greet('Chad')
  File "/path/to/example.py", line 2, in greet
    print('Hello, ' + someon)
NameError: name 'someon' is not defined

このトレースバック出力には、問題の診断に必要なすべての情報が含まれています。 トレースバック出力の最終行には、発生した例外のタイプと、その例外に関するいくつかの関連情報が示されます。 トレースバックの前の行は、例外が発生する原因となったコードを示しています。

上記のトレースバックでは、例外は `+ NameError `でした。つまり、定義されていない名前(変数、関数、クラス)への参照があります。 この場合、参照される名前は ` someon +`です。

この場合の最終行には、問題の修正に役立つ十分な情報が含まれています。 ミススペルである「+ someon +」という名前のコードを検索すると、正しい方向がわかります。 ただし、多くの場合、コードははるかに複雑です。

Pythonトレースバックの読み方

Pythonトレースバックには、コードで発生した例外の理由を判断しようとするときに役立つ多くの情報が含まれています。 このセクションでは、トレースバックに含まれるさまざまな情報を理解するために、さまざまなトレースバックを順を追って説明します。

Pythonトレースバックの概要

すべてのPythonトレースバックには、重要なセクションがいくつかあります。 以下の図は、さまざまな部分を強調しています。

Pythonでは、トレースバックを下から順に読むのが最善です。

  1. *青いボックス:*トレースバックの最後の行はエラーメッセージの行です。 発生した例外名が含まれます。

  2. *緑色のボックス:*例外名の後にエラーメッセージがあります。 通常、このメッセージには、例外が発生する理由を理解するのに役立つ情報が含まれています。

  3. *黄色のボックス:*トレースバックのさらに上には、さまざまな関数呼び出しが下から上へ、最新から最新へと移動します。 これらの呼び出しは、呼び出しごとに2行のエントリで表されます。 各呼び出しの最初の行には、ファイル名、行番号、モジュール名などの情報が含まれており、すべてコードの場所を指定しています。

  4. *赤い下線:*これらの呼び出しの2行目には、実行された実際のコードが含まれています。

コマンドラインでコードを実行する場合と、REPLでコードを実行する場合のトレースバック出力には、いくつかの違いがあります。 以下は、REPLで実行された前のセクションの同じコードと、結果のトレースバック出力です。

>>>

>>> def greet(someone):
...   print('Hello, ' + someon)
...
>>> greet('Chad')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in greet
NameError: name 'someon' is not defined

ファイル名の代わりに、 `" <stdin> "`が表示されることに注意してください。 これは、標準入力を介してコードを入力したので理にかなっています。 また、実行されたコード行はトレースバックに表示されません。

:他のプログラミング言語でスタックトレースを表示することに慣れている場合は、Pythonトレースバックの比較方法に大きな違いがあることに気付くでしょう。 他のほとんどの言語は、例外を一番上に表示してから、上から下へ、最近の呼び出しから最も新しいものへと呼び出します。

すでに述べましたが、繰り返しますが、Pythonトレースバックは下から上に読む必要があります。 これは非常に便利です。トレースバックが印刷され、端末(またはトレースバックを読んでいるところ)が通常出力の最後になり、トレースバックを読み始めるのに最適な場所が得られるからです。

特定のトレースバックウォークスルー

特定のトレースバック出力を調べることで、トレースバックがどのような情報を提供するかをより深く理解し、確認することができます。

以下のコードは、Pythonトレースバックが提供する情報を説明するために、次の例で使用されています。

# greetings.py
def who_to_greet(person):
    return person if person else input('Greet who? ')

def greet(someone, greeting='Hello'):
    print(greeting + ', ' + who_to_greet(someone))

def greet_many(people):
    for person in people:
        try:
            greet(person)
        except Exception:
            print('hi, ' + person)

ここで、 `+ who_to_greet()`は値 ` person +`を取り、それを返すか、代わりに値を返すように要求します。

次に、 + greet()+`は、挨拶される名前、 `+ someone +、およびオプションの `+ greeting `値を取り、 ` print()`を呼び出します。 ` who_to_greet()`も呼び出され、 ` someone +`の値が渡されます。

最後に、 `+ greet_many()`は ` people `のリストを反復処理し、 ` greet()`を呼び出します。 ` greet()+`を呼び出して例外が発生した場合、簡単なバックアップの挨拶が出力されます。

このコードには、正しい入力が提供されている限り例外が発生するバグはありません。

`+ greetings.py `の最後に ` greet()`の呼び出しを追加し、予期しないキーワード引数を指定した場合(たとえば、 ` greet( 'Chad'、greting = 'Yo' )+ `)、次のトレースバックを取得します:

$ python example.py
Traceback (most recent call last):
  File "/path/to/greetings.py", line 19, in <module>
    greet('Chad', greting='Yo')
TypeError: greet() got an unexpected keyword argument 'greting'

この場合も、Pythonトレースバックを使用して、出力を上に移動して後方に作業するのが最善です。 トレースバックの最終行から始めて、例外が + TypeError +`であったことがわかります。 コロンの後のすべての例外タイプに続くメッセージは、いくつかの素晴らしい情報を提供します。 `+ greet()+`が予期しないキーワード引数で呼び出されたことを示しています。 未知の引数名も提供されます: `+ greting +

上に移動すると、例外が発生した行を確認できます。 この場合、 `+ greetings.py `の下部に追加したのは ` greet()+`呼び出しです。

次の行には、コードが存在するファイルへのパス、コードが見つかるファイルの行番号、およびそのモジュールが含まれています。 この場合、コードは他のPythonモジュールを使用していないため、ここでは `+ <module> +`のみが表示されます。これは実行中のファイルであることを意味します。

別のファイルと別の入力を使用すると、トレースバックが問題を見つけるために正しい方向を指し示していることがわかります。 フォローしている場合は、 `+ greetings.py `の下部からバグのある ` greet()+`呼び出しを削除し、次のファイルをディレクトリに追加します。

# example.py
from greetings import greet

greet(1)

ここでは、以前のモジュールである `+ greetings.py `をインポートし、そこから ` greet()`を使用する別のPythonファイルを設定しました。 ここで ` example.py +`を実行するとどうなりますか:

$ python example.py
Traceback (most recent call last):
  File "/path/to/example.py", line 3, in <module>
    greet(1)
  File "/path/to/greetings.py", line 5, in greet
    print(greeting + ', ' + who_to_greet(someone))
TypeError: must be str, not int

この場合に発生する例外は再び `+ TypeError +`ですが、今回はメッセージの有用性が少し低くなります。 これは、コードのどこかで文字列を処理することを期待していたが、整数が与えられたことを示しています。

上に移動すると、実行されたコード行が表示されます。 次に、コードのファイルと行番号。 ただし、今回は、 `+ <module> `の代わりに、実行されていた関数の名前 ` greet()+`を取得します。

コードの次の実行行に移動すると、問題のある `+ greet()+`呼び出しが整数で渡されることがわかります。

例外が発生した後、別のコードがその例外をキャッチし、例外が発生することもあります。 このような状況では、Pythonはすべての例外トレースバックを受信した順に出力し、再び最後に発生した例外のトレースバックで終了します。

これは少しわかりにくいので、例を示します。 `+ greetings.py `の下部に ` greet_many()+`の呼び出しを追加します。

# greetings.py
...
greet_many(['Chad', 'Dan', 1])

これにより、3人全員に挨拶が印刷されます。 ただし、このコードを実行すると、複数のトレースバックが出力される例が表示されます。

$ python greetings.py
Hello, Chad
Hello, Dan
Traceback (most recent call last):
  File "greetings.py", line 10, in greet_many
    greet(person)
  File "greetings.py", line 5, in greet
    print(greeting + ', ' + who_to_greet(someone))
TypeError: must be str, not int

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "greetings.py", line 14, in <module>
    greet_many(['Chad', 'Dan', 1])
  File "greetings.py", line 12, in greet_many
    print('hi, ' + person)
TypeError: must be str, not int

上記の出力で、「+ During handling +」で始まる強調表示された行に注目してください。 すべてのトレースバックの間に、この行が表示されます。 そのメッセージは非常に明確で、コードが前の例外を処理しようとしていたときに別の例外が発生しました。

:以前の例外トレースバックを表示するPythonの機能は、Python 3で追加されました。 Python 2では、最後の例外のトレースバックのみを取得します。

前に、整数で `+ greet()`を呼び出したときの例外を見ました。 挨拶する人のリストに「+1」を追加したため、同じ結果が期待できます。 ただし、関数 `+ greet_many()`は ` greet()`呼び出しを ` try `および ` except `ブロックでラップします。 ` greet()`で例外が発生する場合に備えて、 ` greet_many()+`はデフォルトのグリーティングを印刷したいと考えています。

`+ greetings.py +`の関連部分はここで繰り返されます:

def greet_many(people):
    for person in people:
        try:
            greet(person)
        except Exception:
            print('hi, ' + person)

そのため、整数入力が正しくないために `+ greet()`が ` TypeError `になった場合、 ` greet_many()+`はその例外を処理し、簡単な挨拶を出力しようとします。 ここで、コードは最終的に別の同様の例外になります。 まだ文字列と整数を追加しようとしています。

トレースバックのすべての出力を見ると、例外の本当の原因が何かを確認するのに役立ちます。 最終的な例外が発生し、その結果のトレースバックが表示される場合でも、何が問題なのかがまだわからないことがあります。 そのような場合、通常、前の例外に移動すると、根本的な原因をよりよく理解できます。

Pythonの一般的なトレースバックとは何ですか?

プログラムで例外が発生したときにPythonトレースバックを読み取る方法を知っていると、プログラミングの際に非常に役立ちますが、一般的なトレースバックのいくつかを知っていると、プロセスを高速化できます。

以下に、遭遇する可能性のあるいくつかの一般的な例外、それらが発生する理由とその意味、およびトレースバックで見つけることができる情報を示します。

+ AttributeError +

その属性が定義されていないオブジェクトの属性にアクセスしようとすると、 `+ AttributeError +`が発生します。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ 属性の参照または割り当てが失敗したときに発生します。 https://docs.python.org/3/library/exceptions.html#AttributeError [(ソース)] _

以下に、発生する `+ AttributeError +`の例を示します。

>>>

>>> an_int = 1
>>> an_int.an_attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'an_attribute'

+ AttributeError +`のエラーメッセージ行は、特定のオブジェクトタイプ(この場合は `+ int +)がアクセスされた属性、この場合は `+ an_attribute `を持たないことを示しています。 エラーメッセージ行で「 AttributeError +」を確認すると、アクセスしようとした属性とその修正場所をすばやく特定できます。

ほとんどの場合、この例外が発生するということは、おそらく期待していたタイプではないオブジェクトを操作していることを示しています。

>>>

>>> a_list = (1, 2)
>>> a_list.append(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'

上記の例では、 + a_list +`のタイプがhttps://realpython.com/python-lists-tuples/[+ list `]であり、 ` .append()というメソッドがあることを期待しているかもしれません`。 ` AttributeError `例外を受け取り、 ` .append()+`を呼び出そうとしたときに発生したことを確認すると、おそらく予想していた種類のオブジェクトを処理していないことがわかります。

多くの場合、これは、オブジェクトが関数またはメソッド呼び出しから特定の型で返されることを期待している場合に発生し、 `+ None `型のオブジェクトになります。 この場合、エラーメッセージ行に「 AttributeError: 'NoneType’オブジェクトには属性 'append' + `がありません」と表示されます。

+ ImportError +

importステートメントで何か問題が発生すると、 `+ ImportError `が発生します。 インポートしようとしているモジュールが見つからない場合、またはモジュールに存在しないモジュールから何かをインポートしようとした場合、この例外またはそのサブクラス ` ModuleNotFoundError +`が発生します。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ importステートメントにモジュールのロードに問題がある場合に発生します。 また、「+ from …​」の「from list」が import + `の名前が見つかりません。 https://docs.python.org/3/library/exceptions.html#ImportError [(ソース)] _

以下は、 `+ ImportError `と ` ModuleNotFoundError +`が発生する例です。

>>>

>>> import asdf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'asdf'
>>> from collections import asdf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'asdf'

上記の例では、存在しないモジュール「+ asdf 」をインポートしようとすると、「 ModuleNotFoundError 」が発生することがわかります。 存在しないモジュール ` asdf `を、存在するモジュール ` collections `からインポートしようとすると、結果は ` ImportError `になります。 トレースバックの下部にあるエラーメッセージ行は、インポートできないものを示します。どちらの場合も「 asdf +」です。

+ IndexError +

+ list +`または `+ tuple +のように、シーケンスからインデックスを取得しようとすると、 `+ IndexError +`とインデックスが発生しますシーケンスに見つかりません。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ シーケンスの添え字が範囲外の場合に発生します。 https://docs.python.org/3/library/exceptions.html#IndexError [(ソース)] _

以下は、 `+ IndexError +`を発生させる例です。

>>>

>>> a_list = ['a', 'b']
>>> a_list[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

+ IndexError +`のエラーメッセージ行では、すばらしい情報は得られません。 `+ out of range +`であるシーケンス参照と、シーケンスのタイプ(この場合は `+ list +)があることがわかります。 通常、その情報と残りのトレースバックを組み合わせることで、問題の修正方法をすばやく特定するのに十分です。

+ KeyError +

+ IndexError +`と同様に、マッピングにないキー(通常は `+ dict +)にアクセスしようとすると、 `+ KeyError `が発生します。 これを「 IndexError +」と考えますが、https://realpython.com/python-dicts/[dictionaries]用です。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ マッピング(辞書)キーが既存のキーのセットで見つからない場合に発生します。 https://docs.python.org/3/library/exceptions.html#KeyError [(ソース)] _

以下に、発生する `+ KeyError +`の例を示します。

>>>

>>> a_dict['b']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'b'

`+ KeyError +`のエラーメッセージ行は、見つからなかったキーを提供します。 これはあまり続けることではありませんが、残りのトレースバックと組み合わせると、通常は問題を修正するのに十分です。

`+ KeyError +`の詳細については、https://realpython.com/python-keyerror/[Python KeyErrorの例外とその処理方法]をご覧ください。

+ NameError +

変数、モジュール、クラス、関数、またはコードで定義されていない他の名前を参照すると、 `+ NameError +`が発生します。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ ローカル名またはグローバル名が見つからない場合に発生します。 https://docs.python.org/3/library/exceptions.html#NameError [(ソース)] _

以下のコードでは、 `+ greet()`はパラメーター ` person `を取ります。 しかし、関数自体では、そのパラメーターは ` persn +`につづりが間違っています:

>>>

>>> def greet(person):
...     print(f'Hello, {persn}')
>>> greet('World')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in greet
NameError: name 'persn' is not defined

`+ NameError +`トレースバックのエラーメッセージ行には、欠落している名前が表示されます。 上記の例では、渡された関数の変数またはパラメーターのスペルが間違っています。

スペルミスしたパラメータの場合、 `+ NameError +`も発生します。

>>>

>>> def greet(persn):
...     print(f'Hello, {person}')
>>> greet('World')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in greet
NameError: name 'person' is not defined

ここでは、何も間違ったことをしていないように見えるかもしれません。 トレースバックで実行および参照された最後の行は適切に見えます。 この状況に陥った場合は、 `+ person +`変数が使用および定義されている場所をコードで確認する必要があります。 ここで、パラメーター名のスペルが間違っていることがすぐにわかります。

+ SyntaxError +

コードに誤ったPython構文がある場合、 `+ SyntaxError +`が発生します。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ パーサーが構文エラーを検出すると発生します。 https://docs.python.org/3/library/exceptions.html#SyntaxError [(ソース)] _

以下の問題は、関数定義行の最後にあるはずのコロンが欠落していることです。 Python REPLでは、この構文エラーはEnterキーを押すとすぐに発生します。

>>>

>>> def greet(person)
  File "<stdin>", line 1
    def greet(person)
                    ^
SyntaxError: invalid syntax

`+ SyntaxError `のエラーメッセージ行は、コードの構文に問題があったことのみを伝えます。 上記の行を調べると、問題のある行と、通常は問題箇所を指す「 ^ 」(キャレット)が表示されます。 ここでは、関数の ` def +`ステートメントにコロンがありません。

また、 `+ SyntaxError `トレースバックでは、通常の最初の行である ` Traceback(most recent call last):`が欠落しています。 これは、Pythonがコードを解析しようとしたときに ` SyntaxError +`が発生し、実際には行が実行されていないためです。

+ TypeError +

`+ TypeError +`は、コードが整数に文字列を追加しようとしたり、オブジェクトの長さが定義されていません。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ 不適切なタイプのオブジェクトに操作または関数が適用されると発生します。 https://docs.python.org/3/library/exceptions.html#TypeError [(ソース)] _

以下は、発生する `+ TypeError +`のいくつかの例です。

>>>

>>> 1 + '1'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> '1' + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be str, not int
>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

`+ TypeError +`を発生させる上記の例はすべて、異なるメッセージを含むエラーメッセージ行になります。 それらのそれぞれは、何が間違っているかを通知するという非常に良い仕事をします。

最初の2つの例では、文字列と整数を一緒に追加しようとします。 ただし、それらは微妙に異なります。

  • 1つ目は、「+ int 」に「 str 」を追加しようとすることです。 *2番目は、 ` int `を ` str +`に追加しようとしています。

エラーメッセージの行には、これらの違いが反映されています。

最後の例は、 `+ int `で ` len()`を呼び出そうとします。 エラーメッセージ行には、 ` int +`を使用してそれを実行できないことが示されています。

+ ValueError +

オブジェクトの値が正しくない場合、 `+ ValueError `が発生します。 これは、インデックスの値がシーケンスの範囲内にないために発生する「 IndexError 」と考えることができます。より一般的な場合は「 ValueError +」のみです。 Pythonドキュメントでは、この例外がいつ発生するかを定義しています。

_ 操作または関数が、適切なタイプであるが不適切な値を持つ引数を受け取り、 `+ IndexError +`などのより正確な例外によって状況が説明されない場合に発生します。 https://docs.python.org/3/library/exceptions.html#ValueError [(ソース)] _

以下に、発生する `+ ValueError +`の2つの例を示します。

>>>

>>> a, b, c = [1, 2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
>>> a, b = [1, 2, 3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

これらの例の `+ ValueError +`エラーメッセージ行は、値の問題を正確に示しています。

  1. 最初の例では、多くの値をアンパックしようとしています。 エラーメッセージの行には、3つの値をアンパックすることを期待していましたが、2つの値が得られたことが示されています。

  2. 2番目の例の問題は、取得する値が多すぎて、それらを展開するのに十分な変数がないことです。

トレースバックをどのように記録しますか?

例外とその結果のPythonトレースバックを取得するということは、その対処方法を決定する必要があることを意味します。 通常、コードを修正することが最初のステップですが、予期しない入力や間違った入力に問題がある場合があります。 コードでこれらの状況に対応するのは良いことですが、トレースバックをログに記録して他のことを行うことで、例外を沈黙させたり隠したりするのも理にかなっています。

Pythonのトレースバックを黙らせる必要がある、より現実的なコードの例を次に示します。 この例では、https://2.python-requests.org/en/master/[+ requests + library]を使用します。 Pythonのリクエストライブラリ(ガイド)で詳細を確認できます。

# urlcaller.py
import sys
import requests

response = requests.get(sys.argv[1])

print(response.status_code, response.content)

このコードはうまくいきます。 コマンドライン引数としてURLを指定してこのスクリプトを実行すると、URLが呼び出され、応答からHTTPステータスコードとコンテンツが出力されます。 応答がHTTPエラーステータスの場合でも機能します。

$ python urlcaller.py https://httpbin.org/status/200
200 b''
$ python urlcaller.py https://httpbin.org/status/500
500 b''

ただし、取得するためにスクリプトに指定されたURLが存在しないか、ホストサーバーがダウンしている場合があります。 これらの場合、このスクリプトはキャッチされていない `+ ConnectionError +`例外を発生させ、トレースバックを出力します:

$ python urlcaller.py http://thisurlprobablydoesntexist.com
...
During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "urlcaller.py", line 5, in <module>
    response = requests.get(sys.argv[1])
  File "/path/to/requests/api.py", line 75, in get
    return request('get', url, params=params,* *kwargs)
  File "/path/to/requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "/path/to/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/path/to/requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File "/path/to/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='thisurlprobablydoesntexist.com', port=80): Max retries exceeded with url:/(Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7faf9d671860>: Failed to establish a new connection: [Errno -2] Name or service not known',))

ここでのPythonトレースバックは非常に長く、他の多くの例外が発生し、最終的には `+ requests `によって ` ConnectionError `が発生します。 最終的な例外のトレースバックを上に移動すると、問題はすべてコード内の ` urlcaller.py +`の5行目から始まっていることがわかります。

問題のある行をhttps://realpython.com/python-exceptions/#the-try-and-except-block-handling-exceptions [+ try + and + except + block]でラップすると、適切な例外により、スクリプトはより多くの入力で引き続き動作できます。

# urlcaller.py
...
try:
    response = requests.get(sys.argv[1])
except requests.exceptions.ConnectionError:
    print(-1, 'Connection Error')
else:
    print(response.status_code, response.content)

上記のコードは、「+ try 」および「 except 」ブロックとともに「 else 」句を使用しています。 Pythonのこの機能に慣れていない場合は、https://realpython.com/python-exceptions/#the-else-clause [Python例外:はじめに]の ` else +`句のセクションをご覧ください。

これで、 `+ ConnectionError `が発生するURLでスクリプトを実行すると、ステータスコードの ` -1 `とコンテンツ ` Connection Error +`が出力されます。

$ python urlcaller.py http://thisurlprobablydoesntexist.com
-1 Connection Error

これはとてもうまくいきます。 ただし、ほとんどの実際のシステムでは、例外とその結果のトレースバックを黙らせるだけでなく、トレースバックを記録する必要があります。 トレースバックを記録することにより、プログラムの何が問題になっているのかをより深く理解できます。

注: Pythonのログシステムの詳細については、https://realpython.com/python-logging/[Logging in Python]をご覧ください。

+ logging +`パッケージをインポートしてロガーを取得し、そのロガーで `+ try +`と `+の + except + 部分で + .exception()+ `を呼び出すことで、トレースバックをスクリプトに記録できます。 except + `ブロック。 最終的なスクリプトは、次のコードのようになります。

# urlcaller.py
import logging
import sys
import requests

logger = logging.getLogger(__name__)

try:
    response = requests.get(sys.argv[1])
except requests.exceptions.ConnectionError as e:
    logger.exception()
    print(-1, 'Connection Error')
else:
    print(response.status_code, response.content)

問題のあるURLに対してスクリプトを実行すると、予想される `+ -1 `と ` Connection Error +`が出力されますが、トレースバックもログに記録されます。

$ python urlcaller.py http://thisurlprobablydoesntexist.com
...
  File "/path/to/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='thisurlprobablydoesntexist.com', port=80): Max retries exceeded with url:/(Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7faf9d671860>: Failed to establish a new connection: [Errno -2] Name or service not known',))
-1 Connection Error

デフォルトでは、Pythonはログメッセージを標準エラー( + stderr +)に送信します。 これは、トレースバック出力をまったく抑制していないようです。 ただし、 `+ stderr +`のリダイレクト中に再度呼び出すと、ロギングシステムが機能していることがわかります。ログは後で保存できます。

$ python urlcaller.py http://thisurlprobablydoesntexist.com 2> my-logs.log
-1 Connection Error

結論

Pythonトレースバックには、Pythonコードで何が問題なのかを見つけるのに役立つ素晴らしい情報が含まれています。 これらのトレースバックは少し威圧的に見えるかもしれませんが、それを分解して表示しようとしているものを確認すると、非常に役立ちます。 行ごとにいくつかのトレースバックを実行すると、含まれている情報をよりよく理解でき、それらを最大限に活用できます。

コードの実行時にPythonトレースバック出力を取得することは、コードを改善する機会です。 Pythonがあなたを助けようとする1つの方法です。

Pythonトレースバックの読み方がわかったので、トレースバック出力で通知されている問題を診断するためのいくつかのツールと手法についてさらに学ぶことができます。 Pythonの組み込みhttps://docs.python.org/3.7/library/traceback.html [`+ traceback `モジュール]を使用して、トレースバックを操作および検査できます。 ` traceback +`モジュールは、トレースバック出力をさらに活用する必要がある場合に役立ちます。 また、Pythonコードのhttps://realpython.com/search?q=debugging [デバッグのテクニック]について詳しく知ることも役立ちます。