Retry-декоратор Python: преобразование исключений после исчерпания попыток
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Декораторы Python: задачи на retry, rate limiter, кэш и Circuit Breaker.
Трансляция сбоев в декораторе повторов
Почтовый шлюз отправляет служебные уведомления через сеть, в которой случаются кратковременные помехи связи. Чтобы единичные сбои не срывали доставку, отправку защищают параметризованным декоратором. Он настраивается лимитом попыток, кортежем ожидаемых временных сетевых исключений и специальной функцией-транслятором критической ошибки.
Если операция завершается исключением из заданного кортежа, декоратор повторяет вызов в пределах установленного лимита. Любое неперечисленное стороннее исключение сразу прерывает работу и выходит наружу без изменений.
Когда лимит попыток исчерпан, а вызов так и не завершился успешно, декоратор не пробрасывает исходную ошибку сети напрямую. Он ровно один раз обращается к транслятору, передавая ему последнее перехваченное исключение, и возбуждает полученный от него объект критической ошибки через from exc, сохраняя контекст сбоя.
Реализуйте декоратор, выполняющий повторные попытки отправки и эскалацию финальной ошибки.
Что уже дано
В редакторе представлена заготовка функции translated_retry(max_attempts, retry_exceptions, translate).
Вспомогательные классы и функции для тестирования работы декоратора доступны во время выполнения тестов, реализовывать их заново не требуется.
Что нужно сделать
Реализуйте параметризованный декоратор translated_retry(max_attempts, retry_exceptions, translate).
Параметры фабрики:
max_attempts— положительное целое число от 1 до 20 (включая пользовательские подклассыint), задающее общее количество попыток вызова целевой функции (не дополнительных повторов, а именно суммарных попыток);retry_exceptions— кортеж типов исключений (подклассовBaseException, от 0 до 10 типов), при возникновении которых выполняется повторная попытка. Сопоставление с типами должно работать по стандартным правилам проверки принадлежности подклассам (как в блокеexceptили черезisinstance);translate— синхронная функция, принимающая объект последнего пойманного отслеживаемого исключения и возвращающая объект исключения (экземплярBaseException). Если сама функцияtranslateвозбуждает исключение, оно должно сразу всплывать наружу без повторных попыток.
Возвращаемый декоратор принимает синхронную целевую функцию function и возвращает функцию-обёртку.
Правила работы функции-обёртки:
- Принимает произвольные позиционные и именованные аргументы
*args, **kwargsи передаёт их в целевую функцию без изменений. - Учёт попыток ведётся независимо для каждого отдельного вызова обёртки (от 0 до 1000 вызовов на одну обёртку). Обёртка не должна рекурсивно вызывать саму себя.
- При успешном завершении целевой функции обёртка немедленно возвращает полученный объект результата (включая ложные значения
False,None, пустые коллекции и их подклассы) без повторных вызовов. - Если целевая функция выбрасывает исключение, не относящееся к типам из
retry_exceptions, оно сразу беспрепятственно всплывает наружу тем же самым объектом исключения без повторных вызовов и без вызоваtranslate. - Если возникает отслеживаемое исключение из
retry_exceptions, попытка повторяется вплоть до достиженияmax_attempts. - Если все
max_attemptsпопыток завершились отслеживаемыми исключениями, декоратор ровно один раз вызываетtranslate, передавая туда в точности тот объект исключения, который возник при последней попытке. Полученный отtranslateобъект исключения должен быть возбуждён через операторraise ... from last_exception. Атрибут__cause__итоговой ошибки обязан указывать на это последнее исключение. - Никаких пауз, задержек между попытками, значений по умолчанию при сбое или обязательного сохранения метаданных исходной функции не требуется.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня translated_retry и вызывает её с проверочными аргументами.
Пример использования
attempts=[]
@translated_retry(2,(ConnectionError,),lambda error: RuntimeError("delivery failed"))
def deliver(message):
attempts.append(message)
raise ConnectionError("offline")
try:
deliver("notice")
except RuntimeError as error:
print(str(error))
print(type(error.__cause__).__name__)
print(attempts)
Вывод:
delivery failed
ConnectionError
['notice', 'notice']
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Functools.wraps в Python: имя функции и атрибут wrapped
- Декоратор Circuit Breaker на Python: остановка вызовов после сбоев
- Декораторы Python: проверка цепочки wrapped и метаданных функции
- Декоратор Python: логирование только медленных вызовов
- Кэширование Python: постройте ключ из выбранных аргументов функции
- Rate limiter на Python: декоратор со скользящим окном запросов
