Понимание трассировки Python

Понимание трассировки Python

Python печатаетtraceback, когда в вашем коде возникает исключение. Результат трассировки может быть немного ошеломляющим, если вы видите его впервые или не знаете, что он говорит вам. Но трассировка Python содержит множество информации, которая может помочь вам диагностировать и устранить причину возникновения исключения в вашем коде. Понимание того, какую информацию предоставляет трассировка Python, жизненно важно для того, чтобы стать лучшим программистом Python.

К концу этого урока вы сможете:

  • Смысл следующего следа вы видите

  • Признать некоторые из наиболее распространенных трассировок

  • Зарегистрируйте трассировку успешно, все еще обрабатывая исключение

Free Bonus:Click here to get our free Python Cheat Sheet, который показывает вам основы Python 3, такие как работа с типами данных, словарями, списками и функциями Python.

Что такое трассировка Python?

Обратный вызов - это отчет, содержащий вызовы функций, сделанные в вашем коде в определенный момент. Трассировки известны под разными именами, включаяstack trace,stack traceback,backtrace и, возможно, другие. В Python используется терминtraceback.

Когда ваша программа выдает исключение, Python напечатает текущий трассировщик, чтобы помочь вам узнать, что пошло не так. Ниже приведен пример, иллюстрирующий эту ситуацию:

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

greet('Chad')

Здесьgreet() вызывается с параметромsomeone. Однако вgreet() это имя переменной не используется. Вместо этого он был написан с ошибкой какsomeon в вызовеprint().

Note: Предполагается, что вы знакомы с исключениями Python. Если вы не знакомы или просто хотите освежиться, вам следует проверитьPython Exceptions: An Introduction.

Когда вы запустите эту программу, вы получите следующую трассировку:

$ python example.py
Traceback (most recent call last):
  File "/path/to/example.py", line 4, in 
    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, которые важны. Диаграмма ниже выделяет различные части:

An example Python traceback with call-outs.

В Python лучше читать трассировку снизу вверх:

  1. Blue box: Последняя строка трассировки - это строка сообщения об ошибке. Он содержит имя исключения, которое было поднято.

  2. Green box: После имени исключения следует сообщение об ошибке. Это сообщение обычно содержит полезную информацию для понимания причины возникновения исключения.

  3. Yellow box: Далее по трассировке следуют различные вызовы функций, перемещающиеся снизу вверх, от самых последних до самых последних. Эти вызовы представлены двухстрочными записями для каждого вызова. Первая строка каждого вызова содержит информацию, такую ​​как имя файла, номер строки и имя модуля, все указывает, где можно найти код.

  4. Red underline: Вторая строка для этих вызовов содержит фактический код, который был выполнен.

Есть несколько различий между выводом трассировки, когда вы выполняете свой код в командной строке и выполняете код в REPL. Ниже приведен тот же код из предыдущего раздела, выполненного в REPL, и полученный результат трассировки:

>>>

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

Обратите внимание, что вместо имен файлов вы получаете"<stdin>". Это имеет смысл, поскольку вы вводили код с помощью стандартного ввода. Кроме того, выполненные строки кода не отображаются в трассировке.

Note: Если вы привыкли видеть трассировку стека на других языках программирования, то заметите существенное различие в том, как трассировка 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() возникает исключение, печатается простое резервное приветствие.

В этом коде нет ошибок, которые могли бы привести к возникновению исключения, если предоставлен правильный ввод.

Если вы добавите вызовgreet() в конецgreetings.py и укажете аргумент ключевого слова, которого он не ожидает (например,greet('Chad', greting='Yo')), то вы получите следующую трассировку:

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

Еще раз, с трассировкой Python, лучше работать в обратном направлении, двигаясь вверх по выводу. Начиная с последней строки трассировки, вы можете видеть, что исключение былоTypeError. Сообщения, следующие за типом исключения, все после двоеточия, дают вам отличную информацию. Он сообщает вам, чтоgreet() был вызван с аргументом ключевого слова, которого он не ожидал. Вам также дается неизвестное имя аргумента:greting.

Двигаясь вверх, вы можете увидеть строку, которая привела к исключению. В данном случае это вызовgreet(), который мы добавили в конецgreetings.py.

Следующая строка показывает путь к файлу, в котором существует код, номер строки этого файла, в котором можно найти код, и модуль, в котором он находится. В этом случае, поскольку наш код не использует никаких других модулей Python, мы просто видим здесь<module>, что означает, что это файл, который выполняется.

С другим файлом и другим вводом вы можете увидеть, что трассировка действительно указывает вам правильное направление, чтобы найти проблему. Если вы следуете инструкциям, удалите ошибочный вызовgreet() из нижней частиgreetings.py и добавьте следующий файл в свой каталог:

# example.py
from greetings import greet

greet(1)

Здесь вы настроили другой файл Python, который импортирует ваш предыдущий модуль,greetings.py, и использует из негоgreet(). Вот что произойдет, если вы запуститеexample.py:

