27
27
28
28
class GeocodioClient :
29
29
BASE_PATH = "/v1.8" # keep in sync with Geocodio's current version
30
+ DEFAULT_SINGLE_TIMEOUT = 5.0
31
+ DEFAULT_BATCH_TIMEOUT = 1800.0 # 30 minutes
32
+ LIST_API_TIMEOUT = 60.0
30
33
31
34
@staticmethod
32
35
def get_status_exception_mappings () -> Dict [
@@ -43,13 +46,23 @@ def get_status_exception_mappings() -> Dict[
43
46
500 : GeocodioServerError ,
44
47
}
45
48
46
- def __init__ (self , api_key : Optional [str ] = None , hostname : str = "api.geocod.io" ):
49
+ def __init__ (
50
+ self ,
51
+ api_key : Optional [str ] = None ,
52
+ hostname : str = "api.geocod.io" ,
53
+ single_timeout : Optional [float ] = None ,
54
+ batch_timeout : Optional [float ] = None ,
55
+ list_timeout : Optional [float ] = None ,
56
+ ):
47
57
self .api_key : str = api_key or os .getenv ("GEOCODIO_API_KEY" , "" )
48
58
if not self .api_key :
49
59
raise AuthenticationError (
50
60
detail = "No API key supplied and GEOCODIO_API_KEY is not set."
51
61
)
52
62
self .hostname = hostname .rstrip ("/" )
63
+ self .single_timeout = single_timeout or self .DEFAULT_SINGLE_TIMEOUT
64
+ self .batch_timeout = batch_timeout or self .DEFAULT_BATCH_TIMEOUT
65
+ self .list_timeout = list_timeout or self .LIST_API_TIMEOUT
53
66
self ._http = httpx .Client (base_url = f"https://{ self .hostname } " )
54
67
55
68
# ──────────────────────────────────────────────────────────────────────────
@@ -108,7 +121,8 @@ def geocode(
108
121
params ["q" ] = address
109
122
data = None
110
123
111
- response = self ._request ("POST" if data else "GET" , endpoint , params , json = data )
124
+ timeout = self .batch_timeout if data else self .single_timeout
125
+ response = self ._request ("POST" if data else "GET" , endpoint , params , json = data , timeout = timeout )
112
126
return self ._parse_geocoding_response (response .json ())
113
127
114
128
def reverse (
@@ -144,7 +158,8 @@ def reverse(
144
158
params ["q" ] = coordinate # "lat,lng"
145
159
data = None
146
160
147
- response = self ._request ("POST" if data else "GET" , endpoint , params , json = data )
161
+ timeout = self .batch_timeout if data else self .single_timeout
162
+ response = self ._request ("POST" if data else "GET" , endpoint , params , json = data , timeout = timeout )
148
163
return self ._parse_geocoding_response (response .json ())
149
164
150
165
# ──────────────────────────────────────────────────────────────────────────
@@ -158,13 +173,18 @@ def _request(
158
173
params : dict ,
159
174
json : Optional [dict ] = None ,
160
175
files : Optional [dict ] = None ,
176
+ timeout : Optional [float ] = None ,
161
177
) -> httpx .Response :
162
178
logger .debug (f"Making Request: { method } { endpoint } " )
163
179
logger .debug (f"Params: { params } " )
164
180
logger .debug (f"JSON body: { json } " )
165
181
logger .debug (f"Files: { files } " )
166
182
167
- resp = self ._http .request (method , endpoint , params = params , json = json , files = files , timeout = 60 )
183
+ if timeout is None :
184
+ timeout = self .single_timeout
185
+
186
+ logger .debug (f"Using timeout: { timeout } s" )
187
+ resp = self ._http .request (method , endpoint , params = params , json = json , files = files , timeout = timeout )
168
188
169
189
logger .debug (f"Response status code: { resp .status_code } " )
170
190
logger .debug (f"Response headers: { resp .headers } " )
@@ -290,7 +310,7 @@ def create_list(
290
310
# Join fields with commas as required by the API
291
311
params ["fields" ] = "," .join (fields )
292
312
293
- response = self ._request ("POST" , endpoint , params , files = files )
313
+ response = self ._request ("POST" , endpoint , params , files = files , timeout = self . list_timeout )
294
314
logger .debug (f"Response content: { response .text } " )
295
315
return self ._parse_list_response (response .json (), response = response )
296
316
@@ -304,7 +324,7 @@ def get_lists(self) -> PaginatedResponse:
304
324
params : Dict [str , Union [str , int ]] = {"api_key" : self .api_key }
305
325
endpoint = f"{ self .BASE_PATH } /lists"
306
326
307
- response = self ._request ("GET" , endpoint , params )
327
+ response = self ._request ("GET" , endpoint , params , timeout = self . list_timeout )
308
328
pagination_info = response .json ()
309
329
310
330
logger .debug (f"Pagination info: { pagination_info } " )
@@ -339,7 +359,7 @@ def get_list(self, list_id: str) -> ListResponse:
339
359
params : Dict [str , Union [str , int ]] = {"api_key" : self .api_key }
340
360
endpoint = f"{ self .BASE_PATH } /lists/{ list_id } "
341
361
342
- response = self ._request ("GET" , endpoint , params )
362
+ response = self ._request ("GET" , endpoint , params , timeout = self . list_timeout )
343
363
return self ._parse_list_response (response .json (), response = response )
344
364
345
365
def delete_list (self , list_id : str ) -> None :
@@ -352,7 +372,7 @@ def delete_list(self, list_id: str) -> None:
352
372
params : Dict [str , Union [str , int ]] = {"api_key" : self .api_key }
353
373
endpoint = f"{ self .BASE_PATH } /lists/{ list_id } "
354
374
355
- self ._request ("DELETE" , endpoint , params )
375
+ self ._request ("DELETE" , endpoint , params , timeout = self . list_timeout )
356
376
357
377
@staticmethod
358
378
def _parse_list_response (response_json : dict , response : httpx .Response = None ) -> ListResponse :
@@ -521,7 +541,7 @@ def download(self, list_id: str, filename: Optional[str] = None) -> str | bytes:
521
541
params = {"api_key" : self .api_key }
522
542
endpoint = f"{ self .BASE_PATH } /lists/{ list_id } /download"
523
543
524
- response : httpx .Response = self ._request ("GET" , endpoint , params )
544
+ response : httpx .Response = self ._request ("GET" , endpoint , params , timeout = self . list_timeout )
525
545
if response .headers .get ("content-type" , "" ).startswith ("application/json" ):
526
546
try :
527
547
error = response .json ()
0 commit comments