Django-Admin.ru

Ошибки в Python: как читать traceback и исправлять исключения

Как читать traceback в Python снизу вверх, чем SyntaxError отличается от исключений, таблица встроенных ошибок и отладка через print, logging и pdb.

Редакция Django-Admin.ru
· обновлено · 13 мин чтения

Содержание · 9 разделов
  1. Что такое исключение в Python
  2. Как читать traceback снизу вверх
  3. Traceback, который проходит через библиотеки
  4. Несколько traceback подряд: цепочки исключений
  5. SyntaxError и исключения во время выполнения
  6. Встроенные исключения Python: таблица
  7. Иерархия исключений
  8. Обработка исключений: try/except
  9. Как отлаживать ошибки
  10. print()
  11. logging
  12. pdb и breakpoint()
  13. Отладчик в IDE
  14. Алгоритм: что делать, когда Python выдал ошибку
  15. Частые вопросы
  16. Почему в traceback путь к файлам библиотеки, а не к моему коду?
  17. Как вывести traceback, не останавливая программу?
  18. Что значит «Python was not found» в Windows?
  19. Ошибка No module named после установки пакета — что не так?
  20. Как сохранить traceback в файл?
  21. Что значит «Previous line repeated 996 more times»?
  22. Итог

Когда программа на Python падает, интерпретатор печатает traceback — отчёт о том, какая ошибка произошла и по какой цепочке вызовов программа к ней пришла. Ошибки во время выполнения в Python называются исключениями (exceptions): у каждого есть тип (TypeError, KeyError…) и сообщение.

Быстрый ответ — как читать любую ошибку в Python:

  1. Последняя строка — тип исключения и сообщение. С неё и начинайте.
  2. Строки над ней — файл, номер строки и код, где исключение возникло.
  3. Выше — кто вызвал эту функцию, и так до точки входа в программу.

В этом руководстве разберём устройство traceback на реальных примерах, отличие синтаксических ошибок от исключений, таблицу самых частых встроенных исключений и приёмы отладки — от print() до отладчика pdb. Примеры проверены на Python 3.12.

Что такое исключение в Python

Исключение — это объект, который Python создаёт, когда операция не может быть выполнена: деление на ноль, обращение к несуществующему ключу словаря, открытие отсутствующего файла. Исключение «всплывает» вверх по цепочке вызовов. Если его нигде не перехватили конструкцией try/except, программа завершается и печатает traceback.

prices = {"apple": 100}
print(prices["banana"])
Traceback (most recent call last):
  File "C:\projects\shop\prices.py", line 2, in <module>
    print(prices["banana"])
          ~~~~~~^^^^^^^^^^
KeyError: 'banana'

В разговорной речи «ошибка» и «исключение» — синонимы. Формально все встроенные ошибки — классы исключений, унаследованные от BaseException, а почти все, которые стоит обрабатывать в коде, — от Exception.

Как читать traceback снизу вверх

Возьмём программу, которая считает средний чек по клиентам:

def average_price(items):
    total = sum(item["price"] for item in items)
    return total / len(items)


def report(orders):
    for name, items in orders.items():
        print(name, average_price(items))


orders = {
    "Анна": [{"price": 100}, {"price": 300}],
    "Борис": [],
}
report(orders)

Запуск даёт такой вывод:

Анна 200.0
Traceback (most recent call last):
  File "C:\projects\shop\orders.py", line 15, in <module>
    report(orders)
  File "C:\projects\shop\orders.py", line 8, in report
    print(name, average_price(items))
                ^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\shop\orders.py", line 3, in average_price
    return total / len(items)
           ~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero

Читаем снизу вверх:

  1. ZeroDivisionError: division by zero — что случилось: деление на ноль.
  2. File "...orders.py", line 3, in average_price — где: строка 3, функция average_price, выражение total / len(items). Значки ~ и ^ (появились в Python 3.11) подчёркивают точное место: оператор /.
  3. line 8, in report — функцию вызвали из report() в строке 8.
  4. line 15, in <module> — а report() вызвали с верхнего уровня скрипта.

Заголовок Traceback (most recent call last) как раз и означает «самый последний вызов — внизу». Каждая пара строк File … + код называется кадром (frame). Вывод: у Бориса пустой список заказов, len(items) равно нулю. Исправление — проверить пустой список до деления.