$ python example.py
Traceback (most recent call last):
  File "/path/to/example.py", line 3, in 
    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 выведет все трассировки исключений в том порядке, в котором они были получены, снова заканчивая последним вызовом трассировки исключений.

Так как это может немного сбить с толку, вот пример. Добавьте вызовgreet_many() в конецgreetings.py:

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

Это должно привести к печати приветствия всем трем людям. Однако, если вы запустите этот код, вы увидите пример вывода нескольких обратных трасс:

$ 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 
    greet_many(['Chad', 'Dan', 1])
  File "greetings.py", line 12, in greet_many
    print('hi, ' + person)
TypeError: must be str, not int

Обратите внимание на выделенную строку, начинающуюся сDuring handling в выходных данных выше. Между всеми трассировками вы увидите эту строку. Его сообщение очень четкое, пока ваш код пытался обработать предыдущее исключение, возникло другое исключение.

Note: функция 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 определяет, когда возникает это исключение:

Возникает при сбое ссылки на атрибут или присвоения. (Source)с

Вот пример увеличенияAttributeError:

>>>

>>> an_int = 1
>>> an_int.an_attribute
Traceback (most recent call last):
  File "", line 1, in 
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 "", line 1, in 
AttributeError: 'tuple' object has no attribute 'append'

В приведенном выше примере вы могли ожидать, чтоa_list будет иметь типlist, который имеет метод под названием.append(). Когда вы получаете исключениеAttributeError и видите, что оно было вызвано, когда вы пытаетесь вызвать.append(), это говорит вам, что вы, вероятно, имеете дело не с типом объекта, которого ожидали.

Часто это происходит, когда вы ожидаете, что объект, возвращаемый вызовом функции или метода, будет иметь определенный тип, и в итоге вы получите объект типаNone. В этом случае строка сообщения об ошибке будетAttributeError: 'NoneType' object has no attribute 'append'.

ImportError

ImportError возникает, когда что-то идет не так с оператором импорта. Вы получите это исключение или его подклассModuleNotFoundError, если модуль, который вы пытаетесь импортировать, не может быть найден или если вы попытаетесь импортировать что-то из модуля, которого нет в модуле. Документация Python определяет, когда возникает это исключение:

Возникает, когда в операторе импорта возникают проблемы при попытке загрузить модуль. Также возникает, когда «из списка» вfrom ... import имеет имя, которое не может быть найдено. (Source)

Вот пример повышенияImportError иModuleNotFoundError:

>>>

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

В приведенном выше примере вы можете видеть, что попытка импортировать несуществующий модульasdf приводит к ошибкеModuleNotFoundError. При попытке импортировать то, что не существует,asdf, из модуля, который существует,collections, это приводит кImportError. Строки сообщений об ошибках в нижней части трассировки говорят вам, что не удалось импортировать,asdf в обоих случаях.

IndexError

IndexError возникает, когда вы пытаетесь получить индекс из последовательности, напримерlist or a tuple, и этот индекс не найден в последовательности. Документация Python определяет, когда возникает это исключение:

Возникает, когда нижний индекс последовательности находится вне диапазона. (Source)с

Вот пример, повышающийIndexError:

>>>

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

Строка сообщения об ошибке дляIndexError не дает вам полезной информации. Вы можете видеть, что у вас есть ссылка на последовательностьout of range и тип последовательности, в данном случаеlist. Этой информации в сочетании с остальной частью трассировки обычно достаточно, чтобы помочь вам быстро определить, как решить проблему.

KeyError

ПодобноIndexError,KeyError возникает, когда вы пытаетесь получить доступ к ключу, которого нет в сопоставлении, обычноdict. Думайте об этом как оIndexError, но дляdictionaries. Документация Python определяет, когда возникает это исключение:

Возникает, когда ключ набора (словарь) не найден в наборе существующих ключей. (Source)с

Вот пример увеличенияKeyError:

>>>

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

Строка сообщения об ошибке дляKeyError дает вам ключ, который не может быть найден. Это не так много, но в сочетании с остальной частью трассировки, как правило, достаточно, чтобы решить проблему.

Чтобы более подробно изучитьKeyError, взгляните наPython KeyError Exceptions and How to Handle Them.

NameError

NameError возникает, когда вы ссылаетесь на переменную, модуль, класс, функцию или какое-либо другое имя, которое не было определено в вашем коде. Документация Python определяет, когда возникает это исключение:

Возникает, когда локальное или глобальное имя не найдено. (Source)с

В приведенном ниже кодеgreet() принимает параметрperson. Но в самой функции этот параметр был неправильно написан наpersn:

>>>

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

Здесь может показаться, что вы не сделали ничего плохого. Последняя строка, которая была выполнена и на которую есть ссылка в трассировке, выглядит хорошо. Если вы оказались в такой ситуации, то вам нужно просмотреть свой код, чтобы узнать, где используется и определяется переменнаяperson. Здесь вы можете быстро увидеть, что имя параметра было написано с ошибкой.

SyntaxError

SyntaxError появляется, если в вашем коде неправильный синтаксис Python. Документация Python определяет, когда возникает это исключение:

