Сообщить об ошибке.

Улучшения сообщений об ошибках в Python 3.10

В материале рассматриваются примеры отличия сообщений об ошибках в Python 3.10 от более ранних версий. Улучшения коснулись сообщений об ошибках, таких как SyntaxErrors, IndentationErrors, AttributeErrors и NameErrors.

Содержание:

Улучшения в сообщении исключения SyntaxErrors.

При синтаксическом анализе кода, который содержит незакрытые скобки, с версии Python 3.10, вместо отображения сообщения SyntaxError: unexpected EOF и указание на какое-то неправильное место, интерпретатор включает местоположение незакрытых скобок. Например, рассмотрим следующий код (обратите внимание на незакрытый '{'):

expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
            38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
some_other_code = foo()

Предыдущие версии интерпретатора сообщали о запутанных местах в качестве места синтаксической ошибки:

File "example.py", line 3
    some_other_code = foo()
                    ^
SyntaxError: invalid syntax

но в Python 3.10 выдается более информативная ошибка:

File "example.py", line 1
    expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
               ^
SyntaxError: '{' was never closed

Точно так же ошибки, связанные с незакрытыми строковыми литералами (одинарными и тройными кавычками), теперь указывают на начало строки вместо сообщения EOF/EOL.

Эти улучшения вдохновлены предыдущей работой в интерпретаторе PyPy.

Исключения SyntaxError, вызванные интерпретатором, теперь будут выделять полный диапазон ошибок выражения, составляющего саму синтаксическую ошибку, а не только то место, где обнаружена проблема. Таким образом, вместо отображения (до Python 3.10):

>>> foo(x, z for z in range(10), t, w)
#   File "<stdin>", line 1
#     foo(x, z for z in range(10), t, w)
#            ^
# SyntaxError: Generator expression must be parenthesized

теперь Python 3.10 отобразит исключение как:

>>> foo(x, z for z in range(10), t, w)
#   File "<stdin>", line 1
#     foo(x, z for z in range(10), t, w)
#            ^^^^^^^^^^^^^^^^^^^^
# SyntaxError: Generator expression must be parenthesized

Новые сообщения для исключений SyntaxError.

Для исключений SyntaxError добавлено значительное количество новых специализированных сообщений. Вот некоторые из наиболее примечательных:

Отсутствие двоеточия : перед блоком:

>>> if rocket.position > event_horizon
#   File "<stdin>", line 1
#     if rocket.position > event_horizon
#                                       ^
# SyntaxError: expected ':'

Кортеж без скобок в выражении-генератора:

>>> {x,y for x,y in zip('abcd', '1234')}
#   File "<stdin>", line 1
#     {x,y for x,y in zip('abcd', '1234')}
#      ^
# SyntaxError: did you forget parentheses around the comprehension target?

Отсутствуют запятые в литералах коллекции и между выражениями:

>>> items = {
... x: 1,
... y: 2
... z: 3,
#   File "<stdin>", line 3
#     y: 2
#        ^
# SyntaxError: invalid syntax. Perhaps you forgot a comma?

Несколько типов исключений без скобок:

>>> try:
...     build_dyson_sphere()
... except NotEnoughScienceError, NotEnoughResourcesError:
#   File "<stdin>", line 3
#     except NotEnoughScienceError, NotEnoughResourcesError:
#            ^
# SyntaxError: multiple exception types must be parenthesized

Отсутствует двоеточие : и/или значение ключа в словарях:

>>> values = {
... x: 1,
... y: 2,
... z:
... }
#   File "<stdin>", line 4
#     z:
#      ^
# SyntaxError: expression expected after dictionary key and ':'

>>> values = {x:1, y:2, z w:3}
#   File "<stdin>", line 1
#     values = {x:1, y:2, z w:3}
#                         ^
# SyntaxError: ':' expected after dictionary key

Блок try без except или finally:

>>> try:
...     x = 2
... something = 3
#   File "<stdin>", line 3
#     something  = 3
#     ^^^^^^^^^
# SyntaxError: expected 'except' or 'finally' block

Использование = вместо == при сравнении:

>>> if rocket.position = event_horizon:
#   File "<stdin>", line 1
#     if rocket.position = event_horizon:
#                        ^
# SyntaxError: cannot assign to attribute here. 
# Maybe you meant '==' instead of '='?

Использование * в f-строках:

>>> f"Black holes {*all_black_holes} and revelations"
#   File "<stdin>", line 1
#     (*all_black_holes)
#      ^
# SyntaxError: f-string: cannot use starred expression here

Улучшения в сообщении об ошибке IndentationErrors.

Многие исключения IndentationError теперь имеют больше контекста относительно того, какой блок ожидает отступ, включая расположение оператора:

>>> def foo():
...    if lel:
...    x = 2
#   File "<stdin>", line 3
#     x = 2
#     ^
# IndentationError: expected an indented block after 'if' statement in line 2

Улучшения в сообщении об ошибке AttributeErrors.

При печати исключения AttributeError, функция интерпретатора PyErr_Display() предложит варианты похожих имен атрибутов в объекте, из которого возникло исключение:

>>> collections.namedtoplo
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: module 'collections' has no attribute 'namedtoplo'. 
# Did you mean: namedtuple?

Предупреждение. Обратите внимание, что это не сработает, если для отображения ошибки не вызывается `PyErr_Display(). Это может произойти при использовании какой-либо другой пользовательской функции отображения ошибок. Например, это распространенный сценарий в IPython.

Улучшения в сообщении об ошибке NameErrors.

При печати исключения NameError, функция интерпретатора PyErr_Display() предложит варианты похожих имен переменных в функции, из которой было вызвано исключение:

>>> schwarzschild_black_hole = None
>>> schwarschild_black_hole
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# NameError: name 'schwarschild_black_hole' is not defined. Did you mean: schwarzschild_black_hole?

Предупреждение. Обратите внимание, что это не сработает, если для отображения ошибки не вызывается `PyErr_Display(). Это может произойти при использовании какой-либо другой пользовательской функции отображения ошибок. Например, это распространенный сценарий в IPython.