@@ -118,6 +118,12 @@ def _retry_timeout(response: httpx.Response, retries: int) -> float:
118118 return _add_symmetric_jitter (backoff )
119119
120120
121+ def _retry_timeout_from_retries (retries : int ) -> float :
122+ """Determine retry timeout using exponential backoff when no response is available."""
123+ backoff = min (INITIAL_RETRY_DELAY_SECONDS * pow (2.0 , retries ), MAX_RETRY_DELAY_SECONDS )
124+ return _add_symmetric_jitter (backoff )
125+
126+
121127def _should_retry (response : httpx .Response ) -> bool :
122128 retryable_400s = [429 , 408 , 409 ]
123129 return response .status_code >= 500 or response .status_code in retryable_400s
@@ -265,11 +271,13 @@ def __init__(
265271 base_timeout : typing .Callable [[], typing .Optional [float ]],
266272 base_headers : typing .Callable [[], typing .Dict [str , str ]],
267273 base_url : typing .Optional [typing .Callable [[], str ]] = None ,
274+ base_max_retries : int = 2 ,
268275 logging_config : typing .Optional [typing .Union [LogConfig , Logger ]] = None ,
269276 ):
270277 self .base_url = base_url
271278 self .base_timeout = base_timeout
272279 self .base_headers = base_headers
280+ self .base_max_retries = base_max_retries
273281 self .httpx_client = httpx_client
274282 self .logger = create_logger (logging_config )
275283
@@ -364,19 +372,44 @@ def request(
364372 has_body = json_body is not None or data_body is not None ,
365373 )
366374
367- response = self .httpx_client .request (
368- method = method ,
369- url = _request_url ,
370- headers = _request_headers ,
371- params = _encoded_params if _encoded_params else None ,
372- json = json_body ,
373- data = data_body ,
374- content = content ,
375- files = request_files ,
376- timeout = timeout ,
375+ max_retries : int = (
376+ request_options .get ("max_retries" , self .base_max_retries )
377+ if request_options is not None
378+ else self .base_max_retries
377379 )
378380
379- max_retries : int = request_options .get ("max_retries" , 2 ) if request_options is not None else 2
381+ try :
382+ response = self .httpx_client .request (
383+ method = method ,
384+ url = _request_url ,
385+ headers = _request_headers ,
386+ params = _encoded_params if _encoded_params else None ,
387+ json = json_body ,
388+ data = data_body ,
389+ content = content ,
390+ files = request_files ,
391+ timeout = timeout ,
392+ )
393+ except (httpx .ConnectError , httpx .RemoteProtocolError ):
394+ if retries < max_retries :
395+ time .sleep (_retry_timeout_from_retries (retries = retries ))
396+ return self .request (
397+ path = path ,
398+ method = method ,
399+ base_url = base_url ,
400+ params = params ,
401+ json = json ,
402+ data = data ,
403+ content = content ,
404+ files = files ,
405+ headers = headers ,
406+ request_options = request_options ,
407+ retries = retries + 1 ,
408+ omit = omit ,
409+ force_multipart = force_multipart ,
410+ )
411+ raise
412+
380413 if _should_retry (response = response ):
381414 if retries < max_retries :
382415 time .sleep (_retry_timeout (response = response , retries = retries ))
@@ -386,12 +419,14 @@ def request(
386419 base_url = base_url ,
387420 params = params ,
388421 json = json ,
422+ data = data ,
389423 content = content ,
390424 files = files ,
391425 headers = headers ,
392426 request_options = request_options ,
393427 retries = retries + 1 ,
394428 omit = omit ,
429+ force_multipart = force_multipart ,
395430 )
396431
397432 if self .logger .is_debug ():
@@ -518,12 +553,14 @@ def __init__(
518553 base_timeout : typing .Callable [[], typing .Optional [float ]],
519554 base_headers : typing .Callable [[], typing .Dict [str , str ]],
520555 base_url : typing .Optional [typing .Callable [[], str ]] = None ,
556+ base_max_retries : int = 2 ,
521557 async_base_headers : typing .Optional [typing .Callable [[], typing .Awaitable [typing .Dict [str , str ]]]] = None ,
522558 logging_config : typing .Optional [typing .Union [LogConfig , Logger ]] = None ,
523559 ):
524560 self .base_url = base_url
525561 self .base_timeout = base_timeout
526562 self .base_headers = base_headers
563+ self .base_max_retries = base_max_retries
527564 self .async_base_headers = async_base_headers
528565 self .httpx_client = httpx_client
529566 self .logger = create_logger (logging_config )
@@ -627,19 +664,44 @@ async def request(
627664 has_body = json_body is not None or data_body is not None ,
628665 )
629666
630- response = await self .httpx_client .request (
631- method = method ,
632- url = _request_url ,
633- headers = _request_headers ,
634- params = _encoded_params if _encoded_params else None ,
635- json = json_body ,
636- data = data_body ,
637- content = content ,
638- files = request_files ,
639- timeout = timeout ,
667+ max_retries : int = (
668+ request_options .get ("max_retries" , self .base_max_retries )
669+ if request_options is not None
670+ else self .base_max_retries
640671 )
641672
642- max_retries : int = request_options .get ("max_retries" , 2 ) if request_options is not None else 2
673+ try :
674+ response = await self .httpx_client .request (
675+ method = method ,
676+ url = _request_url ,
677+ headers = _request_headers ,
678+ params = _encoded_params if _encoded_params else None ,
679+ json = json_body ,
680+ data = data_body ,
681+ content = content ,
682+ files = request_files ,
683+ timeout = timeout ,
684+ )
685+ except (httpx .ConnectError , httpx .RemoteProtocolError ):
686+ if retries < max_retries :
687+ await asyncio .sleep (_retry_timeout_from_retries (retries = retries ))
688+ return await self .request (
689+ path = path ,
690+ method = method ,
691+ base_url = base_url ,
692+ params = params ,
693+ json = json ,
694+ data = data ,
695+ content = content ,
696+ files = files ,
697+ headers = headers ,
698+ request_options = request_options ,
699+ retries = retries + 1 ,
700+ omit = omit ,
701+ force_multipart = force_multipart ,
702+ )
703+ raise
704+
643705 if _should_retry (response = response ):
644706 if retries < max_retries :
645707 await asyncio .sleep (_retry_timeout (response = response , retries = retries ))
@@ -649,12 +711,14 @@ async def request(
649711 base_url = base_url ,
650712 params = params ,
651713 json = json ,
714+ data = data ,
652715 content = content ,
653716 files = files ,
654717 headers = headers ,
655718 request_options = request_options ,
656719 retries = retries + 1 ,
657720 omit = omit ,
721+ force_multipart = force_multipart ,
658722 )
659723
660724 if self .logger .is_debug ():
0 commit comments