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

Оглавление

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Просмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Как извлечь максимальную пользу из обратной трассировки на Python

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

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

  • Разберитесь со следующей трассировкой, которую вы увидите
  • Распознайте некоторые наиболее распространенные обратные трассировки
  • Успешно зарегистрируйте обратную трассировку, продолжая обрабатывать исключение

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

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

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

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

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

greet('Chad')


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

Примечание: В этом руководстве предполагается, что вы разбираетесь в исключениях Python. Если вы не знакомы с этим языком или просто хотите освежить его в памяти, то вам следует ознакомиться с Исключениями Python: Введение.

Когда вы запустите эту программу, вы получите следующий результат отслеживания:

$ 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 есть несколько важных разделов. На диаграмме ниже показаны различные части:

An example Python traceback with call-outs.

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

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

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

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

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

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

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

Если вы добавите вызов greet() в нижнюю часть greetings.py и укажете аргумент ключевого слова, который он не ожидает (например, 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.

Двигаясь вверх, вы можете увидеть строку, которая привела к возникновению исключения. В данном случае это вызов 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 <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 выводит все обратные трассировки исключений в том порядке, в котором они были получены, и снова завершается обратным отслеживанием самого последнего исключения raise.

Поскольку это может немного сбить с толку, вот пример. Добавьте вызов 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 <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 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 указано, когда возникает это исключение:

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

Вот пример создания 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 будет иметь тип list,, который имеет метод с именем .append(). Когда вы получаете исключение AttributeError и видите, что оно было вызвано при попытке вызвать .append(), это говорит вам о том, что вы, вероятно, имеете дело не с тем типом объекта, который ожидали.

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

ImportError

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

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

Вот пример создания 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

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

Вызывается, когда индекс последовательности находится вне диапазона. (Источник)

Вот пример, который поднимает вопрос 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, KeyError вызывается при попытке доступа к ключу, которого нет в отображении, обычно это dict. Думайте об этом как о IndexError, но для словарей. В документации Python указано, когда возникает это исключение:

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

Вот пример создания KeyError:

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


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

Для более подробного изучения KeyError, ознакомьтесь с Исключениями Python KeyError и способами их обработки.

NameError

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

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

В приведенном ниже коде 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

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

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

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

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


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

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

TypeError

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

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

Ниже приведены несколько примеров возникновения 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 приводят к появлению строки сообщения об ошибке с разными сообщениями. Каждый из них довольно хорошо информирует вас о том, что не так.

В первых двух примерах сделана попытка объединить строки и целые числа. Однако они немного отличаются друг от друга:

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

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

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

ValueError

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

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

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

>>> 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. Во втором примере проблема заключается в том, что вы получаете слишком много значений и недостаточно переменных для их распаковки.

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

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

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

# urlcaller.py
import sys
import requests

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

print(response.status_code, response.content)


Этот код работает хорошо. Когда вы запустите этот скрипт, указав ему URL-адрес в качестве аргумента командной строки , он вызовет URL-адрес, а затем напечатает код состояния HTTP и содержимое ответа. Это работает даже в том случае, если в ответе был статус HTTP error:

$ 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 здесь может быть очень долгой, поскольку возникает множество других исключений, что в конечном итоге приводит к тому, что ConnectionError вызывается самим requests. Если вы поднимете окончательную трассировку исключений, вы увидите, что вся проблема началась в нашем коде со строки 5 из urlcaller.py.

Если вы заключите строку-нарушительницу в блок try и except, перехват соответствующего исключения позволит вашему скрипту продолжить работу с другими входными данными :

# 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: Введение.

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

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


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

Примечание: Чтобы узнать больше о системе ведения журнала в Python, ознакомьтесь с Ведение журнала в Python.

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

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

<статус завершения article-slug="python-traceback" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-traceback/bookmark/" статус завершения data-api-article-url="/api/v1/articles/python-обратная трассировка/завершение_статуса/"> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0 О том, как понять обратную трассировку Python" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python от @realpython:" url="https://realpython.com/python-traceback /" url-title="Понимание обратной трассировки Python">

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Просмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Как извлечь максимальную пользу из обратной трассировки на Python

Back to Top