1616
1717import requests
1818
19- from . import stone_serializers
19+ from . import files , stone_serializers
2020from .auth import (
2121 AuthError_validator ,
2222 RateLimitError_validator ,
@@ -123,13 +123,17 @@ class _DropboxTransport(object):
123123 # the HTTP body.
124124 _ROUTE_STYLE_RPC = 'rpc'
125125
126+ # This is the default longest time we'll block on receiving data from the server
127+ _DEFAULT_TIMEOUT = 30
128+
126129 def __init__ (self ,
127130 oauth2_access_token ,
128131 max_retries_on_error = 4 ,
129132 max_retries_on_rate_limit = None ,
130133 user_agent = None ,
131134 session = None ,
132- headers = None ):
135+ headers = None ,
136+ timeout = _DEFAULT_TIMEOUT ):
133137 """
134138 :param str oauth2_access_token: OAuth2 access token for making client
135139 requests.
@@ -147,6 +151,10 @@ def __init__(self,
147151 :func:`create_session`.
148152 :type session: :class:`requests.sessions.Session`
149153 :param dict headers: Additional headers to add to requests.
154+ :param Optional[float] timeout: Maximum duration in seconds that
155+ client will wait when receiving data from server. After the timeout
156+ the client will give up on connection. If `None`, client will
157+ wait forever. Defaults to 30 seconds.
150158 """
151159 assert len (oauth2_access_token ) > 0 , \
152160 'OAuth2 access token cannot be empty.'
@@ -185,11 +193,14 @@ def __init__(self,
185193 self ._HOST_CONTENT : self ._api_content_hostname ,
186194 self ._HOST_NOTIFY : self ._api_notify_hostname }
187195
196+ self ._timeout = timeout
197+
188198 def request (self ,
189199 route ,
190200 namespace ,
191201 request_arg ,
192- request_binary ):
202+ request_binary ,
203+ timeout = None ):
193204 """
194205 Makes a request to the Dropbox API and in the process validates that
195206 the route argument and result are the expected data types. The
@@ -204,18 +215,36 @@ def request(self,
204215 validator specified by route.arg_type.
205216 :param request_binary: String or file pointer representing the binary
206217 payload. Use None if there is no binary payload.
218+ :param Optional[float] timeout: Maximum duration in seconds that
219+ client will wait when receiving data from server. After the timeout
220+ the client will give up on connection. If `None`, client will
221+ wait forever. Defaults to `None`.
207222 :return: The route's result.
208223 """
209224 host = route .attrs ['host' ] or 'api'
210225 route_name = namespace + '/' + route .name
211226 route_style = route .attrs ['style' ] or 'rpc'
212227 serialized_arg = stone_serializers .json_encode (route .arg_type ,
213228 request_arg )
229+
230+
231+ if (timeout is None and
232+ route == files .list_folder_longpoll ):
233+ # The client normally sends a timeout value to the
234+ # longpoll route. The server will respond after
235+ # <timeout> + random(0, 90) seconds. We increase the
236+ # socket timeout to the longpoll timeout value plus 90
237+ # seconds so that we don't cut the server response short
238+ # due to a shorter socket timeout.
239+ # NB: This is done here because base.py is auto-generated
240+ timeout = request_arg .timeout + 90
241+
214242 res = self .request_json_string_with_retry (host ,
215243 route_name ,
216244 route_style ,
217245 serialized_arg ,
218- request_binary )
246+ request_binary ,
247+ timeout = timeout )
219248 decoded_obj_result = json .loads (res .obj_result )
220249 if isinstance (res , RouteResult ):
221250 returned_data_type = route .result_type
@@ -249,7 +278,8 @@ def request_json_object(self,
249278 route_name ,
250279 route_style ,
251280 request_arg ,
252- request_binary ):
281+ request_binary ,
282+ timeout = None ):
253283 """
254284 Makes a request to the Dropbox API, taking a JSON-serializable Python
255285 object as an argument, and returning one as a response.
@@ -261,14 +291,19 @@ def request_json_object(self,
261291 the argument for the route.
262292 :param request_binary: String or file pointer representing the binary
263293 payload. Use None if there is no binary payload.
294+ :param Optional[float] timeout: Maximum duration in seconds that
295+ client will wait when receiving data from server. After the timeout
296+ the client will give up on connection. If `None`, client will
297+ wait forever. Defaults to `None`.
264298 :return: The route's result as a JSON-serializable Python object.
265299 """
266300 serialized_arg = json .dumps (request_arg )
267301 res = self .request_json_string_with_retry (host ,
268302 route_name ,
269303 route_style ,
270304 serialized_arg ,
271- request_binary )
305+ request_binary ,
306+ timeout = timeout )
272307 # This can throw a ValueError if the result is not deserializable,
273308 # but that would be completely unexpected.
274309 deserialized_result = json .loads (res .obj_result )
@@ -282,7 +317,8 @@ def request_json_string_with_retry(self,
282317 route_name ,
283318 route_style ,
284319 request_json_arg ,
285- request_binary ):
320+ request_binary ,
321+ timeout = None ):
286322 """
287323 See :meth:`request_json_object` for description of parameters.
288324
@@ -298,7 +334,8 @@ def request_json_string_with_retry(self,
298334 route_name ,
299335 route_style ,
300336 request_json_arg ,
301- request_binary )
337+ request_binary ,
338+ timeout = timeout )
302339 except InternalServerError as e :
303340 attempt += 1
304341 if attempt <= self ._max_retries_on_error :
@@ -327,7 +364,8 @@ def request_json_string(self,
327364 func_name ,
328365 route_style ,
329366 request_json_arg ,
330- request_binary ):
367+ request_binary ,
368+ timeout = None ):
331369 """
332370 See :meth:`request_json_string_with_retry` for description of
333371 parameters.
@@ -365,11 +403,15 @@ def request_json_string(self,
365403 else :
366404 raise ValueError ('Unknown operation style: %r' % route_style )
367405
406+ if timeout is None :
407+ timeout = self ._timeout
408+
368409 r = self ._session .post (url ,
369410 headers = headers ,
370411 data = body ,
371412 stream = stream ,
372413 verify = True ,
414+ timeout = timeout ,
373415 )
374416
375417 request_id = r .headers .get ('x-dropbox-request-id' )
0 commit comments