Возникает, когда синтаксический анализатор обнаруживает синтаксическую ошибку. (Source)с

Ниже проблема заключается в отсутствующем двоеточии, которое должно находиться в конце строки определения функции. В Python REPL эта синтаксическая ошибка возникает сразу после нажатия Enter:

>>>

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

Строка сообщения об ошибкеSyntaxError только сообщает вам, что возникла проблема с синтаксисом вашего кода. Заглянув в строки выше, вы увидите строку с проблемой и, как правило,^ (курсор), указывающий на проблемное место. Здесь двоеточие отсутствует в операторе функцииdef.

Кроме того, при трассировкеSyntaxError обычная первая строкаTraceback (most recent call last): отсутствует. Это потому, чтоSyntaxError возникает, когда Python пытается проанализировать ваш код, а строки на самом деле не выполняются.

TypeError

TypeError возникает, когда ваш код пытается сделать что-то с объектом, который не может этого сделать, например, пытается добавить строку к целому числу или вызываетlen() для объекта, длина которого равна n Не определено. Документация Python определяет, когда возникает это исключение:

Возникает, когда операция или функция применяется к объекту неподходящего типа. (Source)с

Ниже приведены несколько примеров повышенияTypeError:

>>>

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

Все приведенные выше примеры повышенияTypeError приводят к появлению строки сообщения об ошибке с разными сообщениями. Каждый из них довольно хорошо информирует вас о том, что не так.

Первые два примера пытаются добавить строки и целые числа вместе. Тем не менее, они немного отличаются:

  • Первый пытается добавитьstr кint.

  • Второй пытается добавитьint кstr.

Строки сообщений об ошибках отражают эти различия.

Последний пример пытается вызватьlen() наint. Строка сообщения об ошибке сообщает вам, что вы не можете сделать это с помощьюint.

ValueError

ValueError повышается, когда значение объекта неверно. Вы можете думать об этом как оIndexError, которое возникает, потому что значение индекса не находится в диапазоне последовательности, толькоValueError предназначен для более общего случая. Документация Python определяет, когда возникает это исключение:

Возникает, когда операция или функция получает аргумент правильного типа, но неправильное значение, и ситуация не описывается более точным исключением, напримерIndexError. (Source)

Вот два примера поднятияValueError:

>>>

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

Строка сообщения об ошибкеValueError в этих примерах сообщает вам, в чем именно проблема со значениями:

  1. В первом примере вы пытаетесь распаковать слишком много значений. Строка сообщения об ошибке даже говорит о том, что вы ожидали распаковать 3 значения, но получили 2 значения.

  2. Во втором примере проблема в том, что вы получаете слишком много значений и недостаточно переменных для их распаковки.

Как вы регистрируете трассировку?

Получение исключения и полученная в результате трассировка Python означает, что вам нужно решить, что с этим делать. Обычно исправление вашего кода является первым шагом, но иногда проблема заключается в неожиданном или неправильном вводе. Несмотря на то, что в вашем коде полезно предусмотреть такие ситуации, иногда также имеет смысл замолчать или скрыть исключение, регистрируя трассировку и делая что-то еще.

Вот более реальный пример кода, который должен заставить замолчать некоторые трассировки Python. В этом примере используетсяrequests library. Вы можете узнать об этом больше вPython’s Requests Library (Guide):

# 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 
    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(': Failed to establish a new connection: [Errno -2] Name or service not known',))

Отслеживание Python здесь может быть очень долгим, при этом возникает множество других исключений, в результате чегоConnectionError поднимается самимrequests. Если вы перейдете вверх по последней трассировке исключений, вы увидите, что проблема началась в нашем коде со строки 5urlcaller.py.

Если вы заключите оскорбительную строку в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)

В приведенном выше коде используется предложениеelse с блокомtry иexcept. Если вы не знакомы с этой функцией Python, ознакомьтесь с разделом о предложенииelse вPython Exceptions: An Introduction.

Теперь, когда вы запускаете скрипт с URL-адресом, который приведет к появлениюConnectionError, вы получите-1 для кода состояния и содержаниеConnection Error:

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

Это прекрасно работает. Однако в большинстве реальных систем вы не хотите просто отключать исключение и полученную в результате трассировку, а хотите регистрировать трассировку. Регистрация трассировок позволяет лучше понять, что не так в ваших программах.

Note: Чтобы узнать больше о системе журналов Python, посетитеLogging in Python.

Вы можете зарегистрировать трассировку в скрипте, импортировав пакетlogging, получив регистратор и вызвав.exception() на этом регистраторе в частиexcepttry и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(': 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 пытается помочь вам.

Теперь, когда вы знаете, как читать трассировку Python, вы можете больше узнать о некоторых инструментах и ​​методах диагностики проблем, о которых рассказывает ваш вывод трассировки. Встроенный в Pythontraceback module может использоваться для работы и проверки трассировок. Модульtraceback может быть полезен, когда вам нужно получить больше от вывода трассировки. Также было бы полезно узнать больше о некоторыхtechniques for debugging вашего кода Python.