Skip to content

Commit 8414706

Browse files
authored
Handle ReadTimeout from actinia with retries (#65)
* allow retries in case of ReadTimeouts * pass retries for ReadTimeout to polling
1 parent cf0197e commit 8414706

File tree

2 files changed

+43
-14
lines changed

2 files changed

+43
-14
lines changed

src/actinia/job.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ def __update(
4343
for key in actinia_json_dict:
4444
setattr(self, key, actinia_json_dict[key])
4545

46-
def poll(self, quiet=False):
46+
def poll(self, quiet=False, retries=0):
4747
"""
4848
Update job by polling.
49+
50+
Args:
51+
quiet: Bool if the method should log process status
52+
retries: Integer number of retries in case of ReadTimeout
4953
"""
5054
if self.status not in ["accepted", "running"]:
5155
log.warning("The job is not running and can not be updated.")
@@ -56,7 +60,9 @@ def poll(self, quiet=False):
5660
"timeout": self.__actinia.timeout,
5761
}
5862
url = self.urls["status"]
59-
resp = request_and_check("GET", url, status_code=(200, 400), **kwargs)
63+
resp = request_and_check(
64+
"GET", url, status_code=(200, 400), retries=retries, **kwargs
65+
)
6066

6167
if "process_results" not in resp:
6268
resp["process_results"] = {}
@@ -66,19 +72,20 @@ def poll(self, quiet=False):
6672
if not quiet:
6773
log.info(f"Status of {self.name} job is {self.status}.")
6874

69-
def poll_until_finished(self, waiting_time=5, quiet=False):
75+
def poll_until_finished(self, waiting_time=5, quiet=False, retries=0):
7076
"""
7177
Polling job until finished or error.
7278
7379
Args:
7480
waiting_time: Time to wait in seconds for next poll
7581
quiet: Bool if the method should log each process status or only
7682
changed
83+
retries: Integer number of retries in case of ReadTimeout
7784
"""
7885
status_accepted_running = True
7986
status = None
8087
while status_accepted_running:
81-
self.poll(quiet=True)
88+
self.poll(quiet=True, retries=retries)
8289
if self.status not in ["accepted", "running"]:
8390
status_accepted_running = False
8491
msg = (

src/actinia/utils.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@
2121
from datetime import datetime
2222

2323

24-
def request_and_check(method, url, status_code=(200,), **kwargs):
25-
"""Function to send a GET request to an URL and check the status code.
24+
def request_and_check(method, url, status_code=(200,), retries=0, **kwargs):
25+
"""Send a request with the given method to a URL and check the status code.
2626
2727
Parameters:
2828
method (string): Request method (GET, POST, PUT, DELETE, ...)
2929
url (string): URL as string
3030
status_code (tuple): Tuple of acceptable status codes to check
3131
if it is set; default is 200
32+
retries (int): Maximal number of retries in case of read timeouts
33+
default is 0.
3234
**kwargs:
3335
auth (tuple): Tuple of user and password
3436
timeout (tuple): Tuple of connection timeout and read timeout
@@ -38,15 +40,35 @@ def request_and_check(method, url, status_code=(200,), **kwargs):
3840
Returns:
3941
(dict): returns text of the response as dictionary
4042
41-
Throws an error if the request does not have the status_code
43+
Throws an error if the request does not have the right status_code or
44+
the response content cannot be paresd as JSON.
4245
"""
43-
resp = requests.request(method, url, **kwargs)
44-
# Use resp.raise_for_status() ?
45-
if resp.status_code == 401:
46-
raise Exception("Wrong user or password. Please check your inputs.")
47-
elif resp.status_code not in status_code:
48-
raise Exception(f"Error {resp.status_code}: {resp.text}")
49-
return json.loads(resp.text)
46+
attempt = 0
47+
while attempt <= retries:
48+
attempt += 1
49+
resp = requests.request(method, url, **kwargs)
50+
try:
51+
if resp.status_code not in status_code:
52+
resp.raise_for_status()
53+
except requests.exceptions.ReadTimeout as e:
54+
if attempt >= retries:
55+
raise e
56+
continue
57+
except requests.exceptions.RequestException as e:
58+
if resp.status_code == 401:
59+
raise Exception(
60+
"Wrong user or password. Please check your inputs."
61+
) from e
62+
raise requests.exceptions.RequestException(
63+
f"Error {resp.status_code}: {resp.text}", e
64+
) from None
65+
try:
66+
return json.loads(resp.text)
67+
except json.JSONDecodeError:
68+
raise RuntimeError(
69+
"Invalid value returned. Cannot parse JSON from response:",
70+
resp.text,
71+
) from None
5072

5173

5274
def set_job_names(name, default_name="unknown_job"):

0 commit comments

Comments
 (0)