Package: rtry
rtryInstallationpip3installrtryDocumentationtimeoutas context manageras context manager (silent)as context manager (asyncio)as decoratoras decorator (asyncio)as argumentretryattemptsuntilloggerdelayswallowasyncioTimeoutAs context managerfromrtryimporttimeout,CancelledErrortry:withtimeout(3.0):resp=requests.get("https://httpbin.org/status/200")exceptCancelledError:raiseelse:print(resp)As context manager (silent)fromrtryimporttimeout,CancelledErrorresp=Nonewithtimeout(3.0,exception=None):resp=requests.get("https://httpbin.org/status/200")As context manager (asyncio)importasyncioimportaiohttpfromrtryimporttimeout,CancelledErrorasyncdefmain():try:asyncwithaiohttp.ClientSession()assession,timeout(3.0):asyncwithsession.get("https://httpbin.org/status/200")asresp:returnrespexceptCancelledError:raiseelse:print(resp)asyncio.run(main())As decoratorfromrtryimporttimeout,CancelledError@timeout(3.0)deffn():resp=requests.get("https://httpbin.org/status/200")returnresptry:resp=fn()exceptCancelledError:raiseelse:print(resp)As decorator (asyncio)importasyncioimportaiohttpfromrtryimporttimeout,CancelledError@timeout(3.0)asyncdeffn():asyncwithaiohttp.ClientSession()assession:asyncwithsession.get("https://httpbin.org/status/200")asresp:returnrespasyncdefmain():try:resp=awaitfn()exceptCancelledError:raiseelse:print(resp)asyncio.run(main())As argumentfromrtryimportretry,CancelledError@retry(until=lambdar:r.status_code!=200,timeout=3.0)deffn():resp=requests.get("https://httpbin.org/status/200")returnresptry:resp=fn()exceptCancelledError:raiseelse:print(resp)RetryAttempts@retry(attempts=2)deffn():resp=requests.get("https://httpbin.org/status/500")print(resp)assertresp.status_code==200returnrespresp=fn()# <Response [500]># <Response [500]># Traceback:#   AssertionErrorUntil@retry(until=lambdar:r.status_code!=200,attempts=2)deffn():resp=requests.get("https://httpbin.org/status/500")print(resp)returnrespresp=fn()# <Response [500]># <Response [500]>LoggerSimple logger@retry(until=lambdar:r.status_code!=200,attempts=2,logger=print)deffn():resp=requests.get("https://httpbin.org/status/500")returnrespresp=fn()# 1 <Response [500]> <function fn at 0x103dcd268># 2 <Response [500]> <function fn at 0x103dcd268>Custom loggerdeflogger(attempt,result_or_exception,decorated):logging.info("Attempt:%d, Result:%s",attempt,result_or_exception)@retry(until=lambdar:r.status_code!=200,attempts=2,logger=logger)deffn():resp=requests.get("https://httpbin.org/status/500")returnrespresp=fn()# INFO:root:Attempt: 1, Result: <Response [500]># INFO:root:Attempt: 2, Result: <Response [500]>DelayConst delay@retry(until=lambdar:r.status_code!=200,attempts=2,delay=0.1)deffn():resp=requests.get("https://httpbin.org/status/500")returnrespstarted_at=time.monotonic()resp=fn()ended_at=time.monotonic()print('Elapsed{:.2f}'.format(ended_at-started_at))# Elapsed 2.11Custom delayfrommathimportexp@retry(until=lambdar:r.status_code!=200,attempts=2,delay=exp)deffn():resp=requests.get("https://httpbin.org/status/500")returnrespstarted_at=time.monotonic()resp=fn()ended_at=time.monotonic()print('Elapsed{:.2f}'.format(ended_at-started_at))# Elapsed 11.79SwallowFail on first exception@retry(attempts=2,swallow=None,logger=print)deffn():resp=requests.get("http://127.0.0.1/status/500")returnresptry:resp=fn()exceptExceptionase:print(e)# HTTPConnectionPool(host='127.0.0.1', port=80): Max retries exceeded with url: /status/500Swallow only ConnectionErrorfromrequests.exceptionsimportConnectionError@retry(attempts=2,swallow=ConnectionError,logger=print)deffn():resp=requests.get("http://127.0.0.1/status/500")returnresptry:resp=fn()exceptExceptionase:print(e)# 1 HTTPConnectionPool(host='127.0.0.1', port=80): Max retries exceeded with url: /status/500# 2 HTTPConnectionPool(host='127.0.0.1', port=80): Max retries exceeded with url: /status/500# HTTPConnectionPool(host='127.0.0.1', port=80): Max retries exceeded with url: /status/500AsyncIOimportasyncioimportaiohttpfromrtryimportretry@retry(attempts=2)asyncdeffn():asyncwithaiohttp.ClientSession()assession:asyncwithsession.get("https://httpbin.org/status/500")asresp:print(resp)assertresp.status==200returnrespasyncdefmain():resp=awaitfn()# <ClientResponse(https://httpbin.org/status/500) [500 INTERNAL SERVER ERROR]># <ClientResponse(https://httpbin.org/status/500) [500 INTERNAL SERVER ERROR]># Traceback#   AssertionErrorasyncio.run(main())