3434from .version import __version__ as umapi_version
3535
3636
37+ class APIResult :
38+ success_codes = [200 , 201 , 204 ]
39+ timeout_codes = [429 , 502 , 503 , 504 ]
40+ client_error = lambda self , x : 201 <= x < 200
41+ request_error = lambda self , x : 400 <= x < 500
42+
43+ def __init__ (self , result = None , success = False , timeout = None ):
44+ self .result = result
45+ self .success = success
46+ self .timeout = timeout
47+ self .status_code = result .status_code if hasattr (result , 'status_code' ) else 'Error'
48+
49+ def check_result (self ):
50+ if self .result .status_code in self .success_codes :
51+ self .success = True
52+ return self
53+ if self .result .status_code in self .timeout_codes :
54+ self .success = False
55+ self .timeout = self .get_timeout ()
56+ return self
57+ if self .client_error (self .result .status_code ):
58+ raise ClientError ("Unexpected HTTP Status {:d}: {}" .format (self .result .status_code , self .result .text ), self .result )
59+ if self .request_error (self .result .status_code ):
60+ raise RequestError (self .result )
61+ raise ServerError (self .result )
62+
63+ def get_timeout (self ):
64+ if "Retry-After" in self .result .headers :
65+ advice = self .result .headers ["Retry-After" ]
66+ advised_time = parsedate_tz (advice )
67+ if advised_time is not None :
68+ # header contains date
69+ return int (mktime_tz (advised_time ) - time ())
70+ else :
71+ # header contains delta seconds
72+ return int (advice )
73+ return 0
74+
3775class Connection :
3876 """
3977 An org-specific, authorized connection to the UMAPI service. Each method
@@ -446,38 +484,31 @@ def call():
446484 start_time = time ()
447485 result = None
448486 for num_attempts in range (1 , self .retry_max_attempts + 1 ):
487+ checked_result = None
449488 try :
450489 result = call ()
451- if result .status_code in [200 ,201 ,204 ]:
452- return result
453- elif result .status_code in [429 , 502 , 503 , 504 ]:
454- if self .logger : self .logger .warning ("UMAPI timeout...service unavailable (code %d on try %d)" ,
455- result .status_code , num_attempts )
456- retry_wait = 0
457- if "Retry-After" in result .headers :
458- advice = result .headers ["Retry-After" ]
459- advised_time = parsedate_tz (advice )
460- if advised_time is not None :
461- # header contains date
462- retry_wait = int (mktime_tz (advised_time ) - time ())
463- else :
464- # header contains delta seconds
465- retry_wait = int (advice )
466- if retry_wait <= 0 :
467- # use exponential back-off with random delay
468- delay = randint (0 , self .retry_random_delay )
469- retry_wait = (int (pow (2 , num_attempts - 1 )) * self .retry_first_delay ) + delay
470- elif 201 <= result .status_code < 400 :
471- raise ClientError ("Unexpected HTTP Status {:d}: {}" .format (result .status_code , result .text ), result )
472- elif 400 <= result .status_code < 500 :
473- raise RequestError (result )
474- else :
475- raise ServerError (result )
490+ checked_result = APIResult (result ).check_result ()
476491 except requests .Timeout :
477492 if self .logger : self .logger .warning ("UMAPI connection timeout...(%d seconds on try %d)" ,
478493 self .timeout , num_attempts )
479- retry_wait = 0
480- result = None
494+ checked_result = APIResult (success = False , timeout = 0 )
495+ except requests .ConnectionError :
496+ if self .logger : self .logger .warning ("UMAPI connection error...(%d seconds on try %d)" ,
497+ self .timeout , num_attempts )
498+ checked_result = APIResult (success = False , timeout = 0 )
499+
500+ if checked_result .success :
501+ return result
502+
503+ if self .logger : self .logger .warning ("UMAPI timeout...service unavailable (code %s on try %d)" ,
504+ checked_result .status_code , num_attempts )
505+
506+ retry_wait = checked_result .timeout
507+ if retry_wait <= 0 :
508+ # use exponential back-off with random delay
509+ delay = randint (0 , self .retry_random_delay )
510+ retry_wait = (int (pow (2 , num_attempts - 1 )) * self .retry_first_delay ) + delay
511+
481512 if num_attempts < self .retry_max_attempts :
482513 if retry_wait > 0 :
483514 if self .logger : self .logger .warning ("Next retry in %d seconds..." , retry_wait )
@@ -487,4 +518,4 @@ def call():
487518 total_time = int (time () - start_time )
488519 if self .logger : self .logger .error ("UMAPI timeout...giving up after %d attempts (%d seconds)." ,
489520 self .retry_max_attempts , total_time )
490- raise UnavailableError (self .retry_max_attempts , total_time , result )
521+ raise UnavailableError (self .retry_max_attempts , total_time , checked_result . result )
0 commit comments