Coverage for src/fastapi_cloud_cli/api/_retry.py: 100%

45 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-11-09 00:25 +0000

1import logging 1eafbgcd

2import time 1eafbgcd

3from collections.abc import Callable, Generator 1eafbgcd

4from contextlib import contextmanager 1eafbgcd

5from datetime import timedelta 1eafbgcd

6from functools import wraps 1eafbgcd

7from typing import TypeVar 1eafbgcd

8 

9import httpx 1eafbgcd

10from typing_extensions import ParamSpec 1eafbgcd

11 

12from ._errors import StreamLogError, TooManyRetriesError 1eafbgcd

13 

14logger = logging.getLogger(__name__) 1eafbgcd

15 

16STREAM_LOGS_MAX_RETRIES = 3 1eafbgcd

17STREAM_LOGS_TIMEOUT = timedelta(minutes=5) 1eafbgcd

18 

19 

20@contextmanager 1eafbgcd

21def attempt(attempt_number: int) -> Generator[None, None, None]: 1eafbgcd

22 def _backoff() -> None: 2OgcbdbX v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,dPgebfb3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteueQggbhb9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6eRgibjb( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - McbfcfdfefffgfhfSgkblb. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTfTgmbnb@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_fUgobpb{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

23 backoff_seconds = min(2**attempt_number, 30) 2cbdbv w x y h i ebfbz A B C j k gbhbD E F G l m ibjbH I J K n o kblbL M N O p q mbnbP Q R S r s obpbT U V W t u

24 logger.debug( 2cbdbv w x y h i ebfbz A B C j k gbhbD E F G l m ibjbH I J K n o kblbL M N O p q mbnbP Q R S r s obpbT U V W t u

25 "Retrying in %ds (attempt %d)", 

26 backoff_seconds, 

27 attempt_number, 

28 ) 

29 time.sleep(backoff_seconds) 2cbdbv w x y h i ebfbz A B C j k gbhbD E F G l m ibjbH I J K n o kblbL M N O p q mbnbP Q R S r s obpbT U V W t u

30 

31 try: 2OgcbdbX v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,dPgebfb3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteueQggbhb9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6eRgibjb( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - McbfcfdfefffgfhfSgkblb. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTfTgmbnb@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_fUgobpb{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

32 yield 2OgcbdbX v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,dPgebfb3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteueQggbhb9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6eRgibjb( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - McbfcfdfefffgfhfSgkblb. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTfTgmbnb@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_fUgobpb{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

33 

34 except ( 2e cbdbX v w x y h qbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbY Z 0 1 2 Rba ebfb3 z A B C j SbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b4 5 6 7 8 ,bf gbhb9 D E F G l -b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjc! # $ % ' kcb ibjb( H I J K n lcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc) * + , - Mcg kblb. L M N O p NcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%c/ : ; = ? 'cc mbnb@ P Q R S r (c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded[ ] ^ _ ` fdd obpb{ T U V W t gdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGd| } ~ abbbHd

35 httpx.TimeoutException, 

36 httpx.NetworkError, 

37 httpx.RemoteProtocolError, 

38 ) as error: 

39 logger.debug("Network error (will retry): %s", error) 1vwxyzABCDEFGHIJKLMNOPQRSTUVW

40 

41 _backoff() 1vwxyzABCDEFGHIJKLMNOPQRSTUVW

42 

43 except httpx.HTTPStatusError as error: 2e cbdbX h qbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbY Z 0 1 2 Rba ebfb3 j SbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b4 5 6 7 8 ,bf gbhb9 l -b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjc! # $ % ' kcb ibjb( n lcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc) * + , - Mcg kblb. p NcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%c/ : ; = ? 'cc mbnb@ r (c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded[ ] ^ _ ` fdd obpb{ t gdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGd| } ~ abbbHd

44 if error.response.status_code >= 500: 2cbdbX h i Y Z 0 1 2 ebfb3 j k 4 5 6 7 8 gbhb9 l m ! # $ % ' ibjb( n o ) * + , - kblb. p q / : ; = ? mbnb@ r s [ ] ^ _ ` obpb{ t u | } ~ abbb

45 logger.debug( 2cbdbh i ebfbj k gbhbl m ibjbn o kblbp q mbnbr s obpbt u

46 "Server error %d (will retry): %s", 

47 error.response.status_code, 

48 error, 

49 ) 

50 _backoff() 2cbdbh i ebfbj k gbhbl m ibjbn o kblbp q mbnbr s obpbt u

51 else: 

52 # The streaming callers read the body before raising, so the 

53 # server's error detail is available here. 

54 raise StreamLogError( 2X Y Z 0 1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } ~ abbb

55 f"HTTP {error.response.status_code}: {error.response.text}", 

56 status_code=error.response.status_code, 

57 ) from error 

58 

59 

60P = ParamSpec("P") 1eafbgcd

61T = TypeVar("T") 1eafbgcd

62 

63 

64def attempts( 1eafbgcd

65 total_attempts: int = 3, timeout: timedelta = timedelta(minutes=5) 

66) -> Callable[ 

67 [Callable[P, Generator[T, None, None]]], Callable[P, Generator[T, None, None]] 

68]: 

69 def decorator( 1eafbgcd

70 func: Callable[P, Generator[T, None, None]], 

71 ) -> Callable[P, Generator[T, None, None]]: 

72 @wraps(func) 1eafbgcd

73 def wrapper(*args: P.args, **kwargs: P.kwargs) -> Generator[T, None, None]: 1eafbgcd

74 start = time.monotonic() 2X v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,d3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteue9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6e( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - Mcbfcfdfefffgfhf. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTf@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_f{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

75 

76 for attempt_number in range(total_attempts): 2X v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,d3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteue9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6e( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - Mcbfcfdfefffgfhf. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTf@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_f{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

77 if time.monotonic() - start > timeout.total_seconds(): 2X v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,d3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteue9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6e( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - Mcbfcfdfefffgfhf. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTf@ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_f{ T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

78 raise TimeoutError( 1yCGKOSW

79 f"Log streaming timed out after {timeout.total_seconds():.0f}s" 

80 ) 

81 

82 with attempt(attempt_number): 2e X v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,da 3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteuef 9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6eb ( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - Mcbfcfdfefffgfhfg . L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTfc @ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_fd { T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

83 yield from func(*args, **kwargs) 2X v HgIdJdKdLdw x Mdy h NdOdPdqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbi MbNbObPbQbQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$dY Z 0 1 2 Rb%d'd(d)d*d+d,da 3 z Ig-d.d/d:dA B ;dC j =d?d@dSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%bk 'b(b)b*b+b[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemene4 5 6 7 8 ,boepeqereseteue9 D JgvewexeyeE F zeG l AeBeCe-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecm fcgchcicjcDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe! # $ % ' kc0e1e2e3e4e5e6eb ( H Kg7e8e9e!eI J #eK n $e%e'elcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGco HcIcJcKcLc(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eaf) * + , - Mcbfcfdfefffgfhf. L LgifjfkflfM N mfO p nfofpfNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c5c6c7c8cq 9c!c#c$c%cqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMf/ : ; = ? 'cNfOfPfQfRfSfTfc @ P MgUfVfWfXfQ R YfS r Zf0f1f(c)c*c+c,c-c.c/c:c;c=c?c@c[c]c^c_c`c{c|c}c~cs adbdcddded2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f[ ] ^ _ ` fd=f?f@f[f]f^f_fd { T Ng`f{f|f}fU V ~fW t agbgcggdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdu CdDdEdFdGddgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg| } ~ abbbHdAgBgCgDgEgFgGg

84 # If we get here without exception, the generator completed successfully 

85 return 2IdJdKdLdx Mdh NdOdPdQdRdSdTdUdVdWdXdYdZd0d1d2d3d4d5d6d7d8d9d!d#d$d%d'd(d)d*d+d,d-d.d/d:dB ;dj =d?d@d[d]d^d_d`d{d|d}d~daebecedeeefegeheiejekelemeneoepeqereseteuevewexeyeF zel AeBeCeDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe0e1e2e3e4e5e6e7e8e9e!eJ #en $e%e'e(e)e*e+e,e-e.e/e:e;e=e?e@e[e]e^e_e`e{e|e}e~eafbfcfdfefffgfhfifjfkflfN mfp nfofpfqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMfNfOfPfQfRfSfTfUfVfWfXfR Yfr Zf0f1f2f3f4f5f6f7f8f9f!f#f$f%f'f(f)f*f+f,f-f.f/f:f;f=f?f@f[f]f^f_f`f{f|f}fV ~ft agbgcgdgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzgAgBgCgDgEgFgGg

86 

87 raise TooManyRetriesError(f"Failed after {total_attempts} attempts") 1vwizAkDEmHIoLMqPQsTUu

88 

89 return wrapper 1eafbgcd

90 

91 return decorator 1eafbgcd