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

Введение:

Если вы веб-разработчик, вы знаете, насколько раздражающими могут быть ошибки сервера или любая другая ошибка в этом отношении. Представьте себе, что у вас есть огромный блок кода, который загружает / загружает файл, который в конечном итоге выдает exception на n-й минуте. Что ж, к сожалению, это бывает много. Поверь мне, парень, я был там.

Как разработчик, мы должны не только предоставлять безошибочный код (позвольте мне перефразировать его так, чтобы он кодировал с минимальным количеством ошибок 😉), но также и изящный сбой, информативное сообщение об ошибке и, возможно, механизм повторных попыток.

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

К сожалению, повторная попытка больше не поддерживается. У нас есть еще одна библиотека, разветвленная от повторных попыток, которая называется цепкостью. Сегодня давайте обсудим, какие возможности нам предоставляет tenacity.

Упорство:

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

Монтаж:

$pip install tenacity

Простая повторная попытка без условий:

Приведенный ниже код retries навсегда без каких-либо условий. Это базовый декоратор повторных попыток, который можно применить к любой функции, вызывающей исключение.

Выход:

 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...

Если исключения не возникло, повторных попыток не будет, а оператор print будет выполнен только один раз, ну вы поняли.

Повторите попытку с условиями:

Остановить после попытки:

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

Выход:

 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
 Example for simple retry. Retrying forever...
Traceback (most recent call last):
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/tenacity/__init__.py", line 407, in __call__
    result = fn(*args, **kwargs)
  File "/Users/dkb/Code/practice/my_tenacity.py", line 7, in simple_retry
    raise Exception('Raising exception after 5 retries...')
Exception: Raising exception after 5 retries...

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/dkb/Code/practice/my_tenacity.py", line 10, in <module>
    simple_retry()
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/tenacity/__init__.py", line 324, in wrapped_f
    return self(f, *args, **kw)
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/tenacity/__init__.py", line 404, in __call__
    do = self.iter(retry_state=retry_state)
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/tenacity/__init__.py", line 361, in iter
    raise retry_exc from fut.exception()
tenacity.RetryError: RetryError[<Future at 0x100d64c40 state=finished raised Exception>]

Остановка после задержки:

Это будет пытаться до времени, которое мы предоставили. Например, если вы хотите попробовать что-то в течение 10 секунд, а затем остановиться, вам следует выбрать этот метод.

В приведенном ниже коде httpbin.org — это тестовый HTTP-сервер. Вы можете выбрать любой другой HTTP-сервер для тестирования или разместить свой собственный сервер.

Выход:

401
401
401
401
401
401
401
401
401
401
401
401
401
401
401
401
Traceback (most recent call last):
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/tenacity/__init__.py", line 407, in __call__
    result = fn(*args, **kwargs)
  File "/Users/dkb/Code/practice/my_tenacity.py", line 21, in authenticate_user
    response.raise_for_status()
  File "/Users/dkb/VirtualEnvs/practo-env/lib/python3.9/site-packages/requests/models.py", line 953, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: UNAUTHORIZED for url: http://httpbin.org/basic-auth/user/password

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

Комбинация условий остановки:

Иногда серверу требуется некоторое время, чтобы ответить, тогда как в большинстве случаев он может ответить быстро. В некоторых сценариях вам также может потребоваться количественно определить количество повторных попыток. В этих сценариях вы можете комбинировать условия stop_after_delay и stop_after_attempt.

Здесь повторная попытка останавливается в зависимости от того, какое условие будет выполнено первым.

Выход:

401
401
401
401
401
RetryError[<Future at 0x110914c40 state=finished raised HTTPError>]

Внедрение экспоненциального отката:

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

Если вам интересно узнать, как разрешать исключения регулирования в aws, прочитайте этот пост.

Выход:

401 2022-06-28 11:54:40.414397
401 2022-06-28 11:54:43.054521
401 2022-06-28 11:54:45.614282
401 2022-06-28 11:54:48.176247
401 2022-06-28 11:54:50.648000
RetryError[<Future at 0x110f7fd30 state=finished raised HTTPError>]

Здесь мы ждем 2 секунды, прежде чем пытаться повторить попытку, и повторяем попытку 5 раз. Количество повторных попыток может быть изменено и удалено, если вы хотите повторять попытки до тех пор, пока не добьетесь успеха. Таким образом, вы можете смешивать и сопоставлять параметры stop и wait в декораторе retry.

Повторите попытку для определенных исключений:

Как следует из названия, мы могли бы повторить определенные сообщения об ошибках, такие как HTTP errors, ValueError, TypeError или любые пользовательские исключения.

Этот код повторит попытку только в случае HTTPErrors. Так элегантно. Не так ли? Точно так же мы могли бы также retry, если исключение не относится к определенному типу, используя retry_if_not_exception_type().

Повторите попытку на основе возвращаемого значения и явного повтора:

Функцию можно запрограммировать на повторную попытку на основе возвращаемого значения. Например, когда функция возвращает None.

Выход:

 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None
 Retrying . . .: None

Приведенный выше код повторяет попытку, когда вы получаете ошибку 401, используя исключение TryAgain. Я не предоставил вывод для этого, потому что к настоящему времени вы бы его освоили.

Статистика:

Можно получить статистику функции, украшенной retry, используя атрибут statistics.

Выход:

 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
{'start_time': 0.227001672, 'attempt_number': 5, 'idle_for': 0, 'delay_since_first_attempt': 2.319967352}

Статистика печатается в виде словаря с указанием времени начала, количества попыток и т.д.

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

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

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

Выход:

 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Retrying using explicit retry . . .
 Maximum number of retries exceeded. <RetryCallState 4340277104: attempt #5; slept for 0.0; last result: failed (HTTPError 401 Client Error: UNAUTHORIZED for url: http://httpbin.org/basic-auth/user/password)> 
{'start_time': 0.196902178, 'attempt_number': 5, 'idle_for': 0, 'delay_since_first_attempt': 3.0087615750000003}

Аккуратно да?. Здесь get_response() — это функция обратного вызова, которая принимает аргумент retry_state со всеми атрибутами текущего retry.

Краткое содержание:

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

Дополнительные сведения см. в документации, представленной в разделе ссылок.

В следующий раз, если вы реализуете retry для своего кода, я бы посоветовал попробовать упорство.

Использованная литература:

Первоначально опубликовано на https://dock2learn.com 29 июня 2022 г.