Анна 200.0 успела напечататься — всё, что выполнилось до исключения, уже выполнено.

Traceback, который проходит через библиотеки

Часто нижние кадры указывают не на ваш код, а на стандартную библиотеку или установленный пакет:

Traceback (most recent call last):
  File "C:\projects\shop\config.py", line 6, in load_config
    return json.loads(text)
           ^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\json\decoder.py", line 338, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\json\decoder.py", line 354, in raw_decode
    obj, end = self.scan_once(s, idx)
               ^^^^^^^^^^^^^^^^^^^^^^
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

Ошибка почти никогда не в коде json или Django — библиотека просто получила неподходящие данные. Найдите последний снизу кадр из вашего проекта (здесь — config.py, строка 6) и проверьте, что вы туда передали. В этом примере в json.loads() попала строка с некорректным JSON.

Несколько traceback подряд: цепочки исключений

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

Traceback (most recent call last):
  File "C:\projects\shop\handler.py", line 3, in <module>
    value = data["b"]
            ~~~~^^^^^
KeyError: 'b'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\projects\shop\handler.py", line 5, in <module>
    print(undefined_name)
          ^^^^^^^^^^^^^^
NameError: name 'undefined_name' is not defined
  • During handling of the above exception, another exception occurred — новая ошибка случилась внутри блока except, скорее всего, случайно. Чинить нужно обе: и исходную, и ошибку в обработчике.
  • The above exception was the direct cause of the following exception — программист сознательно заменил одно исключение другим через raise ... from e. Верхний traceback — первопричина, нижний — то, как её описал код.

Как правильно перевыбрасывать исключения, разобрано в статье try/except в Python.

SyntaxError и исключения во время выполнения

