Документация Python неофициальный перевод

errors.md

561 строк · 36.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.15/tutorial/errors.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 8. Ошибки и исключения89До сих пор мы лишь упоминали сообщения об ошибках, но если вы пробовали примеры, то, скорее всего, уже с ними сталкивались. Существует (как минимум) два различимых вида ошибок: *синтаксические ошибки* и *исключения*.1011## 8.1. Синтаксические ошибки1213Синтаксические ошибки, также известные как ошибки разбора, – пожалуй, самый распространённый вид неполадок, с которыми вы сталкиваетесь, пока учите Python:1415```python16>>> while True print('Hello world')17  File "<stdin>", line 118    while True print('Hello world')19               ^^^^^20SyntaxError: invalid syntax21```2223Синтаксический анализатор повторяет ошибочную строку и показывает маленькие стрелочки, указывающие на место, где обнаружена ошибка. Заметьте, что это не всегда то место, которое нужно исправить. В примере ошибка обнаружена в районе функции [`print()`](https://python-all.ru/3.15/library/functions.html#print), так как перед ней не хватает двоеточия (`':'`).2425Имя файла (в нашем примере `<stdin>`) и номер строки выводятся, чтобы вы знали, куда смотреть, если входные данные были получены из файла.2627## 8.2. Исключения2829Даже если оператор или выражение синтаксически верны, при попытке его выполнения может возникнуть ошибка. Ошибки, обнаруженные во время выполнения, называются *исключениями* и не являются безусловно фатальными: вы скоро научитесь обрабатывать их в программах на Python. Однако большинство исключений не обрабатываются программами и приводят к сообщениям об ошибках, как показано ниже:3031```python32>>> 10 * (1/0)33Traceback (most recent call last):34  File "<stdin>", line 1, in <module>35    10 * (1/0)36          ~^~37ZeroDivisionError: division by zero38>>> 4 + spam*339Traceback (most recent call last):40  File "<stdin>", line 1, in <module>41    4 + spam*342        ^^^^43NameError: name 'spam' is not defined44>>> '2' + 245Traceback (most recent call last):46  File "<stdin>", line 1, in <module>47    '2' + 248    ~~~~^~~49TypeError: can only concatenate str (not "int") to str50```5152Последняя строка сообщения об ошибке показывает, что произошло. Исключения бывают разных типов, и тип выводится как часть сообщения: в примере это [`ZeroDivisionError`](https://python-all.ru/3.15/library/exceptions.html#ZeroDivisionError), [`NameError`](https://python-all.ru/3.15/library/exceptions.html#NameError) и [`TypeError`](https://python-all.ru/3.15/library/exceptions.html#TypeError). Строка, выводимая как тип исключения, – это имя встроенного исключения, которое произошло. Это верно для всех встроенных исключений, но не обязательно для определяемых пользователем (хотя это полезное соглашение). Стандартные имена исключений – встроенные идентификаторы (не зарезервированные ключевые слова).5354Остальная часть строки содержит подробности в зависимости от типа исключения и того, что его вызвало.5556Предшествующая часть сообщения об ошибке показывает контекст, в котором произошло исключение, в виде трассировки стека. Обычно она содержит трассировку стека с перечислением строк исходного кода; однако строки, прочитанные из стандартного ввода, не отображаются.5758[Встроенные исключения](https://python-all.ru/3.15/library/exceptions.html#bltin-exceptions) содержит список встроенных исключений и их значение.5960## 8.3. Обработка исключений6162Можно писать программы, которые обрабатывают выбранные исключения. Посмотрите на следующий пример, который запрашивает у пользователя ввод до тех пор, пока не будет введено целое число, но при этом позволяет прервать программу (с помощью `Control`-`C` или того, что поддерживает операционная система); обратите внимание, что прерывание, инициированное пользователем, сигнализируется возбуждением исключения [`KeyboardInterrupt`](https://python-all.ru/3.15/library/exceptions.html#KeyboardInterrupt).6364```python65>>> while True:66...     try:67...         x = int(input("Please enter a number: "))68...         break69...     except ValueError:70...         print("Oops!  That was no valid number.  Try again...")71...72```7374Инструкция [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) работает следующим образом.7576- Сначала выполняется *try-блок* (инструкции между ключевыми словами [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) и [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except)).77- Если исключение не произошло, *except-блок* пропускается, и выполнение инструкции [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) завершается.78- Если во время выполнения [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try)-блока происходит исключение, оставшаяся часть этого блока пропускается. Затем, если его тип совпадает с исключением, указанным после ключевого слова [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except), выполняется *except-блок*, после чего выполнение продолжается за пределами блока try/except.79- Если происходит исключение, которое не соответствует исключению, указанному в *except-блоке*, оно передаётся внешним инструкциям [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try); если обработчик не найден, это *необработанное исключение*, и выполнение останавливается с сообщением об ошибке.8081Инструкция [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) может содержать несколько *except-блоков* для указания обработчиков разных исключений. Будет выполнен не более чем один обработчик. Обработчики обрабатывают только исключения, возникшие в соответствующем *try-блоке*, а не в других обработчиках той же инструкции `try`. В *except-блоке* можно перечислить несколько исключений, например:8283```python84... except RuntimeError, TypeError, NameError:85...     pass86```8788Класс в [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except)-блоке соответствует исключениям, которые являются экземплярами самого этого класса или его производных классов (но не наоборот – *except-блок*, перечисляющий производный класс, не соответствует экземплярам его базовых классов). Например, следующий код выведет B, C, D именно в таком порядке:8990```python91class B(Exception):92    pass9394class C(B):95    pass9697class D(C):98    pass99100for cls in [B, C, D]:101    try:102        raise cls()103    except D:104        print("D")105    except C:106        print("C")107    except B:108        print("B")109```110111Обратите внимание: если бы *except-блоки* были переставлены (`except B` стоял бы первым), то вывелось бы B, B, B – срабатывает первый подходящий *except-блок*.112113Когда возникает исключение, у него могут быть связанные значения, также называемые *аргументами* исключения. Наличие и типы аргументов зависят от типа исключения.114115В *except-блоке* можно указать переменную после имени исключения. Эта переменная привязывается к экземпляру исключения, у которого обычно есть атрибут `args`, хранящий аргументы. Для удобства встроенные типы исключений определяют [`__str__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__str__) так, чтобы выводить все аргументы без явного обращения к `.args`.116117```python118>>> try:119...     raise Exception('spam', 'eggs')120... except Exception as inst:121...     print(type(inst))    # тип исключения122...     print(inst.args)     # аргументы, хранящиеся в .args123...     print(inst)          # __str__ позволяет выводить args напрямую, но может быть переопределён в подклассах исключений124...                          # но может быть переопределён в подклассах исключений125...     x, y = inst.args     # распаковать args126...     print('x =', x)127...     print('y =', y)128...129<class 'Exception'>130('spam', 'eggs')131('spam', 'eggs')132x = spam133y = eggs134```135136Вывод [`__str__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__str__) исключения печатается как последняя часть («подробности») сообщения для необработанных исключений.137138[`BaseException`](https://python-all.ru/3.15/library/exceptions.html#BaseException) – это общий базовый класс всех исключений. Один из его подклассов, [`Exception`](https://python-all.ru/3.15/library/exceptions.html#Exception), является базовым классом для всех нефатальных исключений. Исключения, не являющиеся подклассами `Exception`, обычно не обрабатываются, потому что они используются для указания на то, что программа должна завершиться. К ним относятся [`SystemExit`](https://python-all.ru/3.15/library/exceptions.html#SystemExit), возбуждаемое оператором [`sys.exit()`](https://python-all.ru/3.15/library/sys.html#sys.exit), и [`KeyboardInterrupt`](https://python-all.ru/3.15/library/exceptions.html#KeyboardInterrupt), возбуждаемое, когда пользователь хочет прервать программу.139140[`Exception`](https://python-all.ru/3.15/library/exceptions.html#Exception) можно использовать как универсальный обработчик, который ловит (почти) всё. Однако хорошей практикой является максимальная конкретность в типах исключений, которые мы собираемся обрабатывать, и позволять любым неожиданным исключениям распространяться дальше.141142Наиболее распространённый шаблон обработки [`Exception`](https://python-all.ru/3.15/library/exceptions.html#Exception) – вывести или зарегистрировать исключение, а затем возобновить его снова (позволяя вызывающему коду также обработать исключение):143144```python145import sys146147try:148    f = open('myfile.txt')149    s = f.readline()150    i = int(s.strip())151except OSError as err:152    print("OS error:", err)153except ValueError:154    print("Could not convert data to an integer.")155except Exception as err:156    print(f"Unexpected {err=}, {type(err)=}")157    raise158```159160Инструкция [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) … [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except) имеет необязательный *блок else*, который, если присутствует, должен следовать за всеми *блоками except*. Он полезен для кода, который должен быть выполнен, если *блок try* не возбуждает исключение. Например:161162```python163for arg in sys.argv[1:]:164    try:165        f = open(arg, 'r')166    except OSError:167        print('cannot open', arg)168    else:169        print(arg, 'has', len(f.readlines()), 'lines')170        f.close()171```172173Использование блока `else` лучше, чем добавление дополнительного кода в блок [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try), поскольку это позволяет избежать случайного перехвата исключения, которое не было возбуждено кодом, защищённым инструкцией `try``except`.174175Exception handlers do not handle only exceptions that occur immediately in the *try clause*, but also those that occur inside functions that are called (even indirectly) in the *try clause*. For example:176177```python178>>> def this_fails():179...     x = 1/0180...181>>> try:182...     this_fails()183... except ZeroDivisionError as err:184...     print('Handling run-time error:', err)185...186Handling run-time error: division by zero187```188189## 8.4. Возбуждение исключений190191Инструкция [`raise`](https://python-all.ru/3.15/reference/simple_stmts.html#raise) позволяет программисту принудительно вызвать указанное исключение. Например:192193```python194>>> raise NameError('HiThere')195Traceback (most recent call last):196  File "<stdin>", line 1, in <module>197    raise NameError('HiThere')198NameError: HiThere199```200201Единственный аргумент [`raise`](https://python-all.ru/3.15/reference/simple_stmts.html#raise) указывает возбуждаемое исключение. Это должен быть либо экземпляр исключения, либо класс исключения (класс, производный от [`BaseException`](https://python-all.ru/3.15/library/exceptions.html#BaseException), например [`Exception`](https://python-all.ru/3.15/library/exceptions.html#Exception) или один из его подклассов). Если передан класс исключения, он будет неявно создан вызовом его конструктора без аргументов:202203```python204raise ValueError  # сокращение для 'raise ValueError()'205```206207Если нужно определить, было ли возбуждено исключение, но обрабатывать его не предполагается, более простая форма инструкции [`raise`](https://python-all.ru/3.15/reference/simple_stmts.html#raise) позволяет повторно возбудить исключение:208209```python210>>> try:211...     raise NameError('HiThere')212... except NameError:213...     print('An exception flew by!')214...     raise215...216An exception flew by!217Traceback (most recent call last):218  File "<stdin>", line 2, in <module>219    raise NameError('HiThere')220NameError: HiThere221```222223## 8.5. Цепочка исключений224225Если необработанное исключение возникает внутри раздела [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except), к нему будет присоединено обрабатываемое исключение, и оно будет включено в сообщение об ошибке:226227```python228>>> try:229...     open("database.sqlite")230... except OSError:231...     raise RuntimeError("unable to handle error")232...233Traceback (most recent call last):234  File "<stdin>", line 2, in <module>235    open("database.sqlite")236    ~~~~^^^^^^^^^^^^^^^^^^^237FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'238239During handling of the above exception, another exception occurred:240241Traceback (most recent call last):242  File "<stdin>", line 4, in <module>243    raise RuntimeError("unable to handle error")244RuntimeError: unable to handle error245```246247Чтобы указать, что исключение является прямым следствием другого, инструкция [`raise`](https://python-all.ru/3.15/reference/simple_stmts.html#raise) допускает необязательный блок `from`:248249```python250# exc должен быть экземпляром исключения или None.251raise RuntimeError from exc252```253254Это может быть полезно при преобразовании исключений. Например:255256```python257>>> def func():258...     raise ConnectionError259...260>>> try:261...     func()262... except ConnectionError as exc:263...     raise RuntimeError('Failed to open database') from exc264...265Traceback (most recent call last):266  File "<stdin>", line 2, in <module>267    func()268    ~~~~^^269  File "<stdin>", line 2, in func270ConnectionError271272The above exception was the direct cause of the following exception:273274Traceback (most recent call last):275  File "<stdin>", line 4, in <module>276    raise RuntimeError('Failed to open database') from exc277RuntimeError: Failed to open database278```279280Он также позволяет отключить автоматическое сцепление исключений с помощью идиомы `from None`:281282```python283>>> try:284...     open('database.sqlite')285... except OSError:286...     raise RuntimeError from None287...288Traceback (most recent call last):289  File "<stdin>", line 4, in <module>290    raise RuntimeError from None291RuntimeError292```293294Дополнительную информацию о механизме сцепления см. в разделе [Встроенные исключения](https://python-all.ru/3.15/library/exceptions.html#bltin-exceptions).295296## 8.6. Определяемые пользователем исключения297298Программы могут задавать собственные исключения, создавая новый класс исключения (см. [Классы](https://python-all.ru/3.15/tutorial/classes.html#tut-classes) для получения дополнительной информации о классах Python). Исключения обычно должны быть производными от класса [`Exception`](https://python-all.ru/3.15/library/exceptions.html#Exception), прямо или косвенно.299300Классы исключений могут быть определены так, чтобы делать всё, что может любой другой класс, но обычно их оставляют простыми, часто предоставляя лишь несколько атрибутов, позволяющих обработчикам извлекать информацию об ошибке.301302Большинство исключений имеют имена, оканчивающиеся на «Error», подобно именованию стандартных исключений.303304Многие стандартные модули определяют собственные исключения для сообщения об ошибках, которые могут возникнуть в определяемых ими функциях.305306## 8.7. Определение действий по очистке307308Инструкция [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) имеет ещё один необязательный блок, предназначенный для определения действий по очистке, которые должны быть выполнены при любых обстоятельствах. Например:309310```python311>>> try:312...     raise KeyboardInterrupt313... finally:314...     print('Goodbye, world!')315...316Goodbye, world!317Traceback (most recent call last):318  File "<stdin>", line 2, in <module>319    raise KeyboardInterrupt320KeyboardInterrupt321```322323Если присутствует блок [`finally`](https://python-all.ru/3.15/reference/compound_stmts.html#finally), блок `finally` будет выполнен как последняя задача перед завершением инструкции [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try). Блок `finally` выполняется независимо от того, вызывает ли инструкция `try` исключение. В следующих пунктах рассматриваются более сложные случаи возникновения исключения:324325- Если исключение возникает во время выполнения блока `try`, оно может быть обработано блоком [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except). Если исключение не обработано блоком `except`, оно повторно возбуждается после выполнения блока `finally`.326- Исключение может возникнуть во время выполнения блока `except` или `else`. В этом случае исключение также повторно возбуждается после выполнения блока `finally`.327- Если блок `finally` выполняет инструкцию [`break`](https://python-all.ru/3.15/reference/simple_stmts.html#break), [`continue`](https://python-all.ru/3.15/reference/simple_stmts.html#continue) или [`return`](https://python-all.ru/3.15/reference/simple_stmts.html#return), исключения не возбуждаются повторно. Это может вызывать путаницу, поэтому не рекомендуется. Начиная с версии 3.14 компилятор выдаёт [`SyntaxWarning`](https://python-all.ru/3.15/library/exceptions.html#SyntaxWarning) для этого случая (см. [**PEP 765**](https://python-all.ru/3.15/tutorial/errors.html)).328- Если инструкция `try` достигает инструкции [`break`](https://python-all.ru/3.15/reference/simple_stmts.html#break), [`continue`](https://python-all.ru/3.15/reference/simple_stmts.html#continue) или [`return`](https://python-all.ru/3.15/reference/simple_stmts.html#return), блок `finally` выполняется непосредственно перед выполнением инструкции `break`, `continue` или `return`.329- Если блок `finally` содержит инструкцию `return`, возвращаемым значением будет значение из инструкции `return` блока `finally`, а не значение из инструкции `return` блока `try`. Это может вызывать путаницу, поэтому не рекомендуется. Начиная с версии 3.14 компилятор выдаёт [`SyntaxWarning`](https://python-all.ru/3.15/library/exceptions.html#SyntaxWarning) для этого случая (см. [**PEP 765**](https://python-all.ru/3.15/tutorial/errors.html)).330331Например:332333```python334>>> def bool_return():335...     try:336...         return True337...     finally:338...         return False339...340>>> bool_return()341False342```343344Более сложный пример:345346```python347>>> def divide(x, y):348...     try:349...         result = x / y350...     except ZeroDivisionError:351...         print("division by zero!")352...     else:353...         print("result is", result)354...     finally:355...         print("executing finally clause")356...357>>> divide(2, 1)358result is 2.0359executing finally clause360>>> divide(2, 0)361division by zero!362executing finally clause363>>> divide("2", "1")364executing finally clause365Traceback (most recent call last):366  File "<stdin>", line 1, in <module>367    divide("2", "1")368    ~~~~~~^^^^^^^^^^369  File "<stdin>", line 3, in divide370    result = x / y371             ~~^~~372TypeError: unsupported operand type(s) for /: 'str' and 'str'373```374375Как видите, блок [`finally`](https://python-all.ru/3.15/reference/compound_stmts.html#finally) выполняется в любом случае. Исключение [`TypeError`](https://python-all.ru/3.15/library/exceptions.html#TypeError), возбуждённое при делении двух строк, не обрабатывается блоком [`except`](https://python-all.ru/3.15/reference/compound_stmts.html#except) и поэтому повторно возбуждается после выполнения блока `finally`.376377В реальных приложениях блок [`finally`](https://python-all.ru/3.15/reference/compound_stmts.html#finally) полезен для освобождения внешних ресурсов (таких как файлы или сетевые соединения) независимо от того, было ли использование ресурса успешным.378379## 8.8. Предопределённые действия по очистке380381Некоторые объекты определяют стандартные действия по очистке, которые должны быть выполнены, когда объект больше не нужен, независимо от успеха или неудачи операции с этим объектом. Рассмотрим следующий пример, в котором предпринимается попытка открыть файл и вывести его содержимое на экран.382383```python384for line in open("myfile.txt"):385    print(line, end="")386```387388Проблема этого кода в том, что файл остаётся открытым в течение неопределённого времени после завершения выполнения этой части кода. В простых скриптах это не проблема, но может стать проблемой для более крупных приложений. Инструкция [`with`](https://python-all.ru/3.15/reference/compound_stmts.html#with) позволяет использовать такие объекты, как файлы, таким образом, чтобы гарантировать их своевременную и корректную очистку.389390```python391with open("myfile.txt") as f:392    for line in f:393        print(line, end="")394```395396После выполнения инструкции файл *f* всегда закрывается, даже если при обработке строк возникла проблема. Объекты, которые, подобно файлам, предоставляют предопределённые действия по очистке, сообщают об этом в своей документации.397398## 8.9. Возбуждение и обработка нескольких несвязанных исключений399400Бывают ситуации, когда необходимо сообщить о нескольких произошедших исключениях. Это часто встречается во фреймворках параллельного выполнения, когда несколько задач могут завершиться с ошибкой параллельно, но существуют и другие случаи, в которых желательно продолжить выполнение и собрать несколько ошибок, а не возбуждать первое исключение.401402Встроенный [`ExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#ExceptionGroup) оборачивает список экземпляров исключений, чтобы их можно было возбудить вместе. Сам он является исключением, поэтому его можно перехватить, как любое другое исключение.403404```python405>>> def f():406...     excs = [OSError('error 1'), SystemError('error 2')]407...     raise ExceptionGroup('there were problems', excs)408...409>>> f()410  + Exception Group Traceback (most recent call last):411  |   File "<stdin>", line 1, in <module>412  |     f()413  |     ~^^414  |   File "<stdin>", line 3, in f415  |     raise ExceptionGroup('there were problems', excs)416  | ExceptionGroup: there were problems (2 sub-exceptions)417  +-+---------------- 1 ----------------418    | OSError: error 1419    +---------------- 2 ----------------420    | SystemError: error 2421    +------------------------------------422>>> try:423...     f()424... except Exception as e:425...     print(f'caught {type(e)}: {e}')426...427caught <class 'ExceptionGroup'>: there were problems (2 sub-exceptions)428>>>429```430431Используя `except*` вместо `except`, можно выборочно обрабатывать только те исключения в группе, которые соответствуют определённому типу. В следующем примере, демонстрирующем вложенную группу исключений, каждое предложение `except*` извлекает из группы исключения определённого типа, позволяя всем остальным исключениям распространяться к другим предложениям и в конечном итоге быть возбуждёнными снова.432433```python434>>> def f():435...     raise ExceptionGroup(436...         "group1",437...         [438...             OSError(1),439...             SystemError(2),440...             ExceptionGroup(441...                 "group2",442...                 [443...                     OSError(3),444...                     RecursionError(4)445...                 ]446...             )447...         ]448...     )449...450>>> try:451...     f()452... except* OSError as e:453...     print("There were OSErrors")454... except* SystemError as e:455...     print("There were SystemErrors")456...457There were OSErrors458There were SystemErrors459  + Exception Group Traceback (most recent call last):460  |   File "<stdin>", line 2, in <module>461  |     f()462  |     ~^^463  |   File "<stdin>", line 2, in f464  |     raise ExceptionGroup(465  |     ...<12 lines>...466  |     )467  | ExceptionGroup: group1 (1 sub-exception)468  +-+---------------- 1 ----------------469    | ExceptionGroup: group2 (1 sub-exception)470    +-+---------------- 1 ----------------471      | RecursionError: 4472      +------------------------------------473>>>474```475476Обратите внимание, что исключения, вложенные в группу исключений, должны быть экземплярами, а не типами. Это связано с тем, что на практике исключения обычно уже возбуждены и перехвачены программой по следующему шаблону:477478```python479>>> excs = []480... for test in tests:481...     try:482...         test.run()483...     except Exception as e:484...         excs.append(e)485...486>>> if excs:487...    raise ExceptionGroup("Test Failures", excs)488...489```490491## 8.10. Обогащение исключений заметками492493Когда исключение создаётся для возбуждения, оно обычно инициализируется информацией, описывающей произошедшую ошибку. Бывают случаи, когда полезно добавить информацию после перехвата исключения. Для этого у исключений есть метод `add_note(note)`, который принимает строку и добавляет её в список заметок исключения. Стандартный вывод трассировки включает все заметки в порядке их добавления после исключения.494495```python496>>> try:497...     raise TypeError('bad type')498... except Exception as e:499...     e.add_note('Add some information')500...     e.add_note('Add some more information')501...     raise502...503Traceback (most recent call last):504  File "<stdin>", line 2, in <module>505    raise TypeError('bad type')506TypeError: bad type507Add some information508Add some more information509>>>510```511512Например, при сборе исключений в группу исключений может потребоваться добавить контекстную информацию для каждой отдельной ошибки. В следующем примере каждое исключение в группе содержит заметку, указывающую, когда произошла эта ошибка.513514```python515>>> def f():516...     raise OSError('operation failed')517...518>>> excs = []519>>> for i in range(3):520...     try:521...         f()522...     except Exception as e:523...         e.add_note(f'Happened in Iteration {i+1}')524...         excs.append(e)525...526>>> raise ExceptionGroup('We have some problems', excs)527  + Exception Group Traceback (most recent call last):528  |   File "<stdin>", line 1, in <module>529  |     raise ExceptionGroup('We have some problems', excs)530  | ExceptionGroup: We have some problems (3 sub-exceptions)531  +-+---------------- 1 ----------------532    | Traceback (most recent call last):533    |   File "<stdin>", line 3, in <module>534    |     f()535    |     ~^^536    |   File "<stdin>", line 2, in f537    |     raise OSError('operation failed')538    | OSError: operation failed539    | Happened in Iteration 1540    +---------------- 2 ----------------541    | Traceback (most recent call last):542    |   File "<stdin>", line 3, in <module>543    |     f()544    |     ~^^545    |   File "<stdin>", line 2, in f546    |     raise OSError('operation failed')547    | OSError: operation failed548    | Happened in Iteration 2549    +---------------- 3 ----------------550    | Traceback (most recent call last):551    |   File "<stdin>", line 3, in <module>552    |     f()553    |     ~^^554    |   File "<stdin>", line 2, in f555    |     raise OSError('operation failed')556    | OSError: operation failed557    | Happened in Iteration 3558    +------------------------------------559>>>560```561