11import json
22import posixpath
33import re
4- from http import HTTPStatus
54from typing import List , Optional , Sequence , Union
65
76import requests
8- from tenacity import (
9- RetryError ,
10- retry ,
11- retry_if_result ,
12- stop_after_attempt ,
13- wait_random_exponential ,
14- )
7+
8+ from corva .api_utils import get_requests_session , get_retry_strategy
9+ from corva .configuration import SETTINGS
1510
1611
1712class Api :
@@ -21,26 +16,34 @@ class Api:
2116 convenient URL usage and reasonable timeouts to API requests.
2217 """
2318
24- TIMEOUT_LIMITS = (3 , 30 ) # seconds
25- DEFAULT_MAX_RETRIES = int (0 )
26-
2719 def __init__ (
2820 self ,
2921 * ,
3022 api_url : str ,
3123 data_api_url : str ,
3224 api_key : str ,
3325 app_key : str ,
34- timeout : Optional [int ] = None ,
3526 app_connection_id : Optional [int ] = None ,
27+ max_retries : Optional [int ] = 3 ,
28+ backoff_factor_retries : Optional [float ] = 1 ,
29+ pool_conn_count : Optional [int ] = None ,
30+ pool_max_size : Optional [int ] = None ,
31+ pool_block : Optional [bool ] = None ,
3632 ):
3733 self .api_url = api_url
3834 self .data_api_url = data_api_url
3935 self .api_key = api_key
4036 self .app_key = app_key
4137 self .app_connection_id = app_connection_id
42- self .timeout = timeout or self .TIMEOUT_LIMITS [1 ]
43- self ._max_retries = self .DEFAULT_MAX_RETRIES
38+ self ._session = get_requests_session (
39+ retry_strategy = get_retry_strategy (
40+ max_retries = max_retries or SETTINGS .MAX_RETRY_COUNT ,
41+ backoff_factor = backoff_factor_retries or SETTINGS .BACKOFF_FACTOR ,
42+ ),
43+ pool_connections_count = (pool_conn_count or SETTINGS .POOL_CONNECTIONS_COUNT ),
44+ pool_max_size = pool_max_size or SETTINGS .POOL_MAX_SIZE ,
45+ pool_block = pool_block or SETTINGS .POOL_BLOCK ,
46+ )
4447
4548 @property
4649 def default_headers (self ):
@@ -49,16 +52,6 @@ def default_headers(self):
4952 "X-Corva-App" : self .app_key ,
5053 }
5154
52- @property
53- def max_retries (self ) -> int :
54- return self ._max_retries
55-
56- @max_retries .setter
57- def max_retries (self , value : int ):
58- if not (0 <= value <= 10 ):
59- raise ValueError ("Values between 0 and 10 are allowed" )
60- self ._max_retries = value
61-
6255 def get (self , path : str , ** kwargs ):
6356 return self ._request ("GET" , path , ** kwargs )
6457
@@ -99,15 +92,15 @@ def _get_url(self, path: str):
9992
10093 return posixpath .join (self .api_url , path )
10194
102- @staticmethod
10395 def _execute_request (
96+ self ,
10497 method : str ,
10598 url : str ,
10699 params : Optional [dict ],
107100 data : Optional [dict ],
108101 headers : Optional [dict ] = None ,
109102 timeout : Optional [int ] = None ,
110- ):
103+ ) -> requests . Response :
111104 """Executes the request.
112105
113106 Args:
@@ -116,12 +109,12 @@ def _execute_request(
116109 data: request body, that will be casted to json.
117110 params: url query string params.
118111 headers: additional headers to include in request.
119- timeout: custom request timeout in seconds.
120112
121113 Returns:
122114 requests.Response instance.
123115 """
124- return requests .request (
116+
117+ return self ._session .request (
125118 method = method ,
126119 url = url ,
127120 params = params ,
@@ -148,70 +141,25 @@ def _request(
148141 data: request body, that will be casted to json.
149142 params: url query string params.
150143 headers: additional headers to include in request.
151- timeout: custom request timeout in seconds.
152144
153145 Returns:
154146 requests.Response instance.
155147 """
156- retryable_status_codes = [
157- HTTPStatus .TOO_MANY_REQUESTS , # 428
158- HTTPStatus .INTERNAL_SERVER_ERROR , # 500
159- HTTPStatus .BAD_GATEWAY , # 502
160- HTTPStatus .SERVICE_UNAVAILABLE , # 503
161- HTTPStatus .GATEWAY_TIMEOUT , # 504
162- ]
163-
164- timeout = timeout or self .timeout
165- self ._validate_timeout (timeout )
166-
167148 url = self ._get_url (path )
168149
169150 headers = {
170151 ** self .default_headers ,
171152 ** (headers or {}),
172153 }
173154
174- if self .max_retries > 0 :
175- retry_decorator = retry (
176- stop = stop_after_attempt (self .max_retries ),
177- wait = wait_random_exponential (multiplier = 0.25 , max = 10 ),
178- retry = retry_if_result (
179- lambda r : r .status_code in retryable_status_codes
180- ),
181- )
182- retrying_request = retry_decorator (self ._execute_request )
183- try :
184- response = retrying_request (
185- method = method ,
186- url = url ,
187- params = params ,
188- data = data ,
189- headers = headers ,
190- timeout = timeout ,
191- )
192- except RetryError as e :
193- if not e .last_attempt .failed :
194- response = e .last_attempt .result ()
195- else :
196- raise
197- else :
198- response = self ._execute_request (
199- method = method ,
200- url = url ,
201- params = params ,
202- data = data ,
203- headers = headers ,
204- timeout = timeout ,
205- )
206-
207- return response
208-
209- def _validate_timeout (self , timeout : int ) -> None :
210- if self .TIMEOUT_LIMITS [0 ] > timeout or self .TIMEOUT_LIMITS [1 ] < timeout :
211- raise ValueError (
212- f"Timeout must be between { self .TIMEOUT_LIMITS [0 ]} and "
213- f"{ self .TIMEOUT_LIMITS [1 ]} seconds."
214- )
155+ return self ._execute_request (
156+ method = method ,
157+ url = url ,
158+ params = params ,
159+ data = data ,
160+ headers = headers ,
161+ timeout = timeout ,
162+ )
215163
216164 def get_dataset (
217165 self ,
0 commit comments