예제 #1
0
 def _set_api_result(self, result):
     with self._condition:
         if result.state in FAILED:
             try:
                 err_msg = str(result['error'])
             except:
                 err_msg = str(result)
             self._set_api_exception(exc=CivisJobFailure(err_msg, result),
                                     result=result)
         elif result.state in DONE:
             self.set_result(result)
             self.cleanup()
예제 #2
0
    def _check_result(self):
        """Return the job result from Civis. Once the job completes, store the
        result and never poll again."""

        # If we haven't started the polling thread, do it now.
        if self._self_polling_executor is None and self._result is None:
            # Start a single thread continuously polling. It will stop once the
            # job completes.
            self._self_polling_executor = futures.ThreadPoolExecutor(1)
            self._self_polling_executor.submit(self._wait_for_completion)

        with self._condition:
            if self._result is not None:
                # If the job is already completed, just return the stored
                # result.
                return self._result

            # Check to see if the job has finished, but don't poll more
            # frequently than the requested polling frequency.
            now = time.time()
            if not self._last_polled or self._poll_wait_elapsed(now):
                # Poll for a new result
                self._last_polled = now
                try:
                    self._last_result = self._poller(*self._poller_args)
                except Exception as e:
                    # The _poller can raise API exceptions
                    # Set those directly as this Future's exception
                    self._result = Response({"state": FAILED[0]})
                    self._last_result = self._result
                    self.set_exception(e)
                else:
                    # If the job has finished, then register completion and
                    # store the results. Because of the `if self._result` check
                    # up top, we will never get here twice.
                    if self._last_result.state in FAILED:
                        try:
                            err_msg = str(self._last_result['error'])
                        except:
                            err_msg = str(self._last_result)
                        self.set_exception(
                            CivisJobFailure(err_msg, self._last_result))

                        self._result = self._last_result
                    elif self._last_result.state in DONE:
                        self.set_result(self._last_result)

            return self._last_result
예제 #3
0
 def _set_api_result(self, result):
     with self._condition:
         if result.state in FAILED:
             try:
                 err_msg = str(result['error'])
             except:  # NOQA
                 err_msg = str(result)
             job_id = getattr(self, "job_id", None)
             run_id = getattr(self, "run_id", None)
             self._set_api_exception(
                 exc=CivisJobFailure(err_msg, result, job_id, run_id),
                 result=result,
             )
         elif result.state in DONE:
             self.set_result(result)
             self.cleanup()
예제 #4
0
def test_result_exception_no_result(m_sleep):
    # If the job errored but didn't write an output, we should get
    # a generic TransportableException back.
    callback = mock.MagicMock()

    mock_client = create_client_mock_for_container_tests(1,
                                                         2,
                                                         state='failed',
                                                         run_outputs=[])
    fut = ContainerFuture(1, 2, client=mock_client)
    res = civis.parallel._CivisBackendResult(fut, callback)
    fut._set_api_exception(CivisJobFailure(Response({'state': 'failed'})))

    with pytest.raises(TransportableException) as exc:
        res.get()

    assert "{'state': 'failed'}" in str(exc.value)
    assert callback.call_count == 0
예제 #5
0
    def _exception_from_logs(self, exc, nlog=15):
        """Create an exception if the log has a recognizable error

        Search "error" emits in the last ``n_log`` lines.
        This function presently recognizes the following errors:

        - MemoryError
        """
        # Traceback in platform logs may be delayed for a few seconds.
        time.sleep(15)
        logs = self.client.jobs.list_runs_logs(self.job_id,
                                               self.run_id,
                                               limit=nlog)
        # Reverse order as logs come back in reverse chronological order.
        logs = logs[::-1]

        # Check for memory errors
        msgs = [x['message'] for x in logs if x['level'] == 'error']
        mem_err = [m for m in msgs if m.startswith('Process ran out of its')]
        if mem_err:
            err_msg = _err_msg_with_job_run_ids(mem_err[0], self.job_id,
                                                self.run_id)
            exc = MemoryError(err_msg)
        else:
            # Unknown error; return logs to the user as a sort of traceback
            all_logs = '\n'.join([x['message'] for x in logs])
            if isinstance(exc, CivisJobFailure):
                err_msg = _err_msg_with_job_run_ids(
                    all_logs + '\n' + exc.error_message, self.job_id,
                    self.run_id)
                exc.error_message = err_msg
            else:
                err_msg = _err_msg_with_job_run_ids(all_logs, self.job_id,
                                                    self.run_id)
                exc = CivisJobFailure("",
                                      job_id=self.job_id,
                                      run_id=self.run_id)
                exc.error_message = err_msg
        return exc
예제 #6
0
def _exception_from_logs(exc, job_id, run_id, client, nlog=15):
    """Create an exception if the log has a recognizable error

    Search "error" emits in the last ``n_log`` lines.
    This function presently recognizes the following errors:

    - MemoryError
    """
    logs = client.scripts.list_containers_runs_logs(job_id, run_id, limit=nlog)

    # Check for memory errors
    msgs = [l['message'] for l in logs if l['level'] == 'error']
    mem_err = [m for m in msgs if m.startswith('Process ran out of its')]
    if mem_err:
        exc = MemoryError(mem_err[0])
    else:
        # Unknown error; return logs to the user as a sort of traceback
        all_logs = '\n'.join([l['message'] for l in logs])
        if isinstance(exc, CivisJobFailure):
            exc.error_message = all_logs + '\n' + exc.error_message
        else:
            exc = CivisJobFailure(all_logs)
    return exc