code: def task_is_ready(
      session: api.Session,
      task_id: str,
      progress_report: Optional[Callable] = None,
      timeout: int = DEFAULT_NTSC_TIMEOUT,
  ) -> Optional[str]:
      """
      wait until a task is ready
      return: None if task is ready, otherwise return an error message
      """
  
      def _task_is_done_loading() -> Tuple[bool, Optional[str]]:
          task = bindings.get_GetTask(session, taskId=task_id).task
          if progress_report:
              progress_report()
          assert task is not None, "task must be present."
          if task.endTime is not None:
              return True, "task has been terminated."
  
          if len(task.allocations) == 0:
              return False, None
          is_ready = task.allocations[0].isReady
          if is_ready:
              return True, None
  
          return False, ""
  
      err_msg = util.wait_for(
          _task_is_done_loading, timeout=timeout, interval=1
      )
      return err_msg
  
file_path: determined/harness/determined/common/api/_util.py
imports: import enum
  from typing import Callable, Iterator, Optional, Tuple, TypeVar, Union
  import urllib3
  from determined.common import api, util
  from determined.common.api import bindings
label_desc: Use image node_id 5 for calling a global function with example usage: task_is_ready(session, task_id, progress_report, timeout) and returns: err_msg, False,  str, True,  str, False,  None, True,  None