4646
4747class Client (QObject ):
4848 """Performs requests to the ORS API services.
49-
49+
5050 This class handles HTTP communication with the openrouteservice API,
5151 including authentication, retry logic for rate limiting, and progress
5252 tracking.
53-
53+
5454 Signals:
5555 overQueryLimit: Emitted when rate limit is hit, passes delay in seconds
5656 downloadProgress: Emitted during download, passes progress ratio (0-1)
@@ -59,10 +59,9 @@ class Client(QObject):
5959 overQueryLimit = pyqtSignal (int )
6060 downloadProgress = pyqtSignal (int )
6161
62-
6362 def __init__ (self , provider : Optional [dict ] = None , agent : Optional [str ] = None ) -> None :
6463 """Initialize the ORS API client.
65-
64+
6665 :param provider: Provider configuration containing base_url, key, timeout, and ENV_VARS
6766 :type provider: dict
6867 :param agent: User agent string for the HTTP requests
@@ -95,10 +94,10 @@ def _request(
9594 self ,
9695 post_json : Optional [dict ],
9796 blocking_request : QgsBlockingNetworkRequest ,
98- request : QNetworkRequest
97+ request : QNetworkRequest ,
9998 ) -> str :
10099 """Execute a blocking network request.
101-
100+
102101 :param post_json: JSON payload for POST requests, None for GET requests
103102 :type post_json: dict or None
104103 :param blocking_request: QGIS blocking network request handler
@@ -129,11 +128,11 @@ def fetch_with_retry(
129128 max_retries : int = 100 ,
130129 ):
131130 """Fetch data from the API with automatic retry on rate limit errors.
132-
131+
133132 Implements exponential backoff with jitter for retries. The first retry
134133 occurs after exactly 61 seconds, subsequent retries use exponential
135134 backoff capped at 5 minutes.
136-
135+
137136 :param url: API endpoint path (e.g., "/v2/directions/driving-car/geojson")
138137 :type url: str
139138 :param params: URL query parameters
@@ -155,13 +154,13 @@ def fetch_with_retry(
155154 self .url = self .base_url + authed_url
156155
157156 request = QNetworkRequest (QUrl (self .url ))
158-
157+
159158 for header , value in self .headers .items ():
160159 request .setRawHeader (header .encode (), value .encode ())
161160
162161 blocking_request = QgsBlockingNetworkRequest ()
163162
164- blocking_request .downloadProgress .connect (lambda r , t : self .downloadProgress .emit (r / t ))
163+ blocking_request .downloadProgress .connect (lambda r , t : self .downloadProgress .emit (r / t ))
165164
166165 logger .log (f"url: { self .url } \n Parameters: { json .dumps (post_json , indent = 2 )} " , 0 )
167166
@@ -172,7 +171,7 @@ def fetch_with_retry(
172171 reply = self ._request (post_json , blocking_request , request )
173172 content = reply .content ().data ().decode ()
174173 break
175-
174+
176175 except exceptions .OverQueryLimit as e :
177176 if datetime .now () - first_request_time > timedelta (seconds = self .timeout ):
178177 raise exceptions .Timeout ()
@@ -181,7 +180,7 @@ def fetch_with_retry(
181180
182181 delay_seconds = self .get_delay_seconds (i )
183182 self .overQueryLimit .emit (delay_seconds )
184-
183+
185184 loop = QEventLoop ()
186185 QTimer .singleShot (delay_seconds * 1000 , loop .quit )
187186 loop .exec_ ()
@@ -206,11 +205,11 @@ def fetch_with_retry(
206205
207206 def get_delay_seconds (self , retry_counter : int ) -> int :
208207 """Calculate delay before next retry attempt.
209-
208+
210209 First retry waits exactly 61 seconds. Subsequent retries use exponential
211210 backoff with base delay of 60s * 1.5^(retry_counter-1), capped at 300s,
212211 with random jitter between 30-60% of the calculated delay.
213-
212+
214213 :param retry_counter: Zero-based retry attempt number
215214 :type retry_counter: int
216215 :return: Delay in seconds before next retry
@@ -228,7 +227,7 @@ def get_delay_seconds(self, retry_counter: int) -> int:
228227
229228 def _check_status (self , reply : QNetworkReply ) -> None :
230229 """Check HTTP response status and raise appropriate exceptions.
231-
230+
232231 :param reply: Network reply to check
233232 :type reply: QNetworkReply
234233 :raises exceptions.InvalidKey: On 403 Forbidden (invalid API key)
@@ -258,7 +257,7 @@ def _check_status(self, reply: QNetworkReply) -> None:
258257
259258 def _generate_auth_url (self , path : str , params : Union [Dict , List ]) -> str :
260259 """Generate authenticated URL with query parameters.
261-
260+
262261 :param path: API endpoint path
263262 :type path: str
264263 :param params: Query parameters as dict or list of tuples
0 commit comments