Ошибки в Python бывают двух видов.

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

  File "C:\projects\shop\app.py", line 1
    print("hello"
         ^
SyntaxError: '(' was never closed

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

Частые синтаксические ошибки и их сообщения в Python 3.12:

СообщениеПричина
SyntaxError: '(' was never closedНе закрыта скобка
SyntaxError: unterminated string literal (detected at line 1)Не закрыта кавычка
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?= вместо == в условии if
IndentationError: expected an indented block after 'if' statement on line 1Нет отступа после if, for, def
IndentationError: unexpected indentЛишний отступ
TabError: inconsistent use of tabs and spaces in indentationСмешаны табы и пробелы

Важная деталь: номер строки в SyntaxError — место, где парсер заметил проблему. Незакрытая скобка в строке 10 может «всплыть» ошибкой на строке 11 или ниже, поэтому смотрите и на предыдущие строки. Правила отступов и синтаксиса собраны в шпаргалке по синтаксису Python.

Встроенные исключения Python: таблица

Сообщения ниже — вывод Python 3.12 (у ImportError Python дополнительно печатает путь к модулю и подсказку).

ИсключениеПример сообщенияТипичная причина
NameErrorname 'nme' is not defined. Did you mean: 'name'?Опечатка в имени, переменная не создана
TypeErrorcan only concatenate str (not "int") to strОперация с несовместимыми типами
TypeErrorobject of type 'int' has no len()Функция получила объект не того типа
ValueErrorinvalid literal for int() with base 10: 'abc'Тип верный, значение — нет
AttributeError'NoneType' object has no attribute 'upper'Метода нет у объекта; часто переменная оказалась None
KeyError'b'Нет такого ключа в словаре
IndexErrorlist index out of rangeИндекс за пределами списка
ZeroDivisionErrordivision by zeroДеление на ноль
FileNotFoundError[Errno 2] No such file or directory: 'missing.txt'Неверный путь или рабочий каталог
ModuleNotFoundErrorNo module named 'requestz'Пакет не установлен или опечатка в имени
ImportErrorcannot import name 'pathx' from 'os'Модуль есть, но имени в нём нет
UnicodeDecodeError'utf-8' codec can't decode byte 0xff in position 0: invalid start byteФайл в другой кодировке
RecursionErrormaximum recursion depth exceededБесконечная рекурсия

Python 3.12 часто подсказывает исправление прямо в сообщении — Did you mean: 'name'?. Читайте сообщение целиком: подсказка обычно верная.

Пара пояснений к частым случаям:

  • KeyError и IndexError — при чтении словаря используйте dict.get(key, default), если ключа может не быть (подробнее — в статье про словари Python); для списков проверяйте длину или итерируйтесь без индексов (см. списки в Python).
  • TypeError — операция получила объект не того типа: строку вместо числа, None вместо списка, лишний аргумент. Типичные сообщения и исправления собраны в статье «TypeError в Python».
  • ValueError — тип подходит, а значение нет: int("abc"), распаковка списка не той длины. Разбор частых сообщений — в статье «ValueError в Python».
  • AttributeError: 'NoneType' object has no attribute — функция вернула None вместо ожидаемого объекта. Классика: items = items.sort() — метод sort() сортирует на месте и возвращает None. Другие причины, включая module has no attribute, — в статье об AttributeError в Python.
  • ImportError: cannot import name — модуль найден, но нужного имени в нём нет: опечатка, другая версия библиотеки или циклический импорт. Как найти причину — в статье «ImportError в Python».
  • ModuleNotFoundError — самая частая ошибка начинающих, у неё много причин (не тот интерпретатор, venv, имя пакета ≠ имя модуля). Подробный разбор — в статье ModuleNotFoundError: No module named.

Иерархия исключений

Исключения — классы, связанные наследованием. Это важно для обработки: except LookupError поймает и KeyError, и IndexError.

BaseException
├── KeyboardInterrupt        # Ctrl+C
├── SystemExit               # sys.exit()
└── Exception
    ├── ArithmeticError
    │   └── ZeroDivisionError
    ├── ImportError
    │   └── ModuleNotFoundError
    ├── LookupError
    │   ├── IndexError
    │   └── KeyError
    ├── OSError
    │   ├── FileNotFoundError
    │   └── PermissionError
    ├── RuntimeError
    │   └── RecursionError
    ├── SyntaxError
    │   └── IndentationError
    │       └── TabError
    ├── TypeError
    └── ValueError
        └── UnicodeError
            └── UnicodeDecodeError

KeyboardInterrupt и SystemExit наследуются не от Exception, поэтому except Exception не мешает остановить программу через Ctrl+C. Полное дерево — в документации Python.

Обработка исключений: try/except

Если ошибка ожидаема — пользователь ввёл не число, файл может отсутствовать, сеть недоступна, — её перехватывают:

raw = input("Возраст: ")
try:
    age = int(raw)
except ValueError:
    print("Введите число")

Перехватывайте конкретные исключения и только там, где знаете, что делать дальше. Голый except: и «проглатывание» ошибок прячут баги. Блоки else и finally, raise from, собственные классы исключений и ExceptionGroup разобраны в отдельной статье — try/except в Python: обработка исключений.

Как отлаживать ошибки

Traceback говорит, где упала программа. Чтобы понять, почему, нужно посмотреть значения переменных в этот момент.

print()

Самый быстрый способ — вывести значения перед проблемной строкой:

def average_price(items):
    total = sum(item["price"] for item in items)
    print(f"{items=} {total=}")  # отладочный вывод
    return total / len(items)
items=[{'price': 100}, {'price': 300}] total=400
items=[] total=0

Спецификатор = в f-строке (f"{items=}") печатает и имя, и значение. Не забудьте удалить отладочные print() после исправления.

logging

Для кода, который работает долго или на сервере, вместо print() используют модуль logging. Метод logger.exception() внутри except записывает сообщение вместе с полным traceback:

import logging

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("app")

try:
    1 / 0
except ZeroDivisionError:
    log.exception("Не удалось посчитать")
ERROR:app:Не удалось посчитать
Traceback (most recent call last):
  File "C:\projects\shop\calc.py", line 7, in <module>
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero

Если нужно получить traceback строкой (например, чтобы отправить в мессенджер), используйте traceback.format_exc(), а для печати — traceback.print_exc() из стандартного модуля traceback.

pdb и breakpoint()

Встроенный отладчик pdb останавливает программу и даёт выполнять команды в её контексте. Самый простой способ — вставить вызов breakpoint() (доступен с Python 3.7) в нужное место:

def average_price(items):
    total = sum(item["price"] for item in items)
    breakpoint()
    return total / len(items)

При запуске программа остановится, и появится приглашение (Pdb):

> c:\projects\shop\bp.py(4)average_price()
-> return total / len(items)
(Pdb) p total
400
(Pdb) p len(items)
2
(Pdb) c

Основные команды pdb:

КомандаДействие
p выражениеНапечатать значение (pp — красиво)
nВыполнить текущую строку, не заходя в функции
sШаг с заходом внутрь вызываемой функции
cПродолжить выполнение до следующей точки остановки
lПоказать код вокруг текущей строки
wПоказать стек вызовов (как в traceback)
qВыйти из отладчика

Можно не вставлять breakpoint() заранее, а запустить скрипт под отладчиком так, чтобы он остановился в момент падения (post-mortem):

python -m pdb -c continue orders.py

После исключения отладчик выведет Uncaught exception. Entering post mortem debugging и откроет (Pdb) прямо в кадре, где произошла ошибка, — можно напечатать p items и увидеть пустой список. Переменная окружения PYTHONBREAKPOINT=0 отключает все вызовы breakpoint(), но удалять их из кода перед коммитом всё равно нужно.

Отладчик в IDE

В VS Code и PyCharm то же самое делается мышью: клик слева от номера строки ставит точку остановки, а запуск в режиме Debug (F5 в VS Code) показывает значения переменных в боковой панели. Если программа запускается не тем интерпретатором, отладчик тоже «увидит» не те пакеты — выберите интерпретатор виртуального окружения.

Алгоритм: что делать, когда Python выдал ошибку

  1. Прочитайте последнюю строку: тип исключения и сообщение, включая подсказку Did you mean.
  2. Найдите последний кадр из своего кода и откройте эту строку.
  3. Если это SyntaxError — проверьте и строку выше: незакрытые скобки и кавычки.
  4. Посмотрите значения переменных в этой точке: print(), breakpoint() или отладчик IDE.
  5. Если причина неочевидна — скопируйте последнюю строку traceback (без путей к своим файлам) в поиск: типовые сообщения разобраны тысячи раз.
  6. Исправьте причину, а не симптом: try/except вокруг ошибки — не исправление, если вы не знаете, почему она возникла.

Частые вопросы

Почему в traceback путь к файлам библиотеки, а не к моему коду?

Ошибка возникла внутри библиотеки, но из-за данных, которые передал ваш код. Поднимайтесь по traceback вверх, пока не найдёте файл своего проекта, — там и ищите причину.

Как вывести traceback, не останавливая программу?

Перехватите исключение и вызовите traceback.print_exc() или logging.exception() внутри блока except. Программа продолжит работу, а полный traceback попадёт в консоль или лог.

Что значит «Python was not found» в Windows?

Это сообщение выдаёт сама Windows ещё до запуска Python: команда python ведёт на ярлык Microsoft Store, а интерпретатор не установлен или не добавлен в PATH. Как это исправить — в руководстве как установить Python.

Ошибка No module named после установки пакета — что не так?

Скорее всего, пакет установлен в другой интерпретатор. Сравните sys.executable скрипта с выводом python -m pip --version и прочитайте статью как установить библиотеку в Python.

Как сохранить traceback в файл?

В долгоживущих программах настройте logging с записью в файл — тогда log.exception() сохранит traceback вместе со временем и уровнем сообщения. Для разового случая хватит traceback.print_exc() с параметром file:

import traceback

try:
    1 / 0
except ZeroDivisionError:
    with open("errors.log", "a", encoding="utf-8") as log_file:
        traceback.print_exc(file=log_file)

Что значит «Previous line repeated 996 more times»?

Python сократил traceback: одни и те же кадры повторились сотни раз подряд. Почти всегда это RecursionError — функция вызывает саму себя без условия выхода. Смотрите на кадры перед этой строкой: они показывают, какая функция зациклилась.

Итог

Traceback читается снизу вверх: последняя строка говорит, что случилось, кадры над ней — где и по какой цепочке вызовов. Синтаксические ошибки не дают программе запуститься, исключения возникают во время работы и перехватываются через try/except. Для поиска причины хватает print(), logging и breakpoint(). Разборы конкретных ошибок — в разделе «Ошибки и исключения»: начните с ModuleNotFoundError и обработки исключений через try/except.

  • #Python
  • #Traceback
  • #Исключения
  • #Отладка
  • #pdb
↑ ↓ выборEnter открытьEsc закрыть