11import logging
22import re
33
4+ import backoff
45import requests
56
67from . import __about__ , common
@@ -67,7 +68,9 @@ def get(self, endpoint):
6768
6869
6970class Airship (object ):
70- def __init__ (self , key , secret = None , token = None , location = "us" , timeout = None ):
71+ def __init__ (
72+ self , key , secret = None , token = None , location = "us" , timeout = None , retries = 0
73+ ):
7174 """Main client class for interacting with the Airship API.
7275
7376 :param key: [required] An Airship project key used to authenticate
@@ -77,13 +80,17 @@ def __init__(self, key, secret=None, token=None, location="us", timeout=None):
7780 :param location: [optional] The Airship cloud site your project is associated
7881 with. Possible values: 'us', 'eu'. Defaults to 'us'.
7982 :param: timeout: [optional] An integer specifying the number of seconds used
80- for a timeout threshold
83+ for a response timeout threshold
84+ :param retries: [optional] An integer specifying the number of times to retry a
85+ failed request. Retried requests use exponential backoff between requests.
86+ Defaults to 0, no retry.
8187 """
8288 self .key = key
8389 self .secret = secret
8490 self .token = token
8591 self .location = location
8692 self .timeout = timeout
93+ self .retries = retries
8794 self .urls = Urls (self .location )
8895
8996 if all ([secret , token ]):
@@ -99,6 +106,14 @@ def __init__(self, key, secret=None, token=None, location="us", timeout=None):
99106 else :
100107 raise ValueError ("Either token or secret must be included" )
101108
109+ @property
110+ def retries (self ):
111+ return self ._retries
112+
113+ @retries .setter
114+ def retries (self , value ):
115+ self ._retries = value
116+
102117 @property
103118 def timeout (self ):
104119 return self ._timeout
@@ -182,33 +197,46 @@ def _request(
182197 if encoding :
183198 headers ["Content-Encoding" ] = encoding
184199
185- logger .debug (
186- "Making %s request to %s. Headers:\n \t %s\n Body:\n \t %s" ,
187- method ,
188- url ,
189- "\n \t " .join ("%s: %s" % (key , value ) for (key , value ) in headers .items ()),
190- body ,
200+ @backoff .on_exception (
201+ backoff .expo , common .AirshipFailure , max_tries = (self .retries + 1 )
191202 )
203+ def make_retryable_request (method , url , body , params , headers ):
204+ response = self .session .request (
205+ method ,
206+ url ,
207+ data = body ,
208+ params = params ,
209+ headers = headers ,
210+ timeout = self .timeout ,
211+ )
192212
193- response = self .session .request (
194- method , url , data = body , params = params , headers = headers , timeout = self .timeout
195- )
213+ logger .debug (
214+ "Making %s request to %s. Headers:\n \t %s\n Body:\n \t %s" ,
215+ method ,
216+ url ,
217+ "\n \t " .join (
218+ "%s: %s" % (key , value ) for (key , value ) in headers .items ()
219+ ),
220+ body ,
221+ )
196222
197- logger .debug (
198- "Received %s response. Headers:\n \t %s\n Body:\n \t %s" ,
199- response .status_code ,
200- "\n \t " .join (
201- "%s: %s" % (key , value ) for (key , value ) in response .headers .items ()
202- ),
203- response .content ,
204- )
223+ logger .debug (
224+ "Received %s response. Headers:\n \t %s\n Body:\n \t %s" ,
225+ response .status_code ,
226+ "\n \t " .join (
227+ "%s: %s" % (key , value ) for (key , value ) in response .headers .items ()
228+ ),
229+ response .content ,
230+ )
231+
232+ if response .status_code == 401 :
233+ raise common .Unauthorized
234+ elif not (200 <= response .status_code < 300 ):
235+ raise common .AirshipFailure .from_response (response )
205236
206- if response .status_code == 401 :
207- raise common .Unauthorized
208- elif not (200 <= response .status_code < 300 ):
209- raise common .AirshipFailure .from_response (response )
237+ return response
210238
211- return response
239+ return make_retryable_request ( method , url , body , params , headers )
212240
213241 def create_push (self ):
214242 """Create a Push notification."""
0 commit comments