@@ -316,30 +316,11 @@ def _fanout_failure_from_response(
316316 )
317317
318318
319- def _process_delayed_json_response (
319+ def _process_json_response (
320320 unit : str ,
321321 response : Response ,
322- * ,
323- max_attempts : int = 300 ,
324- retry_sleep_s : float = 0.1 ,
322+ data : dict [str , Any ],
325323) -> tuple [str , Any ]:
326- """
327- Handle delayed HTTP responses (202 with result_url_path) and immediate 2xx responses.
328- Returns the unit and the appropriate JSON data or result value.
329- """
330- data = response .json ()
331- if response .status_code == 202 and "result_url_path" in data :
332- # Follow up shortly on async responses where the unit returns a result URL.
333- if max_attempts <= 0 :
334- return unit , fanout_failure (
335- unit ,
336- "task_timeout" ,
337- "Timed out waiting for unit task result." ,
338- retryable = True ,
339- status_code = response .status_code ,
340- )
341- sleep (retry_sleep_s )
342- return _get_from_unit (unit , data ["result_url_path" ], max_attempts = max_attempts - 1 )
343324 if 200 <= response .status_code < 300 :
344325 if "task_id" in data :
345326 if data .get ("status" ) == "succeeded" :
@@ -371,6 +352,69 @@ def _process_delayed_json_response(
371352 )
372353
373354
355+ def _process_delayed_json_response (
356+ unit : str ,
357+ address : str ,
358+ response : Response ,
359+ * ,
360+ max_attempts : int ,
361+ timeout : float ,
362+ retry_sleep_s : float = 0.1 ,
363+ ) -> tuple [str , Any ]:
364+ """
365+ Handle delayed HTTP responses (202 with result_url_path) and immediate 2xx responses.
366+ Returns the unit and the appropriate JSON data or result value.
367+ """
368+ data = response .json ()
369+ remaining_attempts = max_attempts
370+
371+ while response .status_code == 202 and "result_url_path" in data :
372+ if remaining_attempts <= 0 :
373+ return unit , fanout_failure (
374+ unit ,
375+ "task_timeout" ,
376+ "Timed out waiting for unit task result." ,
377+ retryable = True ,
378+ status_code = response .status_code ,
379+ )
380+
381+ endpoint = data ["result_url_path" ]
382+ remaining_attempts -= 1
383+ sleep (retry_sleep_s )
384+
385+ delayed_response : Response | None = None
386+ try :
387+ delayed_response = get_from (address , endpoint , timeout = timeout )
388+ delayed_response .raise_for_status ()
389+ response = delayed_response
390+ data = response .json ()
391+ except (HTTPErrorStatus , HTTPException ) as e :
392+ logger .debug (
393+ f"Could not get from { unit } 's { address = } , { endpoint = } , sent json=None and returned { e } ."
394+ f"{ _summarize_unit_api_error (delayed_response )} "
395+ )
396+ return unit , _fanout_failure_from_response (
397+ unit ,
398+ delayed_response ,
399+ fallback_kind = "http_error" if delayed_response is not None else "connection_error" ,
400+ fallback_message = f"Could not GET from { unit } 's { endpoint } ." ,
401+ retryable = delayed_response is None or delayed_response .status_code >= 500 ,
402+ )
403+ except DecodeError :
404+ logger .debug (
405+ f"Could not decode response from { unit } 's { endpoint = } , sent json=None and returned { _response_body_for_logging (delayed_response )} ."
406+ )
407+ return unit , fanout_failure (
408+ unit ,
409+ "decode_error" ,
410+ f"Could not decode response from { unit } 's { endpoint } ." ,
411+ retryable = False ,
412+ status_code = delayed_response .status_code if delayed_response is not None else None ,
413+ )
414+
415+ return _process_json_response (unit , response , data )
416+
417+
374418def _delayed_result_max_attempts (timeout : float , retry_sleep_s : float = 0.1 ) -> int :
375419 return max (1 , math .ceil (timeout / retry_sleep_s ))
376420
@@ -1559,7 +1603,13 @@ def post_into_unit(
15591603 return unit , fanout_success (unit , None )
15601604
15611605 # delayed or immediate JSON response
1562- return _process_delayed_json_response (unit , r , max_attempts = _delayed_result_max_attempts (timeout ))
1606+ return _process_delayed_json_response (
1607+ unit ,
1608+ address ,
1609+ r ,
1610+ max_attempts = _delayed_result_max_attempts (timeout ),
1611+ timeout = timeout ,
1612+ )
15631613
15641614 except (HTTPErrorStatus , HTTPException ) as e :
15651615 logger .debug (
@@ -1750,8 +1800,10 @@ def _get_from_unit(
17501800 # delayed or immediate JSON response
17511801 return _process_delayed_json_response (
17521802 unit ,
1803+ address ,
17531804 r ,
17541805 max_attempts = max_attempts if max_attempts is not None else _delayed_result_max_attempts (timeout ),
1806+ timeout = timeout ,
17551807 )
17561808
17571809 except (HTTPErrorStatus , HTTPException ) as e :
@@ -1874,7 +1926,13 @@ def patch_into_unit(
18741926 return unit , fanout_success (unit , None )
18751927
18761928 # delayed or immediate JSON response
1877- return _process_delayed_json_response (unit , r , max_attempts = _delayed_result_max_attempts (timeout ))
1929+ return _process_delayed_json_response (
1930+ unit ,
1931+ address ,
1932+ r ,
1933+ max_attempts = _delayed_result_max_attempts (timeout ),
1934+ timeout = timeout ,
1935+ )
18781936
18791937 except (HTTPErrorStatus , HTTPException ) as e :
18801938 logger .debug (
0 commit comments