Пример #1
0
    def await_done(self, timeout=None):
        """
        Await completion of connection-observer.

        CAUTION: if you call it from asynchronous code (async def) you may block events loop for long time.
        You should rather await it via:
        result = await connection_observer
        or (to have timeout)
        result = await asyncio.wait_for(connection_observer, timeout=10)
        or you may delegate blocking call execution to separate thread,
        see: https://pymotw.com/3/asyncio/executors.html

        :param timeout:
        :return: observer result
        """
        if self.done():
            return self.result()
        with exception_stored_if_not_main_thread(self):
            if not self.life_status._is_running:
                raise ConnectionObserverNotStarted(self)
            # check if already is running
            self.runner.wait_for(connection_observer=self,
                                 connection_observer_future=self._future,
                                 timeout=timeout)
        return self.result()
Пример #2
0
 def start(self, timeout=None, *args, **kwargs):
     """Start background execution of connection-observer."""
     with exception_stored_if_not_main_thread(self):
         if timeout:
             self.timeout = timeout
         self._validate_start(*args, **kwargs)
         # After start we treat it as started even if it's underlying
         # parallelism machinery (threads, coroutines, ...) has not started yet
         # (thread didn't get control, coro didn't start in async-loop)
         # That is so, since observer lifetime starts with it's timeout-clock
         # and timeout is counted from calling observer.start()
         self._is_running = True
         self.start_time = time.time()
         # Besides not started parallelism machinery causing start-delay
         # we can have start-delay caused by commands queue on connection
         # (can't submit command to background-run till previous stops running)
         CommandScheduler.enqueue_starting_on_connection(connection_observer=self)
         # Above line will set self._future when it is possible to submit
         # observer to background-run (observer not command, or empty commands queue)
         # or setting self._future will be delayed by nonempty commands queue.
     return self
Пример #3
0
    def wait_for(self,
                 connection_observer,
                 connection_observer_future,
                 timeout=None):
        """
        Await for connection_observer running in background or timeout.

        :param connection_observer: The one we are awaiting for.
        :param connection_observer_future: Future of connection-observer returned from submit().
        :param timeout: Max time (in float seconds) to await before give up. If None then taken from connection_observer
        :return:
        """
        if connection_observer.done():
            # 1. done() might mean "timed out" before future created (future is None)
            #    Observer lifetime started with its timeout clock so, it might timeout even before
            #    future created by runner.submit() - may happen for nonempty commands queue
            # 2. done() might mean "timed out" before future start
            #    Observer lifetime started with its timeout clock so, it might timeout even before
            #    connection_observer_future started - since future's coro might not get control yet
            # 3. done() might mean "timed out" before wait_for()
            #    wait_for() might be called so late after submit() that observer already "timed out"
            # 4. done() might mean have result or got exception
            #    wait_for() might be called so late after submit() that observer already got result/exception
            #
            # In all above cases we want to stop future if it is still running
            self.logger.debug("go foreground: {} is already done".format(
                connection_observer))
            self._cancel_submitted_future(connection_observer,
                                          connection_observer_future)
            return None

        max_timeout = timeout
        observer_timeout = connection_observer.timeout
        # we count timeout from now if timeout is given; else we use .start_time and .timeout of observer
        start_time = time.time(
        ) if max_timeout else connection_observer.start_time
        await_timeout = max_timeout if max_timeout else observer_timeout
        if max_timeout:
            remain_time, msg = his_remaining_time("await max.",
                                                  timeout=max_timeout,
                                                  from_start_time=start_time)
        else:
            remain_time, msg = his_remaining_time("remaining",
                                                  timeout=observer_timeout,
                                                  from_start_time=start_time)

        self.logger.debug("go foreground: {} - {}".format(
            connection_observer, msg))
        event_loop, its_new = thread_secure_get_event_loop()
        assert not its_new  # should not happen since submit() is called first

        with exception_stored_if_not_main_thread(connection_observer,
                                                 logger=self.logger):
            try:
                # we might be already called from within running event loop
                # or we are just in synchronous code
                if event_loop.is_running():
                    # wait_for() should not be called from 'async def'
                    self._raise_wrong_usage_of_wait_for(connection_observer)

                if connection_observer_future is None:
                    end_of_life, remain_time = await_future_or_eol(
                        connection_observer, remain_time, start_time,
                        await_timeout, self.logger)
                    if end_of_life:
                        return None
                    if remain_time <= 0.0:
                        raise asyncio.futures.TimeoutError()
                    connection_observer_future = connection_observer._future
                    assert connection_observer_future is not None

                self._run_via_asyncio(event_loop, connection_observer_future,
                                      max_timeout, remain_time)

            except asyncio.futures.CancelledError:
                self.logger.debug("canceled {}".format(connection_observer))
                connection_observer.cancel()
            except asyncio.futures.TimeoutError:
                self._wait_for_time_out(connection_observer,
                                        connection_observer_future,
                                        timeout=await_timeout)
            finally:
                self._cancel_submitted_future(connection_observer,
                                              connection_observer_future)
        return None