Пример #1
0
    def _advance_tasklet(self, send_value=None, error=None):
        """Advance a tasklet one step by sending in a value or error."""
        try:
            with self.context.use():
                # Send the next value or exception into the generator
                if error:
                    self.generator.throw(type(error), error)

                # send_value will be None if this is the first time
                yielded = self.generator.send(send_value)

                # Context may have changed in tasklet
                self.context = context_module.get_context()

        except StopIteration as stop:
            # Generator has signalled exit, get the return value. This tasklet
            # has finished.
            self.set_result(_get_return_value(stop))
            return

        except Exception as error:
            # An error has occurred in the tasklet. This tasklet has finished.
            self.set_exception(error)
            return

        # This tasklet has yielded a value. We expect this to be a future
        # object (either NDB or gRPC) or a sequence of futures, in the case of
        # parallel yield.

        def done_callback(yielded):
            # To be called when a future dependency has completed.  Advance the
            # tasklet with the yielded value or error.
            #
            # It might be worth noting that legacy NDB added a callback to the
            # event loop which, in turn, called _help_tasklet_along. I don't
            # see a compelling reason not to go ahead and call _advance_tasklet
            # immediately here, rather than queue it up to be called soon by
            # the event loop. This is subject to change if the reason for the
            # indirection in the original implementation becomes apparent.
            error = yielded.exception()
            if error:
                self._advance_tasklet(error=error)
            else:
                self._advance_tasklet(yielded.result())

        if isinstance(yielded, Future):
            yielded.add_done_callback(done_callback)

        elif isinstance(yielded, _remote.RemoteCall):
            _eventloop.queue_rpc(yielded, done_callback)

        elif isinstance(yielded, (list, tuple)):
            future = _MultiFuture(yielded)
            future.add_done_callback(done_callback)

        else:
            raise RuntimeError(
                "A tasklet yielded an illegal value: {!r}".format(yielded))
Пример #2
0
    def _advance_tasklet(self, send_value=None, error=None):
        """Advance a tasklet one step by sending in a value or error."""
        try:
            with self.context.use():
                # Send the next value or exception into the generator
                if error:
                    self.generator.throw(type(error), error)

                # send_value will be None if this is the first time
                yielded = self.generator.send(send_value)

                # Context may have changed in tasklet
                self.context = context_module.get_context()

        except StopIteration as stop:
            # Generator has signalled exit, get the return value. This tasklet
            # has finished.
            self.set_result(_get_return_value(stop))
            return

        except Exception as error:
            # An error has occurred in the tasklet. This tasklet has finished.
            self.set_exception(error)
            return

        # This tasklet has yielded a value. We expect this to be a future
        # object (either NDB or gRPC) or a sequence of futures, in the case of
        # parallel yield.

        def done_callback(yielded):
            # To be called when a future dependency has completed.  Advance the
            # tasklet with the yielded value or error.
            #
            # It was tempting to call `_advance_tasklet` (`_help_tasklet_along`
            # in Legacy) directly. Doing so, it has been found, can lead to
            # exceeding the maximum recursion depth. Queing it up to run on the
            # event loop avoids this issue by keeping the call stack shallow.
            error = yielded.exception()
            if error:
                _eventloop.call_soon(self._advance_tasklet, error=error)
            else:
                _eventloop.call_soon(self._advance_tasklet, yielded.result())

        if isinstance(yielded, Future):
            yielded.add_done_callback(done_callback)

        elif isinstance(yielded, _remote.RemoteCall):
            _eventloop.queue_rpc(yielded, done_callback)

        elif isinstance(yielded, (list, tuple)):
            future = _MultiFuture(yielded)
            future.add_done_callback(done_callback)

        else:
            raise RuntimeError(
                "A tasklet yielded an illegal value: {!r}".format(yielded))
Пример #3
0
    def idle_callback(self):
        """Perform a Datastore Lookup on all batched Lookup requests."""
        keys = []
        for todo_key in self.todo.keys():
            key_pb = entity_pb2.Key()
            key_pb.ParseFromString(todo_key)
            keys.append(key_pb)

        read_options = _get_read_options(self.options)
        rpc = _datastore_lookup(keys, read_options)
        _eventloop.queue_rpc(rpc, self.lookup_callback)
Пример #4
0
def _perform_batch_lookup():
    """Perform a Datastore Lookup on all batched Lookup requests.

    Meant to be used as an idle callback, so that calls to lookup entities can
    be batched into a single request to the back end service as soon as running
    code has need of one of the results.
    """
    state = _runstate.current()
    batch = state.batches.pop(_BATCH_LOOKUP, None)
    if batch is None:
        return

    rpc = _datastore_lookup(batch.keys())
    _eventloop.queue_rpc(rpc, BatchLookupCallback(batch))
    def _advance_tasklet(self, send_value=None, error=None):
        """Advance a tasklet one step by sending in a value or error."""
        try:
            # Send the next value or exception into the generator
            if error:
                self.generator.throw(type(error), error)

            # send_value will be None if this is the first time
            yielded = self.generator.send(send_value)

        except StopIteration as stop:
            # Generator has signalled exit, get the return value. This tasklet
            # has finished.
            self.set_result(_get_return_value(stop))
            return

        except Exception as error:
            # An error has occurred in the tasklet. This tasklet has finished.
            self.set_exception(error)
            return

        # This tasklet has yielded a value. We expect this to be a future
        # object (either NDB or gRPC) or a sequence of futures, in the case of
        # parallel yield.

        def done_callback(yielded):
            # To be called when a future dependency has completed.
            # Advance the tasklet with the yielded value or error.
            error = yielded.exception()
            if error:
                self._advance_tasklet(error=error)
            else:
                self._advance_tasklet(yielded.result())

        if isinstance(yielded, Future):
            yielded.add_done_callback(done_callback)

        elif isinstance(yielded, grpc.Future):
            _eventloop.queue_rpc(yielded, done_callback)

        elif isinstance(yielded, (list, tuple)):
            future = MultiFuture(yielded)
            future.add_done_callback(done_callback)

        else:
            raise RuntimeError(
                "A tasklet yielded an illegal value: {!r}".format(yielded))
Пример #6
0
def test_queue_rpc(context):
    loop = mock.Mock(spec=("run", "queue_rpc"))
    with context.new(eventloop=loop).use():
        _eventloop.queue_rpc("foo", "bar")
        loop.queue_rpc.assert_called_once_with("foo", "bar")
def test_queue_rpc(EventLoop):
    EventLoop.return_value = loop = unittest.mock.Mock(spec=("run",
                                                             "queue_rpc"))
    with _runstate.state_context(None):
        _eventloop.queue_rpc("foo", "bar")
        loop.queue_rpc.assert_called_once_with("foo", "bar")
Пример #8
0
def test_queue_rpc():
    with pytest.raises(NotImplementedError):
        eventloop.queue_rpc()
Пример #9
0
 def idle_callback(self):
     """Send the commit for this batch to Datastore."""
     rpc = _datastore_commit(self.mutations, _get_transaction(self.options))
     _eventloop.queue_rpc(rpc, self.commit_callback)
Пример #10
0
def test_queue_rpc():
    with pytest.raises(NotImplementedError):
        _eventloop.queue_rpc()