mirror of https://github.com/grpc/grpc.git
This code has been unsupported for more than six months.pull/6331/head
parent
e2fd65343e
commit
cab9470ee8
51 changed files with 0 additions and 10522 deletions
@ -1,363 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" |
||||
|
||||
import enum |
||||
import logging |
||||
import threading |
||||
import time |
||||
|
||||
from grpc._adapter import _common |
||||
from grpc._adapter import _intermediary_low as _low |
||||
from grpc.framework.base import interfaces as base_interfaces |
||||
from grpc.framework.base import null |
||||
from grpc.framework.foundation import activated |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
_THREAD_POOL_SIZE = 10 |
||||
|
||||
|
||||
@enum.unique |
||||
class _LowWrite(enum.Enum): |
||||
"""The possible categories of low-level write state.""" |
||||
|
||||
OPEN = 'OPEN' |
||||
ACTIVE = 'ACTIVE' |
||||
CLOSED = 'CLOSED' |
||||
|
||||
|
||||
def _write(call, rpc_state, payload): |
||||
serialized_payload = rpc_state.serializer(payload) |
||||
if rpc_state.write.low is _LowWrite.OPEN: |
||||
call.write(serialized_payload, call, 0) |
||||
rpc_state.write.low = _LowWrite.ACTIVE |
||||
else: |
||||
rpc_state.write.pending.append(serialized_payload) |
||||
|
||||
|
||||
def _status(call, rpc_state): |
||||
call.status(_low.Status(_low.Code.OK, ''), call) |
||||
rpc_state.write.low = _LowWrite.CLOSED |
||||
|
||||
|
||||
class ForeLink(base_interfaces.ForeLink, activated.Activated): |
||||
"""A service-side bridge between RPC Framework and the C-ish _low code.""" |
||||
|
||||
def __init__( |
||||
self, pool, request_deserializers, response_serializers, |
||||
root_certificates, key_chain_pairs, port=None): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
pool: A thread pool. |
||||
request_deserializers: A dict from RPC method names to request object |
||||
deserializer behaviors. |
||||
response_serializers: A dict from RPC method names to response object |
||||
serializer behaviors. |
||||
root_certificates: The PEM-encoded client root certificates as a |
||||
bytestring or None. |
||||
key_chain_pairs: A sequence of PEM-encoded private key-certificate chain |
||||
pairs. |
||||
port: The port on which to serve, or None to have a port selected |
||||
automatically. |
||||
""" |
||||
self._condition = threading.Condition() |
||||
self._pool = pool |
||||
self._request_deserializers = request_deserializers |
||||
self._response_serializers = response_serializers |
||||
self._root_certificates = root_certificates |
||||
self._key_chain_pairs = key_chain_pairs |
||||
self._requested_port = port |
||||
|
||||
self._rear_link = null.NULL_REAR_LINK |
||||
self._completion_queue = None |
||||
self._server = None |
||||
self._rpc_states = {} |
||||
self._spinning = False |
||||
self._port = None |
||||
|
||||
def _on_stop_event(self): |
||||
self._spinning = False |
||||
self._condition.notify_all() |
||||
|
||||
def _on_service_acceptance_event(self, event, server): |
||||
"""Handle a service invocation event.""" |
||||
service_acceptance = event.service_acceptance |
||||
if service_acceptance is None: |
||||
return |
||||
|
||||
call = service_acceptance.call |
||||
call.accept(self._completion_queue, call) |
||||
# TODO(nathaniel): Metadata support. |
||||
call.premetadata() |
||||
call.read(call) |
||||
method = service_acceptance.method |
||||
|
||||
self._rpc_states[call] = _common.CommonRPCState( |
||||
_common.WriteState(_LowWrite.OPEN, _common.HighWrite.OPEN, []), 1, |
||||
self._request_deserializers[method], |
||||
self._response_serializers[method]) |
||||
|
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, 0, base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT, method, |
||||
base_interfaces.ServicedSubscription.Kind.FULL, None, None, |
||||
service_acceptance.deadline - time.time()) |
||||
self._rear_link.accept_front_to_back_ticket(ticket) |
||||
|
||||
server.service(None) |
||||
|
||||
def _on_read_event(self, event): |
||||
"""Handle data arriving during an RPC.""" |
||||
call = event.tag |
||||
rpc_state = self._rpc_states.get(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
sequence_number = rpc_state.sequence_number |
||||
rpc_state.sequence_number += 1 |
||||
if event.bytes is None: |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, |
||||
None, None) |
||||
else: |
||||
call.read(call) |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, |
||||
None, rpc_state.deserializer(event.bytes), None) |
||||
|
||||
self._rear_link.accept_front_to_back_ticket(ticket) |
||||
|
||||
def _on_write_event(self, event): |
||||
call = event.tag |
||||
rpc_state = self._rpc_states.get(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
if rpc_state.write.pending: |
||||
serialized_payload = rpc_state.write.pending.pop(0) |
||||
call.write(serialized_payload, call, 0) |
||||
elif rpc_state.write.high is _common.HighWrite.CLOSED: |
||||
_status(call, rpc_state) |
||||
else: |
||||
rpc_state.write.low = _LowWrite.OPEN |
||||
|
||||
def _on_complete_event(self, event): |
||||
if not event.complete_accepted: |
||||
logging.error('Complete not accepted! %s', (event,)) |
||||
call = event.tag |
||||
rpc_state = self._rpc_states.pop(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
sequence_number = rpc_state.sequence_number |
||||
rpc_state.sequence_number += 1 |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, |
||||
None, None, None, None) |
||||
self._rear_link.accept_front_to_back_ticket(ticket) |
||||
|
||||
def _on_finish_event(self, event): |
||||
"""Handle termination of an RPC.""" |
||||
call = event.tag |
||||
rpc_state = self._rpc_states.pop(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
code = event.status.code |
||||
if code is _low.Code.OK: |
||||
return |
||||
|
||||
sequence_number = rpc_state.sequence_number |
||||
rpc_state.sequence_number += 1 |
||||
if code is _low.Code.CANCELLED: |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None, |
||||
None, None, None) |
||||
elif code is _low.Code.DEADLINE_EXCEEDED: |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None, |
||||
None, None) |
||||
else: |
||||
# TODO(nathaniel): Better mapping of codes to ticket-categories |
||||
ticket = base_interfaces.FrontToBackTicket( |
||||
call, sequence_number, |
||||
base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, |
||||
None, None, None, None) |
||||
self._rear_link.accept_front_to_back_ticket(ticket) |
||||
|
||||
def _spin(self, completion_queue, server): |
||||
while True: |
||||
event = completion_queue.get(None) |
||||
|
||||
with self._condition: |
||||
if event.kind is _low.Event.Kind.STOP: |
||||
self._on_stop_event() |
||||
return |
||||
elif self._server is None: |
||||
continue |
||||
elif event.kind is _low.Event.Kind.SERVICE_ACCEPTED: |
||||
self._on_service_acceptance_event(event, server) |
||||
elif event.kind is _low.Event.Kind.READ_ACCEPTED: |
||||
self._on_read_event(event) |
||||
elif event.kind is _low.Event.Kind.WRITE_ACCEPTED: |
||||
self._on_write_event(event) |
||||
elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: |
||||
self._on_complete_event(event) |
||||
elif event.kind is _low.Event.Kind.FINISH: |
||||
self._on_finish_event(event) |
||||
else: |
||||
logging.error('Illegal event! %s', (event,)) |
||||
|
||||
def _continue(self, call, payload): |
||||
rpc_state = self._rpc_states.get(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
_write(call, rpc_state, payload) |
||||
|
||||
def _complete(self, call, payload): |
||||
"""Handle completion of the writes of an RPC.""" |
||||
rpc_state = self._rpc_states.get(call, None) |
||||
if rpc_state is None: |
||||
return |
||||
|
||||
if rpc_state.write.low is _LowWrite.OPEN: |
||||
if payload is None: |
||||
_status(call, rpc_state) |
||||
else: |
||||
_write(call, rpc_state, payload) |
||||
elif rpc_state.write.low is _LowWrite.ACTIVE: |
||||
if payload is not None: |
||||
rpc_state.write.pending.append(rpc_state.serializer(payload)) |
||||
else: |
||||
raise ValueError('Called to complete after having already completed!') |
||||
rpc_state.write.high = _common.HighWrite.CLOSED |
||||
|
||||
def _cancel(self, call): |
||||
call.cancel() |
||||
self._rpc_states.pop(call, None) |
||||
|
||||
def join_rear_link(self, rear_link): |
||||
"""See base_interfaces.ForeLink.join_rear_link for specification.""" |
||||
self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link |
||||
|
||||
def _start(self): |
||||
"""Starts this ForeLink. |
||||
|
||||
This method must be called before attempting to exchange tickets with this |
||||
object. |
||||
""" |
||||
with self._condition: |
||||
address = '[::]:%d' % ( |
||||
0 if self._requested_port is None else self._requested_port) |
||||
self._completion_queue = _low.CompletionQueue() |
||||
if self._root_certificates is None and not self._key_chain_pairs: |
||||
self._server = _low.Server(self._completion_queue) |
||||
self._port = self._server.add_http2_addr(address) |
||||
else: |
||||
server_credentials = _low.ServerCredentials( |
||||
self._root_certificates, self._key_chain_pairs, False) |
||||
self._server = _low.Server(self._completion_queue) |
||||
self._port = self._server.add_secure_http2_addr( |
||||
address, server_credentials) |
||||
self._server.start() |
||||
|
||||
self._server.service(None) |
||||
|
||||
self._pool.submit(self._spin, self._completion_queue, self._server) |
||||
self._spinning = True |
||||
|
||||
return self |
||||
|
||||
# TODO(nathaniel): Expose graceful-shutdown semantics in which this object |
||||
# enters a state in which it finishes ongoing RPCs but refuses new ones. |
||||
def _stop(self): |
||||
"""Stops this ForeLink. |
||||
|
||||
This method must be called for proper termination of this object, and no |
||||
attempts to exchange tickets with this object may be made after this method |
||||
has been called. |
||||
""" |
||||
with self._condition: |
||||
self._server.stop() |
||||
# TODO(nathaniel): Yep, this is weird. Deleting a server shouldn't have a |
||||
# behaviorally significant side-effect. |
||||
self._server = None |
||||
self._completion_queue.stop() |
||||
|
||||
while self._spinning: |
||||
self._condition.wait() |
||||
|
||||
self._port = None |
||||
|
||||
def __enter__(self): |
||||
"""See activated.Activated.__enter__ for specification.""" |
||||
return self._start() |
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): |
||||
"""See activated.Activated.__exit__ for specification.""" |
||||
self._stop() |
||||
return False |
||||
|
||||
def start(self): |
||||
"""See activated.Activated.start for specification.""" |
||||
return self._start() |
||||
|
||||
def stop(self): |
||||
"""See activated.Activated.stop for specification.""" |
||||
self._stop() |
||||
|
||||
def port(self): |
||||
"""Identifies the port on which this ForeLink is servicing RPCs. |
||||
|
||||
Returns: |
||||
The number of the port on which this ForeLink is servicing RPCs, or None |
||||
if this ForeLink is not currently activated and servicing RPCs. |
||||
""" |
||||
with self._condition: |
||||
return self._port |
||||
|
||||
def accept_back_to_front_ticket(self, ticket): |
||||
"""See base_interfaces.ForeLink.accept_back_to_front_ticket for spec.""" |
||||
with self._condition: |
||||
if self._server is None: |
||||
return |
||||
|
||||
if ticket.kind is base_interfaces.BackToFrontTicket.Kind.CONTINUATION: |
||||
self._continue(ticket.operation_id, ticket.payload) |
||||
elif ticket.kind is base_interfaces.BackToFrontTicket.Kind.COMPLETION: |
||||
self._complete(ticket.operation_id, ticket.payload) |
||||
else: |
||||
self._cancel(ticket.operation_id) |
@ -1,395 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" |
||||
|
||||
import enum |
||||
import logging |
||||
import threading |
||||
import time |
||||
|
||||
from grpc._adapter import _common |
||||
from grpc._adapter import _intermediary_low as _low |
||||
from grpc.framework.base import interfaces as base_interfaces |
||||
from grpc.framework.base import null |
||||
from grpc.framework.foundation import activated |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
_THREAD_POOL_SIZE = 10 |
||||
|
||||
_INVOCATION_EVENT_KINDS = ( |
||||
_low.Event.Kind.METADATA_ACCEPTED, |
||||
_low.Event.Kind.FINISH |
||||
) |
||||
|
||||
|
||||
@enum.unique |
||||
class _LowWrite(enum.Enum): |
||||
"""The possible categories of low-level write state.""" |
||||
|
||||
OPEN = 'OPEN' |
||||
ACTIVE = 'ACTIVE' |
||||
CLOSED = 'CLOSED' |
||||
|
||||
|
||||
class _RPCState(object): |
||||
"""The full state of any tracked RPC. |
||||
|
||||
Attributes: |
||||
call: The _low.Call object for the RPC. |
||||
outstanding: The set of Event.Kind values describing expected future events |
||||
for the RPC. |
||||
active: A boolean indicating whether or not the RPC is active. |
||||
common: An _common.RPCState describing additional state for the RPC. |
||||
""" |
||||
|
||||
def __init__(self, call, outstanding, active, common): |
||||
self.call = call |
||||
self.outstanding = outstanding |
||||
self.active = active |
||||
self.common = common |
||||
|
||||
|
||||
def _write(operation_id, call, outstanding, write_state, serialized_payload): |
||||
if write_state.low is _LowWrite.OPEN: |
||||
call.write(serialized_payload, operation_id, 0) |
||||
outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) |
||||
write_state.low = _LowWrite.ACTIVE |
||||
elif write_state.low is _LowWrite.ACTIVE: |
||||
write_state.pending.append(serialized_payload) |
||||
else: |
||||
raise ValueError('Write attempted after writes completed!') |
||||
|
||||
|
||||
class RearLink(base_interfaces.RearLink, activated.Activated): |
||||
"""An invocation-side bridge between RPC Framework and the C-ish _low code.""" |
||||
|
||||
def __init__( |
||||
self, host, port, pool, request_serializers, response_deserializers, |
||||
secure, root_certificates, private_key, certificate_chain, |
||||
metadata_transformer=None, server_host_override=None): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
host: The host to which to connect for RPC service. |
||||
port: The port to which to connect for RPC service. |
||||
pool: A thread pool. |
||||
request_serializers: A dict from RPC method names to request object |
||||
serializer behaviors. |
||||
response_deserializers: A dict from RPC method names to response object |
||||
deserializer behaviors. |
||||
secure: A boolean indicating whether or not to use a secure connection. |
||||
root_certificates: The PEM-encoded root certificates or None to ask for |
||||
them to be retrieved from a default location. |
||||
private_key: The PEM-encoded private key to use or None if no private |
||||
key should be used. |
||||
certificate_chain: The PEM-encoded certificate chain to use or None if |
||||
no certificate chain should be used. |
||||
metadata_transformer: A function that given a metadata object produces |
||||
another metadata to be used in the underlying communication on the |
||||
wire. |
||||
server_host_override: (For testing only) the target name used for SSL |
||||
host name checking. |
||||
""" |
||||
self._condition = threading.Condition() |
||||
self._host = host |
||||
self._port = port |
||||
self._pool = pool |
||||
self._request_serializers = request_serializers |
||||
self._response_deserializers = response_deserializers |
||||
|
||||
self._fore_link = null.NULL_FORE_LINK |
||||
self._completion_queue = None |
||||
self._channel = None |
||||
self._rpc_states = {} |
||||
self._spinning = False |
||||
if secure: |
||||
self._client_credentials = _low.ClientCredentials( |
||||
root_certificates, private_key, certificate_chain) |
||||
else: |
||||
self._client_credentials = None |
||||
self._root_certificates = root_certificates |
||||
self._private_key = private_key |
||||
self._certificate_chain = certificate_chain |
||||
self._metadata_transformer = metadata_transformer |
||||
self._server_host_override = server_host_override |
||||
|
||||
def _on_write_event(self, operation_id, event, rpc_state): |
||||
if event.write_accepted: |
||||
if rpc_state.common.write.pending: |
||||
rpc_state.call.write( |
||||
rpc_state.common.write.pending.pop(0), operation_id, 0) |
||||
rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) |
||||
elif rpc_state.common.write.high is _common.HighWrite.CLOSED: |
||||
rpc_state.call.complete(operation_id) |
||||
rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) |
||||
rpc_state.common.write.low = _LowWrite.CLOSED |
||||
else: |
||||
rpc_state.common.write.low = _LowWrite.OPEN |
||||
else: |
||||
logging.error('RPC write not accepted! Event: %s', (event,)) |
||||
rpc_state.active = False |
||||
ticket = base_interfaces.BackToFrontTicket( |
||||
operation_id, rpc_state.common.sequence_number, |
||||
base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) |
||||
rpc_state.common.sequence_number += 1 |
||||
self._fore_link.accept_back_to_front_ticket(ticket) |
||||
|
||||
def _on_read_event(self, operation_id, event, rpc_state): |
||||
if event.bytes is not None: |
||||
rpc_state.call.read(operation_id) |
||||
rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) |
||||
|
||||
ticket = base_interfaces.BackToFrontTicket( |
||||
operation_id, rpc_state.common.sequence_number, |
||||
base_interfaces.BackToFrontTicket.Kind.CONTINUATION, |
||||
rpc_state.common.deserializer(event.bytes)) |
||||
rpc_state.common.sequence_number += 1 |
||||
self._fore_link.accept_back_to_front_ticket(ticket) |
||||
|
||||
def _on_complete_event(self, operation_id, event, rpc_state): |
||||
if not event.complete_accepted: |
||||
logging.error('RPC complete not accepted! Event: %s', (event,)) |
||||
rpc_state.active = False |
||||
ticket = base_interfaces.BackToFrontTicket( |
||||
operation_id, rpc_state.common.sequence_number, |
||||
base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) |
||||
rpc_state.common.sequence_number += 1 |
||||
self._fore_link.accept_back_to_front_ticket(ticket) |
||||
|
||||
# TODO(nathaniel): Metadata support. |
||||
def _on_metadata_event(self, operation_id, event, rpc_state): # pylint: disable=unused-argument |
||||
rpc_state.call.read(operation_id) |
||||
rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) |
||||
|
||||
def _on_finish_event(self, operation_id, event, rpc_state): |
||||
"""Handle termination of an RPC.""" |
||||
# TODO(nathaniel): Cover all statuses. |
||||
if event.status.code is _low.Code.OK: |
||||
kind = base_interfaces.BackToFrontTicket.Kind.COMPLETION |
||||
elif event.status.code is _low.Code.CANCELLED: |
||||
kind = base_interfaces.BackToFrontTicket.Kind.CANCELLATION |
||||
elif event.status.code is _low.Code.DEADLINE_EXCEEDED: |
||||
kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION |
||||
else: |
||||
kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE |
||||
ticket = base_interfaces.BackToFrontTicket( |
||||
operation_id, rpc_state.common.sequence_number, kind, None) |
||||
rpc_state.common.sequence_number += 1 |
||||
self._fore_link.accept_back_to_front_ticket(ticket) |
||||
|
||||
def _spin(self, completion_queue): |
||||
while True: |
||||
event = completion_queue.get(None) |
||||
operation_id = event.tag |
||||
|
||||
with self._condition: |
||||
rpc_state = self._rpc_states[operation_id] |
||||
rpc_state.outstanding.remove(event.kind) |
||||
if rpc_state.active and self._completion_queue is not None: |
||||
if event.kind is _low.Event.Kind.WRITE_ACCEPTED: |
||||
self._on_write_event(operation_id, event, rpc_state) |
||||
elif event.kind is _low.Event.Kind.METADATA_ACCEPTED: |
||||
self._on_metadata_event(operation_id, event, rpc_state) |
||||
elif event.kind is _low.Event.Kind.READ_ACCEPTED: |
||||
self._on_read_event(operation_id, event, rpc_state) |
||||
elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: |
||||
self._on_complete_event(operation_id, event, rpc_state) |
||||
elif event.kind is _low.Event.Kind.FINISH: |
||||
self._on_finish_event(operation_id, event, rpc_state) |
||||
else: |
||||
logging.error('Illegal RPC event! %s', (event,)) |
||||
|
||||
if not rpc_state.outstanding: |
||||
self._rpc_states.pop(operation_id) |
||||
if not self._rpc_states: |
||||
self._spinning = False |
||||
self._condition.notify_all() |
||||
return |
||||
|
||||
def _invoke(self, operation_id, name, high_state, payload, timeout): |
||||
"""Invoke an RPC. |
||||
|
||||
Args: |
||||
operation_id: Any object to be used as an operation ID for the RPC. |
||||
name: The RPC method name. |
||||
high_state: A _common.HighWrite value representing the "high write state" |
||||
of the RPC. |
||||
payload: A payload object for the RPC or None if no payload was given at |
||||
invocation-time. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
""" |
||||
request_serializer = self._request_serializers[name] |
||||
call = _low.Call(self._channel, self._completion_queue, name, self._host, time.time() + timeout) |
||||
if self._metadata_transformer is not None: |
||||
metadata = self._metadata_transformer([]) |
||||
for metadata_key, metadata_value in metadata: |
||||
call.add_metadata(metadata_key, metadata_value) |
||||
call.invoke(self._completion_queue, operation_id, operation_id) |
||||
outstanding = set(_INVOCATION_EVENT_KINDS) |
||||
|
||||
if payload is None: |
||||
if high_state is _common.HighWrite.CLOSED: |
||||
call.complete(operation_id) |
||||
low_state = _LowWrite.CLOSED |
||||
outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) |
||||
else: |
||||
low_state = _LowWrite.OPEN |
||||
else: |
||||
serialized_payload = request_serializer(payload) |
||||
call.write(serialized_payload, operation_id, 0) |
||||
outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) |
||||
low_state = _LowWrite.ACTIVE |
||||
|
||||
write_state = _common.WriteState(low_state, high_state, []) |
||||
common_state = _common.CommonRPCState( |
||||
write_state, 0, self._response_deserializers[name], request_serializer) |
||||
self._rpc_states[operation_id] = _RPCState( |
||||
call, outstanding, True, common_state) |
||||
|
||||
if not self._spinning: |
||||
self._pool.submit(self._spin, self._completion_queue) |
||||
self._spinning = True |
||||
|
||||
def _commence(self, operation_id, name, payload, timeout): |
||||
self._invoke(operation_id, name, _common.HighWrite.OPEN, payload, timeout) |
||||
|
||||
def _continue(self, operation_id, payload): |
||||
rpc_state = self._rpc_states.get(operation_id, None) |
||||
if rpc_state is None or not rpc_state.active: |
||||
return |
||||
|
||||
_write( |
||||
operation_id, rpc_state.call, rpc_state.outstanding, |
||||
rpc_state.common.write, rpc_state.common.serializer(payload)) |
||||
|
||||
def _complete(self, operation_id, payload): |
||||
"""Close writes associated with an ongoing RPC. |
||||
|
||||
Args: |
||||
operation_id: Any object being use as an operation ID for the RPC. |
||||
payload: A payload object for the RPC (and thus the last payload object |
||||
for the RPC) or None if no payload was given along with the instruction |
||||
to indicate the end of writes for the RPC. |
||||
""" |
||||
rpc_state = self._rpc_states.get(operation_id, None) |
||||
if rpc_state is None or not rpc_state.active: |
||||
return |
||||
|
||||
write_state = rpc_state.common.write |
||||
if payload is None: |
||||
if write_state.low is _LowWrite.OPEN: |
||||
rpc_state.call.complete(operation_id) |
||||
rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) |
||||
write_state.low = _LowWrite.CLOSED |
||||
else: |
||||
_write( |
||||
operation_id, rpc_state.call, rpc_state.outstanding, write_state, |
||||
rpc_state.common.serializer(payload)) |
||||
write_state.high = _common.HighWrite.CLOSED |
||||
|
||||
def _entire(self, operation_id, name, payload, timeout): |
||||
self._invoke(operation_id, name, _common.HighWrite.CLOSED, payload, timeout) |
||||
|
||||
def _cancel(self, operation_id): |
||||
rpc_state = self._rpc_states.get(operation_id, None) |
||||
if rpc_state is not None and rpc_state.active: |
||||
rpc_state.call.cancel() |
||||
rpc_state.active = False |
||||
|
||||
def join_fore_link(self, fore_link): |
||||
"""See base_interfaces.RearLink.join_fore_link for specification.""" |
||||
with self._condition: |
||||
self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link |
||||
|
||||
def _start(self): |
||||
"""Starts this RearLink. |
||||
|
||||
This method must be called before attempting to exchange tickets with this |
||||
object. |
||||
""" |
||||
with self._condition: |
||||
self._completion_queue = _low.CompletionQueue() |
||||
self._channel = _low.Channel( |
||||
'%s:%d' % (self._host, self._port), self._client_credentials, |
||||
server_host_override=self._server_host_override) |
||||
return self |
||||
|
||||
def _stop(self): |
||||
"""Stops this RearLink. |
||||
|
||||
This method must be called for proper termination of this object, and no |
||||
attempts to exchange tickets with this object may be made after this method |
||||
has been called. |
||||
""" |
||||
with self._condition: |
||||
self._completion_queue.stop() |
||||
self._completion_queue = None |
||||
|
||||
while self._spinning: |
||||
self._condition.wait() |
||||
|
||||
def __enter__(self): |
||||
"""See activated.Activated.__enter__ for specification.""" |
||||
return self._start() |
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): |
||||
"""See activated.Activated.__exit__ for specification.""" |
||||
self._stop() |
||||
return False |
||||
|
||||
def start(self): |
||||
"""See activated.Activated.start for specification.""" |
||||
return self._start() |
||||
|
||||
def stop(self): |
||||
"""See activated.Activated.stop for specification.""" |
||||
self._stop() |
||||
|
||||
def accept_front_to_back_ticket(self, ticket): |
||||
"""See base_interfaces.RearLink.accept_front_to_back_ticket for spec.""" |
||||
with self._condition: |
||||
if self._completion_queue is None: |
||||
return |
||||
|
||||
if ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT: |
||||
self._commence( |
||||
ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) |
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CONTINUATION: |
||||
self._continue(ticket.operation_id, ticket.payload) |
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMPLETION: |
||||
self._complete(ticket.operation_id, ticket.payload) |
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.ENTIRE: |
||||
self._entire( |
||||
ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) |
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CANCELLATION: |
||||
self._cancel(ticket.operation_id) |
||||
else: |
||||
# NOTE(nathaniel): All other categories are treated as cancellation. |
||||
self._cancel(ticket.operation_id) |
@ -1,35 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import warnings |
||||
|
||||
warnings.simplefilter('always', DeprecationWarning) |
||||
warnings.warn('the alpha API (includes this package) is deprecated, ' |
||||
'unmaintained, and no longer tested. Please migrate to the beta ' |
||||
'API.', DeprecationWarning, stacklevel=2) |
@ -1,262 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Entry points into GRPC.""" |
||||
|
||||
import threading |
||||
|
||||
from grpc._adapter import fore as _fore |
||||
from grpc._adapter import rear as _rear |
||||
from grpc.framework.alpha import _face_utilities |
||||
from grpc.framework.alpha import _reexport |
||||
from grpc.framework.alpha import interfaces |
||||
from grpc.framework.base import implementations as _base_implementations |
||||
from grpc.framework.base import util as _base_utilities |
||||
from grpc.framework.face import implementations as _face_implementations |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
_DEFAULT_THREAD_POOL_SIZE = 8 |
||||
_ONE_DAY_IN_SECONDS = 24 * 60 * 60 |
||||
|
||||
|
||||
class _Server(interfaces.Server): |
||||
|
||||
def __init__( |
||||
self, breakdown, port, private_key, certificate_chain, |
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): |
||||
self._lock = threading.Lock() |
||||
self._breakdown = breakdown |
||||
self._port = port |
||||
if private_key is None or certificate_chain is None: |
||||
self._key_chain_pairs = () |
||||
else: |
||||
self._key_chain_pairs = ((private_key, certificate_chain),) |
||||
|
||||
self._pool_size = thread_pool_size |
||||
self._pool = None |
||||
self._back = None |
||||
self._fore_link = None |
||||
|
||||
def _start(self): |
||||
with self._lock: |
||||
if self._pool is None: |
||||
self._pool = logging_pool.pool(self._pool_size) |
||||
servicer = _face_implementations.servicer( |
||||
self._pool, self._breakdown.implementations, None) |
||||
self._back = _base_implementations.back_link( |
||||
servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS, |
||||
_ONE_DAY_IN_SECONDS) |
||||
self._fore_link = _fore.ForeLink( |
||||
self._pool, self._breakdown.request_deserializers, |
||||
self._breakdown.response_serializers, None, self._key_chain_pairs, |
||||
port=self._port) |
||||
self._back.join_fore_link(self._fore_link) |
||||
self._fore_link.join_rear_link(self._back) |
||||
self._fore_link.start() |
||||
else: |
||||
raise ValueError('Server currently running!') |
||||
|
||||
def _stop(self): |
||||
with self._lock: |
||||
if self._pool is None: |
||||
raise ValueError('Server not running!') |
||||
else: |
||||
self._fore_link.stop() |
||||
_base_utilities.wait_for_idle(self._back) |
||||
self._pool.shutdown(wait=True) |
||||
self._fore_link = None |
||||
self._back = None |
||||
self._pool = None |
||||
|
||||
def __enter__(self): |
||||
self._start() |
||||
return self |
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): |
||||
self._stop() |
||||
return False |
||||
|
||||
def start(self): |
||||
self._start() |
||||
|
||||
def stop(self): |
||||
self._stop() |
||||
|
||||
def port(self): |
||||
with self._lock: |
||||
return self._fore_link.port() |
||||
|
||||
|
||||
class _Stub(interfaces.Stub): |
||||
|
||||
def __init__( |
||||
self, breakdown, host, port, secure, root_certificates, private_key, |
||||
certificate_chain, metadata_transformer=None, server_host_override=None, |
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): |
||||
self._lock = threading.Lock() |
||||
self._breakdown = breakdown |
||||
self._host = host |
||||
self._port = port |
||||
self._secure = secure |
||||
self._root_certificates = root_certificates |
||||
self._private_key = private_key |
||||
self._certificate_chain = certificate_chain |
||||
self._metadata_transformer = metadata_transformer |
||||
self._server_host_override = server_host_override |
||||
|
||||
self._pool_size = thread_pool_size |
||||
self._pool = None |
||||
self._front = None |
||||
self._rear_link = None |
||||
self._understub = None |
||||
|
||||
def __enter__(self): |
||||
with self._lock: |
||||
if self._pool is None: |
||||
self._pool = logging_pool.pool(self._pool_size) |
||||
self._front = _base_implementations.front_link( |
||||
self._pool, self._pool, self._pool) |
||||
self._rear_link = _rear.RearLink( |
||||
self._host, self._port, self._pool, |
||||
self._breakdown.request_serializers, |
||||
self._breakdown.response_deserializers, self._secure, |
||||
self._root_certificates, self._private_key, self._certificate_chain, |
||||
metadata_transformer=self._metadata_transformer, |
||||
server_host_override=self._server_host_override) |
||||
self._front.join_rear_link(self._rear_link) |
||||
self._rear_link.join_fore_link(self._front) |
||||
self._rear_link.start() |
||||
self._understub = _face_implementations.dynamic_stub( |
||||
self._breakdown.face_cardinalities, self._front, self._pool, '') |
||||
else: |
||||
raise ValueError('Tried to __enter__ already-__enter__ed Stub!') |
||||
return self |
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): |
||||
with self._lock: |
||||
if self._pool is None: |
||||
raise ValueError('Tried to __exit__ non-__enter__ed Stub!') |
||||
else: |
||||
self._rear_link.stop() |
||||
_base_utilities.wait_for_idle(self._front) |
||||
self._pool.shutdown(wait=True) |
||||
self._rear_link = None |
||||
self._front = None |
||||
self._pool = None |
||||
self._understub = None |
||||
return False |
||||
|
||||
def __getattr__(self, attr): |
||||
with self._lock: |
||||
if self._pool is None: |
||||
raise ValueError('Tried to __getattr__ non-__enter__ed Stub!') |
||||
else: |
||||
method_cardinality = self._breakdown.cardinalities.get(attr) |
||||
underlying_attr = getattr( |
||||
self._understub, self._breakdown.qualified_names.get(attr), None) |
||||
if method_cardinality is interfaces.Cardinality.UNARY_UNARY: |
||||
return _reexport.unary_unary_sync_async(underlying_attr) |
||||
elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: |
||||
return lambda request, timeout: _reexport.cancellable_iterator( |
||||
underlying_attr(request, timeout)) |
||||
elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: |
||||
return _reexport.stream_unary_sync_async(underlying_attr) |
||||
elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: |
||||
return lambda request_iterator, timeout: ( |
||||
_reexport.cancellable_iterator(underlying_attr( |
||||
request_iterator, timeout))) |
||||
else: |
||||
raise AttributeError(attr) |
||||
|
||||
|
||||
def stub( |
||||
service_name, methods, host, port, metadata_transformer=None, secure=False, |
||||
root_certificates=None, private_key=None, certificate_chain=None, |
||||
server_host_override=None, thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): |
||||
"""Constructs an interfaces.Stub. |
||||
|
||||
Args: |
||||
service_name: The package-qualified full name of the service. |
||||
methods: A dictionary from RPC method name to |
||||
interfaces.RpcMethodInvocationDescription describing the RPCs to be |
||||
supported by the created stub. The RPC method names in the dictionary are |
||||
not qualified by the service name or decorated in any other way. |
||||
host: The host to which to connect for RPC service. |
||||
port: The port to which to connect for RPC service. |
||||
metadata_transformer: A callable that given a metadata object produces |
||||
another metadata object to be used in the underlying communication on the |
||||
wire. |
||||
secure: Whether or not to construct the stub with a secure connection. |
||||
root_certificates: The PEM-encoded root certificates or None to ask for |
||||
them to be retrieved from a default location. |
||||
private_key: The PEM-encoded private key to use or None if no private key |
||||
should be used. |
||||
certificate_chain: The PEM-encoded certificate chain to use or None if no |
||||
certificate chain should be used. |
||||
server_host_override: (For testing only) the target name used for SSL |
||||
host name checking. |
||||
thread_pool_size: The maximum number of threads to allow in the backing |
||||
thread pool. |
||||
|
||||
Returns: |
||||
An interfaces.Stub affording RPC invocation. |
||||
""" |
||||
breakdown = _face_utilities.break_down_invocation(service_name, methods) |
||||
return _Stub( |
||||
breakdown, host, port, secure, root_certificates, private_key, |
||||
certificate_chain, server_host_override=server_host_override, |
||||
metadata_transformer=metadata_transformer, |
||||
thread_pool_size=thread_pool_size) |
||||
|
||||
|
||||
def server( |
||||
service_name, methods, port, private_key=None, certificate_chain=None, |
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): |
||||
"""Constructs an interfaces.Server. |
||||
|
||||
Args: |
||||
service_name: The package-qualified full name of the service. |
||||
methods: A dictionary from RPC method name to |
||||
interfaces.RpcMethodServiceDescription describing the RPCs to |
||||
be serviced by the created server. The RPC method names in the dictionary |
||||
are not qualified by the service name or decorated in any other way. |
||||
port: The port on which to serve or zero to ask for a port to be |
||||
automatically selected. |
||||
private_key: A pem-encoded private key, or None for an insecure server. |
||||
certificate_chain: A pem-encoded certificate chain, or None for an insecure |
||||
server. |
||||
thread_pool_size: The maximum number of threads to allow in the backing |
||||
thread pool. |
||||
|
||||
Returns: |
||||
An interfaces.Server that will serve secure traffic. |
||||
""" |
||||
breakdown = _face_utilities.break_down_service(service_name, methods) |
||||
return _Server(breakdown, port, private_key, certificate_chain, |
||||
thread_pool_size=thread_pool_size) |
@ -1,35 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import warnings |
||||
|
||||
warnings.simplefilter('always', DeprecationWarning) |
||||
warnings.warn('the alpha API (includes this package) is deprecated, ' |
||||
'unmaintained, and no longer tested. Please migrate to the beta ' |
||||
'API.', DeprecationWarning, stacklevel=2) |
@ -1,183 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import abc |
||||
import collections |
||||
|
||||
import six |
||||
|
||||
# face_interfaces is referenced from specification in this module. |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import |
||||
from grpc.framework.face import utilities as face_utilities |
||||
from grpc.framework.alpha import _reexport |
||||
from grpc.framework.alpha import interfaces |
||||
|
||||
|
||||
def _qualified_name(service_name, method_name): |
||||
return '/%s/%s' % (service_name, method_name) |
||||
|
||||
|
||||
# TODO(nathaniel): This structure is getting bloated; it could be shrunk if |
||||
# implementations._Stub used a generic rather than a dynamic underlying |
||||
# face-layer stub. |
||||
class InvocationBreakdown(six.with_metaclass(abc.ABCMeta)): |
||||
"""An intermediate representation of invocation-side views of RPC methods. |
||||
|
||||
Attributes: |
||||
cardinalities: A dictionary from RPC method name to interfaces.Cardinality |
||||
value. |
||||
qualified_names: A dictionary from unqualified RPC method name to |
||||
service-qualified RPC method name. |
||||
face_cardinalities: A dictionary from service-qualified RPC method name to |
||||
to cardinality.Cardinality value. |
||||
request_serializers: A dictionary from service-qualified RPC method name to |
||||
callable behavior to be used serializing request values for the RPC. |
||||
response_deserializers: A dictionary from service-qualified RPC method name |
||||
to callable behavior to be used deserializing response values for the |
||||
RPC. |
||||
""" |
||||
|
||||
|
||||
class _EasyInvocationBreakdown( |
||||
InvocationBreakdown, |
||||
collections.namedtuple( |
||||
'_EasyInvocationBreakdown', |
||||
('cardinalities', 'qualified_names', 'face_cardinalities', |
||||
'request_serializers', 'response_deserializers'))): |
||||
pass |
||||
|
||||
|
||||
class ServiceBreakdown(six.with_metaclass(abc.ABCMeta)): |
||||
"""An intermediate representation of service-side views of RPC methods. |
||||
|
||||
Attributes: |
||||
implementations: A dictionary from service-qualified RPC method name to |
||||
face_interfaces.MethodImplementation implementing the RPC method. |
||||
request_deserializers: A dictionary from service-qualified RPC method name |
||||
to callable behavior to be used deserializing request values for the RPC. |
||||
response_serializers: A dictionary from service-qualified RPC method name |
||||
to callable behavior to be used serializing response values for the RPC. |
||||
""" |
||||
|
||||
|
||||
class _EasyServiceBreakdown( |
||||
ServiceBreakdown, |
||||
collections.namedtuple( |
||||
'_EasyServiceBreakdown', |
||||
('implementations', 'request_deserializers', 'response_serializers'))): |
||||
pass |
||||
|
||||
|
||||
def break_down_invocation(service_name, method_descriptions): |
||||
"""Derives an InvocationBreakdown from several RPC method descriptions. |
||||
|
||||
Args: |
||||
service_name: The package-qualified full name of the service. |
||||
method_descriptions: A dictionary from RPC method name to |
||||
interfaces.RpcMethodInvocationDescription describing the RPCs. |
||||
|
||||
Returns: |
||||
An InvocationBreakdown corresponding to the given method descriptions. |
||||
""" |
||||
cardinalities = {} |
||||
qualified_names = {} |
||||
face_cardinalities = {} |
||||
request_serializers = {} |
||||
response_deserializers = {} |
||||
for name, method_description in six.iteritems(method_descriptions): |
||||
qualified_name = _qualified_name(service_name, name) |
||||
method_cardinality = method_description.cardinality() |
||||
cardinalities[name] = method_description.cardinality() |
||||
qualified_names[name] = qualified_name |
||||
face_cardinalities[qualified_name] = _reexport.common_cardinality( |
||||
method_cardinality) |
||||
request_serializers[qualified_name] = method_description.serialize_request |
||||
response_deserializers[qualified_name] = ( |
||||
method_description.deserialize_response) |
||||
return _EasyInvocationBreakdown( |
||||
cardinalities, qualified_names, face_cardinalities, request_serializers, |
||||
response_deserializers) |
||||
|
||||
|
||||
def break_down_service(service_name, method_descriptions): |
||||
"""Derives a ServiceBreakdown from several RPC method descriptions. |
||||
|
||||
Args: |
||||
method_descriptions: A dictionary from RPC method name to |
||||
interfaces.RpcMethodServiceDescription describing the RPCs. |
||||
|
||||
Returns: |
||||
A ServiceBreakdown corresponding to the given method descriptions. |
||||
""" |
||||
implementations = {} |
||||
request_deserializers = {} |
||||
response_serializers = {} |
||||
for name, method_description in six.iteritems(method_descriptions): |
||||
qualified_name = _qualified_name(service_name, name) |
||||
method_cardinality = method_description.cardinality() |
||||
if method_cardinality is interfaces.Cardinality.UNARY_UNARY: |
||||
def service( |
||||
request, face_rpc_context, |
||||
service_behavior=method_description.service_unary_unary): |
||||
return service_behavior( |
||||
request, _reexport.rpc_context(face_rpc_context)) |
||||
implementations[qualified_name] = face_utilities.unary_unary_inline( |
||||
service) |
||||
elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: |
||||
def service( |
||||
request, face_rpc_context, |
||||
service_behavior=method_description.service_unary_stream): |
||||
return service_behavior( |
||||
request, _reexport.rpc_context(face_rpc_context)) |
||||
implementations[qualified_name] = face_utilities.unary_stream_inline( |
||||
service) |
||||
elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: |
||||
def service( |
||||
request_iterator, face_rpc_context, |
||||
service_behavior=method_description.service_stream_unary): |
||||
return service_behavior( |
||||
request_iterator, _reexport.rpc_context(face_rpc_context)) |
||||
implementations[qualified_name] = face_utilities.stream_unary_inline( |
||||
service) |
||||
elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: |
||||
def service( |
||||
request_iterator, face_rpc_context, |
||||
service_behavior=method_description.service_stream_stream): |
||||
return service_behavior( |
||||
request_iterator, _reexport.rpc_context(face_rpc_context)) |
||||
implementations[qualified_name] = face_utilities.stream_stream_inline( |
||||
service) |
||||
request_deserializers[qualified_name] = ( |
||||
method_description.deserialize_request) |
||||
response_serializers[qualified_name] = ( |
||||
method_description.serialize_response) |
||||
|
||||
return _EasyServiceBreakdown( |
||||
implementations, request_deserializers, response_serializers) |
@ -1,205 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.face import exceptions as face_exceptions |
||||
from grpc.framework.face import interfaces as face_interfaces |
||||
from grpc.framework.foundation import future |
||||
from grpc.framework.alpha import exceptions |
||||
from grpc.framework.alpha import interfaces |
||||
|
||||
_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = { |
||||
interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, |
||||
interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, |
||||
interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, |
||||
interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, |
||||
} |
||||
|
||||
_ABORTION_REEXPORT = { |
||||
face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED, |
||||
face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED, |
||||
face_interfaces.Abortion.NETWORK_FAILURE: |
||||
interfaces.Abortion.NETWORK_FAILURE, |
||||
face_interfaces.Abortion.SERVICED_FAILURE: |
||||
interfaces.Abortion.SERVICED_FAILURE, |
||||
face_interfaces.Abortion.SERVICER_FAILURE: |
||||
interfaces.Abortion.SERVICER_FAILURE, |
||||
} |
||||
|
||||
|
||||
class _RpcError(exceptions.RpcError): |
||||
pass |
||||
|
||||
|
||||
def _reexport_error(face_rpc_error): |
||||
if isinstance(face_rpc_error, face_exceptions.CancellationError): |
||||
return exceptions.CancellationError() |
||||
elif isinstance(face_rpc_error, face_exceptions.ExpirationError): |
||||
return exceptions.ExpirationError() |
||||
else: |
||||
return _RpcError() |
||||
|
||||
|
||||
def _as_face_abortion_callback(abortion_callback): |
||||
def face_abortion_callback(face_abortion): |
||||
abortion_callback(_ABORTION_REEXPORT[face_abortion]) |
||||
return face_abortion_callback |
||||
|
||||
|
||||
class _ReexportedFuture(future.Future): |
||||
|
||||
def __init__(self, face_future): |
||||
self._face_future = face_future |
||||
|
||||
def cancel(self): |
||||
return self._face_future.cancel() |
||||
|
||||
def cancelled(self): |
||||
return self._face_future.cancelled() |
||||
|
||||
def running(self): |
||||
return self._face_future.running() |
||||
|
||||
def done(self): |
||||
return self._face_future.done() |
||||
|
||||
def result(self, timeout=None): |
||||
try: |
||||
return self._face_future.result(timeout=timeout) |
||||
except face_exceptions.RpcError as e: |
||||
raise _reexport_error(e) |
||||
|
||||
def exception(self, timeout=None): |
||||
face_error = self._face_future.exception(timeout=timeout) |
||||
return None if face_error is None else _reexport_error(face_error) |
||||
|
||||
def traceback(self, timeout=None): |
||||
return self._face_future.traceback(timeout=timeout) |
||||
|
||||
def add_done_callback(self, fn): |
||||
self._face_future.add_done_callback(lambda unused_face_future: fn(self)) |
||||
|
||||
|
||||
def _call_reexporting_errors(behavior, *args, **kwargs): |
||||
try: |
||||
return behavior(*args, **kwargs) |
||||
except face_exceptions.RpcError as e: |
||||
raise _reexport_error(e) |
||||
|
||||
|
||||
def _reexported_future(face_future): |
||||
return _ReexportedFuture(face_future) |
||||
|
||||
|
||||
class _CancellableIterator(interfaces.CancellableIterator): |
||||
|
||||
def __init__(self, face_cancellable_iterator): |
||||
self._face_cancellable_iterator = face_cancellable_iterator |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def next(self): |
||||
return _call_reexporting_errors(self._face_cancellable_iterator.next) |
||||
|
||||
def cancel(self): |
||||
self._face_cancellable_iterator.cancel() |
||||
|
||||
|
||||
class _RpcContext(interfaces.RpcContext): |
||||
|
||||
def __init__(self, face_rpc_context): |
||||
self._face_rpc_context = face_rpc_context |
||||
|
||||
def is_active(self): |
||||
return self._face_rpc_context.is_active() |
||||
|
||||
def time_remaining(self): |
||||
return self._face_rpc_context.time_remaining() |
||||
|
||||
def add_abortion_callback(self, abortion_callback): |
||||
self._face_rpc_context.add_abortion_callback( |
||||
_as_face_abortion_callback(abortion_callback)) |
||||
|
||||
|
||||
class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync): |
||||
|
||||
def __init__(self, face_unary_unary_multi_callable): |
||||
self._underlying = face_unary_unary_multi_callable |
||||
|
||||
def __call__(self, request, timeout): |
||||
return _call_reexporting_errors( |
||||
self._underlying, request, timeout) |
||||
|
||||
def async(self, request, timeout): |
||||
return _ReexportedFuture(self._underlying.future(request, timeout)) |
||||
|
||||
|
||||
class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync): |
||||
|
||||
def __init__(self, face_stream_unary_multi_callable): |
||||
self._underlying = face_stream_unary_multi_callable |
||||
|
||||
def __call__(self, request_iterator, timeout): |
||||
return _call_reexporting_errors( |
||||
self._underlying, request_iterator, timeout) |
||||
|
||||
def async(self, request_iterator, timeout): |
||||
return _ReexportedFuture(self._underlying.future(request_iterator, timeout)) |
||||
|
||||
|
||||
def common_cardinality(early_adopter_cardinality): |
||||
return _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ |
||||
early_adopter_cardinality] |
||||
|
||||
|
||||
def common_cardinalities(early_adopter_cardinalities): |
||||
common_cardinalities = {} |
||||
for name, early_adopter_cardinality in six.iteritems(early_adopter_cardinalities): |
||||
common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ |
||||
early_adopter_cardinality] |
||||
return common_cardinalities |
||||
|
||||
|
||||
def rpc_context(face_rpc_context): |
||||
return _RpcContext(face_rpc_context) |
||||
|
||||
|
||||
def cancellable_iterator(face_cancellable_iterator): |
||||
return _CancellableIterator(face_cancellable_iterator) |
||||
|
||||
|
||||
def unary_unary_sync_async(face_unary_unary_multi_callable): |
||||
return _UnaryUnarySyncAsync(face_unary_unary_multi_callable) |
||||
|
||||
|
||||
def stream_unary_sync_async(face_stream_unary_multi_callable): |
||||
return _StreamUnarySyncAsync(face_stream_unary_multi_callable) |
@ -1,47 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Exceptions raised by GRPC. |
||||
|
||||
Only GRPC should instantiate and raise these exceptions. |
||||
""" |
||||
import abc |
||||
|
||||
import six |
||||
|
||||
class RpcError(six.with_metaclass(abc.ABCMeta, Exception)): |
||||
"""Common super type for all exceptions raised by GRPC.""" |
||||
|
||||
|
||||
class CancellationError(RpcError): |
||||
"""Indicates that an RPC has been cancelled.""" |
||||
|
||||
|
||||
class ExpirationError(RpcError): |
||||
"""Indicates that an RPC has expired ("timed out").""" |
@ -1,384 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Interfaces of GRPC.""" |
||||
|
||||
import abc |
||||
import enum |
||||
|
||||
import six |
||||
|
||||
# exceptions is referenced from specification in this module. |
||||
from grpc.framework.alpha import exceptions # pylint: disable=unused-import |
||||
from grpc.framework.foundation import activated |
||||
from grpc.framework.foundation import future |
||||
|
||||
|
||||
@enum.unique |
||||
class Cardinality(enum.Enum): |
||||
"""Constants for the four cardinalities of RPC.""" |
||||
|
||||
UNARY_UNARY = 'request-unary/response-unary' |
||||
UNARY_STREAM = 'request-unary/response-streaming' |
||||
STREAM_UNARY = 'request-streaming/response-unary' |
||||
STREAM_STREAM = 'request-streaming/response-streaming' |
||||
|
||||
|
||||
@enum.unique |
||||
class Abortion(enum.Enum): |
||||
"""Categories of RPC abortion.""" |
||||
|
||||
CANCELLED = 'cancelled' |
||||
EXPIRED = 'expired' |
||||
NETWORK_FAILURE = 'network failure' |
||||
SERVICED_FAILURE = 'serviced failure' |
||||
SERVICER_FAILURE = 'servicer failure' |
||||
|
||||
|
||||
class CancellableIterator(six.with_metaclass(abc.ABCMeta)): |
||||
"""Implements the Iterator protocol and affords a cancel method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __iter__(self): |
||||
"""Returns the self object in accordance with the Iterator protocol.""" |
||||
raise NotImplementedError() |
||||
|
||||
def __next__(self): |
||||
return self.next() |
||||
|
||||
@abc.abstractmethod |
||||
def next(self): |
||||
"""Returns a value or raises StopIteration per the Iterator protocol.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self): |
||||
"""Requests cancellation of whatever computation underlies this iterator.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RpcContext(six.with_metaclass(abc.ABCMeta)): |
||||
"""Provides RPC-related information and action.""" |
||||
|
||||
@abc.abstractmethod |
||||
def is_active(self): |
||||
"""Describes whether the RPC is active or has terminated.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def time_remaining(self): |
||||
"""Describes the length of allowed time remaining for the RPC. |
||||
Returns: |
||||
A nonnegative float indicating the length of allowed time in seconds |
||||
remaining for the RPC to complete before it is considered to have timed |
||||
out. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_abortion_callback(self, abortion_callback): |
||||
"""Registers a callback to be called if the RPC is aborted. |
||||
Args: |
||||
abortion_callback: A callable to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryUnarySyncAsync(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a unary-unary RPC synchronously or asynchronously. |
||||
Values implementing this interface are directly callable and present an |
||||
"async" method. Both calls take a request value and a numeric timeout. |
||||
Direct invocation of a value of this type invokes its associated RPC and |
||||
blocks until the RPC's response is available. Calling the "async" method |
||||
of a value of this type invokes its associated RPC and immediately returns a |
||||
future.Future bound to the asynchronous execution of the RPC. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
Args: |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
Returns: |
||||
The response value for the RPC. |
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def async(self, request, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
Args: |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future's result value will be the response value of the RPC. |
||||
In the event of RPC abortion, the returned Future's exception value |
||||
will be an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamUnarySyncAsync(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a stream-unary RPC synchronously or asynchronously. |
||||
Values implementing this interface are directly callable and present an |
||||
"async" method. Both calls take an iterator of request values and a numeric |
||||
timeout. Direct invocation of a value of this type invokes its associated RPC |
||||
and blocks until the RPC's response is available. Calling the "async" method |
||||
of a value of this type invokes its associated RPC and immediately returns a |
||||
future.Future bound to the asynchronous execution of the RPC. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request_iterator, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request_iterator: An iterator that yields request values for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
The response value for the RPC. |
||||
|
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def async(self, request_iterator, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request_iterator: An iterator that yields request values for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future's result value will be the response value of the RPC. |
||||
In the event of RPC abortion, the returned Future's exception value |
||||
will be an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RpcMethodDescription(six.with_metaclass(abc.ABCMeta)): |
||||
"""A type for the common aspects of RPC method descriptions.""" |
||||
|
||||
@abc.abstractmethod |
||||
def cardinality(self): |
||||
"""Identifies the cardinality of this RpcMethodDescription. |
||||
|
||||
Returns: |
||||
A Cardinality value identifying whether or not this |
||||
RpcMethodDescription is request-unary or request-streaming and |
||||
whether or not it is response-unary or response-streaming. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RpcMethodInvocationDescription(six.with_metaclass(abc.ABCMeta, RpcMethodDescription)): |
||||
"""Invocation-side description of an RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def serialize_request(self, request): |
||||
"""Serializes a request value. |
||||
|
||||
Args: |
||||
request: A request value appropriate for the RPC method described by this |
||||
RpcMethodInvocationDescription. |
||||
|
||||
Returns: |
||||
The serialization of the given request value as a |
||||
bytestring. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def deserialize_response(self, serialized_response): |
||||
"""Deserializes a response value. |
||||
|
||||
Args: |
||||
serialized_response: A bytestring that is the serialization of a response |
||||
value appropriate for the RPC method described by this |
||||
RpcMethodInvocationDescription. |
||||
|
||||
Returns: |
||||
A response value corresponding to the given bytestring. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RpcMethodServiceDescription(six.with_metaclass(abc.ABCMeta, RpcMethodDescription)): |
||||
"""Service-side description of an RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def deserialize_request(self, serialized_request): |
||||
"""Deserializes a request value. |
||||
|
||||
Args: |
||||
serialized_request: A bytestring that is the serialization of a request |
||||
value appropriate for the RPC method described by this |
||||
RpcMethodServiceDescription. |
||||
|
||||
Returns: |
||||
A request value corresponding to the given bytestring. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def serialize_response(self, response): |
||||
"""Serializes a response value. |
||||
|
||||
Args: |
||||
response: A response value appropriate for the RPC method described by |
||||
this RpcMethodServiceDescription. |
||||
|
||||
Returns: |
||||
The serialization of the given response value as a |
||||
bytestring. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def service_unary_unary(self, request, context): |
||||
"""Carries out this RPC. |
||||
|
||||
This method may only be called if the cardinality of this |
||||
RpcMethodServiceDescription is Cardinality.UNARY_UNARY. |
||||
|
||||
Args: |
||||
request: A request value appropriate for the RPC method described by this |
||||
RpcMethodServiceDescription. |
||||
context: An RpcContext object for the RPC. |
||||
|
||||
Returns: |
||||
A response value appropriate for the RPC method described by this |
||||
RpcMethodServiceDescription. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def service_unary_stream(self, request, context): |
||||
"""Carries out this RPC. |
||||
|
||||
This method may only be called if the cardinality of this |
||||
RpcMethodServiceDescription is Cardinality.UNARY_STREAM. |
||||
|
||||
Args: |
||||
request: A request value appropriate for the RPC method described by this |
||||
RpcMethodServiceDescription. |
||||
context: An RpcContext object for the RPC. |
||||
|
||||
Yields: |
||||
Zero or more response values appropriate for the RPC method described by |
||||
this RpcMethodServiceDescription. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def service_stream_unary(self, request_iterator, context): |
||||
"""Carries out this RPC. |
||||
|
||||
This method may only be called if the cardinality of this |
||||
RpcMethodServiceDescription is Cardinality.STREAM_UNARY. |
||||
|
||||
Args: |
||||
request_iterator: An iterator of request values appropriate for the RPC |
||||
method described by this RpcMethodServiceDescription. |
||||
context: An RpcContext object for the RPC. |
||||
|
||||
Returns: |
||||
A response value appropriate for the RPC method described by this |
||||
RpcMethodServiceDescription. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def service_stream_stream(self, request_iterator, context): |
||||
"""Carries out this RPC. |
||||
|
||||
This method may only be called if the cardinality of this |
||||
RpcMethodServiceDescription is Cardinality.STREAM_STREAM. |
||||
|
||||
Args: |
||||
request_iterator: An iterator of request values appropriate for the RPC |
||||
method described by this RpcMethodServiceDescription. |
||||
context: An RpcContext object for the RPC. |
||||
|
||||
Yields: |
||||
Zero or more response values appropriate for the RPC method described by |
||||
this RpcMethodServiceDescription. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Stub(six.with_metaclass(abc.ABCMeta)): |
||||
"""A stub with callable RPC method names for attributes. |
||||
|
||||
Instances of this type are context managers and only afford RPC invocation |
||||
when used in context. |
||||
|
||||
Instances of this type, when used in context, respond to attribute access |
||||
as follows: if the requested attribute is the name of a unary-unary RPC |
||||
method, the value of the attribute will be a UnaryUnarySyncAsync with which |
||||
to invoke the RPC method. If the requested attribute is the name of a |
||||
unary-stream RPC method, the value of the attribute will be a callable taking |
||||
a request object and a timeout parameter and returning a CancellableIterator |
||||
that yields the response values of the RPC. If the requested attribute is the |
||||
name of a stream-unary RPC method, the value of the attribute will be a |
||||
StreamUnarySyncAsync with which to invoke the RPC method. If the requested |
||||
attribute is the name of a stream-stream RPC method, the value of the |
||||
attribute will be a callable taking an iterator of request objects and a |
||||
timeout and returning a CancellableIterator that yields the response values |
||||
of the RPC. |
||||
|
||||
In all cases indication of abortion is indicated by raising of |
||||
exceptions.RpcError, exceptions.CancellationError, |
||||
and exceptions.ExpirationError. |
||||
""" |
||||
|
||||
|
||||
class Server(six.with_metaclass(abc.ABCMeta, activated.Activated)): |
||||
"""A GRPC Server.""" |
||||
|
||||
@abc.abstractmethod |
||||
def port(self): |
||||
"""Reports the port on which the server is serving. |
||||
|
||||
This method may only be called while the server is activated. |
||||
|
||||
Returns: |
||||
The port on which the server is serving. |
||||
""" |
||||
raise NotImplementedError() |
@ -1,269 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Utilities for use with GRPC.""" |
||||
|
||||
from grpc.framework.alpha import interfaces |
||||
|
||||
|
||||
class _RpcMethodDescription( |
||||
interfaces.RpcMethodInvocationDescription, |
||||
interfaces.RpcMethodServiceDescription): |
||||
|
||||
def __init__( |
||||
self, cardinality, unary_unary, unary_stream, stream_unary, |
||||
stream_stream, request_serializer, request_deserializer, |
||||
response_serializer, response_deserializer): |
||||
self._cardinality = cardinality |
||||
self._unary_unary = unary_unary |
||||
self._unary_stream = unary_stream |
||||
self._stream_unary = stream_unary |
||||
self._stream_stream = stream_stream |
||||
self._request_serializer = request_serializer |
||||
self._request_deserializer = request_deserializer |
||||
self._response_serializer = response_serializer |
||||
self._response_deserializer = response_deserializer |
||||
|
||||
def cardinality(self): |
||||
"""See interfaces.RpcMethodDescription.cardinality for specification.""" |
||||
return self._cardinality |
||||
|
||||
def serialize_request(self, request): |
||||
"""See interfaces.RpcMethodInvocationDescription.serialize_request.""" |
||||
return self._request_serializer(request) |
||||
|
||||
def deserialize_request(self, serialized_request): |
||||
"""See interfaces.RpcMethodServiceDescription.deserialize_request.""" |
||||
return self._request_deserializer(serialized_request) |
||||
|
||||
def serialize_response(self, response): |
||||
"""See interfaces.RpcMethodServiceDescription.serialize_response.""" |
||||
return self._response_serializer(response) |
||||
|
||||
def deserialize_response(self, serialized_response): |
||||
"""See interfaces.RpcMethodInvocationDescription.deserialize_response.""" |
||||
return self._response_deserializer(serialized_response) |
||||
|
||||
def service_unary_unary(self, request, context): |
||||
"""See interfaces.RpcMethodServiceDescription.service_unary_unary.""" |
||||
return self._unary_unary(request, context) |
||||
|
||||
def service_unary_stream(self, request, context): |
||||
"""See interfaces.RpcMethodServiceDescription.service_unary_stream.""" |
||||
return self._unary_stream(request, context) |
||||
|
||||
def service_stream_unary(self, request_iterator, context): |
||||
"""See interfaces.RpcMethodServiceDescription.service_stream_unary.""" |
||||
return self._stream_unary(request_iterator, context) |
||||
|
||||
def service_stream_stream(self, request_iterator, context): |
||||
"""See interfaces.RpcMethodServiceDescription.service_stream_stream.""" |
||||
return self._stream_stream(request_iterator, context) |
||||
|
||||
|
||||
def unary_unary_invocation_description( |
||||
request_serializer, response_deserializer): |
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method. |
||||
|
||||
Args: |
||||
request_serializer: A callable that when called on a request |
||||
value returns a bytestring corresponding to that value. |
||||
response_deserializer: A callable that when called on a |
||||
bytestring returns the response value corresponding to |
||||
that bytestring. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodInvocationDescription constructed from the given |
||||
arguments representing a unary-request/unary-response RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.UNARY_UNARY, None, None, None, None, |
||||
request_serializer, None, None, response_deserializer) |
||||
|
||||
|
||||
def unary_stream_invocation_description( |
||||
request_serializer, response_deserializer): |
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method. |
||||
|
||||
Args: |
||||
request_serializer: A callable that when called on a request |
||||
value returns a bytestring corresponding to that value. |
||||
response_deserializer: A callable that when called on a |
||||
bytestring returns the response value corresponding to |
||||
that bytestring. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodInvocationDescription constructed from the given |
||||
arguments representing a unary-request/streaming-response RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.UNARY_STREAM, None, None, None, None, |
||||
request_serializer, None, None, response_deserializer) |
||||
|
||||
|
||||
def stream_unary_invocation_description( |
||||
request_serializer, response_deserializer): |
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method. |
||||
|
||||
Args: |
||||
request_serializer: A callable that when called on a request |
||||
value returns a bytestring corresponding to that value. |
||||
response_deserializer: A callable that when called on a |
||||
bytestring returns the response value corresponding to |
||||
that bytestring. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodInvocationDescription constructed from the given |
||||
arguments representing a streaming-request/unary-response RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.STREAM_UNARY, None, None, None, None, |
||||
request_serializer, None, None, response_deserializer) |
||||
|
||||
|
||||
def stream_stream_invocation_description( |
||||
request_serializer, response_deserializer): |
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method. |
||||
|
||||
Args: |
||||
request_serializer: A callable that when called on a request |
||||
value returns a bytestring corresponding to that value. |
||||
response_deserializer: A callable that when called on a |
||||
bytestring returns the response value corresponding to |
||||
that bytestring. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodInvocationDescription constructed from the given |
||||
arguments representing a streaming-request/streaming-response RPC |
||||
method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.STREAM_STREAM, None, None, None, None, |
||||
request_serializer, None, None, response_deserializer) |
||||
|
||||
|
||||
def unary_unary_service_description( |
||||
behavior, request_deserializer, response_serializer): |
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior. |
||||
|
||||
Args: |
||||
behavior: A callable that implements a unary-unary RPC |
||||
method that accepts a single request and an interfaces.RpcContext and |
||||
returns a single response. |
||||
request_deserializer: A callable that when called on a |
||||
bytestring returns the request value corresponding to that |
||||
bytestring. |
||||
response_serializer: A callable that when called on a |
||||
response value returns the bytestring corresponding to |
||||
that value. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodServiceDescription constructed from the given |
||||
arguments representing a unary-request/unary-response RPC |
||||
method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None, |
||||
None, request_deserializer, response_serializer, None) |
||||
|
||||
|
||||
def unary_stream_service_description( |
||||
behavior, request_deserializer, response_serializer): |
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior. |
||||
|
||||
Args: |
||||
behavior: A callable that implements a unary-stream RPC |
||||
method that accepts a single request and an interfaces.RpcContext |
||||
and returns an iterator of zero or more responses. |
||||
request_deserializer: A callable that when called on a |
||||
bytestring returns the request value corresponding to that |
||||
bytestring. |
||||
response_serializer: A callable that when called on a |
||||
response value returns the bytestring corresponding to |
||||
that value. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodServiceDescription constructed from the given |
||||
arguments representing a unary-request/streaming-response |
||||
RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None, |
||||
None, request_deserializer, response_serializer, None) |
||||
|
||||
|
||||
def stream_unary_service_description( |
||||
behavior, request_deserializer, response_serializer): |
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior. |
||||
|
||||
Args: |
||||
behavior: A callable that implements a stream-unary RPC |
||||
method that accepts an iterator of zero or more requests |
||||
and an interfaces.RpcContext and returns a single response. |
||||
request_deserializer: A callable that when called on a |
||||
bytestring returns the request value corresponding to that |
||||
bytestring. |
||||
response_serializer: A callable that when called on a |
||||
response value returns the bytestring corresponding to |
||||
that value. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodServiceDescription constructed from the given |
||||
arguments representing a streaming-request/unary-response |
||||
RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None, |
||||
None, request_deserializer, response_serializer, None) |
||||
|
||||
|
||||
def stream_stream_service_description( |
||||
behavior, request_deserializer, response_serializer): |
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior. |
||||
|
||||
Args: |
||||
behavior: A callable that implements a stream-stream RPC |
||||
method that accepts an iterator of zero or more requests |
||||
and an interfaces.RpcContext and returns an iterator of |
||||
zero or more responses. |
||||
request_deserializer: A callable that when called on a |
||||
bytestring returns the request value corresponding to that |
||||
bytestring. |
||||
response_serializer: A callable that when called on a |
||||
response value returns the bytestring corresponding to |
||||
that value. |
||||
|
||||
Returns: |
||||
An interfaces.RpcMethodServiceDescription constructed from the given |
||||
arguments representing a |
||||
streaming-request/streaming-response RPC method. |
||||
""" |
||||
return _RpcMethodDescription( |
||||
interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior, |
||||
None, request_deserializer, response_serializer, None) |
@ -1,35 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import warnings |
||||
|
||||
warnings.simplefilter('always', DeprecationWarning) |
||||
warnings.warn('the alpha API (includes this package) is deprecated, ' |
||||
'unmaintained, and no longer tested. Please migrate to the beta ' |
||||
'API.', DeprecationWarning, stacklevel=2) |
@ -1,64 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for operation cancellation.""" |
||||
|
||||
from grpc.framework.base import _interfaces |
||||
from grpc.framework.base import interfaces |
||||
|
||||
|
||||
class CancellationManager(_interfaces.CancellationManager): |
||||
"""An implementation of _interfaces.CancellationManager.""" |
||||
|
||||
def __init__( |
||||
self, lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
ingestion_manager: The _interfaces.IngestionManager for the operation. |
||||
expiration_manager: The _interfaces.ExpirationManager for the operation. |
||||
""" |
||||
self._lock = lock |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def cancel(self): |
||||
"""See _interfaces.CancellationManager.cancel for specification.""" |
||||
with self._lock: |
||||
self._termination_manager.abort(interfaces.Outcome.CANCELLED) |
||||
self._transmission_manager.abort(interfaces.Outcome.CANCELLED) |
||||
self._ingestion_manager.abort() |
||||
self._expiration_manager.abort() |
@ -1,32 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Private constants for the package.""" |
||||
|
||||
INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Base) internal error! :-(' |
@ -1,99 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for operation context.""" |
||||
|
||||
import time |
||||
|
||||
# _interfaces is referenced from specification in this module. |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.base import _interfaces # pylint: disable=unused-import |
||||
|
||||
|
||||
class OperationContext(interfaces.OperationContext): |
||||
"""An implementation of interfaces.OperationContext.""" |
||||
|
||||
def __init__( |
||||
self, lock, operation_id, local_failure, termination_manager, |
||||
transmission_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
operation_id: An object identifying the operation. |
||||
local_failure: Whichever one of interfaces.Outcome.SERVICED_FAILURE or |
||||
interfaces.Outcome.SERVICER_FAILURE describes local failure of |
||||
customer code. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
""" |
||||
self._lock = lock |
||||
self._local_failure = local_failure |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = None |
||||
self._expiration_manager = None |
||||
|
||||
self.operation_id = operation_id |
||||
|
||||
def set_ingestion_and_expiration_managers( |
||||
self, ingestion_manager, expiration_manager): |
||||
"""Sets managers with which this OperationContext cooperates. |
||||
|
||||
Args: |
||||
ingestion_manager: The _interfaces.IngestionManager for the operation. |
||||
expiration_manager: The _interfaces.ExpirationManager for the operation. |
||||
""" |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def is_active(self): |
||||
"""See interfaces.OperationContext.is_active for specification.""" |
||||
with self._lock: |
||||
return self._termination_manager.is_active() |
||||
|
||||
def add_termination_callback(self, callback): |
||||
"""See interfaces.OperationContext.add_termination_callback.""" |
||||
with self._lock: |
||||
self._termination_manager.add_callback(callback) |
||||
|
||||
def time_remaining(self): |
||||
"""See interfaces.OperationContext.time_remaining for specification.""" |
||||
with self._lock: |
||||
deadline = self._expiration_manager.deadline() |
||||
return max(0.0, deadline - time.time()) |
||||
|
||||
def fail(self, exception): |
||||
"""See interfaces.OperationContext.fail for specification.""" |
||||
with self._lock: |
||||
self._termination_manager.abort(self._local_failure) |
||||
self._transmission_manager.abort(self._local_failure) |
||||
self._ingestion_manager.abort() |
||||
self._expiration_manager.abort() |
@ -1,125 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for handling emitted values.""" |
||||
|
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.base import _interfaces |
||||
|
||||
|
||||
class _EmissionManager(_interfaces.EmissionManager): |
||||
"""An implementation of _interfaces.EmissionManager.""" |
||||
|
||||
def __init__( |
||||
self, lock, failure_outcome, termination_manager, transmission_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
failure_outcome: Whichever one of interfaces.Outcome.SERVICED_FAILURE or |
||||
interfaces.Outcome.SERVICER_FAILURE describes this object's methods |
||||
being called inappropriately by customer code. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
""" |
||||
self._lock = lock |
||||
self._failure_outcome = failure_outcome |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = None |
||||
self._expiration_manager = None |
||||
|
||||
self._emission_complete = False |
||||
|
||||
def set_ingestion_manager_and_expiration_manager( |
||||
self, ingestion_manager, expiration_manager): |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def _abort(self): |
||||
self._termination_manager.abort(self._failure_outcome) |
||||
self._transmission_manager.abort(self._failure_outcome) |
||||
self._ingestion_manager.abort() |
||||
self._expiration_manager.abort() |
||||
|
||||
def consume(self, value): |
||||
with self._lock: |
||||
if self._emission_complete: |
||||
self._abort() |
||||
else: |
||||
self._transmission_manager.inmit(value, False) |
||||
|
||||
def terminate(self): |
||||
with self._lock: |
||||
if not self._emission_complete: |
||||
self._termination_manager.emission_complete() |
||||
self._transmission_manager.inmit(None, True) |
||||
self._emission_complete = True |
||||
|
||||
def consume_and_terminate(self, value): |
||||
with self._lock: |
||||
if self._emission_complete: |
||||
self._abort() |
||||
else: |
||||
self._termination_manager.emission_complete() |
||||
self._transmission_manager.inmit(value, True) |
||||
self._emission_complete = True |
||||
|
||||
|
||||
def front_emission_manager(lock, termination_manager, transmission_manager): |
||||
"""Creates an _interfaces.EmissionManager appropriate for front-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the operation. |
||||
|
||||
Returns: |
||||
An _interfaces.EmissionManager appropriate for front-side use. |
||||
""" |
||||
return _EmissionManager( |
||||
lock, interfaces.Outcome.SERVICED_FAILURE, termination_manager, |
||||
transmission_manager) |
||||
|
||||
|
||||
def back_emission_manager(lock, termination_manager, transmission_manager): |
||||
"""Creates an _interfaces.EmissionManager appropriate for back-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the operation. |
||||
|
||||
Returns: |
||||
An _interfaces.EmissionManager appropriate for back-side use. |
||||
""" |
||||
return _EmissionManager( |
||||
lock, interfaces.Outcome.SERVICER_FAILURE, termination_manager, |
||||
transmission_manager) |
@ -1,399 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Implementations of FrontLinks and BackLinks.""" |
||||
|
||||
import collections |
||||
import threading |
||||
import uuid |
||||
|
||||
# _interfaces is referenced from specification in this module. |
||||
from grpc.framework.base import _cancellation |
||||
from grpc.framework.base import _context |
||||
from grpc.framework.base import _emission |
||||
from grpc.framework.base import _expiration |
||||
from grpc.framework.base import _ingestion |
||||
from grpc.framework.base import _interfaces # pylint: disable=unused-import |
||||
from grpc.framework.base import _reception |
||||
from grpc.framework.base import _termination |
||||
from grpc.framework.base import _transmission |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import callable_util |
||||
|
||||
_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' |
||||
|
||||
|
||||
class _EasyOperation(interfaces.Operation): |
||||
"""A trivial implementation of interfaces.Operation.""" |
||||
|
||||
def __init__(self, emission_manager, context, cancellation_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
emission_manager: The _interfaces.EmissionManager for the operation that |
||||
will accept values emitted by customer code. |
||||
context: The interfaces.OperationContext for use by the customer |
||||
during the operation. |
||||
cancellation_manager: The _interfaces.CancellationManager for the |
||||
operation. |
||||
""" |
||||
self.consumer = emission_manager |
||||
self.context = context |
||||
self._cancellation_manager = cancellation_manager |
||||
|
||||
def cancel(self): |
||||
self._cancellation_manager.cancel() |
||||
|
||||
|
||||
class _Endlette(object): |
||||
"""Utility for stateful behavior common to Fronts and Backs.""" |
||||
|
||||
def __init__(self, pool): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
pool: A thread pool to use when calling registered idle actions. |
||||
""" |
||||
self._lock = threading.Lock() |
||||
self._pool = pool |
||||
# Dictionary from operation IDs to ReceptionManager-or-None. A None value |
||||
# indicates an in-progress fire-and-forget operation for which the customer |
||||
# has chosen to ignore results. |
||||
self._operations = {} |
||||
self._stats = {outcome: 0 for outcome in interfaces.Outcome} |
||||
self._idle_actions = [] |
||||
|
||||
def terminal_action(self, operation_id): |
||||
"""Constructs the termination action for a single operation. |
||||
|
||||
Args: |
||||
operation_id: An operation ID. |
||||
|
||||
Returns: |
||||
A callable that takes an operation outcome for an argument to be used as |
||||
the termination action for the operation associated with the given |
||||
operation ID. |
||||
""" |
||||
def termination_action(outcome): |
||||
with self._lock: |
||||
self._stats[outcome] += 1 |
||||
self._operations.pop(operation_id, None) |
||||
if not self._operations: |
||||
for action in self._idle_actions: |
||||
self._pool.submit(callable_util.with_exceptions_logged( |
||||
action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) |
||||
self._idle_actions = [] |
||||
return termination_action |
||||
|
||||
def __enter__(self): |
||||
self._lock.acquire() |
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): |
||||
self._lock.release() |
||||
|
||||
def get_operation(self, operation_id): |
||||
return self._operations.get(operation_id, None) |
||||
|
||||
def add_operation(self, operation_id, operation_reception_manager): |
||||
self._operations[operation_id] = operation_reception_manager |
||||
|
||||
def operation_stats(self): |
||||
with self._lock: |
||||
return dict(self._stats) |
||||
|
||||
def add_idle_action(self, action): |
||||
with self._lock: |
||||
if self._operations: |
||||
self._idle_actions.append(action) |
||||
else: |
||||
self._pool.submit(callable_util.with_exceptions_logged( |
||||
action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) |
||||
|
||||
|
||||
class _FrontManagement( |
||||
collections.namedtuple( |
||||
'_FrontManagement', |
||||
('reception', 'emission', 'operation', 'cancellation'))): |
||||
"""Just a trivial helper class to bundle four fellow-traveling objects.""" |
||||
|
||||
|
||||
def _front_operate( |
||||
callback, work_pool, transmission_pool, utility_pool, |
||||
termination_action, operation_id, name, payload, complete, timeout, |
||||
subscription, trace_id): |
||||
"""Constructs objects necessary for front-side operation management. |
||||
|
||||
Args: |
||||
callback: A callable that accepts interfaces.FrontToBackTickets and |
||||
delivers them to the other side of the operation. Execution of this |
||||
callable may take any arbitrary length of time. |
||||
work_pool: A thread pool in which to execute customer code. |
||||
transmission_pool: A thread pool to use for transmitting to the other side |
||||
of the operation. |
||||
utility_pool: A thread pool for utility tasks. |
||||
termination_action: A no-arg behavior to be called upon operation |
||||
completion. |
||||
operation_id: An object identifying the operation. |
||||
name: The name of the method being called during the operation. |
||||
payload: The first customer-significant value to be transmitted to the other |
||||
side. May be None if there is no such value or if the customer chose not |
||||
to pass it at operation invocation. |
||||
complete: A boolean indicating whether or not additional payloads will be |
||||
supplied by the customer. |
||||
timeout: A length of time in seconds to allow for the operation. |
||||
subscription: A interfaces.ServicedSubscription describing the |
||||
customer's interest in the results of the operation. |
||||
trace_id: A uuid.UUID identifying a set of related operations to which this |
||||
operation belongs. May be None. |
||||
|
||||
Returns: |
||||
A _FrontManagement object bundling together the |
||||
_interfaces.ReceptionManager, _interfaces.EmissionManager, |
||||
_context.OperationContext, and _interfaces.CancellationManager for the |
||||
operation. |
||||
""" |
||||
lock = threading.Lock() |
||||
with lock: |
||||
termination_manager = _termination.front_termination_manager( |
||||
work_pool, utility_pool, termination_action, subscription.kind) |
||||
transmission_manager = _transmission.front_transmission_manager( |
||||
lock, transmission_pool, callback, operation_id, name, |
||||
subscription.kind, trace_id, timeout, termination_manager) |
||||
operation_context = _context.OperationContext( |
||||
lock, operation_id, interfaces.Outcome.SERVICED_FAILURE, |
||||
termination_manager, transmission_manager) |
||||
emission_manager = _emission.front_emission_manager( |
||||
lock, termination_manager, transmission_manager) |
||||
ingestion_manager = _ingestion.front_ingestion_manager( |
||||
lock, work_pool, subscription, termination_manager, |
||||
transmission_manager, operation_context) |
||||
expiration_manager = _expiration.front_expiration_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
timeout) |
||||
reception_manager = _reception.front_reception_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager) |
||||
cancellation_manager = _cancellation.CancellationManager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager) |
||||
|
||||
termination_manager.set_expiration_manager(expiration_manager) |
||||
transmission_manager.set_ingestion_and_expiration_managers( |
||||
ingestion_manager, expiration_manager) |
||||
operation_context.set_ingestion_and_expiration_managers( |
||||
ingestion_manager, expiration_manager) |
||||
emission_manager.set_ingestion_manager_and_expiration_manager( |
||||
ingestion_manager, expiration_manager) |
||||
ingestion_manager.set_expiration_manager(expiration_manager) |
||||
|
||||
transmission_manager.inmit(payload, complete) |
||||
|
||||
if subscription.kind is interfaces.ServicedSubscription.Kind.NONE: |
||||
returned_reception_manager = None |
||||
else: |
||||
returned_reception_manager = reception_manager |
||||
|
||||
return _FrontManagement( |
||||
returned_reception_manager, emission_manager, operation_context, |
||||
cancellation_manager) |
||||
|
||||
|
||||
class FrontLink(interfaces.FrontLink): |
||||
"""An implementation of interfaces.FrontLink.""" |
||||
|
||||
def __init__(self, work_pool, transmission_pool, utility_pool): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
work_pool: A thread pool to be used for executing customer code. |
||||
transmission_pool: A thread pool to be used for transmitting values to |
||||
the other side of the operation. |
||||
utility_pool: A thread pool to be used for utility tasks. |
||||
""" |
||||
self._endlette = _Endlette(utility_pool) |
||||
self._work_pool = work_pool |
||||
self._transmission_pool = transmission_pool |
||||
self._utility_pool = utility_pool |
||||
self._callback = None |
||||
|
||||
self._operations = {} |
||||
|
||||
def join_rear_link(self, rear_link): |
||||
"""See interfaces.ForeLink.join_rear_link for specification.""" |
||||
with self._endlette: |
||||
self._callback = rear_link.accept_front_to_back_ticket |
||||
|
||||
def operation_stats(self): |
||||
"""See interfaces.End.operation_stats for specification.""" |
||||
return self._endlette.operation_stats() |
||||
|
||||
def add_idle_action(self, action): |
||||
"""See interfaces.End.add_idle_action for specification.""" |
||||
self._endlette.add_idle_action(action) |
||||
|
||||
def operate( |
||||
self, name, payload, complete, timeout, subscription, trace_id): |
||||
"""See interfaces.Front.operate for specification.""" |
||||
operation_id = uuid.uuid4() |
||||
with self._endlette: |
||||
management = _front_operate( |
||||
self._callback, self._work_pool, self._transmission_pool, |
||||
self._utility_pool, self._endlette.terminal_action(operation_id), |
||||
operation_id, name, payload, complete, timeout, subscription, |
||||
trace_id) |
||||
self._endlette.add_operation(operation_id, management.reception) |
||||
return _EasyOperation( |
||||
management.emission, management.operation, management.cancellation) |
||||
|
||||
def accept_back_to_front_ticket(self, ticket): |
||||
"""See interfaces.End.act for specification.""" |
||||
with self._endlette: |
||||
reception_manager = self._endlette.get_operation(ticket.operation_id) |
||||
if reception_manager: |
||||
reception_manager.receive_ticket(ticket) |
||||
|
||||
|
||||
def _back_operate( |
||||
servicer, callback, work_pool, transmission_pool, utility_pool, |
||||
termination_action, ticket, default_timeout, maximum_timeout): |
||||
"""Constructs objects necessary for back-side operation management. |
||||
|
||||
Also begins back-side operation by feeding the first received ticket into the |
||||
constructed _interfaces.ReceptionManager. |
||||
|
||||
Args: |
||||
servicer: An interfaces.Servicer for servicing operations. |
||||
callback: A callable that accepts interfaces.BackToFrontTickets and |
||||
delivers them to the other side of the operation. Execution of this |
||||
callable may take any arbitrary length of time. |
||||
work_pool: A thread pool in which to execute customer code. |
||||
transmission_pool: A thread pool to use for transmitting to the other side |
||||
of the operation. |
||||
utility_pool: A thread pool for utility tasks. |
||||
termination_action: A no-arg behavior to be called upon operation |
||||
completion. |
||||
ticket: The first interfaces.FrontToBackTicket received for the operation. |
||||
default_timeout: A length of time in seconds to be used as the default |
||||
time alloted for a single operation. |
||||
maximum_timeout: A length of time in seconds to be used as the maximum |
||||
time alloted for a single operation. |
||||
|
||||
Returns: |
||||
The _interfaces.ReceptionManager to be used for the operation. |
||||
""" |
||||
lock = threading.Lock() |
||||
with lock: |
||||
termination_manager = _termination.back_termination_manager( |
||||
work_pool, utility_pool, termination_action, ticket.subscription) |
||||
transmission_manager = _transmission.back_transmission_manager( |
||||
lock, transmission_pool, callback, ticket.operation_id, |
||||
termination_manager, ticket.subscription) |
||||
operation_context = _context.OperationContext( |
||||
lock, ticket.operation_id, interfaces.Outcome.SERVICER_FAILURE, |
||||
termination_manager, transmission_manager) |
||||
emission_manager = _emission.back_emission_manager( |
||||
lock, termination_manager, transmission_manager) |
||||
ingestion_manager = _ingestion.back_ingestion_manager( |
||||
lock, work_pool, servicer, termination_manager, |
||||
transmission_manager, operation_context, emission_manager) |
||||
expiration_manager = _expiration.back_expiration_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
ticket.timeout, default_timeout, maximum_timeout) |
||||
reception_manager = _reception.back_reception_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager) |
||||
|
||||
termination_manager.set_expiration_manager(expiration_manager) |
||||
transmission_manager.set_ingestion_and_expiration_managers( |
||||
ingestion_manager, expiration_manager) |
||||
operation_context.set_ingestion_and_expiration_managers( |
||||
ingestion_manager, expiration_manager) |
||||
emission_manager.set_ingestion_manager_and_expiration_manager( |
||||
ingestion_manager, expiration_manager) |
||||
ingestion_manager.set_expiration_manager(expiration_manager) |
||||
|
||||
reception_manager.receive_ticket(ticket) |
||||
|
||||
return reception_manager |
||||
|
||||
|
||||
class BackLink(interfaces.BackLink): |
||||
"""An implementation of interfaces.BackLink.""" |
||||
|
||||
def __init__( |
||||
self, servicer, work_pool, transmission_pool, utility_pool, |
||||
default_timeout, maximum_timeout): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
servicer: An interfaces.Servicer for servicing operations. |
||||
work_pool: A thread pool in which to execute customer code. |
||||
transmission_pool: A thread pool to use for transmitting to the other side |
||||
of the operation. |
||||
utility_pool: A thread pool for utility tasks. |
||||
default_timeout: A length of time in seconds to be used as the default |
||||
time alloted for a single operation. |
||||
maximum_timeout: A length of time in seconds to be used as the maximum |
||||
time alloted for a single operation. |
||||
""" |
||||
self._endlette = _Endlette(utility_pool) |
||||
self._servicer = servicer |
||||
self._work_pool = work_pool |
||||
self._transmission_pool = transmission_pool |
||||
self._utility_pool = utility_pool |
||||
self._default_timeout = default_timeout |
||||
self._maximum_timeout = maximum_timeout |
||||
self._callback = None |
||||
|
||||
def join_fore_link(self, fore_link): |
||||
"""See interfaces.RearLink.join_fore_link for specification.""" |
||||
with self._endlette: |
||||
self._callback = fore_link.accept_back_to_front_ticket |
||||
|
||||
def accept_front_to_back_ticket(self, ticket): |
||||
"""See interfaces.RearLink.accept_front_to_back_ticket for specification.""" |
||||
with self._endlette: |
||||
reception_manager = self._endlette.get_operation(ticket.operation_id) |
||||
if reception_manager is None: |
||||
reception_manager = _back_operate( |
||||
self._servicer, self._callback, self._work_pool, |
||||
self._transmission_pool, self._utility_pool, |
||||
self._endlette.terminal_action(ticket.operation_id), ticket, |
||||
self._default_timeout, self._maximum_timeout) |
||||
self._endlette.add_operation(ticket.operation_id, reception_manager) |
||||
else: |
||||
reception_manager.receive_ticket(ticket) |
||||
|
||||
def operation_stats(self): |
||||
"""See interfaces.End.operation_stats for specification.""" |
||||
return self._endlette.operation_stats() |
||||
|
||||
def add_idle_action(self, action): |
||||
"""See interfaces.End.add_idle_action for specification.""" |
||||
self._endlette.add_idle_action(action) |
@ -1,158 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for operation expiration.""" |
||||
|
||||
import time |
||||
|
||||
from grpc.framework.base import _interfaces |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import later |
||||
|
||||
|
||||
class _ExpirationManager(_interfaces.ExpirationManager): |
||||
"""An implementation of _interfaces.ExpirationManager.""" |
||||
|
||||
def __init__( |
||||
self, lock, termination_manager, transmission_manager, ingestion_manager, |
||||
commencement, timeout, maximum_timeout): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
ingestion_manager: The _interfaces.IngestionManager for the operation. |
||||
commencement: The time in seconds since the epoch at which the operation |
||||
began. |
||||
timeout: A length of time in seconds to allow for the operation to run. |
||||
maximum_timeout: The maximum length of time in seconds to allow for the |
||||
operation to run despite what is requested via this object's |
||||
change_timout method. |
||||
""" |
||||
self._lock = lock |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = ingestion_manager |
||||
self._commencement = commencement |
||||
self._maximum_timeout = maximum_timeout |
||||
|
||||
self._timeout = timeout |
||||
self._deadline = commencement + timeout |
||||
self._index = None |
||||
self._future = None |
||||
|
||||
def _expire(self, index): |
||||
with self._lock: |
||||
if self._future is not None and index == self._index: |
||||
self._future = None |
||||
self._termination_manager.abort(interfaces.Outcome.EXPIRED) |
||||
self._transmission_manager.abort(interfaces.Outcome.EXPIRED) |
||||
self._ingestion_manager.abort() |
||||
|
||||
def start(self): |
||||
self._index = 0 |
||||
self._future = later.later(self._timeout, lambda: self._expire(0)) |
||||
|
||||
def change_timeout(self, timeout): |
||||
if self._future is not None and timeout != self._timeout: |
||||
self._future.cancel() |
||||
new_timeout = min(timeout, self._maximum_timeout) |
||||
new_index = self._index + 1 |
||||
self._timeout = new_timeout |
||||
self._deadline = self._commencement + new_timeout |
||||
self._index = new_index |
||||
delay = self._deadline - time.time() |
||||
self._future = later.later( |
||||
delay, lambda: self._expire(new_index)) |
||||
|
||||
def deadline(self): |
||||
return self._deadline |
||||
|
||||
def abort(self): |
||||
if self._future: |
||||
self._future.cancel() |
||||
self._future = None |
||||
self._deadline_index = None |
||||
|
||||
|
||||
def front_expiration_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
timeout): |
||||
"""Creates an _interfaces.ExpirationManager appropriate for front-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
ingestion_manager: The _interfaces.IngestionManager for the operation. |
||||
timeout: A length of time in seconds to allow for the operation to run. |
||||
|
||||
Returns: |
||||
An _interfaces.ExpirationManager appropriate for front-side use. |
||||
""" |
||||
commencement = time.time() |
||||
expiration_manager = _ExpirationManager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
commencement, timeout, timeout) |
||||
expiration_manager.start() |
||||
return expiration_manager |
||||
|
||||
|
||||
def back_expiration_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
timeout, default_timeout, maximum_timeout): |
||||
"""Creates an _interfaces.ExpirationManager appropriate for back-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
ingestion_manager: The _interfaces.IngestionManager for the operation. |
||||
timeout: A length of time in seconds to allow for the operation to run. May |
||||
be None in which case default_timeout will be used. |
||||
default_timeout: The default length of time in seconds to allow for the |
||||
operation to run if the front-side customer has not specified such a value |
||||
(or if the value they specified is not yet known). |
||||
maximum_timeout: The maximum length of time in seconds to allow for the |
||||
operation to run. |
||||
|
||||
Returns: |
||||
An _interfaces.ExpirationManager appropriate for back-side use. |
||||
""" |
||||
commencement = time.time() |
||||
expiration_manager = _ExpirationManager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
commencement, default_timeout if timeout is None else timeout, |
||||
maximum_timeout) |
||||
expiration_manager.start() |
||||
return expiration_manager |
@ -1,443 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for ingestion during an operation.""" |
||||
|
||||
import abc |
||||
import collections |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.base import _constants |
||||
from grpc.framework.base import _interfaces |
||||
from grpc.framework.base import exceptions |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import abandonment |
||||
from grpc.framework.foundation import callable_util |
||||
from grpc.framework.foundation import stream |
||||
|
||||
_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' |
||||
_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' |
||||
|
||||
|
||||
class _ConsumerCreation(collections.namedtuple( |
||||
'_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))): |
||||
"""A sum type for the outcome of ingestion initialization. |
||||
|
||||
Either consumer will be non-None, remote_error will be True, or abandoned will |
||||
be True. |
||||
|
||||
Attributes: |
||||
consumer: A stream.Consumer for ingesting payloads. |
||||
remote_error: A boolean indicating that the consumer could not be created |
||||
due to an error on the remote side of the operation. |
||||
abandoned: A boolean indicating that the consumer creation was abandoned. |
||||
""" |
||||
|
||||
|
||||
class _EmptyConsumer(stream.Consumer): |
||||
"""A no-operative stream.Consumer that ignores all inputs and calls.""" |
||||
|
||||
def consume(self, value): |
||||
"""See stream.Consumer.consume for specification.""" |
||||
|
||||
def terminate(self): |
||||
"""See stream.Consumer.terminate for specification.""" |
||||
|
||||
def consume_and_terminate(self, value): |
||||
"""See stream.Consumer.consume_and_terminate for specification.""" |
||||
|
||||
|
||||
class _ConsumerCreator(six.with_metaclass(abc.ABCMeta)): |
||||
"""Common specification of different consumer-creating behavior.""" |
||||
|
||||
@abc.abstractmethod |
||||
def create_consumer(self, requirement): |
||||
"""Creates the stream.Consumer to which customer payloads will be delivered. |
||||
|
||||
Any exceptions raised by this method should be attributed to and treated as |
||||
defects in the serviced or servicer code called by this method. |
||||
|
||||
Args: |
||||
requirement: A value required by this _ConsumerCreator for consumer |
||||
creation. |
||||
|
||||
Returns: |
||||
A _ConsumerCreation describing the result of consumer creation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _FrontConsumerCreator(_ConsumerCreator): |
||||
"""A _ConsumerCreator appropriate for front-side use.""" |
||||
|
||||
def __init__(self, subscription, operation_context): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
subscription: The serviced's interfaces.ServicedSubscription for the |
||||
operation. |
||||
operation_context: The interfaces.OperationContext object for the |
||||
operation. |
||||
""" |
||||
self._subscription = subscription |
||||
self._operation_context = operation_context |
||||
|
||||
def create_consumer(self, requirement): |
||||
"""See _ConsumerCreator.create_consumer for specification.""" |
||||
if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL: |
||||
try: |
||||
return _ConsumerCreation( |
||||
self._subscription.ingestor.consumer(self._operation_context), |
||||
False, False) |
||||
except abandonment.Abandoned: |
||||
return _ConsumerCreation(None, False, True) |
||||
else: |
||||
return _ConsumerCreation(_EmptyConsumer(), False, False) |
||||
|
||||
|
||||
class _BackConsumerCreator(_ConsumerCreator): |
||||
"""A _ConsumerCreator appropriate for back-side use.""" |
||||
|
||||
def __init__(self, servicer, operation_context, emission_consumer): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
servicer: The interfaces.Servicer that will service the operation. |
||||
operation_context: The interfaces.OperationContext object for the |
||||
operation. |
||||
emission_consumer: The stream.Consumer object to which payloads emitted |
||||
from the operation will be passed. |
||||
""" |
||||
self._servicer = servicer |
||||
self._operation_context = operation_context |
||||
self._emission_consumer = emission_consumer |
||||
|
||||
def create_consumer(self, requirement): |
||||
"""See _ConsumerCreator.create_consumer for full specification. |
||||
|
||||
Args: |
||||
requirement: The name of the Servicer method to be called during this |
||||
operation. |
||||
|
||||
Returns: |
||||
A _ConsumerCreation describing the result of consumer creation. |
||||
""" |
||||
try: |
||||
return _ConsumerCreation( |
||||
self._servicer.service( |
||||
requirement, self._operation_context, self._emission_consumer), |
||||
False, False) |
||||
except exceptions.NoSuchMethodError: |
||||
return _ConsumerCreation(None, True, False) |
||||
except abandonment.Abandoned: |
||||
return _ConsumerCreation(None, False, True) |
||||
|
||||
|
||||
class _WrappedConsumer(object): |
||||
"""Wraps a consumer to catch the exceptions that it is allowed to throw.""" |
||||
|
||||
def __init__(self, consumer): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
consumer: A stream.Consumer that may raise abandonment.Abandoned from any |
||||
of its methods. |
||||
""" |
||||
self._consumer = consumer |
||||
|
||||
def moar(self, payload, complete): |
||||
"""Makes progress with the wrapped consumer. |
||||
|
||||
This method catches all exceptions allowed to be thrown by the wrapped |
||||
consumer. Any exceptions raised by this method should be blamed on the |
||||
customer-supplied consumer. |
||||
|
||||
Args: |
||||
payload: A customer-significant payload object. May be None only if |
||||
complete is True. |
||||
complete: Whether or not the end of the payload sequence has been reached. |
||||
Must be True if payload is None. |
||||
|
||||
Returns: |
||||
True if the wrapped consumer made progress or False if the wrapped |
||||
consumer raised abandonment.Abandoned to indicate its abandonment of |
||||
progress. |
||||
""" |
||||
try: |
||||
if payload is None: |
||||
self._consumer.terminate() |
||||
elif complete: |
||||
self._consumer.consume_and_terminate(payload) |
||||
else: |
||||
self._consumer.consume(payload) |
||||
return True |
||||
except abandonment.Abandoned: |
||||
return False |
||||
|
||||
|
||||
class _IngestionManager(_interfaces.IngestionManager): |
||||
"""An implementation of _interfaces.IngestionManager.""" |
||||
|
||||
def __init__( |
||||
self, lock, pool, consumer_creator, failure_outcome, termination_manager, |
||||
transmission_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
pool: A thread pool in which to execute customer code. |
||||
consumer_creator: A _ConsumerCreator wrapping the portion of customer code |
||||
that when called returns the stream.Consumer with which the customer |
||||
code will ingest payload values. |
||||
failure_outcome: Whichever one of |
||||
interfaces.Outcome.SERVICED_FAILURE or |
||||
interfaces.Outcome.SERVICER_FAILURE describes local failure of |
||||
customer code. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
""" |
||||
self._lock = lock |
||||
self._pool = pool |
||||
self._consumer_creator = consumer_creator |
||||
self._failure_outcome = failure_outcome |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._expiration_manager = None |
||||
|
||||
self._wrapped_ingestion_consumer = None |
||||
self._pending_ingestion = [] |
||||
self._ingestion_complete = False |
||||
self._processing = False |
||||
|
||||
def set_expiration_manager(self, expiration_manager): |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def _abort_internal_only(self): |
||||
self._wrapped_ingestion_consumer = None |
||||
self._pending_ingestion = None |
||||
|
||||
def _abort_and_notify(self, outcome): |
||||
self._abort_internal_only() |
||||
self._termination_manager.abort(outcome) |
||||
self._transmission_manager.abort(outcome) |
||||
self._expiration_manager.abort() |
||||
|
||||
def _next(self): |
||||
"""Computes the next step for ingestion. |
||||
|
||||
Returns: |
||||
A payload, complete, continue triplet indicating what payload (if any) is |
||||
available to feed into customer code, whether or not the sequence of |
||||
payloads has terminated, and whether or not there is anything |
||||
immediately actionable to call customer code to do. |
||||
""" |
||||
if self._pending_ingestion is None: |
||||
return None, False, False |
||||
elif self._pending_ingestion: |
||||
payload = self._pending_ingestion.pop(0) |
||||
complete = self._ingestion_complete and not self._pending_ingestion |
||||
return payload, complete, True |
||||
elif self._ingestion_complete: |
||||
return None, True, True |
||||
else: |
||||
return None, False, False |
||||
|
||||
def _process(self, wrapped_ingestion_consumer, payload, complete): |
||||
"""A method to call to execute customer code. |
||||
|
||||
This object's lock must *not* be held when calling this method. |
||||
|
||||
Args: |
||||
wrapped_ingestion_consumer: The _WrappedConsumer with which to pass |
||||
payloads to customer code. |
||||
payload: A customer payload. May be None only if complete is True. |
||||
complete: Whether or not the sequence of payloads to pass to the customer |
||||
has concluded. |
||||
""" |
||||
while True: |
||||
consumption_outcome = callable_util.call_logging_exceptions( |
||||
wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE, |
||||
payload, complete) |
||||
if consumption_outcome.exception is None: |
||||
if consumption_outcome.return_value: |
||||
with self._lock: |
||||
if complete: |
||||
self._pending_ingestion = None |
||||
self._termination_manager.ingestion_complete() |
||||
return |
||||
else: |
||||
payload, complete, moar = self._next() |
||||
if not moar: |
||||
self._processing = False |
||||
return |
||||
else: |
||||
with self._lock: |
||||
if self._pending_ingestion is not None: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
self._processing = False |
||||
return |
||||
else: |
||||
with self._lock: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
self._processing = False |
||||
return |
||||
|
||||
def start(self, requirement): |
||||
if self._pending_ingestion is not None: |
||||
def initialize(): |
||||
consumer_creation_outcome = callable_util.call_logging_exceptions( |
||||
self._consumer_creator.create_consumer, |
||||
_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement) |
||||
if consumer_creation_outcome.return_value is None: |
||||
with self._lock: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
self._processing = False |
||||
elif consumer_creation_outcome.return_value.remote_error: |
||||
with self._lock: |
||||
self._abort_and_notify(interfaces.Outcome.RECEPTION_FAILURE) |
||||
self._processing = False |
||||
elif consumer_creation_outcome.return_value.abandoned: |
||||
with self._lock: |
||||
if self._pending_ingestion is not None: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
self._processing = False |
||||
else: |
||||
wrapped_ingestion_consumer = _WrappedConsumer( |
||||
consumer_creation_outcome.return_value.consumer) |
||||
with self._lock: |
||||
self._wrapped_ingestion_consumer = wrapped_ingestion_consumer |
||||
payload, complete, moar = self._next() |
||||
if not moar: |
||||
self._processing = False |
||||
return |
||||
|
||||
self._process(wrapped_ingestion_consumer, payload, complete) |
||||
|
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE)) |
||||
self._processing = True |
||||
|
||||
def consume(self, payload): |
||||
if self._ingestion_complete: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
elif self._pending_ingestion is not None: |
||||
if self._processing: |
||||
self._pending_ingestion.append(payload) |
||||
else: |
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
self._wrapped_ingestion_consumer, payload, False) |
||||
self._processing = True |
||||
|
||||
def terminate(self): |
||||
if self._ingestion_complete: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
else: |
||||
self._ingestion_complete = True |
||||
if self._pending_ingestion is not None and not self._processing: |
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
self._wrapped_ingestion_consumer, None, True) |
||||
self._processing = True |
||||
|
||||
def consume_and_terminate(self, payload): |
||||
if self._ingestion_complete: |
||||
self._abort_and_notify(self._failure_outcome) |
||||
else: |
||||
self._ingestion_complete = True |
||||
if self._pending_ingestion is not None: |
||||
if self._processing: |
||||
self._pending_ingestion.append(payload) |
||||
else: |
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
self._wrapped_ingestion_consumer, payload, True) |
||||
self._processing = True |
||||
|
||||
def abort(self): |
||||
"""See _interfaces.IngestionManager.abort for specification.""" |
||||
self._abort_internal_only() |
||||
|
||||
|
||||
def front_ingestion_manager( |
||||
lock, pool, subscription, termination_manager, transmission_manager, |
||||
operation_context): |
||||
"""Creates an IngestionManager appropriate for front-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
pool: A thread pool in which to execute customer code. |
||||
subscription: A interfaces.ServicedSubscription indicating the |
||||
customer's interest in the results of the operation. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
operation_context: A interfaces.OperationContext for the operation. |
||||
|
||||
Returns: |
||||
An IngestionManager appropriate for front-side use. |
||||
""" |
||||
ingestion_manager = _IngestionManager( |
||||
lock, pool, _FrontConsumerCreator(subscription, operation_context), |
||||
interfaces.Outcome.SERVICED_FAILURE, termination_manager, |
||||
transmission_manager) |
||||
ingestion_manager.start(None) |
||||
return ingestion_manager |
||||
|
||||
|
||||
def back_ingestion_manager( |
||||
lock, pool, servicer, termination_manager, transmission_manager, |
||||
operation_context, emission_consumer): |
||||
"""Creates an IngestionManager appropriate for back-side use. |
||||
|
||||
Args: |
||||
lock: The operation-wide lock. |
||||
pool: A thread pool in which to execute customer code. |
||||
servicer: A interfaces.Servicer for servicing the operation. |
||||
termination_manager: The _interfaces.TerminationManager for the operation. |
||||
transmission_manager: The _interfaces.TransmissionManager for the |
||||
operation. |
||||
operation_context: A interfaces.OperationContext for the operation. |
||||
emission_consumer: The _interfaces.EmissionConsumer for the operation. |
||||
|
||||
Returns: |
||||
An IngestionManager appropriate for back-side use. |
||||
""" |
||||
ingestion_manager = _IngestionManager( |
||||
lock, pool, _BackConsumerCreator( |
||||
servicer, operation_context, emission_consumer), |
||||
interfaces.Outcome.SERVICER_FAILURE, termination_manager, |
||||
transmission_manager) |
||||
return ingestion_manager |
@ -1,266 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Package-internal interfaces.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# interfaces is referenced from specification in this module. |
||||
from grpc.framework.base import interfaces # pylint: disable=unused-import |
||||
from grpc.framework.foundation import stream |
||||
|
||||
|
||||
class TerminationManager(six.with_metaclass(abc.ABCMeta)): |
||||
"""An object responsible for handling the termination of an operation.""" |
||||
|
||||
@abc.abstractmethod |
||||
def set_expiration_manager(self, expiration_manager): |
||||
"""Sets the ExpirationManager with which this object will cooperate.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def is_active(self): |
||||
"""Reports whether or not the operation is active. |
||||
|
||||
Returns: |
||||
True if the operation is active or False if the operation has terminated. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_callback(self, callback): |
||||
"""Registers a callback to be called on operation termination. |
||||
|
||||
If the operation has already terminated, the callback will be called |
||||
immediately. |
||||
|
||||
Args: |
||||
callback: A callable that will be passed an interfaces.Outcome value. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def emission_complete(self): |
||||
"""Indicates that emissions from customer code have completed.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def transmission_complete(self): |
||||
"""Indicates that transmissions to the remote end are complete.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def ingestion_complete(self): |
||||
"""Indicates that customer code ingestion of received values is complete.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def abort(self, outcome): |
||||
"""Indicates that the operation must abort for the indicated reason. |
||||
|
||||
Args: |
||||
outcome: An interfaces.Outcome indicating operation abortion. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class TransmissionManager(six.with_metaclass(abc.ABCMeta)): |
||||
"""A manager responsible for transmitting to the other end of an operation.""" |
||||
|
||||
@abc.abstractmethod |
||||
def inmit(self, emission, complete): |
||||
"""Accepts a value for transmission to the other end of the operation. |
||||
|
||||
Args: |
||||
emission: A value of some significance to the customer to be transmitted |
||||
to the other end of the operation. May be None only if complete is True. |
||||
complete: A boolean that if True indicates that customer code has emitted |
||||
all values it intends to emit. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def abort(self, outcome): |
||||
"""Indicates that the operation has aborted for the indicated reason. |
||||
|
||||
Args: |
||||
outcome: An interfaces.Outcome indicating operation abortion. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class EmissionManager(six.with_metaclass(abc.ABCMeta, stream.Consumer)): |
||||
"""A manager of values emitted by customer code.""" |
||||
|
||||
@abc.abstractmethod |
||||
def set_ingestion_manager_and_expiration_manager( |
||||
self, ingestion_manager, expiration_manager): |
||||
"""Sets two other objects with which this EmissionManager will cooperate. |
||||
|
||||
Args: |
||||
ingestion_manager: The IngestionManager for the operation. |
||||
expiration_manager: The ExpirationManager for the operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def consume(self, value): |
||||
"""Accepts a value emitted by customer code. |
||||
|
||||
This method should only be called by customer code. |
||||
|
||||
Args: |
||||
value: Any value of significance to the customer. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def terminate(self): |
||||
"""Indicates that no more values will be emitted by customer code. |
||||
|
||||
This method should only be called by customer code. |
||||
|
||||
Implementations of this method may be idempotent and forgive customer code |
||||
calling this method more than once. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def consume_and_terminate(self, value): |
||||
"""Accepts the last value emitted by customer code. |
||||
|
||||
This method should only be called by customer code. |
||||
|
||||
Args: |
||||
value: Any value of significance to the customer. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class IngestionManager(six.with_metaclass(abc.ABCMeta, stream.Consumer)): |
||||
"""A manager responsible for executing customer code.""" |
||||
|
||||
@abc.abstractmethod |
||||
def set_expiration_manager(self, expiration_manager): |
||||
"""Sets the ExpirationManager with which this object will cooperate.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def start(self, requirement): |
||||
"""Commences execution of customer code. |
||||
|
||||
Args: |
||||
requirement: Some value unavailable at the time of this object's |
||||
construction that is required to begin executing customer code. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def consume(self, payload): |
||||
"""Accepts a customer-significant value to be supplied to customer code. |
||||
|
||||
Args: |
||||
payload: Some customer-significant value. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def terminate(self): |
||||
"""Indicates the end of values to be supplied to customer code.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def consume_and_terminate(self, payload): |
||||
"""Accepts the last value to be supplied to customer code. |
||||
|
||||
Args: |
||||
payload: Some customer-significant value (and the last such value). |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def abort(self): |
||||
"""Indicates to this manager that the operation has aborted.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ExpirationManager(six.with_metaclass(abc.ABCMeta)): |
||||
"""A manager responsible for aborting the operation if it runs out of time.""" |
||||
|
||||
@abc.abstractmethod |
||||
def change_timeout(self, timeout): |
||||
"""Changes the timeout allotted for the operation. |
||||
|
||||
Operation duration is always measure from the beginning of the operation; |
||||
calling this method changes the operation's allotted time to timeout total |
||||
seconds, not timeout seconds from the time of this method call. |
||||
|
||||
Args: |
||||
timeout: A length of time in seconds to allow for the operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def deadline(self): |
||||
"""Returns the time until which the operation is allowed to run. |
||||
|
||||
Returns: |
||||
The time (seconds since the epoch) at which the operation will expire. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def abort(self): |
||||
"""Indicates to this manager that the operation has aborted.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ReceptionManager(six.with_metaclass(abc.ABCMeta)): |
||||
"""A manager responsible for receiving tickets from the other end.""" |
||||
|
||||
@abc.abstractmethod |
||||
def receive_ticket(self, ticket): |
||||
"""Handle a ticket from the other side of the operation. |
||||
|
||||
Args: |
||||
ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket |
||||
appropriate to this end of the operation and this object. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class CancellationManager(six.with_metaclass(abc.ABCMeta)): |
||||
"""A manager of operation cancellation.""" |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self): |
||||
"""Cancels the operation.""" |
||||
raise NotImplementedError() |
@ -1,400 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for ticket reception.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.base import _interfaces |
||||
|
||||
_INITIAL_FRONT_TO_BACK_TICKET_KINDS = ( |
||||
interfaces.FrontToBackTicket.Kind.COMMENCEMENT, |
||||
interfaces.FrontToBackTicket.Kind.ENTIRE, |
||||
) |
||||
|
||||
|
||||
class _Receiver(six.with_metaclass(abc.ABCMeta)): |
||||
"""Common specification of different ticket-handling behavior.""" |
||||
|
||||
@abc.abstractmethod |
||||
def abort_if_abortive(self, ticket): |
||||
"""Aborts the operation if the ticket is abortive. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
|
||||
Returns: |
||||
A boolean indicating whether or not this Receiver aborted the operation |
||||
based on the ticket. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def receive(self, ticket): |
||||
"""Handles a just-arrived ticket. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
|
||||
Returns: |
||||
A boolean indicating whether or not the ticket was terminal (i.e. whether |
||||
or not non-abortive tickets are legal after this one). |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def reception_failure(self): |
||||
"""Aborts the operation with an indication of reception failure.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
def _abort( |
||||
outcome, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Indicates abortion with the given outcome to the given managers.""" |
||||
termination_manager.abort(outcome) |
||||
transmission_manager.abort(outcome) |
||||
ingestion_manager.abort() |
||||
expiration_manager.abort() |
||||
|
||||
|
||||
def _abort_if_abortive( |
||||
ticket, abortive, termination_manager, transmission_manager, |
||||
ingestion_manager, expiration_manager): |
||||
"""Determines a ticket's being abortive and if so aborts the operation. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
abortive: A callable that takes a ticket and returns an interfaces.Outcome |
||||
indicating that the operation should be aborted or None indicating that |
||||
the operation should not be aborted. |
||||
termination_manager: The operation's _interfaces.TerminationManager. |
||||
transmission_manager: The operation's _interfaces.TransmissionManager. |
||||
ingestion_manager: The operation's _interfaces.IngestionManager. |
||||
expiration_manager: The operation's _interfaces.ExpirationManager. |
||||
|
||||
Returns: |
||||
True if the operation was aborted; False otherwise. |
||||
""" |
||||
abortion_outcome = abortive(ticket) |
||||
if abortion_outcome is None: |
||||
return False |
||||
else: |
||||
_abort( |
||||
abortion_outcome, termination_manager, transmission_manager, |
||||
ingestion_manager, expiration_manager) |
||||
return True |
||||
|
||||
|
||||
def _reception_failure( |
||||
termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Aborts the operation with an indication of reception failure.""" |
||||
_abort( |
||||
interfaces.Outcome.RECEPTION_FAILURE, termination_manager, |
||||
transmission_manager, ingestion_manager, expiration_manager) |
||||
|
||||
|
||||
class _BackReceiver(_Receiver): |
||||
"""Ticket-handling specific to the back side of an operation.""" |
||||
|
||||
def __init__( |
||||
self, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
termination_manager: The operation's _interfaces.TerminationManager. |
||||
transmission_manager: The operation's _interfaces.TransmissionManager. |
||||
ingestion_manager: The operation's _interfaces.IngestionManager. |
||||
expiration_manager: The operation's _interfaces.ExpirationManager. |
||||
""" |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
self._first_ticket_seen = False |
||||
self._last_ticket_seen = False |
||||
|
||||
def _abortive(self, ticket): |
||||
"""Determines whether or not (and if so, how) a ticket is abortive. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
|
||||
Returns: |
||||
An interfaces.Outcome value describing operation abortion if the |
||||
ticket is abortive or None if the ticket is not abortive. |
||||
""" |
||||
if ticket.kind is interfaces.FrontToBackTicket.Kind.CANCELLATION: |
||||
return interfaces.Outcome.CANCELLED |
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.EXPIRATION: |
||||
return interfaces.Outcome.EXPIRED |
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE: |
||||
return interfaces.Outcome.SERVICED_FAILURE |
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE: |
||||
return interfaces.Outcome.SERVICED_FAILURE |
||||
elif (ticket.kind in _INITIAL_FRONT_TO_BACK_TICKET_KINDS and |
||||
self._first_ticket_seen): |
||||
return interfaces.Outcome.RECEPTION_FAILURE |
||||
elif self._last_ticket_seen: |
||||
return interfaces.Outcome.RECEPTION_FAILURE |
||||
else: |
||||
return None |
||||
|
||||
def abort_if_abortive(self, ticket): |
||||
"""See _Receiver.abort_if_abortive for specification.""" |
||||
return _abort_if_abortive( |
||||
ticket, self._abortive, self._termination_manager, |
||||
self._transmission_manager, self._ingestion_manager, |
||||
self._expiration_manager) |
||||
|
||||
def receive(self, ticket): |
||||
"""See _Receiver.receive for specification.""" |
||||
if ticket.timeout is not None: |
||||
self._expiration_manager.change_timeout(ticket.timeout) |
||||
|
||||
if ticket.kind is interfaces.FrontToBackTicket.Kind.COMMENCEMENT: |
||||
self._first_ticket_seen = True |
||||
self._ingestion_manager.start(ticket.name) |
||||
if ticket.payload is not None: |
||||
self._ingestion_manager.consume(ticket.payload) |
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.CONTINUATION: |
||||
self._ingestion_manager.consume(ticket.payload) |
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.COMPLETION: |
||||
self._last_ticket_seen = True |
||||
if ticket.payload is None: |
||||
self._ingestion_manager.terminate() |
||||
else: |
||||
self._ingestion_manager.consume_and_terminate(ticket.payload) |
||||
else: |
||||
self._first_ticket_seen = True |
||||
self._last_ticket_seen = True |
||||
self._ingestion_manager.start(ticket.name) |
||||
if ticket.payload is None: |
||||
self._ingestion_manager.terminate() |
||||
else: |
||||
self._ingestion_manager.consume_and_terminate(ticket.payload) |
||||
|
||||
def reception_failure(self): |
||||
"""See _Receiver.reception_failure for specification.""" |
||||
_reception_failure( |
||||
self._termination_manager, self._transmission_manager, |
||||
self._ingestion_manager, self._expiration_manager) |
||||
|
||||
|
||||
class _FrontReceiver(_Receiver): |
||||
"""Ticket-handling specific to the front side of an operation.""" |
||||
|
||||
def __init__( |
||||
self, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
termination_manager: The operation's _interfaces.TerminationManager. |
||||
transmission_manager: The operation's _interfaces.TransmissionManager. |
||||
ingestion_manager: The operation's _interfaces.IngestionManager. |
||||
expiration_manager: The operation's _interfaces.ExpirationManager. |
||||
""" |
||||
self._termination_manager = termination_manager |
||||
self._transmission_manager = transmission_manager |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
self._last_ticket_seen = False |
||||
|
||||
def _abortive(self, ticket): |
||||
"""Determines whether or not (and if so, how) a ticket is abortive. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
|
||||
Returns: |
||||
An interfaces.Outcome value describing operation abortion if the ticket |
||||
is abortive or None if the ticket is not abortive. |
||||
""" |
||||
if ticket.kind is interfaces.BackToFrontTicket.Kind.CANCELLATION: |
||||
return interfaces.Outcome.CANCELLED |
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.EXPIRATION: |
||||
return interfaces.Outcome.EXPIRED |
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE: |
||||
return interfaces.Outcome.SERVICER_FAILURE |
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE: |
||||
return interfaces.Outcome.SERVICER_FAILURE |
||||
elif self._last_ticket_seen: |
||||
return interfaces.Outcome.RECEPTION_FAILURE |
||||
else: |
||||
return None |
||||
|
||||
def abort_if_abortive(self, ticket): |
||||
"""See _Receiver.abort_if_abortive for specification.""" |
||||
return _abort_if_abortive( |
||||
ticket, self._abortive, self._termination_manager, |
||||
self._transmission_manager, self._ingestion_manager, |
||||
self._expiration_manager) |
||||
|
||||
def receive(self, ticket): |
||||
"""See _Receiver.receive for specification.""" |
||||
if ticket.kind is interfaces.BackToFrontTicket.Kind.CONTINUATION: |
||||
self._ingestion_manager.consume(ticket.payload) |
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.COMPLETION: |
||||
self._last_ticket_seen = True |
||||
if ticket.payload is None: |
||||
self._ingestion_manager.terminate() |
||||
else: |
||||
self._ingestion_manager.consume_and_terminate(ticket.payload) |
||||
|
||||
def reception_failure(self): |
||||
"""See _Receiver.reception_failure for specification.""" |
||||
_reception_failure( |
||||
self._termination_manager, self._transmission_manager, |
||||
self._ingestion_manager, self._expiration_manager) |
||||
|
||||
|
||||
class _ReceptionManager(_interfaces.ReceptionManager): |
||||
"""A ReceptionManager based around a _Receiver passed to it.""" |
||||
|
||||
def __init__(self, lock, receiver): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
receiver: A _Receiver responsible for handling received tickets. |
||||
""" |
||||
self._lock = lock |
||||
self._receiver = receiver |
||||
|
||||
self._lowest_unseen_sequence_number = 0 |
||||
self._out_of_sequence_tickets = {} |
||||
self._completed_sequence_number = None |
||||
self._aborted = False |
||||
|
||||
def _sequence_failure(self, ticket): |
||||
"""Determines a just-arrived ticket's sequential legitimacy. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket. |
||||
|
||||
Returns: |
||||
True if the ticket is sequentially legitimate; False otherwise. |
||||
""" |
||||
if ticket.sequence_number < self._lowest_unseen_sequence_number: |
||||
return True |
||||
elif ticket.sequence_number in self._out_of_sequence_tickets: |
||||
return True |
||||
elif (self._completed_sequence_number is not None and |
||||
self._completed_sequence_number <= ticket.sequence_number): |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def _process(self, ticket): |
||||
"""Process those tickets ready to be processed. |
||||
|
||||
Args: |
||||
ticket: A just-arrived ticket the sequence number of which matches this |
||||
_ReceptionManager's _lowest_unseen_sequence_number field. |
||||
""" |
||||
while True: |
||||
completed = self._receiver.receive(ticket) |
||||
if completed: |
||||
self._out_of_sequence_tickets.clear() |
||||
self._completed_sequence_number = ticket.sequence_number |
||||
self._lowest_unseen_sequence_number = ticket.sequence_number + 1 |
||||
return |
||||
else: |
||||
next_ticket = self._out_of_sequence_tickets.pop( |
||||
ticket.sequence_number + 1, None) |
||||
if next_ticket is None: |
||||
self._lowest_unseen_sequence_number = ticket.sequence_number + 1 |
||||
return |
||||
else: |
||||
ticket = next_ticket |
||||
|
||||
def receive_ticket(self, ticket): |
||||
"""See _interfaces.ReceptionManager.receive_ticket for specification.""" |
||||
with self._lock: |
||||
if self._aborted: |
||||
return |
||||
elif self._sequence_failure(ticket): |
||||
self._receiver.reception_failure() |
||||
self._aborted = True |
||||
elif self._receiver.abort_if_abortive(ticket): |
||||
self._aborted = True |
||||
elif ticket.sequence_number == self._lowest_unseen_sequence_number: |
||||
self._process(ticket) |
||||
else: |
||||
self._out_of_sequence_tickets[ticket.sequence_number] = ticket |
||||
|
||||
|
||||
def front_reception_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Creates a _interfaces.ReceptionManager for front-side use. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
termination_manager: The operation's _interfaces.TerminationManager. |
||||
transmission_manager: The operation's _interfaces.TransmissionManager. |
||||
ingestion_manager: The operation's _interfaces.IngestionManager. |
||||
expiration_manager: The operation's _interfaces.ExpirationManager. |
||||
|
||||
Returns: |
||||
A _interfaces.ReceptionManager appropriate for front-side use. |
||||
""" |
||||
return _ReceptionManager( |
||||
lock, _FrontReceiver( |
||||
termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager)) |
||||
|
||||
|
||||
def back_reception_manager( |
||||
lock, termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager): |
||||
"""Creates a _interfaces.ReceptionManager for back-side use. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
termination_manager: The operation's _interfaces.TerminationManager. |
||||
transmission_manager: The operation's _interfaces.TransmissionManager. |
||||
ingestion_manager: The operation's _interfaces.IngestionManager. |
||||
expiration_manager: The operation's _interfaces.ExpirationManager. |
||||
|
||||
Returns: |
||||
A _interfaces.ReceptionManager appropriate for back-side use. |
||||
""" |
||||
return _ReceptionManager( |
||||
lock, _BackReceiver( |
||||
termination_manager, transmission_manager, ingestion_manager, |
||||
expiration_manager)) |
@ -1,204 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for operation termination.""" |
||||
|
||||
import enum |
||||
|
||||
from grpc.framework.base import _constants |
||||
from grpc.framework.base import _interfaces |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import callable_util |
||||
|
||||
_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' |
||||
|
||||
|
||||
@enum.unique |
||||
class _Requirement(enum.Enum): |
||||
"""Symbols indicating events required for termination.""" |
||||
|
||||
EMISSION = 'emission' |
||||
TRANSMISSION = 'transmission' |
||||
INGESTION = 'ingestion' |
||||
|
||||
_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,) |
||||
_BACK_NOT_LISTENING_REQUIREMENTS = ( |
||||
_Requirement.EMISSION, _Requirement.INGESTION,) |
||||
_LISTENING_REQUIREMENTS = ( |
||||
_Requirement.TRANSMISSION, _Requirement.INGESTION,) |
||||
|
||||
|
||||
class _TerminationManager(_interfaces.TerminationManager): |
||||
"""An implementation of _interfaces.TerminationManager.""" |
||||
|
||||
def __init__( |
||||
self, work_pool, utility_pool, action, requirements, local_failure): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
work_pool: A thread pool in which customer work will be done. |
||||
utility_pool: A thread pool in which work utility work will be done. |
||||
action: An action to call on operation termination. |
||||
requirements: A combination of _Requirement values identifying what |
||||
must finish for the operation to be considered completed. |
||||
local_failure: An interfaces.Outcome specifying what constitutes local |
||||
failure of customer work. |
||||
""" |
||||
self._work_pool = work_pool |
||||
self._utility_pool = utility_pool |
||||
self._action = action |
||||
self._local_failure = local_failure |
||||
self._has_locally_failed = False |
||||
self._expiration_manager = None |
||||
|
||||
self._outstanding_requirements = set(requirements) |
||||
self._outcome = None |
||||
self._callbacks = [] |
||||
|
||||
def set_expiration_manager(self, expiration_manager): |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def _terminate(self, outcome): |
||||
"""Terminates the operation. |
||||
|
||||
Args: |
||||
outcome: An interfaces.Outcome describing the outcome of the operation. |
||||
""" |
||||
self._expiration_manager.abort() |
||||
self._outstanding_requirements = None |
||||
callbacks = list(self._callbacks) |
||||
self._callbacks = None |
||||
self._outcome = outcome |
||||
|
||||
act = callable_util.with_exceptions_logged( |
||||
self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) |
||||
|
||||
if self._has_locally_failed: |
||||
self._utility_pool.submit(act, outcome) |
||||
else: |
||||
def call_callbacks_and_act(callbacks, outcome): |
||||
for callback in callbacks: |
||||
callback_outcome = callable_util.call_logging_exceptions( |
||||
callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) |
||||
if callback_outcome.exception is not None: |
||||
outcome = self._local_failure |
||||
break |
||||
self._utility_pool.submit(act, outcome) |
||||
|
||||
self._work_pool.submit(callable_util.with_exceptions_logged( |
||||
call_callbacks_and_act, |
||||
_constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
callbacks, outcome) |
||||
|
||||
def is_active(self): |
||||
"""See _interfaces.TerminationManager.is_active for specification.""" |
||||
return self._outstanding_requirements is not None |
||||
|
||||
def add_callback(self, callback): |
||||
"""See _interfaces.TerminationManager.add_callback for specification.""" |
||||
if not self._has_locally_failed: |
||||
if self._outstanding_requirements is None: |
||||
self._work_pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), self._outcome) |
||||
else: |
||||
self._callbacks.append(callback) |
||||
|
||||
def emission_complete(self): |
||||
"""See superclass method for specification.""" |
||||
if self._outstanding_requirements is not None: |
||||
self._outstanding_requirements.discard(_Requirement.EMISSION) |
||||
if not self._outstanding_requirements: |
||||
self._terminate(interfaces.Outcome.COMPLETED) |
||||
|
||||
def transmission_complete(self): |
||||
"""See superclass method for specification.""" |
||||
if self._outstanding_requirements is not None: |
||||
self._outstanding_requirements.discard(_Requirement.TRANSMISSION) |
||||
if not self._outstanding_requirements: |
||||
self._terminate(interfaces.Outcome.COMPLETED) |
||||
|
||||
def ingestion_complete(self): |
||||
"""See superclass method for specification.""" |
||||
if self._outstanding_requirements is not None: |
||||
self._outstanding_requirements.discard(_Requirement.INGESTION) |
||||
if not self._outstanding_requirements: |
||||
self._terminate(interfaces.Outcome.COMPLETED) |
||||
|
||||
def abort(self, outcome): |
||||
"""See _interfaces.TerminationManager.abort for specification.""" |
||||
if outcome is self._local_failure: |
||||
self._has_failed_locally = True |
||||
if self._outstanding_requirements is not None: |
||||
self._terminate(outcome) |
||||
|
||||
|
||||
def front_termination_manager( |
||||
work_pool, utility_pool, action, subscription_kind): |
||||
"""Creates a TerminationManager appropriate for front-side use. |
||||
|
||||
Args: |
||||
work_pool: A thread pool in which customer work will be done. |
||||
utility_pool: A thread pool in which work utility work will be done. |
||||
action: An action to call on operation termination. |
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value. |
||||
|
||||
Returns: |
||||
A TerminationManager appropriate for front-side use. |
||||
""" |
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: |
||||
requirements = _FRONT_NOT_LISTENING_REQUIREMENTS |
||||
else: |
||||
requirements = _LISTENING_REQUIREMENTS |
||||
|
||||
return _TerminationManager( |
||||
work_pool, utility_pool, action, requirements, |
||||
interfaces.Outcome.SERVICED_FAILURE) |
||||
|
||||
|
||||
def back_termination_manager(work_pool, utility_pool, action, subscription_kind): |
||||
"""Creates a TerminationManager appropriate for back-side use. |
||||
|
||||
Args: |
||||
work_pool: A thread pool in which customer work will be done. |
||||
utility_pool: A thread pool in which work utility work will be done. |
||||
action: An action to call on operation termination. |
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value. |
||||
|
||||
Returns: |
||||
A TerminationManager appropriate for back-side use. |
||||
""" |
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: |
||||
requirements = _BACK_NOT_LISTENING_REQUIREMENTS |
||||
else: |
||||
requirements = _LISTENING_REQUIREMENTS |
||||
|
||||
return _TerminationManager( |
||||
work_pool, utility_pool, action, requirements, |
||||
interfaces.Outcome.SERVICER_FAILURE) |
@ -1,429 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for ticket transmission during an operation.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.base import _constants |
||||
from grpc.framework.base import _interfaces |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import callable_util |
||||
|
||||
_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' |
||||
|
||||
_FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES = ( |
||||
interfaces.Outcome.SERVICER_FAILURE, |
||||
) |
||||
_BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES = ( |
||||
interfaces.Outcome.CANCELLED, |
||||
interfaces.Outcome.SERVICED_FAILURE, |
||||
) |
||||
|
||||
_ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND = { |
||||
interfaces.Outcome.CANCELLED: |
||||
interfaces.FrontToBackTicket.Kind.CANCELLATION, |
||||
interfaces.Outcome.EXPIRED: |
||||
interfaces.FrontToBackTicket.Kind.EXPIRATION, |
||||
interfaces.Outcome.RECEPTION_FAILURE: |
||||
interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE, |
||||
interfaces.Outcome.TRANSMISSION_FAILURE: |
||||
interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, |
||||
interfaces.Outcome.SERVICED_FAILURE: |
||||
interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE, |
||||
interfaces.Outcome.SERVICER_FAILURE: |
||||
interfaces.FrontToBackTicket.Kind.SERVICER_FAILURE, |
||||
} |
||||
|
||||
_ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND = { |
||||
interfaces.Outcome.CANCELLED: |
||||
interfaces.BackToFrontTicket.Kind.CANCELLATION, |
||||
interfaces.Outcome.EXPIRED: |
||||
interfaces.BackToFrontTicket.Kind.EXPIRATION, |
||||
interfaces.Outcome.RECEPTION_FAILURE: |
||||
interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE, |
||||
interfaces.Outcome.TRANSMISSION_FAILURE: |
||||
interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, |
||||
interfaces.Outcome.SERVICED_FAILURE: |
||||
interfaces.BackToFrontTicket.Kind.SERVICED_FAILURE, |
||||
interfaces.Outcome.SERVICER_FAILURE: |
||||
interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE, |
||||
} |
||||
|
||||
|
||||
class _Ticketizer(six.with_metaclass(abc.ABCMeta)): |
||||
"""Common specification of different ticket-creating behavior.""" |
||||
|
||||
@abc.abstractmethod |
||||
def ticketize(self, operation_id, sequence_number, payload, complete): |
||||
"""Creates a ticket indicating ordinary operation progress. |
||||
|
||||
Args: |
||||
operation_id: The operation ID for the current operation. |
||||
sequence_number: A sequence number for the ticket. |
||||
payload: A customer payload object. May be None if sequence_number is |
||||
zero or complete is true. |
||||
complete: A boolean indicating whether or not the ticket should describe |
||||
itself as (but for a later indication of operation abortion) the last |
||||
ticket to be sent. |
||||
|
||||
Returns: |
||||
An object of an appropriate type suitable for transmission to the other |
||||
side of the operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome): |
||||
"""Creates a ticket indicating that the operation is aborted. |
||||
|
||||
Args: |
||||
operation_id: The operation ID for the current operation. |
||||
sequence_number: A sequence number for the ticket. |
||||
outcome: An interfaces.Outcome value describing the operation abortion. |
||||
|
||||
Returns: |
||||
An object of an appropriate type suitable for transmission to the other |
||||
side of the operation, or None if transmission is not appropriate for |
||||
the given outcome. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _FrontTicketizer(_Ticketizer): |
||||
"""Front-side ticket-creating behavior.""" |
||||
|
||||
def __init__(self, name, subscription_kind, trace_id, timeout): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
name: The name of the operation. |
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value |
||||
describing the interest the front has in tickets sent from the back. |
||||
trace_id: A uuid.UUID identifying a set of related operations to which |
||||
this operation belongs. |
||||
timeout: A length of time in seconds to allow for the entire operation. |
||||
""" |
||||
self._name = name |
||||
self._subscription_kind = subscription_kind |
||||
self._trace_id = trace_id |
||||
self._timeout = timeout |
||||
|
||||
def ticketize(self, operation_id, sequence_number, payload, complete): |
||||
"""See _Ticketizer.ticketize for specification.""" |
||||
if sequence_number: |
||||
if complete: |
||||
kind = interfaces.FrontToBackTicket.Kind.COMPLETION |
||||
else: |
||||
kind = interfaces.FrontToBackTicket.Kind.CONTINUATION |
||||
return interfaces.FrontToBackTicket( |
||||
operation_id, sequence_number, kind, self._name, |
||||
self._subscription_kind, self._trace_id, payload, self._timeout) |
||||
else: |
||||
if complete: |
||||
kind = interfaces.FrontToBackTicket.Kind.ENTIRE |
||||
else: |
||||
kind = interfaces.FrontToBackTicket.Kind.COMMENCEMENT |
||||
return interfaces.FrontToBackTicket( |
||||
operation_id, 0, kind, self._name, self._subscription_kind, |
||||
self._trace_id, payload, self._timeout) |
||||
|
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome): |
||||
"""See _Ticketizer.ticketize_abortion for specification.""" |
||||
if outcome in _FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES: |
||||
return None |
||||
else: |
||||
kind = _ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND[outcome] |
||||
return interfaces.FrontToBackTicket( |
||||
operation_id, sequence_number, kind, None, None, None, None, None) |
||||
|
||||
|
||||
class _BackTicketizer(_Ticketizer): |
||||
"""Back-side ticket-creating behavior.""" |
||||
|
||||
def ticketize(self, operation_id, sequence_number, payload, complete): |
||||
"""See _Ticketizer.ticketize for specification.""" |
||||
if complete: |
||||
kind = interfaces.BackToFrontTicket.Kind.COMPLETION |
||||
else: |
||||
kind = interfaces.BackToFrontTicket.Kind.CONTINUATION |
||||
return interfaces.BackToFrontTicket( |
||||
operation_id, sequence_number, kind, payload) |
||||
|
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome): |
||||
"""See _Ticketizer.ticketize_abortion for specification.""" |
||||
if outcome in _BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES: |
||||
return None |
||||
else: |
||||
kind = _ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND[outcome] |
||||
return interfaces.BackToFrontTicket( |
||||
operation_id, sequence_number, kind, None) |
||||
|
||||
|
||||
class TransmissionManager(six.with_metaclass(abc.ABCMeta, _interfaces.TransmissionManager)): |
||||
"""A _interfaces.TransmissionManager on which other managers may be set.""" |
||||
|
||||
@abc.abstractmethod |
||||
def set_ingestion_and_expiration_managers( |
||||
self, ingestion_manager, expiration_manager): |
||||
"""Sets two of the other managers with which this manager may interact. |
||||
|
||||
Args: |
||||
ingestion_manager: The _interfaces.IngestionManager associated with the |
||||
current operation. |
||||
expiration_manager: The _interfaces.ExpirationManager associated with the |
||||
current operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _EmptyTransmissionManager(TransmissionManager): |
||||
"""A completely no-operative _interfaces.TransmissionManager.""" |
||||
|
||||
def set_ingestion_and_expiration_managers( |
||||
self, ingestion_manager, expiration_manager): |
||||
"""See overriden method for specification.""" |
||||
|
||||
def inmit(self, emission, complete): |
||||
"""See _interfaces.TransmissionManager.inmit for specification.""" |
||||
|
||||
def abort(self, outcome): |
||||
"""See _interfaces.TransmissionManager.abort for specification.""" |
||||
|
||||
|
||||
class _TransmittingTransmissionManager(TransmissionManager): |
||||
"""A TransmissionManager implementation that sends tickets.""" |
||||
|
||||
def __init__( |
||||
self, lock, pool, callback, operation_id, ticketizer, |
||||
termination_manager): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
pool: A thread pool in which the work of transmitting tickets will be |
||||
performed. |
||||
callback: A callable that accepts tickets and sends them to the other side |
||||
of the operation. |
||||
operation_id: The operation's ID. |
||||
ticketizer: A _Ticketizer for ticket creation. |
||||
termination_manager: The _interfaces.TerminationManager associated with |
||||
this operation. |
||||
""" |
||||
self._lock = lock |
||||
self._pool = pool |
||||
self._callback = callback |
||||
self._operation_id = operation_id |
||||
self._ticketizer = ticketizer |
||||
self._termination_manager = termination_manager |
||||
self._ingestion_manager = None |
||||
self._expiration_manager = None |
||||
|
||||
self._emissions = [] |
||||
self._emission_complete = False |
||||
self._outcome = None |
||||
self._lowest_unused_sequence_number = 0 |
||||
self._transmitting = False |
||||
|
||||
def set_ingestion_and_expiration_managers( |
||||
self, ingestion_manager, expiration_manager): |
||||
"""See overridden method for specification.""" |
||||
self._ingestion_manager = ingestion_manager |
||||
self._expiration_manager = expiration_manager |
||||
|
||||
def _lead_ticket(self, emission, complete): |
||||
"""Creates a ticket suitable for leading off the transmission loop. |
||||
|
||||
Args: |
||||
emission: A customer payload object to be sent to the other side of the |
||||
operation. |
||||
complete: Whether or not the sequence of customer payloads ends with |
||||
the passed object. |
||||
|
||||
Returns: |
||||
A ticket with which to lead off the transmission loop. |
||||
""" |
||||
sequence_number = self._lowest_unused_sequence_number |
||||
self._lowest_unused_sequence_number += 1 |
||||
return self._ticketizer.ticketize( |
||||
self._operation_id, sequence_number, emission, complete) |
||||
|
||||
def _abortive_response_ticket(self, outcome): |
||||
"""Creates a ticket indicating operation abortion. |
||||
|
||||
Args: |
||||
outcome: An interfaces.Outcome value describing operation abortion. |
||||
|
||||
Returns: |
||||
A ticket indicating operation abortion. |
||||
""" |
||||
ticket = self._ticketizer.ticketize_abortion( |
||||
self._operation_id, self._lowest_unused_sequence_number, outcome) |
||||
if ticket is None: |
||||
return None |
||||
else: |
||||
self._lowest_unused_sequence_number += 1 |
||||
return ticket |
||||
|
||||
def _next_ticket(self): |
||||
"""Creates the next ticket to be sent to the other side of the operation. |
||||
|
||||
Returns: |
||||
A (completed, ticket) tuple comprised of a boolean indicating whether or |
||||
not the sequence of tickets has completed normally and a ticket to send |
||||
to the other side if the sequence of tickets hasn't completed. The tuple |
||||
will never have both a True first element and a non-None second element. |
||||
""" |
||||
if self._emissions is None: |
||||
return False, None |
||||
elif self._outcome is None: |
||||
if self._emissions: |
||||
payload = self._emissions.pop(0) |
||||
complete = self._emission_complete and not self._emissions |
||||
sequence_number = self._lowest_unused_sequence_number |
||||
self._lowest_unused_sequence_number += 1 |
||||
return complete, self._ticketizer.ticketize( |
||||
self._operation_id, sequence_number, payload, complete) |
||||
else: |
||||
return self._emission_complete, None |
||||
else: |
||||
ticket = self._abortive_response_ticket(self._outcome) |
||||
self._emissions = None |
||||
return False, None if ticket is None else ticket |
||||
|
||||
def _transmit(self, ticket): |
||||
"""Commences the transmission loop sending tickets. |
||||
|
||||
Args: |
||||
ticket: A ticket to be sent to the other side of the operation. |
||||
""" |
||||
def transmit(ticket): |
||||
while True: |
||||
transmission_outcome = callable_util.call_logging_exceptions( |
||||
self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket) |
||||
if transmission_outcome.exception is None: |
||||
with self._lock: |
||||
complete, ticket = self._next_ticket() |
||||
if ticket is None: |
||||
if complete: |
||||
self._termination_manager.transmission_complete() |
||||
self._transmitting = False |
||||
return |
||||
else: |
||||
with self._lock: |
||||
self._emissions = None |
||||
self._termination_manager.abort( |
||||
interfaces.Outcome.TRANSMISSION_FAILURE) |
||||
self._ingestion_manager.abort() |
||||
self._expiration_manager.abort() |
||||
self._transmitting = False |
||||
return |
||||
|
||||
self._pool.submit(callable_util.with_exceptions_logged( |
||||
transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket) |
||||
self._transmitting = True |
||||
|
||||
def inmit(self, emission, complete): |
||||
"""See _interfaces.TransmissionManager.inmit for specification.""" |
||||
if self._emissions is not None and self._outcome is None: |
||||
self._emission_complete = complete |
||||
if self._transmitting: |
||||
self._emissions.append(emission) |
||||
else: |
||||
self._transmit(self._lead_ticket(emission, complete)) |
||||
|
||||
def abort(self, outcome): |
||||
"""See _interfaces.TransmissionManager.abort for specification.""" |
||||
if self._emissions is not None and self._outcome is None: |
||||
self._outcome = outcome |
||||
if not self._transmitting: |
||||
ticket = self._abortive_response_ticket(outcome) |
||||
self._emissions = None |
||||
if ticket is not None: |
||||
self._transmit(ticket) |
||||
|
||||
|
||||
def front_transmission_manager( |
||||
lock, pool, callback, operation_id, name, subscription_kind, trace_id, |
||||
timeout, termination_manager): |
||||
"""Creates a TransmissionManager appropriate for front-side use. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
pool: A thread pool in which the work of transmitting tickets will be |
||||
performed. |
||||
callback: A callable that accepts tickets and sends them to the other side |
||||
of the operation. |
||||
operation_id: The operation's ID. |
||||
name: The name of the operation. |
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value |
||||
describing the interest the front has in tickets sent from the back. |
||||
trace_id: A uuid.UUID identifying a set of related operations to which |
||||
this operation belongs. |
||||
timeout: A length of time in seconds to allow for the entire operation. |
||||
termination_manager: The _interfaces.TerminationManager associated with |
||||
this operation. |
||||
|
||||
Returns: |
||||
A TransmissionManager appropriate for front-side use. |
||||
""" |
||||
return _TransmittingTransmissionManager( |
||||
lock, pool, callback, operation_id, _FrontTicketizer( |
||||
name, subscription_kind, trace_id, timeout), |
||||
termination_manager) |
||||
|
||||
|
||||
def back_transmission_manager( |
||||
lock, pool, callback, operation_id, termination_manager, |
||||
subscription_kind): |
||||
"""Creates a TransmissionManager appropriate for back-side use. |
||||
|
||||
Args: |
||||
lock: The operation-servicing-wide lock object. |
||||
pool: A thread pool in which the work of transmitting tickets will be |
||||
performed. |
||||
callback: A callable that accepts tickets and sends them to the other side |
||||
of the operation. |
||||
operation_id: The operation's ID. |
||||
termination_manager: The _interfaces.TerminationManager associated with |
||||
this operation. |
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value |
||||
describing the interest the front has in tickets sent from the back. |
||||
|
||||
Returns: |
||||
A TransmissionManager appropriate for back-side use. |
||||
""" |
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: |
||||
return _EmptyTransmissionManager() |
||||
else: |
||||
return _TransmittingTransmissionManager( |
||||
lock, pool, callback, operation_id, _BackTicketizer(), |
||||
termination_manager) |
@ -1,34 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Exceptions defined and used by the base layer of RPC Framework.""" |
||||
|
||||
|
||||
class NoSuchMethodError(Exception): |
||||
"""Indicates that an operation with an unrecognized name has been called.""" |
@ -1,77 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Entry points into the ticket-exchange-based base layer implementation.""" |
||||
|
||||
# interfaces is referenced from specification in this module. |
||||
from grpc.framework.base import _ends |
||||
from grpc.framework.base import interfaces # pylint: disable=unused-import |
||||
|
||||
|
||||
def front_link(work_pool, transmission_pool, utility_pool): |
||||
"""Factory function for creating interfaces.FrontLinks. |
||||
|
||||
Args: |
||||
work_pool: A thread pool to be used for doing work within the created |
||||
FrontLink object. |
||||
transmission_pool: A thread pool to be used within the created FrontLink |
||||
object for transmitting values to a joined RearLink object. |
||||
utility_pool: A thread pool to be used within the created FrontLink object |
||||
for utility tasks. |
||||
|
||||
Returns: |
||||
An interfaces.FrontLink. |
||||
""" |
||||
return _ends.FrontLink(work_pool, transmission_pool, utility_pool) |
||||
|
||||
|
||||
def back_link( |
||||
servicer, work_pool, transmission_pool, utility_pool, default_timeout, |
||||
maximum_timeout): |
||||
"""Factory function for creating interfaces.BackLinks. |
||||
|
||||
Args: |
||||
servicer: An interfaces.Servicer for servicing operations. |
||||
work_pool: A thread pool to be used for doing work within the created |
||||
BackLink object. |
||||
transmission_pool: A thread pool to be used within the created BackLink |
||||
object for transmitting values to a joined ForeLink object. |
||||
utility_pool: A thread pool to be used within the created BackLink object |
||||
for utility tasks. |
||||
default_timeout: A length of time in seconds to be used as the default |
||||
time alloted for a single operation. |
||||
maximum_timeout: A length of time in seconds to be used as the maximum |
||||
time alloted for a single operation. |
||||
|
||||
Returns: |
||||
An interfaces.BackLink. |
||||
""" |
||||
return _ends.BackLink( |
||||
servicer, work_pool, transmission_pool, utility_pool, default_timeout, |
||||
maximum_timeout) |
@ -1,108 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""In-memory implementations of base layer interfaces.""" |
||||
|
||||
import threading |
||||
|
||||
from grpc.framework.base import _constants |
||||
from grpc.framework.base import interfaces |
||||
from grpc.framework.foundation import callable_util |
||||
|
||||
|
||||
class _Serializer(object): |
||||
"""A utility for serializing values that may arrive concurrently.""" |
||||
|
||||
def __init__(self, pool): |
||||
self._lock = threading.Lock() |
||||
self._pool = pool |
||||
self._sink = None |
||||
self._spinning = False |
||||
self._values = [] |
||||
|
||||
def _spin(self, sink, value): |
||||
while True: |
||||
sink(value) |
||||
with self._lock: |
||||
if self._sink is None or not self._values: |
||||
self._spinning = False |
||||
return |
||||
else: |
||||
sink, value = self._sink, self._values.pop(0) |
||||
|
||||
def set_sink(self, sink): |
||||
with self._lock: |
||||
self._sink = sink |
||||
if sink is not None and self._values and not self._spinning: |
||||
self._spinning = True |
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
sink, self._values.pop(0)) |
||||
|
||||
def add_value(self, value): |
||||
with self._lock: |
||||
if self._sink and not self._spinning: |
||||
self._spinning = True |
||||
self._pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), |
||||
self._sink, value) |
||||
else: |
||||
self._values.append(value) |
||||
|
||||
|
||||
class Link(interfaces.ForeLink, interfaces.RearLink): |
||||
"""A trivial implementation of interfaces.ForeLink and interfaces.RearLink.""" |
||||
|
||||
def __init__(self, pool): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
pool: A thread pool to be used for serializing ticket exchange in each |
||||
direction. |
||||
""" |
||||
self._front_to_back = _Serializer(pool) |
||||
self._back_to_front = _Serializer(pool) |
||||
|
||||
def join_fore_link(self, fore_link): |
||||
"""See interfaces.RearLink.join_fore_link for specification.""" |
||||
self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket) |
||||
|
||||
def join_rear_link(self, rear_link): |
||||
"""See interfaces.ForeLink.join_rear_link for specification.""" |
||||
self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket) |
||||
|
||||
def accept_front_to_back_ticket(self, ticket): |
||||
"""See interfaces.ForeLink.accept_front_to_back_ticket for specification.""" |
||||
self._front_to_back.add_value(ticket) |
||||
|
||||
def accept_back_to_front_ticket(self, ticket): |
||||
"""See interfaces.RearLink.accept_back_to_front_ticket for specification.""" |
||||
self._back_to_front.add_value(ticket) |
@ -1,353 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Interfaces defined and used by the base layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
import collections |
||||
import enum |
||||
|
||||
import six |
||||
|
||||
# stream is referenced from specification in this module. |
||||
from grpc.framework.foundation import stream # pylint: disable=unused-import |
||||
|
||||
|
||||
@enum.unique |
||||
class Outcome(enum.Enum): |
||||
"""Operation outcomes.""" |
||||
|
||||
COMPLETED = 'completed' |
||||
CANCELLED = 'cancelled' |
||||
EXPIRED = 'expired' |
||||
RECEPTION_FAILURE = 'reception failure' |
||||
TRANSMISSION_FAILURE = 'transmission failure' |
||||
SERVICER_FAILURE = 'servicer failure' |
||||
SERVICED_FAILURE = 'serviced failure' |
||||
|
||||
|
||||
class OperationContext(six.with_metaclass(abc.ABCMeta)): |
||||
"""Provides operation-related information and action. |
||||
|
||||
Attributes: |
||||
trace_id: A uuid.UUID identifying a particular set of related operations. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def is_active(self): |
||||
"""Describes whether the operation is active or has terminated.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_termination_callback(self, callback): |
||||
"""Adds a function to be called upon operation termination. |
||||
|
||||
Args: |
||||
callback: A callable that will be passed an Outcome value. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def time_remaining(self): |
||||
"""Describes the length of allowed time remaining for the operation. |
||||
|
||||
Returns: |
||||
A nonnegative float indicating the length of allowed time in seconds |
||||
remaining for the operation to complete before it is considered to have |
||||
timed out. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def fail(self, exception): |
||||
"""Indicates that the operation has failed. |
||||
|
||||
Args: |
||||
exception: An exception germane to the operation failure. May be None. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Servicer(six.with_metaclass(abc.ABCMeta)): |
||||
"""Interface for service implementations.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, name, context, output_consumer): |
||||
"""Services an operation. |
||||
|
||||
Args: |
||||
name: The name of the operation. |
||||
context: A ServicerContext object affording contextual information and |
||||
actions. |
||||
output_consumer: A stream.Consumer that will accept output values of |
||||
the operation. |
||||
|
||||
Returns: |
||||
A stream.Consumer that will accept input values for the operation. |
||||
|
||||
Raises: |
||||
exceptions.NoSuchMethodError: If this Servicer affords no method with the |
||||
given name. |
||||
abandonment.Abandoned: If the operation has been aborted and there no |
||||
longer is any reason to service the operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Operation(six.with_metaclass(abc.ABCMeta)): |
||||
"""Representation of an in-progress operation. |
||||
|
||||
Attributes: |
||||
consumer: A stream.Consumer into which payloads constituting the operation's |
||||
input may be passed. |
||||
context: An OperationContext affording information and action about the |
||||
operation. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self): |
||||
"""Cancels this operation.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ServicedIngestor(six.with_metaclass(abc.ABCMeta)): |
||||
"""Responsible for accepting the result of an operation.""" |
||||
|
||||
@abc.abstractmethod |
||||
def consumer(self, operation_context): |
||||
"""Affords a consumer to which operation results will be passed. |
||||
|
||||
Args: |
||||
operation_context: An OperationContext object for the current operation. |
||||
|
||||
Returns: |
||||
A stream.Consumer to which the results of the current operation will be |
||||
passed. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: If the operation has been aborted and there no |
||||
longer is any reason to service the operation. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ServicedSubscription(six.with_metaclass(abc.ABCMeta)): |
||||
"""A sum type representing a serviced's interest in an operation. |
||||
|
||||
Attributes: |
||||
kind: A Kind value. |
||||
ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must |
||||
be None if kind is Kind.TERMINATION_ONLY or Kind.NONE. |
||||
""" |
||||
|
||||
@enum.unique |
||||
class Kind(enum.Enum): |
||||
"""Kinds of subscription.""" |
||||
|
||||
FULL = 'full' |
||||
TERMINATION_ONLY = 'termination only' |
||||
NONE = 'none' |
||||
|
||||
|
||||
class End(six.with_metaclass(abc.ABCMeta)): |
||||
"""Common type for entry-point objects on both sides of an operation.""" |
||||
|
||||
@abc.abstractmethod |
||||
def operation_stats(self): |
||||
"""Reports the number of terminated operations broken down by outcome. |
||||
|
||||
Returns: |
||||
A dictionary from Outcome value to an integer identifying the number |
||||
of operations that terminated with that outcome. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_idle_action(self, action): |
||||
"""Adds an action to be called when this End has no ongoing operations. |
||||
|
||||
Args: |
||||
action: A callable that accepts no arguments. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Front(six.with_metaclass(abc.ABCMeta, End)): |
||||
"""Clientish objects that afford the invocation of operations.""" |
||||
|
||||
@abc.abstractmethod |
||||
def operate( |
||||
self, name, payload, complete, timeout, subscription, trace_id): |
||||
"""Commences an operation. |
||||
|
||||
Args: |
||||
name: The name of the method invoked for the operation. |
||||
payload: An initial payload for the operation. May be None. |
||||
complete: A boolean indicating whether or not additional payloads to be |
||||
sent to the servicer may be supplied after this call. |
||||
timeout: A length of time in seconds to allow for the operation. |
||||
subscription: A ServicedSubscription for the operation. |
||||
trace_id: A uuid.UUID identifying a set of related operations to which |
||||
this operation belongs. |
||||
|
||||
Returns: |
||||
An Operation object affording information and action about the operation |
||||
in progress. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Back(six.with_metaclass(abc.ABCMeta, End)): |
||||
"""Serverish objects that perform the work of operations.""" |
||||
|
||||
|
||||
class FrontToBackTicket( |
||||
collections.namedtuple( |
||||
'FrontToBackTicket', |
||||
['operation_id', 'sequence_number', 'kind', 'name', 'subscription', |
||||
'trace_id', 'payload', 'timeout'])): |
||||
"""A sum type for all values sent from a front to a back. |
||||
|
||||
Attributes: |
||||
operation_id: A unique-with-respect-to-equality hashable object identifying |
||||
a particular operation. |
||||
sequence_number: A zero-indexed integer sequence number identifying the |
||||
ticket's place among all the tickets sent from front to back for this |
||||
particular operation. Must be zero if kind is Kind.COMMENCEMENT or |
||||
Kind.ENTIRE. Must be positive for any other kind. |
||||
kind: A Kind value describing the overall kind of ticket. |
||||
name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT |
||||
or Kind.ENTIRE. Must be None for any other kind. |
||||
subscription: An ServicedSubscription.Kind value describing the interest |
||||
the front has in tickets sent from the back. Must be present if |
||||
kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind. |
||||
trace_id: A uuid.UUID identifying a set of related operations to which this |
||||
operation belongs. May be None. |
||||
payload: A customer payload object. Must be present if kind is |
||||
Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None |
||||
for any other kind. |
||||
timeout: An optional length of time (measured from the beginning of the |
||||
operation) to allow for the entire operation. If None, a default value on |
||||
the back will be used. If present and excessively large, the back may |
||||
limit the operation to a smaller duration of its choice. May be present |
||||
for any ticket kind; setting a value on a later ticket allows fronts |
||||
to request time extensions (or even time reductions!) on in-progress |
||||
operations. |
||||
""" |
||||
|
||||
@enum.unique |
||||
class Kind(enum.Enum): |
||||
"""Identifies the overall kind of a FrontToBackTicket.""" |
||||
|
||||
COMMENCEMENT = 'commencement' |
||||
CONTINUATION = 'continuation' |
||||
COMPLETION = 'completion' |
||||
ENTIRE = 'entire' |
||||
CANCELLATION = 'cancellation' |
||||
EXPIRATION = 'expiration' |
||||
SERVICER_FAILURE = 'servicer failure' |
||||
SERVICED_FAILURE = 'serviced failure' |
||||
RECEPTION_FAILURE = 'reception failure' |
||||
TRANSMISSION_FAILURE = 'transmission failure' |
||||
|
||||
|
||||
class BackToFrontTicket( |
||||
collections.namedtuple( |
||||
'BackToFrontTicket', |
||||
['operation_id', 'sequence_number', 'kind', 'payload'])): |
||||
"""A sum type for all values sent from a back to a front. |
||||
|
||||
Attributes: |
||||
operation_id: A unique-with-respect-to-equality hashable object identifying |
||||
a particular operation. |
||||
sequence_number: A zero-indexed integer sequence number identifying the |
||||
ticket's place among all the tickets sent from back to front for this |
||||
particular operation. |
||||
kind: A Kind value describing the overall kind of ticket. |
||||
payload: A customer payload object. Must be present if kind is |
||||
Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None |
||||
otherwise. |
||||
""" |
||||
|
||||
@enum.unique |
||||
class Kind(enum.Enum): |
||||
"""Identifies the overall kind of a BackToFrontTicket.""" |
||||
|
||||
CONTINUATION = 'continuation' |
||||
COMPLETION = 'completion' |
||||
CANCELLATION = 'cancellation' |
||||
EXPIRATION = 'expiration' |
||||
SERVICER_FAILURE = 'servicer failure' |
||||
SERVICED_FAILURE = 'serviced failure' |
||||
RECEPTION_FAILURE = 'reception failure' |
||||
TRANSMISSION_FAILURE = 'transmission failure' |
||||
|
||||
|
||||
class ForeLink(six.with_metaclass(abc.ABCMeta)): |
||||
"""Accepts back-to-front tickets and emits front-to-back tickets.""" |
||||
|
||||
@abc.abstractmethod |
||||
def accept_back_to_front_ticket(self, ticket): |
||||
"""Accept a BackToFrontTicket. |
||||
|
||||
Args: |
||||
ticket: Any BackToFrontTicket. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def join_rear_link(self, rear_link): |
||||
"""Mates this object with a peer with which it will exchange tickets.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RearLink(six.with_metaclass(abc.ABCMeta)): |
||||
"""Accepts front-to-back tickets and emits back-to-front tickets.""" |
||||
|
||||
@abc.abstractmethod |
||||
def accept_front_to_back_ticket(self, ticket): |
||||
"""Accepts a FrontToBackTicket. |
||||
|
||||
Args: |
||||
ticket: Any FrontToBackTicket. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def join_fore_link(self, fore_link): |
||||
"""Mates this object with a peer with which it will exchange tickets.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class FrontLink(six.with_metaclass(abc.ABCMeta, Front, ForeLink)): |
||||
"""Clientish objects that operate by sending and receiving tickets.""" |
||||
|
||||
|
||||
class BackLink(six.with_metaclass(abc.ABCMeta, Back, RearLink)): |
||||
"""Serverish objects that operate by sending and receiving tickets.""" |
@ -1,56 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Null links that ignore tickets passed to them.""" |
||||
|
||||
from grpc.framework.base import interfaces |
||||
|
||||
|
||||
class _NullForeLink(interfaces.ForeLink): |
||||
"""A do-nothing ForeLink.""" |
||||
|
||||
def accept_back_to_front_ticket(self, ticket): |
||||
pass |
||||
|
||||
def join_rear_link(self, rear_link): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _NullRearLink(interfaces.RearLink): |
||||
"""A do-nothing RearLink.""" |
||||
|
||||
def accept_front_to_back_ticket(self, ticket): |
||||
pass |
||||
|
||||
def join_fore_link(self, fore_link): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
NULL_FORE_LINK = _NullForeLink() |
||||
NULL_REAR_LINK = _NullRearLink() |
@ -1,94 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Utilities helpful for working with the base layer of RPC Framework.""" |
||||
|
||||
import collections |
||||
import threading |
||||
|
||||
from grpc.framework.base import interfaces |
||||
|
||||
|
||||
class _ServicedSubscription( |
||||
collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']), |
||||
interfaces.ServicedSubscription): |
||||
"""See interfaces.ServicedSubscription for specification.""" |
||||
|
||||
_NONE_SUBSCRIPTION = _ServicedSubscription( |
||||
interfaces.ServicedSubscription.Kind.NONE, None) |
||||
_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( |
||||
interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None) |
||||
|
||||
|
||||
def none_serviced_subscription(): |
||||
"""Creates a "none" interfaces.ServicedSubscription object. |
||||
|
||||
Returns: |
||||
An interfaces.ServicedSubscription indicating no subscription to an |
||||
operation's results (such as would be the case for a fire-and-forget |
||||
operation invocation). |
||||
""" |
||||
return _NONE_SUBSCRIPTION |
||||
|
||||
|
||||
def termination_only_serviced_subscription(): |
||||
"""Creates a "termination only" interfaces.ServicedSubscription object. |
||||
|
||||
Returns: |
||||
An interfaces.ServicedSubscription indicating that the front-side customer |
||||
is interested only in the overall termination outcome of the operation |
||||
(such as completion or expiration) and would ignore the actual results of |
||||
the operation. |
||||
""" |
||||
return _TERMINATION_ONLY_SUBSCRIPTION |
||||
|
||||
|
||||
def full_serviced_subscription(ingestor): |
||||
"""Creates a "full" interfaces.ServicedSubscription object. |
||||
|
||||
Args: |
||||
ingestor: An interfaces.ServicedIngestor. |
||||
|
||||
Returns: |
||||
An interfaces.ServicedSubscription object indicating a full |
||||
subscription. |
||||
""" |
||||
return _ServicedSubscription( |
||||
interfaces.ServicedSubscription.Kind.FULL, ingestor) |
||||
|
||||
|
||||
def wait_for_idle(end): |
||||
"""Waits for an interfaces.End to complete all operations. |
||||
|
||||
Args: |
||||
end: Any interfaces.End. |
||||
""" |
||||
event = threading.Event() |
||||
end.add_idle_action(event.set) |
||||
event.wait() |
@ -1,35 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
import warnings |
||||
|
||||
warnings.simplefilter('always', DeprecationWarning) |
||||
warnings.warn('the alpha API (includes this package) is deprecated, ' |
||||
'unmaintained, and no longer tested. Please migrate to the beta ' |
||||
'API.', DeprecationWarning, stacklevel=2) |
@ -1,422 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Utility functions for invoking RPCs.""" |
||||
|
||||
import sys |
||||
import threading |
||||
|
||||
from grpc.framework.base import interfaces as base_interfaces |
||||
from grpc.framework.base import util as base_util |
||||
from grpc.framework.face import _control |
||||
from grpc.framework.face import interfaces |
||||
from grpc.framework.foundation import callable_util |
||||
from grpc.framework.foundation import future |
||||
|
||||
_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' |
||||
_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' |
||||
|
||||
|
||||
class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor): |
||||
|
||||
def __init__(self, rendezvous): |
||||
self._rendezvous = rendezvous |
||||
|
||||
def consumer(self, operation_context): |
||||
return self._rendezvous |
||||
|
||||
|
||||
class _EventServicedIngestor(base_interfaces.ServicedIngestor): |
||||
|
||||
def __init__(self, result_consumer, abortion_callback): |
||||
self._result_consumer = result_consumer |
||||
self._abortion_callback = abortion_callback |
||||
|
||||
def consumer(self, operation_context): |
||||
operation_context.add_termination_callback( |
||||
_control.as_operation_termination_callback(self._abortion_callback)) |
||||
return self._result_consumer |
||||
|
||||
|
||||
def _rendezvous_subscription(rendezvous): |
||||
return base_util.full_serviced_subscription( |
||||
_RendezvousServicedIngestor(rendezvous)) |
||||
|
||||
|
||||
def _unary_event_subscription(completion_callback, abortion_callback): |
||||
return base_util.full_serviced_subscription( |
||||
_EventServicedIngestor( |
||||
_control.UnaryConsumer(completion_callback), abortion_callback)) |
||||
|
||||
|
||||
def _stream_event_subscription(result_consumer, abortion_callback): |
||||
return base_util.full_serviced_subscription( |
||||
_EventServicedIngestor(result_consumer, abortion_callback)) |
||||
|
||||
|
||||
# NOTE(nathaniel): This class has some extremely special semantics around |
||||
# cancellation that allow it to be used by both "blocking" APIs and "futures" |
||||
# APIs. |
||||
# |
||||
# Since futures.Future defines its own exception for cancellation, we want these |
||||
# objects, when returned by methods of a returning-Futures-from-other-methods |
||||
# object, to raise the same exception for cancellation. But that's weird in a |
||||
# blocking API - why should this object, also returned by methods of blocking |
||||
# APIs, raise exceptions from the "future" module? Should we do something like |
||||
# have this class be parameterized by the type of exception that it raises in |
||||
# cancellation circumstances? |
||||
# |
||||
# We don't have to take such a dramatic step: since blocking APIs define no |
||||
# cancellation semantics whatsoever, there is no supported way for |
||||
# blocking-API-users of these objects to cancel RPCs, and thus no supported way |
||||
# for them to see an exception the type of which would be weird to them. |
||||
# |
||||
# Bonus: in both blocking and futures APIs, this object still properly raises |
||||
# exceptions.CancellationError for any *server-side cancellation* of an RPC. |
||||
class _OperationCancellableIterator(interfaces.CancellableIterator): |
||||
"""An interfaces.CancellableIterator for response-streaming operations.""" |
||||
|
||||
def __init__(self, rendezvous, operation): |
||||
self._lock = threading.Lock() |
||||
self._rendezvous = rendezvous |
||||
self._operation = operation |
||||
self._cancelled = False |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def next(self): |
||||
with self._lock: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
return next(self._rendezvous) |
||||
|
||||
def cancel(self): |
||||
with self._lock: |
||||
self._cancelled = True |
||||
self._operation.cancel() |
||||
self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED) |
||||
|
||||
|
||||
class _OperationFuture(future.Future): |
||||
"""A future.Future interface to an operation.""" |
||||
|
||||
def __init__(self, rendezvous, operation): |
||||
self._condition = threading.Condition() |
||||
self._rendezvous = rendezvous |
||||
self._operation = operation |
||||
|
||||
self._cancelled = False |
||||
self._computed = False |
||||
self._payload = None |
||||
self._exception = None |
||||
self._traceback = None |
||||
self._callbacks = [] |
||||
|
||||
def cancel(self): |
||||
"""See future.Future.cancel for specification.""" |
||||
with self._condition: |
||||
if not self._cancelled and not self._computed: |
||||
self._operation.cancel() |
||||
self._cancelled = True |
||||
self._condition.notify_all() |
||||
return False |
||||
|
||||
def cancelled(self): |
||||
"""See future.Future.cancelled for specification.""" |
||||
with self._condition: |
||||
return self._cancelled |
||||
|
||||
def running(self): |
||||
"""See future.Future.running for specification.""" |
||||
with self._condition: |
||||
return not self._cancelled and not self._computed |
||||
|
||||
def done(self): |
||||
"""See future.Future.done for specification.""" |
||||
with self._condition: |
||||
return self._cancelled or self._computed |
||||
|
||||
def result(self, timeout=None): |
||||
"""See future.Future.result for specification.""" |
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
if self._computed: |
||||
if self._payload is None: |
||||
raise self._exception # pylint: disable=raising-bad-type |
||||
else: |
||||
return self._payload |
||||
|
||||
condition = threading.Condition() |
||||
def notify_condition(unused_future): |
||||
with condition: |
||||
condition.notify() |
||||
self._callbacks.append(notify_condition) |
||||
|
||||
with condition: |
||||
condition.wait(timeout=timeout) |
||||
|
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
elif self._computed: |
||||
if self._payload is None: |
||||
raise self._exception # pylint: disable=raising-bad-type |
||||
else: |
||||
return self._payload |
||||
else: |
||||
raise future.TimeoutError() |
||||
|
||||
def exception(self, timeout=None): |
||||
"""See future.Future.exception for specification.""" |
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
if self._computed: |
||||
return self._exception |
||||
|
||||
condition = threading.Condition() |
||||
def notify_condition(unused_future): |
||||
with condition: |
||||
condition.notify() |
||||
self._callbacks.append(notify_condition) |
||||
|
||||
with condition: |
||||
condition.wait(timeout=timeout) |
||||
|
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
elif self._computed: |
||||
return self._exception |
||||
else: |
||||
raise future.TimeoutError() |
||||
|
||||
def traceback(self, timeout=None): |
||||
"""See future.Future.traceback for specification.""" |
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
if self._computed: |
||||
return self._traceback |
||||
|
||||
condition = threading.Condition() |
||||
def notify_condition(unused_future): |
||||
with condition: |
||||
condition.notify() |
||||
self._callbacks.append(notify_condition) |
||||
|
||||
with condition: |
||||
condition.wait(timeout=timeout) |
||||
|
||||
with self._condition: |
||||
if self._cancelled: |
||||
raise future.CancelledError() |
||||
elif self._computed: |
||||
return self._traceback |
||||
else: |
||||
raise future.TimeoutError() |
||||
|
||||
def add_done_callback(self, fn): |
||||
"""See future.Future.add_done_callback for specification.""" |
||||
with self._condition: |
||||
if self._callbacks is not None: |
||||
self._callbacks.append(fn) |
||||
return |
||||
|
||||
callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self) |
||||
|
||||
def on_operation_termination(self, operation_outcome): |
||||
"""Indicates to this object that the operation has terminated. |
||||
|
||||
Args: |
||||
operation_outcome: A base_interfaces.Outcome value indicating the |
||||
outcome of the operation. |
||||
""" |
||||
with self._condition: |
||||
cancelled = self._cancelled |
||||
if cancelled: |
||||
callbacks = list(self._callbacks) |
||||
self._callbacks = None |
||||
else: |
||||
rendezvous = self._rendezvous |
||||
|
||||
if not cancelled: |
||||
payload = None |
||||
exception = None |
||||
traceback = None |
||||
if operation_outcome == base_interfaces.Outcome.COMPLETED: |
||||
try: |
||||
payload = next(rendezvous) |
||||
except Exception as e: # pylint: disable=broad-except |
||||
exception = e |
||||
traceback = sys.exc_info()[2] |
||||
else: |
||||
try: |
||||
# We raise and then immediately catch in order to create a traceback. |
||||
raise _control.abortion_outcome_to_exception(operation_outcome) |
||||
except Exception as e: # pylint: disable=broad-except |
||||
exception = e |
||||
traceback = sys.exc_info()[2] |
||||
with self._condition: |
||||
if not self._cancelled: |
||||
self._computed = True |
||||
self._payload = payload |
||||
self._exception = exception |
||||
self._traceback = traceback |
||||
callbacks = list(self._callbacks) |
||||
self._callbacks = None |
||||
|
||||
for callback in callbacks: |
||||
callable_util.call_logging_exceptions( |
||||
callback, _DONE_CALLBACK_LOG_MESSAGE, self) |
||||
|
||||
|
||||
class _Call(interfaces.Call): |
||||
|
||||
def __init__(self, operation): |
||||
self._operation = operation |
||||
self.context = _control.RpcContext(operation.context) |
||||
|
||||
def cancel(self): |
||||
self._operation.cancel() |
||||
|
||||
|
||||
def blocking_value_in_value_out(front, name, payload, timeout, trace_id): |
||||
"""Services in a blocking fashion a value-in value-out servicer method.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate( |
||||
name, payload, True, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
return next(rendezvous) |
||||
|
||||
|
||||
def future_value_in_value_out(front, name, payload, timeout, trace_id): |
||||
"""Services a value-in value-out servicer method by returning a Future.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate( |
||||
name, payload, True, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
operation_future = _OperationFuture(rendezvous, operation) |
||||
operation.context.add_termination_callback( |
||||
operation_future.on_operation_termination) |
||||
return operation_future |
||||
|
||||
|
||||
def inline_value_in_stream_out(front, name, payload, timeout, trace_id): |
||||
"""Services a value-in stream-out servicer method.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate( |
||||
name, payload, True, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
return _OperationCancellableIterator(rendezvous, operation) |
||||
|
||||
|
||||
def blocking_stream_in_value_out( |
||||
front, name, payload_iterator, timeout, trace_id): |
||||
"""Services in a blocking fashion a stream-in value-out servicer method.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
for payload in payload_iterator: |
||||
operation.consumer.consume(payload) |
||||
operation.consumer.terminate() |
||||
return next(rendezvous) |
||||
|
||||
|
||||
def future_stream_in_value_out( |
||||
front, name, payload_iterator, timeout, trace_id, pool): |
||||
"""Services a stream-in value-out servicer method by returning a Future.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
_control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), |
||||
payload_iterator, operation.consumer, lambda: True, True) |
||||
operation_future = _OperationFuture(rendezvous, operation) |
||||
operation.context.add_termination_callback( |
||||
operation_future.on_operation_termination) |
||||
return operation_future |
||||
|
||||
|
||||
def inline_stream_in_stream_out( |
||||
front, name, payload_iterator, timeout, trace_id, pool): |
||||
"""Services a stream-in stream-out servicer method.""" |
||||
rendezvous = _control.Rendezvous() |
||||
subscription = _rendezvous_subscription(rendezvous) |
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id) |
||||
operation.context.add_termination_callback(rendezvous.set_outcome) |
||||
pool.submit( |
||||
callable_util.with_exceptions_logged( |
||||
_control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), |
||||
payload_iterator, operation.consumer, lambda: True, True) |
||||
return _OperationCancellableIterator(rendezvous, operation) |
||||
|
||||
|
||||
def event_value_in_value_out( |
||||
front, name, payload, completion_callback, abortion_callback, timeout, |
||||
trace_id): |
||||
subscription = _unary_event_subscription( |
||||
completion_callback, abortion_callback) |
||||
operation = front.operate( |
||||
name, payload, True, timeout, subscription, trace_id) |
||||
return _Call(operation) |
||||
|
||||
|
||||
def event_value_in_stream_out( |
||||
front, name, payload, result_payload_consumer, abortion_callback, timeout, |
||||
trace_id): |
||||
subscription = _stream_event_subscription( |
||||
result_payload_consumer, abortion_callback) |
||||
operation = front.operate( |
||||
name, payload, True, timeout, subscription, trace_id) |
||||
return _Call(operation) |
||||
|
||||
|
||||
def event_stream_in_value_out( |
||||
front, name, completion_callback, abortion_callback, timeout, trace_id): |
||||
subscription = _unary_event_subscription( |
||||
completion_callback, abortion_callback) |
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id) |
||||
return _Call(operation), operation.consumer |
||||
|
||||
|
||||
def event_stream_in_stream_out( |
||||
front, name, result_payload_consumer, abortion_callback, timeout, trace_id): |
||||
subscription = _stream_event_subscription( |
||||
result_payload_consumer, abortion_callback) |
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id) |
||||
return _Call(operation), operation.consumer |
@ -1,201 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""State and behavior for translating between sync and async control flow.""" |
||||
|
||||
import threading |
||||
|
||||
from grpc.framework.base import interfaces as base_interfaces |
||||
from grpc.framework.face import exceptions |
||||
from grpc.framework.face import interfaces |
||||
from grpc.framework.foundation import abandonment |
||||
from grpc.framework.foundation import stream |
||||
|
||||
INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' |
||||
|
||||
_OPERATION_OUTCOME_TO_RPC_ABORTION = { |
||||
base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED, |
||||
base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED, |
||||
base_interfaces.Outcome.RECEPTION_FAILURE: |
||||
interfaces.Abortion.NETWORK_FAILURE, |
||||
base_interfaces.Outcome.TRANSMISSION_FAILURE: |
||||
interfaces.Abortion.NETWORK_FAILURE, |
||||
base_interfaces.Outcome.SERVICED_FAILURE: |
||||
interfaces.Abortion.SERVICED_FAILURE, |
||||
base_interfaces.Outcome.SERVICER_FAILURE: |
||||
interfaces.Abortion.SERVICER_FAILURE, |
||||
} |
||||
|
||||
|
||||
def _as_operation_termination_callback(rpc_abortion_callback): |
||||
def operation_termination_callback(operation_outcome): |
||||
rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get( |
||||
operation_outcome, None) |
||||
if rpc_abortion is not None: |
||||
rpc_abortion_callback(rpc_abortion) |
||||
return operation_termination_callback |
||||
|
||||
|
||||
def _abortion_outcome_to_exception(abortion_outcome): |
||||
if abortion_outcome == base_interfaces.Outcome.CANCELLED: |
||||
return exceptions.CancellationError() |
||||
elif abortion_outcome == base_interfaces.Outcome.EXPIRED: |
||||
return exceptions.ExpirationError() |
||||
elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE: |
||||
return exceptions.ServicerError() |
||||
elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE: |
||||
return exceptions.ServicedError() |
||||
else: |
||||
return exceptions.NetworkError() |
||||
|
||||
|
||||
class UnaryConsumer(stream.Consumer): |
||||
"""A stream.Consumer that should only ever be passed one value.""" |
||||
|
||||
def __init__(self, on_termination): |
||||
self._on_termination = on_termination |
||||
self._value = None |
||||
|
||||
def consume(self, value): |
||||
self._value = value |
||||
|
||||
def terminate(self): |
||||
self._on_termination(self._value) |
||||
|
||||
def consume_and_terminate(self, value): |
||||
self._on_termination(value) |
||||
|
||||
|
||||
class Rendezvous(stream.Consumer): |
||||
"""A rendez-vous with stream.Consumer and iterator interfaces.""" |
||||
|
||||
def __init__(self): |
||||
self._condition = threading.Condition() |
||||
self._values = [] |
||||
self._values_completed = False |
||||
self._abortion = None |
||||
|
||||
def consume(self, value): |
||||
with self._condition: |
||||
self._values.append(value) |
||||
self._condition.notify() |
||||
|
||||
def terminate(self): |
||||
with self._condition: |
||||
self._values_completed = True |
||||
self._condition.notify() |
||||
|
||||
def consume_and_terminate(self, value): |
||||
with self._condition: |
||||
self._values.append(value) |
||||
self._values_completed = True |
||||
self._condition.notify() |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def __next__(self): |
||||
return self.next() |
||||
|
||||
def next(self): |
||||
with self._condition: |
||||
while ((self._abortion is None) and |
||||
(not self._values) and |
||||
(not self._values_completed)): |
||||
self._condition.wait() |
||||
if self._abortion is not None: |
||||
raise _abortion_outcome_to_exception(self._abortion) |
||||
elif self._values: |
||||
return self._values.pop(0) |
||||
elif self._values_completed: |
||||
raise StopIteration() |
||||
else: |
||||
raise AssertionError('Unreachable code reached!') |
||||
|
||||
def set_outcome(self, outcome): |
||||
with self._condition: |
||||
if outcome is not base_interfaces.Outcome.COMPLETED: |
||||
self._abortion = outcome |
||||
self._condition.notify() |
||||
|
||||
|
||||
class RpcContext(interfaces.RpcContext): |
||||
"""A wrapped base_interfaces.OperationContext.""" |
||||
|
||||
def __init__(self, operation_context): |
||||
self._operation_context = operation_context |
||||
|
||||
def is_active(self): |
||||
return self._operation_context.is_active() |
||||
|
||||
def time_remaining(self): |
||||
return self._operation_context.time_remaining() |
||||
|
||||
def add_abortion_callback(self, abortion_callback): |
||||
self._operation_context.add_termination_callback( |
||||
_as_operation_termination_callback(abortion_callback)) |
||||
|
||||
|
||||
def pipe_iterator_to_consumer(iterator, consumer, active, terminate): |
||||
"""Pipes values emitted from an iterator to a stream.Consumer. |
||||
|
||||
Args: |
||||
iterator: An iterator from which values will be emitted. |
||||
consumer: A stream.Consumer to which values will be passed. |
||||
active: A no-argument callable that returns True if the work being done by |
||||
this function is still valid and should not be abandoned and False if the |
||||
work being done by this function should be abandoned. |
||||
terminate: A boolean indicating whether or not this function should |
||||
terminate the given consumer after passing to it all values emitted by the |
||||
given iterator. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: If this function quits early after seeing False |
||||
returned by the active function passed to it. |
||||
Exception: This function raises whatever exceptions are raised by iterating |
||||
over the given iterator. |
||||
""" |
||||
for element in iterator: |
||||
if not active(): |
||||
raise abandonment.Abandoned() |
||||
|
||||
consumer.consume(element) |
||||
|
||||
if not active(): |
||||
raise abandonment.Abandoned() |
||||
if terminate: |
||||
consumer.terminate() |
||||
|
||||
|
||||
def abortion_outcome_to_exception(abortion_outcome): |
||||
return _abortion_outcome_to_exception(abortion_outcome) |
||||
|
||||
|
||||
def as_operation_termination_callback(rpc_abortion_callback): |
||||
return _as_operation_termination_callback(rpc_abortion_callback) |
@ -1,187 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Behaviors for servicing RPCs.""" |
||||
|
||||
# base_interfaces and interfaces are referenced from specification in this |
||||
# module. |
||||
from grpc.framework.base import interfaces as base_interfaces # pylint: disable=unused-import |
||||
from grpc.framework.face import _control |
||||
from grpc.framework.face import exceptions |
||||
from grpc.framework.face import interfaces # pylint: disable=unused-import |
||||
from grpc.framework.foundation import abandonment |
||||
from grpc.framework.foundation import callable_util |
||||
from grpc.framework.foundation import stream |
||||
from grpc.framework.foundation import stream_util |
||||
|
||||
|
||||
class _ValueInStreamOutConsumer(stream.Consumer): |
||||
"""A stream.Consumer that maps inputs one-to-many onto outputs.""" |
||||
|
||||
def __init__(self, behavior, context, downstream): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
behavior: A callable that takes a single value and an |
||||
interfaces.RpcContext and returns a generator of arbitrarily many |
||||
values. |
||||
context: An interfaces.RpcContext. |
||||
downstream: A stream.Consumer to which to pass the values generated by the |
||||
given behavior. |
||||
""" |
||||
self._behavior = behavior |
||||
self._context = context |
||||
self._downstream = downstream |
||||
|
||||
def consume(self, value): |
||||
_control.pipe_iterator_to_consumer( |
||||
self._behavior(value, self._context), self._downstream, |
||||
self._context.is_active, False) |
||||
|
||||
def terminate(self): |
||||
self._downstream.terminate() |
||||
|
||||
def consume_and_terminate(self, value): |
||||
_control.pipe_iterator_to_consumer( |
||||
self._behavior(value, self._context), self._downstream, |
||||
self._context.is_active, True) |
||||
|
||||
|
||||
def _pool_wrap(behavior, operation_context): |
||||
"""Wraps an operation-related behavior so that it may be called in a pool. |
||||
|
||||
Args: |
||||
behavior: A callable related to carrying out an operation. |
||||
operation_context: A base_interfaces.OperationContext for the operation. |
||||
|
||||
Returns: |
||||
A callable that when called carries out the behavior of the given callable |
||||
and handles whatever exceptions it raises appropriately. |
||||
""" |
||||
def translation(*args): |
||||
try: |
||||
behavior(*args) |
||||
except ( |
||||
abandonment.Abandoned, |
||||
exceptions.ExpirationError, |
||||
exceptions.CancellationError, |
||||
exceptions.ServicedError, |
||||
exceptions.NetworkError) as e: |
||||
if operation_context.is_active(): |
||||
operation_context.fail(e) |
||||
except Exception as e: |
||||
operation_context.fail(e) |
||||
return callable_util.with_exceptions_logged( |
||||
translation, _control.INTERNAL_ERROR_LOG_MESSAGE) |
||||
|
||||
|
||||
def adapt_inline_value_in_value_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
rpc_context = _control.RpcContext(operation_context) |
||||
return stream_util.TransformingConsumer( |
||||
lambda request: method(request, rpc_context), response_consumer) |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_inline_value_in_stream_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
rpc_context = _control.RpcContext(operation_context) |
||||
return _ValueInStreamOutConsumer(method, rpc_context, response_consumer) |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_inline_stream_in_value_out(method, pool): |
||||
def adaptation(response_consumer, operation_context): |
||||
rendezvous = _control.Rendezvous() |
||||
operation_context.add_termination_callback(rendezvous.set_outcome) |
||||
def in_pool_thread(): |
||||
response_consumer.consume_and_terminate( |
||||
method(rendezvous, _control.RpcContext(operation_context))) |
||||
pool.submit(_pool_wrap(in_pool_thread, operation_context)) |
||||
return rendezvous |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_inline_stream_in_stream_out(method, pool): |
||||
"""Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers. |
||||
|
||||
RPCs may be serviced by calling the return value of this function, passing |
||||
request values to the stream.Consumer returned from that call, and receiving |
||||
response values from the stream.Consumer passed to that call. |
||||
|
||||
Args: |
||||
method: An interfaces.InlineStreamInStreamOutMethod. |
||||
pool: A thread pool. |
||||
|
||||
Returns: |
||||
A callable that takes a stream.Consumer and a |
||||
base_interfaces.OperationContext and returns a stream.Consumer. |
||||
""" |
||||
def adaptation(response_consumer, operation_context): |
||||
rendezvous = _control.Rendezvous() |
||||
operation_context.add_termination_callback(rendezvous.set_outcome) |
||||
def in_pool_thread(): |
||||
_control.pipe_iterator_to_consumer( |
||||
method(rendezvous, _control.RpcContext(operation_context)), |
||||
response_consumer, operation_context.is_active, True) |
||||
pool.submit(_pool_wrap(in_pool_thread, operation_context)) |
||||
return rendezvous |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_event_value_in_value_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
def on_payload(payload): |
||||
method( |
||||
payload, response_consumer.consume_and_terminate, |
||||
_control.RpcContext(operation_context)) |
||||
return _control.UnaryConsumer(on_payload) |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_event_value_in_stream_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
def on_payload(payload): |
||||
method( |
||||
payload, response_consumer, _control.RpcContext(operation_context)) |
||||
return _control.UnaryConsumer(on_payload) |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_event_stream_in_value_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
rpc_context = _control.RpcContext(operation_context) |
||||
return method(response_consumer.consume_and_terminate, rpc_context) |
||||
return adaptation |
||||
|
||||
|
||||
def adapt_event_stream_in_stream_out(method): |
||||
def adaptation(response_consumer, operation_context): |
||||
return method(response_consumer, _control.RpcContext(operation_context)) |
||||
return adaptation |
@ -1,118 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Demonstration-suitable implementation of the face layer of RPC Framework.""" |
||||
|
||||
from grpc.framework.base import util as _base_util |
||||
from grpc.framework.base import implementations as _base_implementations |
||||
from grpc.framework.face import implementations |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
_POOL_SIZE_LIMIT = 5 |
||||
|
||||
_MAXIMUM_TIMEOUT = 90 |
||||
|
||||
|
||||
class LinkedPair(object): |
||||
"""A Server and Stub that are linked to one another. |
||||
|
||||
Attributes: |
||||
server: A Server. |
||||
stub: A Stub. |
||||
""" |
||||
|
||||
def shut_down(self): |
||||
"""Shuts down this object and releases its resources.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _LinkedPair(LinkedPair): |
||||
|
||||
def __init__(self, server, stub, front, back, pools): |
||||
self.server = server |
||||
self.stub = stub |
||||
self._front = front |
||||
self._back = back |
||||
self._pools = pools |
||||
|
||||
def shut_down(self): |
||||
_base_util.wait_for_idle(self._front) |
||||
_base_util.wait_for_idle(self._back) |
||||
|
||||
for pool in self._pools: |
||||
pool.shutdown(wait=True) |
||||
|
||||
|
||||
def server_and_stub( |
||||
default_timeout, |
||||
inline_value_in_value_out_methods=None, |
||||
inline_value_in_stream_out_methods=None, |
||||
inline_stream_in_value_out_methods=None, |
||||
inline_stream_in_stream_out_methods=None, |
||||
event_value_in_value_out_methods=None, |
||||
event_value_in_stream_out_methods=None, |
||||
event_stream_in_value_out_methods=None, |
||||
event_stream_in_stream_out_methods=None, |
||||
multi_method=None): |
||||
"""Creates a Server and Stub linked together for use.""" |
||||
front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
pools = ( |
||||
front_work_pool, front_transmission_pool, front_utility_pool, |
||||
back_work_pool, back_transmission_pool, back_utility_pool, |
||||
stub_pool) |
||||
|
||||
servicer = implementations.servicer( |
||||
back_work_pool, |
||||
inline_value_in_value_out_methods=inline_value_in_value_out_methods, |
||||
inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, |
||||
inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, |
||||
inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, |
||||
event_value_in_value_out_methods=event_value_in_value_out_methods, |
||||
event_value_in_stream_out_methods=event_value_in_stream_out_methods, |
||||
event_stream_in_value_out_methods=event_stream_in_value_out_methods, |
||||
event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, |
||||
multi_method=multi_method) |
||||
|
||||
front = _base_implementations.front_link( |
||||
front_work_pool, front_transmission_pool, front_utility_pool) |
||||
back = _base_implementations.back_link( |
||||
servicer, back_work_pool, back_transmission_pool, back_utility_pool, |
||||
default_timeout, _MAXIMUM_TIMEOUT) |
||||
front.join_rear_link(back) |
||||
back.join_fore_link(front) |
||||
|
||||
stub = implementations.stub(front, stub_pool) |
||||
|
||||
return _LinkedPair(implementations.server(), stub, front, back, pools) |
@ -1,78 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Exceptions used in the Face layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
|
||||
class NoSuchMethodError(Exception): |
||||
"""Raised by customer code to indicate an unrecognized RPC method name. |
||||
|
||||
Attributes: |
||||
name: The unrecognized name. |
||||
""" |
||||
|
||||
def __init__(self, name): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
name: The unrecognized RPC method name. |
||||
""" |
||||
super(NoSuchMethodError, self).__init__() |
||||
self.name = name |
||||
|
||||
|
||||
class RpcError(six.with_metaclass(abc.ABCMeta, Exception)): |
||||
"""Common super type for all exceptions raised by the Face layer. |
||||
|
||||
Only RPC Framework should instantiate and raise these exceptions. |
||||
""" |
||||
|
||||
|
||||
class CancellationError(RpcError): |
||||
"""Indicates that an RPC has been cancelled.""" |
||||
|
||||
|
||||
class ExpirationError(RpcError): |
||||
"""Indicates that an RPC has expired ("timed out").""" |
||||
|
||||
|
||||
class NetworkError(RpcError): |
||||
"""Indicates that some error occurred on the network.""" |
||||
|
||||
|
||||
class ServicedError(RpcError): |
||||
"""Indicates that the Serviced failed in the course of an RPC.""" |
||||
|
||||
|
||||
class ServicerError(RpcError): |
||||
"""Indicates that the Servicer failed in the course of servicing an RPC.""" |
@ -1,320 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Entry points into the Face layer of RPC Framework.""" |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.common import style |
||||
from grpc.framework.base import exceptions as _base_exceptions |
||||
from grpc.framework.base import interfaces as base_interfaces |
||||
from grpc.framework.face import _calls |
||||
from grpc.framework.face import _service |
||||
from grpc.framework.face import exceptions |
||||
from grpc.framework.face import interfaces |
||||
|
||||
|
||||
class _BaseServicer(base_interfaces.Servicer): |
||||
|
||||
def __init__(self, methods, multi_method): |
||||
self._methods = methods |
||||
self._multi_method = multi_method |
||||
|
||||
def service(self, name, context, output_consumer): |
||||
method = self._methods.get(name, None) |
||||
if method is not None: |
||||
return method(output_consumer, context) |
||||
elif self._multi_method is not None: |
||||
try: |
||||
return self._multi_method.service(name, output_consumer, context) |
||||
except exceptions.NoSuchMethodError: |
||||
raise _base_exceptions.NoSuchMethodError() |
||||
else: |
||||
raise _base_exceptions.NoSuchMethodError() |
||||
|
||||
|
||||
class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable): |
||||
|
||||
def __init__(self, front, name): |
||||
self._front = front |
||||
self._name = name |
||||
|
||||
def __call__(self, request, timeout): |
||||
return _calls.blocking_value_in_value_out( |
||||
self._front, self._name, request, timeout, 'unused trace ID') |
||||
|
||||
def future(self, request, timeout): |
||||
return _calls.future_value_in_value_out( |
||||
self._front, self._name, request, timeout, 'unused trace ID') |
||||
|
||||
def event(self, request, response_callback, abortion_callback, timeout): |
||||
return _calls.event_value_in_value_out( |
||||
self._front, self._name, request, response_callback, abortion_callback, |
||||
timeout, 'unused trace ID') |
||||
|
||||
|
||||
class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable): |
||||
|
||||
def __init__(self, front, name): |
||||
self._front = front |
||||
self._name = name |
||||
|
||||
def __call__(self, request, timeout): |
||||
return _calls.inline_value_in_stream_out( |
||||
self._front, self._name, request, timeout, 'unused trace ID') |
||||
|
||||
def event(self, request, response_consumer, abortion_callback, timeout): |
||||
return _calls.event_value_in_stream_out( |
||||
self._front, self._name, request, response_consumer, abortion_callback, |
||||
timeout, 'unused trace ID') |
||||
|
||||
|
||||
class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable): |
||||
|
||||
def __init__(self, front, name, pool): |
||||
self._front = front |
||||
self._name = name |
||||
self._pool = pool |
||||
|
||||
def __call__(self, request_iterator, timeout): |
||||
return _calls.blocking_stream_in_value_out( |
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID') |
||||
|
||||
def future(self, request_iterator, timeout): |
||||
return _calls.future_stream_in_value_out( |
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID', |
||||
self._pool) |
||||
|
||||
def event(self, response_callback, abortion_callback, timeout): |
||||
return _calls.event_stream_in_value_out( |
||||
self._front, self._name, response_callback, abortion_callback, timeout, |
||||
'unused trace ID') |
||||
|
||||
|
||||
class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable): |
||||
|
||||
def __init__(self, front, name, pool): |
||||
self._front = front |
||||
self._name = name |
||||
self._pool = pool |
||||
|
||||
def __call__(self, request_iterator, timeout): |
||||
return _calls.inline_stream_in_stream_out( |
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID', |
||||
self._pool) |
||||
|
||||
def event(self, response_consumer, abortion_callback, timeout): |
||||
return _calls.event_stream_in_stream_out( |
||||
self._front, self._name, response_consumer, abortion_callback, timeout, |
||||
'unused trace ID') |
||||
|
||||
|
||||
class _GenericStub(interfaces.GenericStub): |
||||
"""An interfaces.GenericStub implementation.""" |
||||
|
||||
def __init__(self, front, pool): |
||||
self._front = front |
||||
self._pool = pool |
||||
|
||||
def blocking_value_in_value_out(self, name, request, timeout): |
||||
return _calls.blocking_value_in_value_out( |
||||
self._front, name, request, timeout, 'unused trace ID') |
||||
|
||||
def future_value_in_value_out(self, name, request, timeout): |
||||
return _calls.future_value_in_value_out( |
||||
self._front, name, request, timeout, 'unused trace ID') |
||||
|
||||
def inline_value_in_stream_out(self, name, request, timeout): |
||||
return _calls.inline_value_in_stream_out( |
||||
self._front, name, request, timeout, 'unused trace ID') |
||||
|
||||
def blocking_stream_in_value_out(self, name, request_iterator, timeout): |
||||
return _calls.blocking_stream_in_value_out( |
||||
self._front, name, request_iterator, timeout, 'unused trace ID') |
||||
|
||||
def future_stream_in_value_out(self, name, request_iterator, timeout): |
||||
return _calls.future_stream_in_value_out( |
||||
self._front, name, request_iterator, timeout, 'unused trace ID', |
||||
self._pool) |
||||
|
||||
def inline_stream_in_stream_out(self, name, request_iterator, timeout): |
||||
return _calls.inline_stream_in_stream_out( |
||||
self._front, name, request_iterator, timeout, 'unused trace ID', |
||||
self._pool) |
||||
|
||||
def event_value_in_value_out( |
||||
self, name, request, response_callback, abortion_callback, timeout): |
||||
return _calls.event_value_in_value_out( |
||||
self._front, name, request, response_callback, abortion_callback, |
||||
timeout, 'unused trace ID') |
||||
|
||||
def event_value_in_stream_out( |
||||
self, name, request, response_consumer, abortion_callback, timeout): |
||||
return _calls.event_value_in_stream_out( |
||||
self._front, name, request, response_consumer, abortion_callback, |
||||
timeout, 'unused trace ID') |
||||
|
||||
def event_stream_in_value_out( |
||||
self, name, response_callback, abortion_callback, timeout): |
||||
return _calls.event_stream_in_value_out( |
||||
self._front, name, response_callback, abortion_callback, timeout, |
||||
'unused trace ID') |
||||
|
||||
def event_stream_in_stream_out( |
||||
self, name, response_consumer, abortion_callback, timeout): |
||||
return _calls.event_stream_in_stream_out( |
||||
self._front, name, response_consumer, abortion_callback, timeout, |
||||
'unused trace ID') |
||||
|
||||
def unary_unary_multi_callable(self, name): |
||||
return _UnaryUnaryMultiCallable(self._front, name) |
||||
|
||||
def unary_stream_multi_callable(self, name): |
||||
return _UnaryStreamMultiCallable(self._front, name) |
||||
|
||||
def stream_unary_multi_callable(self, name): |
||||
return _StreamUnaryMultiCallable(self._front, name, self._pool) |
||||
|
||||
def stream_stream_multi_callable(self, name): |
||||
return _StreamStreamMultiCallable(self._front, name, self._pool) |
||||
|
||||
|
||||
class _DynamicStub(interfaces.DynamicStub): |
||||
"""An interfaces.DynamicStub implementation.""" |
||||
|
||||
def __init__(self, cardinalities, front, pool): |
||||
self._cardinalities = cardinalities |
||||
self._front = front |
||||
self._pool = pool |
||||
|
||||
def __getattr__(self, attr): |
||||
method_cardinality = self._cardinalities.get(attr) |
||||
if method_cardinality is cardinality.Cardinality.UNARY_UNARY: |
||||
return _UnaryUnaryMultiCallable(self._front, attr) |
||||
elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||
return _UnaryStreamMultiCallable(self._front, attr) |
||||
elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||
return _StreamUnaryMultiCallable(self._front, attr, self._pool) |
||||
elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||
return _StreamStreamMultiCallable(self._front, attr, self._pool) |
||||
else: |
||||
raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr) |
||||
|
||||
|
||||
def _adapt_method_implementations(method_implementations, pool): |
||||
adapted_implementations = {} |
||||
for name, method_implementation in six.iteritems(method_implementations): |
||||
if method_implementation.style is style.Service.INLINE: |
||||
if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: |
||||
adapted_implementations[name] = _service.adapt_inline_value_in_value_out( |
||||
method_implementation.unary_unary_inline) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||
adapted_implementations[name] = _service.adapt_inline_value_in_stream_out( |
||||
method_implementation.unary_stream_inline) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||
adapted_implementations[name] = _service.adapt_inline_stream_in_value_out( |
||||
method_implementation.stream_unary_inline, pool) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||
adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out( |
||||
method_implementation.stream_stream_inline, pool) |
||||
elif method_implementation.style is style.Service.EVENT: |
||||
if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: |
||||
adapted_implementations[name] = _service.adapt_event_value_in_value_out( |
||||
method_implementation.unary_unary_event) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||
adapted_implementations[name] = _service.adapt_event_value_in_stream_out( |
||||
method_implementation.unary_stream_event) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||
adapted_implementations[name] = _service.adapt_event_stream_in_value_out( |
||||
method_implementation.stream_unary_event) |
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||
adapted_implementations[name] = _service.adapt_event_stream_in_stream_out( |
||||
method_implementation.stream_stream_event) |
||||
return adapted_implementations |
||||
|
||||
|
||||
def servicer(pool, method_implementations, multi_method_implementation): |
||||
"""Creates a base_interfaces.Servicer. |
||||
|
||||
It is guaranteed that any passed interfaces.MultiMethodImplementation will |
||||
only be called to service an RPC if there is no |
||||
interfaces.MethodImplementation for the RPC method in the passed |
||||
method_implementations dictionary. |
||||
|
||||
Args: |
||||
pool: A thread pool. |
||||
method_implementations: A dictionary from RPC method name to |
||||
interfaces.MethodImplementation object to be used to service the named |
||||
RPC method. |
||||
multi_method_implementation: An interfaces.MultiMethodImplementation to be |
||||
used to service any RPCs not serviced by the |
||||
interfaces.MethodImplementations given in the method_implementations |
||||
dictionary, or None. |
||||
|
||||
Returns: |
||||
A base_interfaces.Servicer that services RPCs via the given implementations. |
||||
""" |
||||
adapted_implementations = _adapt_method_implementations( |
||||
method_implementations, pool) |
||||
return _BaseServicer(adapted_implementations, multi_method_implementation) |
||||
|
||||
|
||||
def generic_stub(front, pool): |
||||
"""Creates an interfaces.GenericStub. |
||||
|
||||
Args: |
||||
front: A base_interfaces.Front. |
||||
pool: A futures.ThreadPoolExecutor. |
||||
|
||||
Returns: |
||||
An interfaces.GenericStub that performs RPCs via the given |
||||
base_interfaces.Front. |
||||
""" |
||||
return _GenericStub(front, pool) |
||||
|
||||
|
||||
def dynamic_stub(cardinalities, front, pool, prefix): |
||||
"""Creates an interfaces.DynamicStub. |
||||
|
||||
Args: |
||||
cardinalities: A dict from RPC method name to cardinality.Cardinality |
||||
value identifying the cardinality of every RPC method to be supported by |
||||
the created interfaces.DynamicStub. |
||||
front: A base_interfaces.Front. |
||||
pool: A futures.ThreadPoolExecutor. |
||||
prefix: A string to prepend when mapping requested attribute name to RPC |
||||
method name during attribute access on the created |
||||
interfaces.DynamicStub. |
||||
|
||||
Returns: |
||||
An interfaces.DynamicStub that performs RPCs via the given |
||||
base_interfaces.Front. |
||||
""" |
||||
return _DynamicStub(cardinalities, front, pool) |
@ -1,634 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Interfaces for the face layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
import enum |
||||
|
||||
import six |
||||
|
||||
# cardinality, style, exceptions, abandonment, future, and stream are |
||||
# referenced from specification in this module. |
||||
from grpc.framework.common import cardinality # pylint: disable=unused-import |
||||
from grpc.framework.common import style # pylint: disable=unused-import |
||||
from grpc.framework.face import exceptions # pylint: disable=unused-import |
||||
from grpc.framework.foundation import abandonment # pylint: disable=unused-import |
||||
from grpc.framework.foundation import future # pylint: disable=unused-import |
||||
from grpc.framework.foundation import stream # pylint: disable=unused-import |
||||
|
||||
|
||||
@enum.unique |
||||
class Abortion(enum.Enum): |
||||
"""Categories of RPC abortion.""" |
||||
CANCELLED = 'cancelled' |
||||
EXPIRED = 'expired' |
||||
NETWORK_FAILURE = 'network failure' |
||||
SERVICED_FAILURE = 'serviced failure' |
||||
SERVICER_FAILURE = 'servicer failure' |
||||
|
||||
|
||||
class CancellableIterator(six.with_metaclass(abc.ABCMeta)): |
||||
"""Implements the Iterator protocol and affords a cancel method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __iter__(self): |
||||
"""Returns the self object in accordance with the Iterator protocol.""" |
||||
raise NotImplementedError() |
||||
|
||||
def __next__(self): |
||||
return self.next() |
||||
|
||||
@abc.abstractmethod |
||||
def next(self): |
||||
"""Returns a value or raises StopIteration per the Iterator protocol.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self): |
||||
"""Requests cancellation of whatever computation underlies this iterator.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class RpcContext(six.with_metaclass(abc.ABCMeta)): |
||||
"""Provides RPC-related information and action.""" |
||||
|
||||
@abc.abstractmethod |
||||
def is_active(self): |
||||
"""Describes whether the RPC is active or has terminated.""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def time_remaining(self): |
||||
"""Describes the length of allowed time remaining for the RPC. |
||||
|
||||
Returns: |
||||
A nonnegative float indicating the length of allowed time in seconds |
||||
remaining for the RPC to complete before it is considered to have timed |
||||
out. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_abortion_callback(self, abortion_callback): |
||||
"""Registers a callback to be called if the RPC is aborted. |
||||
|
||||
Args: |
||||
abortion_callback: A callable to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Call(six.with_metaclass(abc.ABCMeta)): |
||||
"""Invocation-side representation of an RPC. |
||||
|
||||
Attributes: |
||||
context: An RpcContext affording information about the RPC. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self): |
||||
"""Requests cancellation of the RPC.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a unary-unary RPC in any call style.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
The response value for the RPC. |
||||
|
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def future(self, request, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future's result value will be the response value of the RPC. |
||||
In the event of RPC abortion, the returned Future's exception value |
||||
will be an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event(self, request, response_callback, abortion_callback, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
response_callback: A callback to be called to accept the restponse value |
||||
of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A Call object for the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a unary-stream RPC in any call style.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A CancellableIterator that yields the response values of the RPC and |
||||
affords RPC cancellation. Drawing response values from the returned |
||||
CancellableIterator may raise exceptions.RpcError indicating abortion |
||||
of the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event(self, request, response_consumer, abortion_callback, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
response_consumer: A stream.Consumer to be called to accept the restponse |
||||
values of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A Call object for the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a stream-unary RPC in any call style.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request_iterator, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request_iterator: An iterator that yields request values for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
The response value for the RPC. |
||||
|
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def future(self, request_iterator, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request_iterator: An iterator that yields request values for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future's result value will be the response value of the RPC. |
||||
In the event of RPC abortion, the returned Future's exception value |
||||
will be an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event(self, response_callback, abortion_callback, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request: The request value for the RPC. |
||||
response_callback: A callback to be called to accept the restponse value |
||||
of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A pair of a Call object for the RPC and a stream.Consumer to which the |
||||
request values of the RPC should be passed. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords invoking a stream-stream RPC in any call style.""" |
||||
|
||||
@abc.abstractmethod |
||||
def __call__(self, request_iterator, timeout): |
||||
"""Synchronously invokes the underlying RPC. |
||||
|
||||
Args: |
||||
request_iterator: An iterator that yields request values for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A CancellableIterator that yields the response values of the RPC and |
||||
affords RPC cancellation. Drawing response values from the returned |
||||
CancellableIterator may raise exceptions.RpcError indicating abortion |
||||
of the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event(self, response_consumer, abortion_callback, timeout): |
||||
"""Asynchronously invokes the underlying RPC. |
||||
|
||||
l Args: |
||||
response_consumer: A stream.Consumer to be called to accept the restponse |
||||
values of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A pair of a Call object for the RPC and a stream.Consumer to which the |
||||
request values of the RPC should be passed. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class MethodImplementation(six.with_metaclass(abc.ABCMeta)): |
||||
"""A sum type that describes an RPC method implementation. |
||||
|
||||
Attributes: |
||||
cardinality: A cardinality.Cardinality value. |
||||
style: A style.Service value. |
||||
unary_unary_inline: The implementation of the RPC method as a callable |
||||
value that takes a request value and an RpcContext object and returns a |
||||
response value. Only non-None if cardinality is |
||||
cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE. |
||||
unary_stream_inline: The implementation of the RPC method as a callable |
||||
value that takes a request value and an RpcContext object and returns an |
||||
iterator of response values. Only non-None if cardinality is |
||||
cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE. |
||||
stream_unary_inline: The implementation of the RPC method as a callable |
||||
value that takes an iterator of request values and an RpcContext object |
||||
and returns a response value. Only non-None if cardinality is |
||||
cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE. |
||||
stream_stream_inline: The implementation of the RPC method as a callable |
||||
value that takes an iterator of request values and an RpcContext object |
||||
and returns an iterator of response values. Only non-None if cardinality |
||||
is cardinality.Cardinality.STREAM_STREAM and style is |
||||
style.Service.INLINE. |
||||
unary_unary_event: The implementation of the RPC method as a callable value |
||||
that takes a request value, a response callback to which to pass the |
||||
response value of the RPC, and an RpcContext. Only non-None if |
||||
cardinality is cardinality.Cardinality.UNARY_UNARY and style is |
||||
style.Service.EVENT. |
||||
unary_stream_event: The implementation of the RPC method as a callable |
||||
value that takes a request value, a stream.Consumer to which to pass the |
||||
the response values of the RPC, and an RpcContext. Only non-None if |
||||
cardinality is cardinality.Cardinality.UNARY_STREAM and style is |
||||
style.Service.EVENT. |
||||
stream_unary_event: The implementation of the RPC method as a callable |
||||
value that takes a response callback to which to pass the response value |
||||
of the RPC and an RpcContext and returns a stream.Consumer to which the |
||||
request values of the RPC should be passed. Only non-None if cardinality |
||||
is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT. |
||||
stream_stream_event: The implementation of the RPC method as a callable |
||||
value that takes a stream.Consumer to which to pass the response values |
||||
of the RPC and an RpcContext and returns a stream.Consumer to which the |
||||
request values of the RPC should be passed. Only non-None if cardinality |
||||
is cardinality.Cardinality.STREAM_STREAM and style is |
||||
style.Service.EVENT. |
||||
""" |
||||
|
||||
|
||||
class MultiMethodImplementation(six.with_metaclass(abc.ABCMeta)): |
||||
"""A general type able to service many RPC methods.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, name, response_consumer, context): |
||||
"""Services an RPC. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
response_consumer: A stream.Consumer to be called to accept the response |
||||
values of the RPC. |
||||
context: An RpcContext object. |
||||
|
||||
Returns: |
||||
A stream.Consumer with which to accept the request values of the RPC. The |
||||
consumer returned from this method may or may not be invoked to |
||||
completion: in the case of RPC abortion, RPC Framework will simply stop |
||||
passing values to this object. Implementations must not assume that this |
||||
object will be called to completion of the request stream or even called |
||||
at all. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: May or may not be raised when the RPC has been |
||||
aborted. |
||||
exceptions.NoSuchMethodError: If this MultiMethod does not recognize the |
||||
given RPC method name and is not able to service the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class GenericStub(six.with_metaclass(abc.ABCMeta)): |
||||
"""Affords RPC methods to callers.""" |
||||
|
||||
@abc.abstractmethod |
||||
def blocking_value_in_value_out(self, name, request, timeout): |
||||
"""Invokes a unary-request-unary-response RPC method. |
||||
|
||||
This method blocks until either returning the response value of the RPC |
||||
(in the event of RPC completion) or raising an exception (in the event of |
||||
RPC abortion). |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
The response value for the RPC. |
||||
|
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def future_value_in_value_out(self, name, request, timeout): |
||||
"""Invokes a unary-request-unary-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future will return an outcome indicating that the RPC returned |
||||
the response value of the RPC. In the event of RPC abortion, the |
||||
returned Future will return an outcome indicating that the RPC raised |
||||
an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def inline_value_in_stream_out(self, name, request, timeout): |
||||
"""Invokes a unary-request-stream-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request: The request value for the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A CancellableIterator that yields the response values of the RPC and |
||||
affords RPC cancellation. Drawing response values from the returned |
||||
CancellableIterator may raise exceptions.RpcError indicating abortion of |
||||
the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def blocking_stream_in_value_out(self, name, request_iterator, timeout): |
||||
"""Invokes a stream-request-unary-response RPC method. |
||||
|
||||
This method blocks until either returning the response value of the RPC |
||||
(in the event of RPC completion) or raising an exception (in the event of |
||||
RPC abortion). |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request_iterator: An iterator that yields the request values of the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
The response value for the RPC. |
||||
|
||||
Raises: |
||||
exceptions.RpcError: Indicating that the RPC was aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def future_stream_in_value_out(self, name, request_iterator, timeout): |
||||
"""Invokes a stream-request-unary-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request_iterator: An iterator that yields the request values of the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A future.Future representing the RPC. In the event of RPC completion, the |
||||
returned Future will return an outcome indicating that the RPC returned |
||||
the response value of the RPC. In the event of RPC abortion, the |
||||
returned Future will return an outcome indicating that the RPC raised |
||||
an exceptions.RpcError. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def inline_stream_in_stream_out(self, name, request_iterator, timeout): |
||||
"""Invokes a stream-request-stream-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request_iterator: An iterator that yields the request values of the RPC. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A CancellableIterator that yields the response values of the RPC and |
||||
affords RPC cancellation. Drawing response values from the returned |
||||
CancellableIterator may raise exceptions.RpcError indicating abortion of |
||||
the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event_value_in_value_out( |
||||
self, name, request, response_callback, abortion_callback, timeout): |
||||
"""Event-driven invocation of a unary-request-unary-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request: The request value for the RPC. |
||||
response_callback: A callback to be called to accept the response value |
||||
of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A Call object for the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event_value_in_stream_out( |
||||
self, name, request, response_consumer, abortion_callback, timeout): |
||||
"""Event-driven invocation of a unary-request-stream-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
request: The request value for the RPC. |
||||
response_consumer: A stream.Consumer to be called to accept the response |
||||
values of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A Call object for the RPC. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event_stream_in_value_out( |
||||
self, name, response_callback, abortion_callback, timeout): |
||||
"""Event-driven invocation of a unary-request-unary-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
response_callback: A callback to be called to accept the response value |
||||
of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A pair of a Call object for the RPC and a stream.Consumer to which the |
||||
request values of the RPC should be passed. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def event_stream_in_stream_out( |
||||
self, name, response_consumer, abortion_callback, timeout): |
||||
"""Event-driven invocation of a unary-request-stream-response RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
response_consumer: A stream.Consumer to be called to accept the response |
||||
values of the RPC. |
||||
abortion_callback: A callback to be called and passed an Abortion value |
||||
in the event of RPC abortion. |
||||
timeout: A duration of time in seconds to allow for the RPC. |
||||
|
||||
Returns: |
||||
A pair of a Call object for the RPC and a stream.Consumer to which the |
||||
request values of the RPC should be passed. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def unary_unary_multi_callable(self, name): |
||||
"""Creates a UnaryUnaryMultiCallable for a unary-unary RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
|
||||
Returns: |
||||
A UnaryUnaryMultiCallable value for the named unary-unary RPC method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def unary_stream_multi_callable(self, name): |
||||
"""Creates a UnaryStreamMultiCallable for a unary-stream RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
|
||||
Returns: |
||||
A UnaryStreamMultiCallable value for the name unary-stream RPC method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def stream_unary_multi_callable(self, name): |
||||
"""Creates a StreamUnaryMultiCallable for a stream-unary RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
|
||||
Returns: |
||||
A StreamUnaryMultiCallable value for the named stream-unary RPC method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def stream_stream_multi_callable(self, name): |
||||
"""Creates a StreamStreamMultiCallable for a stream-stream RPC method. |
||||
|
||||
Args: |
||||
name: The RPC method name. |
||||
|
||||
Returns: |
||||
A StreamStreamMultiCallable value for the named stream-stream RPC method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class DynamicStub(six.with_metaclass(abc.ABCMeta)): |
||||
"""A stub with RPC-method-bound multi-callable attributes. |
||||
|
||||
Instances of this type responsd to attribute access as follows: if the |
||||
requested attribute is the name of a unary-unary RPC method, the value of the |
||||
attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC |
||||
method; if the requested attribute is the name of a unary-stream RPC method, |
||||
the value of the attribute will be a UnaryStreamMultiCallable with which to |
||||
invoke the RPC method; if the requested attribute is the name of a |
||||
stream-unary RPC method, the value of the attribute will be a |
||||
StreamUnaryMultiCallable with which to invoke the RPC method; and if the |
||||
requested attribute is the name of a stream-stream RPC method, the value of |
||||
the attribute will be a StreamStreamMultiCallable with which to invoke the |
||||
RPC method. |
||||
""" |
@ -1,177 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Utilities for RPC framework's face layer.""" |
||||
|
||||
import collections |
||||
|
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.common import style |
||||
from grpc.framework.face import interfaces |
||||
from grpc.framework.foundation import stream |
||||
|
||||
|
||||
class _MethodImplementation( |
||||
interfaces.MethodImplementation, |
||||
collections.namedtuple( |
||||
'_MethodImplementation', |
||||
['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline', |
||||
'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event', |
||||
'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])): |
||||
pass |
||||
|
||||
|
||||
def unary_unary_inline(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a unary-unary RPC method as a callable value |
||||
that takes a request value and an interfaces.RpcContext object and |
||||
returns a response value. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior, |
||||
None, None, None, None, None, None, None) |
||||
|
||||
|
||||
def unary_stream_inline(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a unary-stream RPC method as a callable |
||||
value that takes a request value and an interfaces.RpcContext object and |
||||
returns an iterator of response values. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None, |
||||
behavior, None, None, None, None, None, None) |
||||
|
||||
|
||||
def stream_unary_inline(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a stream-unary RPC method as a callable |
||||
value that takes an iterator of request values and an |
||||
interfaces.RpcContext object and returns a response value. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None, |
||||
behavior, None, None, None, None, None) |
||||
|
||||
|
||||
def stream_stream_inline(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a stream-stream RPC method as a callable |
||||
value that takes an iterator of request values and an |
||||
interfaces.RpcContext object and returns an iterator of response values. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None, |
||||
None, behavior, None, None, None, None) |
||||
|
||||
|
||||
def unary_unary_event(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a unary-unary RPC method as a callable |
||||
value that takes a request value, a response callback to which to pass |
||||
the response value of the RPC, and an interfaces.RpcContext. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None, |
||||
None, None, behavior, None, None, None) |
||||
|
||||
|
||||
def unary_stream_event(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a unary-stream RPC method as a callable |
||||
value that takes a request value, a stream.Consumer to which to pass the |
||||
the response values of the RPC, and an interfaces.RpcContext. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None, |
||||
None, None, None, behavior, None, None) |
||||
|
||||
|
||||
def stream_unary_event(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a stream-unary RPC method as a callable |
||||
value that takes a response callback to which to pass the response value |
||||
of the RPC and an interfaces.RpcContext and returns a stream.Consumer to |
||||
which the request values of the RPC should be passed. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None, |
||||
None, None, None, None, behavior, None) |
||||
|
||||
|
||||
def stream_stream_event(behavior): |
||||
"""Creates an interfaces.MethodImplementation for the given behavior. |
||||
|
||||
Args: |
||||
behavior: The implementation of a stream-stream RPC method as a callable |
||||
value that takes a stream.Consumer to which to pass the response values |
||||
of the RPC and an interfaces.RpcContext and returns a stream.Consumer to |
||||
which the request values of the RPC should be passed. |
||||
|
||||
Returns: |
||||
An interfaces.MethodImplementation derived from the given behavior. |
||||
""" |
||||
return _MethodImplementation( |
||||
cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None, |
||||
None, None, None, None, None, behavior) |
@ -1,30 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
|
@ -1,30 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
|
@ -1,102 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Utilities for creating Base-layer objects for use in Face-layer tests.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# interfaces is referenced from specification in this module. |
||||
from grpc.framework.base import util as _base_util |
||||
from grpc.framework.base import implementations |
||||
from grpc.framework.base import in_memory |
||||
from grpc.framework.base import interfaces # pylint: disable=unused-import |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
_POOL_SIZE_LIMIT = 5 |
||||
|
||||
_MAXIMUM_TIMEOUT = 90 |
||||
|
||||
|
||||
class LinkedPair(six.with_metaclass(abc.ABCMeta)): |
||||
"""A Front and Back that are linked to one another. |
||||
|
||||
Attributes: |
||||
front: An interfaces.Front. |
||||
back: An interfaces.Back. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def shut_down(self): |
||||
"""Shuts down this object and releases its resources.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class _LinkedPair(LinkedPair): |
||||
|
||||
def __init__(self, front, back, pools): |
||||
self.front = front |
||||
self.back = back |
||||
self._pools = pools |
||||
|
||||
def shut_down(self): |
||||
_base_util.wait_for_idle(self.front) |
||||
_base_util.wait_for_idle(self.back) |
||||
|
||||
for pool in self._pools: |
||||
pool.shutdown(wait=True) |
||||
|
||||
|
||||
def linked_pair(servicer, default_timeout): |
||||
"""Creates a Server and Stub linked together for use.""" |
||||
link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) |
||||
pools = ( |
||||
link_pool, |
||||
front_work_pool, front_transmission_pool, front_utility_pool, |
||||
back_work_pool, back_transmission_pool, back_utility_pool) |
||||
|
||||
link = in_memory.Link(link_pool) |
||||
front = implementations.front_link( |
||||
front_work_pool, front_transmission_pool, front_utility_pool) |
||||
back = implementations.back_link( |
||||
servicer, back_work_pool, back_transmission_pool, back_utility_pool, |
||||
default_timeout, _MAXIMUM_TIMEOUT) |
||||
front.join_rear_link(link) |
||||
link.join_fore_link(front) |
||||
back.join_fore_link(link) |
||||
link.join_rear_link(back) |
||||
|
||||
return _LinkedPair(front, back, pools) |
@ -1,224 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""A test to verify an implementation of the Face layer of RPC Framework.""" |
||||
|
||||
# unittest is referenced from specification in this module. |
||||
import abc |
||||
import unittest # pylint: disable=unused-import |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.face import exceptions |
||||
from tests.unit.framework.common import test_constants |
||||
from tests.unit.framework.face.testing import control |
||||
from tests.unit.framework.face.testing import coverage |
||||
from tests.unit.framework.face.testing import digest |
||||
from tests.unit.framework.face.testing import stock_service |
||||
from tests.unit.framework.face.testing import test_case |
||||
|
||||
|
||||
class BlockingInvocationInlineServiceTestCase( |
||||
six.with_metaclass(abc.ABCMeta, |
||||
test_case.FaceTestCase, coverage.BlockingCoverage)): |
||||
"""A test of the Face layer of RPC Framework. |
||||
|
||||
Concrete subclasses must also extend unittest.TestCase. |
||||
""" |
||||
|
||||
def setUp(self): |
||||
"""See unittest.TestCase.setUp for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.control = control.PauseFailControl() |
||||
self.digest = digest.digest( |
||||
stock_service.STOCK_TEST_SERVICE, self.control, None) |
||||
|
||||
self.stub, self.memo = self.set_up_implementation( |
||||
self.digest.name, self.digest.methods, |
||||
self.digest.inline_method_implementations, None) |
||||
|
||||
def tearDown(self): |
||||
"""See unittest.TestCase.tearDown for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.tear_down_implementation(self.memo) |
||||
|
||||
def testSuccessfulUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
response = self.stub.blocking_value_in_value_out( |
||||
name, request, test_constants.LONG_TIMEOUT) |
||||
|
||||
test_messages.verify(request, response, self) |
||||
|
||||
def testSuccessfulUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.LONG_TIMEOUT) |
||||
responses = list(response_iterator) |
||||
|
||||
test_messages.verify(request, responses, self) |
||||
|
||||
def testSuccessfulStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
response = self.stub.blocking_stream_in_value_out( |
||||
name, iter(requests), test_constants.LONG_TIMEOUT) |
||||
|
||||
test_messages.verify(requests, response, self) |
||||
|
||||
def testSuccessfulStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.LONG_TIMEOUT) |
||||
responses = list(response_iterator) |
||||
|
||||
test_messages.verify(requests, responses, self) |
||||
|
||||
def testSequentialInvocations(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
first_request = test_messages.request() |
||||
second_request = test_messages.request() |
||||
|
||||
first_response = self.stub.blocking_value_in_value_out( |
||||
name, first_request, test_constants.SHORT_TIMEOUT) |
||||
|
||||
test_messages.verify(first_request, first_response, self) |
||||
|
||||
second_response = self.stub.blocking_value_in_value_out( |
||||
name, second_request, test_constants.SHORT_TIMEOUT) |
||||
|
||||
test_messages.verify(second_request, second_response, self) |
||||
|
||||
def testExpiredUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(), self.assertRaises( |
||||
exceptions.ExpirationError): |
||||
multi_callable = self.stub.unary_unary_multi_callable(name) |
||||
multi_callable(request, test_constants.SHORT_TIMEOUT) |
||||
|
||||
def testExpiredUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(), self.assertRaises( |
||||
exceptions.ExpirationError): |
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
||||
|
||||
def testExpiredStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(), self.assertRaises( |
||||
exceptions.ExpirationError): |
||||
multi_callable = self.stub.stream_unary_multi_callable(name) |
||||
multi_callable(iter(requests), test_constants.SHORT_TIMEOUT) |
||||
|
||||
def testExpiredStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(), self.assertRaises( |
||||
exceptions.ExpirationError): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
||||
|
||||
def testFailedUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.fail(), self.assertRaises(exceptions.ServicerError): |
||||
self.stub.blocking_value_in_value_out(name, request, |
||||
test_constants.SHORT_TIMEOUT) |
||||
|
||||
def testFailedUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.fail(), self.assertRaises(exceptions.ServicerError): |
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
||||
|
||||
def testFailedStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.fail(), self.assertRaises(exceptions.ServicerError): |
||||
self.stub.blocking_stream_in_value_out(name, iter(requests), |
||||
test_constants.SHORT_TIMEOUT) |
||||
|
||||
def testFailedStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.fail(), self.assertRaises(exceptions.ServicerError): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
@ -1,94 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""A utility useful in tests of asynchronous, event-driven interfaces.""" |
||||
|
||||
import threading |
||||
|
||||
from grpc.framework.foundation import stream |
||||
|
||||
|
||||
class Callback(stream.Consumer): |
||||
"""A utility object useful in tests of asynchronous code.""" |
||||
|
||||
def __init__(self): |
||||
self._condition = threading.Condition() |
||||
self._unary_response = None |
||||
self._streamed_responses = [] |
||||
self._completed = False |
||||
self._abortion = None |
||||
|
||||
def abort(self, abortion): |
||||
with self._condition: |
||||
self._abortion = abortion |
||||
self._condition.notify_all() |
||||
|
||||
def complete(self, unary_response): |
||||
with self._condition: |
||||
self._unary_response = unary_response |
||||
self._completed = True |
||||
self._condition.notify_all() |
||||
|
||||
def consume(self, streamed_response): |
||||
with self._condition: |
||||
self._streamed_responses.append(streamed_response) |
||||
|
||||
def terminate(self): |
||||
with self._condition: |
||||
self._completed = True |
||||
self._condition.notify_all() |
||||
|
||||
def consume_and_terminate(self, streamed_response): |
||||
with self._condition: |
||||
self._streamed_responses.append(streamed_response) |
||||
self._completed = True |
||||
self._condition.notify_all() |
||||
|
||||
def block_until_terminated(self): |
||||
with self._condition: |
||||
while self._abortion is None and not self._completed: |
||||
self._condition.wait() |
||||
|
||||
def response(self): |
||||
with self._condition: |
||||
if self._abortion is None: |
||||
return self._unary_response |
||||
else: |
||||
raise AssertionError('Aborted with abortion "%s"!' % self._abortion) |
||||
|
||||
def responses(self): |
||||
with self._condition: |
||||
if self._abortion is None: |
||||
return list(self._streamed_responses) |
||||
else: |
||||
raise AssertionError('Aborted with abortion "%s"!' % self._abortion) |
||||
|
||||
def abortion(self): |
||||
with self._condition: |
||||
return self._abortion |
@ -1,87 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Code for instructing systems under test to block or fail.""" |
||||
|
||||
import abc |
||||
import contextlib |
||||
import threading |
||||
|
||||
import six |
||||
|
||||
|
||||
class Control(six.with_metaclass(abc.ABCMeta)): |
||||
"""An object that accepts program control from a system under test. |
||||
|
||||
Systems under test passed a Control should call its control() method |
||||
frequently during execution. The control() method may block, raise an |
||||
exception, or do nothing, all according to the enclosing test's desire for |
||||
the system under test to simulate hanging, failing, or functioning. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def control(self): |
||||
"""Potentially does anything.""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class PauseFailControl(Control): |
||||
"""A Control that can be used to pause or fail code under control.""" |
||||
|
||||
def __init__(self): |
||||
self._condition = threading.Condition() |
||||
self._paused = False |
||||
self._fail = False |
||||
|
||||
def control(self): |
||||
with self._condition: |
||||
if self._fail: |
||||
raise ValueError() |
||||
|
||||
while self._paused: |
||||
self._condition.wait() |
||||
|
||||
@contextlib.contextmanager |
||||
def pause(self): |
||||
"""Pauses code under control while controlling code is in context.""" |
||||
with self._condition: |
||||
self._paused = True |
||||
yield |
||||
with self._condition: |
||||
self._paused = False |
||||
self._condition.notify_all() |
||||
|
||||
@contextlib.contextmanager |
||||
def fail(self): |
||||
"""Fails code under control while controlling code is in context.""" |
||||
with self._condition: |
||||
self._fail = True |
||||
yield |
||||
with self._condition: |
||||
self._fail = False |
@ -1,121 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Governs coverage for the tests of the Face layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# These classes are only valid when inherited by unittest.TestCases. |
||||
# pylint: disable=invalid-name |
||||
|
||||
|
||||
class BlockingCoverage(six.with_metaclass(abc.ABCMeta)): |
||||
"""Specification of test coverage for blocking behaviors.""" |
||||
|
||||
@abc.abstractmethod |
||||
def testSuccessfulUnaryRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testSuccessfulUnaryRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testSuccessfulStreamRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testSuccessfulStreamRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testSequentialInvocations(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testExpiredUnaryRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testExpiredUnaryRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testExpiredStreamRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testExpiredStreamRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testFailedUnaryRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testFailedUnaryRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testFailedStreamRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testFailedStreamRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class FullCoverage(six.with_metaclass(abc.ABCMeta, BlockingCoverage)): |
||||
"""Specification of test coverage for non-blocking behaviors.""" |
||||
|
||||
@abc.abstractmethod |
||||
def testParallelInvocations(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testWaitingForSomeButNotAllParallelInvocations(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testCancelledUnaryRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testCancelledUnaryRequestStreamResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testCancelledStreamRequestUnaryResponse(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def testCancelledStreamRequestStreamResponse(self): |
||||
raise NotImplementedError() |
@ -1,452 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Code for making a service.TestService more amenable to use in tests.""" |
||||
|
||||
import collections |
||||
import threading |
||||
|
||||
import six |
||||
|
||||
# testing_control, interfaces, and testing_service are referenced from |
||||
# specification in this module. |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.common import style |
||||
from grpc.framework.face import exceptions |
||||
from grpc.framework.face import interfaces as face_interfaces |
||||
from grpc.framework.foundation import stream |
||||
from grpc.framework.foundation import stream_util |
||||
from tests.unit.framework.face.testing import control as testing_control # pylint: disable=unused-import |
||||
from tests.unit.framework.face.testing import interfaces # pylint: disable=unused-import |
||||
from tests.unit.framework.face.testing import service as testing_service # pylint: disable=unused-import |
||||
|
||||
_IDENTITY = lambda x: x |
||||
|
||||
|
||||
class TestServiceDigest( |
||||
collections.namedtuple( |
||||
'TestServiceDigest', |
||||
['name', |
||||
'methods', |
||||
'inline_method_implementations', |
||||
'event_method_implementations', |
||||
'multi_method_implementation', |
||||
'unary_unary_messages_sequences', |
||||
'unary_stream_messages_sequences', |
||||
'stream_unary_messages_sequences', |
||||
'stream_stream_messages_sequences'])): |
||||
"""A transformation of a service.TestService. |
||||
|
||||
Attributes: |
||||
name: The RPC service name to be used in the test. |
||||
methods: A sequence of interfaces.Method objects describing the RPC |
||||
methods that will be called during the test. |
||||
inline_method_implementations: A dict from RPC method name to |
||||
face_interfaces.MethodImplementation object to be used in tests of |
||||
in-line calls to behaviors under test. |
||||
event_method_implementations: A dict from RPC method name to |
||||
face_interfaces.MethodImplementation object to be used in tests of |
||||
event-driven calls to behaviors under test. |
||||
multi_method_implementation: A face_interfaces.MultiMethodImplementation to |
||||
be used in tests of generic calls to behaviors under test. |
||||
unary_unary_messages_sequences: A dict from method name to sequence of |
||||
service.UnaryUnaryTestMessages objects to be used to test the method |
||||
with the given name. |
||||
unary_stream_messages_sequences: A dict from method name to sequence of |
||||
service.UnaryStreamTestMessages objects to be used to test the method |
||||
with the given name. |
||||
stream_unary_messages_sequences: A dict from method name to sequence of |
||||
service.StreamUnaryTestMessages objects to be used to test the method |
||||
with the given name. |
||||
stream_stream_messages_sequences: A dict from method name to sequence of |
||||
service.StreamStreamTestMessages objects to be used to test the |
||||
method with the given name. |
||||
serialization: A serial.Serialization object describing serialization |
||||
behaviors for all the RPC methods. |
||||
""" |
||||
|
||||
|
||||
class _BufferingConsumer(stream.Consumer): |
||||
"""A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" |
||||
|
||||
def __init__(self): |
||||
self.consumed = [] |
||||
self.terminated = False |
||||
|
||||
def consume(self, value): |
||||
self.consumed.append(value) |
||||
|
||||
def terminate(self): |
||||
self.terminated = True |
||||
|
||||
def consume_and_terminate(self, value): |
||||
self.consumed.append(value) |
||||
self.terminated = True |
||||
|
||||
|
||||
class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, unary_unary_test_method, control): |
||||
self._test_method = unary_unary_test_method |
||||
self._control = control |
||||
|
||||
self.cardinality = cardinality.Cardinality.UNARY_UNARY |
||||
self.style = style.Service.INLINE |
||||
|
||||
def unary_unary_inline(self, request, context): |
||||
response_list = [] |
||||
self._test_method.service( |
||||
request, response_list.append, context, self._control) |
||||
return response_list.pop(0) |
||||
|
||||
|
||||
class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, unary_unary_test_method, control, pool): |
||||
self._test_method = unary_unary_test_method |
||||
self._control = control |
||||
self._pool = pool |
||||
|
||||
self.cardinality = cardinality.Cardinality.UNARY_UNARY |
||||
self.style = style.Service.EVENT |
||||
|
||||
def unary_unary_event(self, request, response_callback, context): |
||||
if self._pool is None: |
||||
self._test_method.service( |
||||
request, response_callback, context, self._control) |
||||
else: |
||||
self._pool.submit( |
||||
self._test_method.service, request, response_callback, context, |
||||
self._control) |
||||
|
||||
|
||||
class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, unary_stream_test_method, control): |
||||
self._test_method = unary_stream_test_method |
||||
self._control = control |
||||
|
||||
self.cardinality = cardinality.Cardinality.UNARY_STREAM |
||||
self.style = style.Service.INLINE |
||||
|
||||
def unary_stream_inline(self, request, context): |
||||
response_consumer = _BufferingConsumer() |
||||
self._test_method.service( |
||||
request, response_consumer, context, self._control) |
||||
for response in response_consumer.consumed: |
||||
yield response |
||||
|
||||
|
||||
class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, unary_stream_test_method, control, pool): |
||||
self._test_method = unary_stream_test_method |
||||
self._control = control |
||||
self._pool = pool |
||||
|
||||
self.cardinality = cardinality.Cardinality.UNARY_STREAM |
||||
self.style = style.Service.EVENT |
||||
|
||||
def unary_stream_event(self, request, response_consumer, context): |
||||
if self._pool is None: |
||||
self._test_method.service( |
||||
request, response_consumer, context, self._control) |
||||
else: |
||||
self._pool.submit( |
||||
self._test_method.service, request, response_consumer, context, |
||||
self._control) |
||||
|
||||
|
||||
class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, stream_unary_test_method, control): |
||||
self._test_method = stream_unary_test_method |
||||
self._control = control |
||||
|
||||
self.cardinality = cardinality.Cardinality.STREAM_UNARY |
||||
self.style = style.Service.INLINE |
||||
|
||||
def stream_unary_inline(self, request_iterator, context): |
||||
response_list = [] |
||||
request_consumer = self._test_method.service( |
||||
response_list.append, context, self._control) |
||||
for request in request_iterator: |
||||
request_consumer.consume(request) |
||||
request_consumer.terminate() |
||||
return response_list.pop(0) |
||||
|
||||
|
||||
class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, stream_unary_test_method, control, pool): |
||||
self._test_method = stream_unary_test_method |
||||
self._control = control |
||||
self._pool = pool |
||||
|
||||
self.cardinality = cardinality.Cardinality.STREAM_UNARY |
||||
self.style = style.Service.EVENT |
||||
|
||||
def stream_unary_event(self, response_callback, context): |
||||
request_consumer = self._test_method.service( |
||||
response_callback, context, self._control) |
||||
if self._pool is None: |
||||
return request_consumer |
||||
else: |
||||
return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) |
||||
|
||||
|
||||
class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, stream_stream_test_method, control): |
||||
self._test_method = stream_stream_test_method |
||||
self._control = control |
||||
|
||||
self.cardinality = cardinality.Cardinality.STREAM_STREAM |
||||
self.style = style.Service.INLINE |
||||
|
||||
def stream_stream_inline(self, request_iterator, context): |
||||
response_consumer = _BufferingConsumer() |
||||
request_consumer = self._test_method.service( |
||||
response_consumer, context, self._control) |
||||
|
||||
for request in request_iterator: |
||||
request_consumer.consume(request) |
||||
while response_consumer.consumed: |
||||
yield response_consumer.consumed.pop(0) |
||||
response_consumer.terminate() |
||||
|
||||
|
||||
class _EventStreamStreamMethod(face_interfaces.MethodImplementation): |
||||
|
||||
def __init__(self, stream_stream_test_method, control, pool): |
||||
self._test_method = stream_stream_test_method |
||||
self._control = control |
||||
self._pool = pool |
||||
|
||||
self.cardinality = cardinality.Cardinality.STREAM_STREAM |
||||
self.style = style.Service.EVENT |
||||
|
||||
def stream_stream_event(self, response_consumer, context): |
||||
request_consumer = self._test_method.service( |
||||
response_consumer, context, self._control) |
||||
if self._pool is None: |
||||
return request_consumer |
||||
else: |
||||
return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) |
||||
|
||||
|
||||
class _UnaryConsumer(stream.Consumer): |
||||
"""A Consumer that only allows consumption of exactly one value.""" |
||||
|
||||
def __init__(self, action): |
||||
self._lock = threading.Lock() |
||||
self._action = action |
||||
self._consumed = False |
||||
self._terminated = False |
||||
|
||||
def consume(self, value): |
||||
with self._lock: |
||||
if self._consumed: |
||||
raise ValueError('Unary consumer already consumed!') |
||||
elif self._terminated: |
||||
raise ValueError('Unary consumer already terminated!') |
||||
else: |
||||
self._consumed = True |
||||
|
||||
self._action(value) |
||||
|
||||
def terminate(self): |
||||
with self._lock: |
||||
if not self._consumed: |
||||
raise ValueError('Unary consumer hasn\'t yet consumed!') |
||||
elif self._terminated: |
||||
raise ValueError('Unary consumer already terminated!') |
||||
else: |
||||
self._terminated = True |
||||
|
||||
def consume_and_terminate(self, value): |
||||
with self._lock: |
||||
if self._consumed: |
||||
raise ValueError('Unary consumer already consumed!') |
||||
elif self._terminated: |
||||
raise ValueError('Unary consumer already terminated!') |
||||
else: |
||||
self._consumed = True |
||||
self._terminated = True |
||||
|
||||
self._action(value) |
||||
|
||||
|
||||
class _UnaryUnaryAdaptation(object): |
||||
|
||||
def __init__(self, unary_unary_test_method): |
||||
self._method = unary_unary_test_method |
||||
|
||||
def service(self, response_consumer, context, control): |
||||
def action(request): |
||||
self._method.service( |
||||
request, response_consumer.consume_and_terminate, context, control) |
||||
return _UnaryConsumer(action) |
||||
|
||||
|
||||
class _UnaryStreamAdaptation(object): |
||||
|
||||
def __init__(self, unary_stream_test_method): |
||||
self._method = unary_stream_test_method |
||||
|
||||
def service(self, response_consumer, context, control): |
||||
def action(request): |
||||
self._method.service(request, response_consumer, context, control) |
||||
return _UnaryConsumer(action) |
||||
|
||||
|
||||
class _StreamUnaryAdaptation(object): |
||||
|
||||
def __init__(self, stream_unary_test_method): |
||||
self._method = stream_unary_test_method |
||||
|
||||
def service(self, response_consumer, context, control): |
||||
return self._method.service( |
||||
response_consumer.consume_and_terminate, context, control) |
||||
|
||||
|
||||
class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): |
||||
|
||||
def __init__(self, methods, control, pool): |
||||
self._methods = methods |
||||
self._control = control |
||||
self._pool = pool |
||||
|
||||
def service(self, name, response_consumer, context): |
||||
method = self._methods.get(name, None) |
||||
if method is None: |
||||
raise exceptions.NoSuchMethodError(name) |
||||
elif self._pool is None: |
||||
return method(response_consumer, context, self._control) |
||||
else: |
||||
request_consumer = method(response_consumer, context, self._control) |
||||
return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) |
||||
|
||||
|
||||
class _Assembly( |
||||
collections.namedtuple( |
||||
'_Assembly', |
||||
['methods', 'inlines', 'events', 'adaptations', 'messages'])): |
||||
"""An intermediate structure created when creating a TestServiceDigest.""" |
||||
|
||||
|
||||
def _assemble( |
||||
scenarios, names, inline_method_constructor, event_method_constructor, |
||||
adapter, control, pool): |
||||
"""Creates an _Assembly from the given scenarios.""" |
||||
methods = [] |
||||
inlines = {} |
||||
events = {} |
||||
adaptations = {} |
||||
messages = {} |
||||
for name, scenario in six.iteritems(scenarios): |
||||
if name in names: |
||||
raise ValueError('Repeated name "%s"!' % name) |
||||
|
||||
test_method = scenario[0] |
||||
inline_method = inline_method_constructor(test_method, control) |
||||
event_method = event_method_constructor(test_method, control, pool) |
||||
adaptation = adapter(test_method) |
||||
|
||||
methods.append(test_method) |
||||
inlines[name] = inline_method |
||||
events[name] = event_method |
||||
adaptations[name] = adaptation |
||||
messages[name] = scenario[1] |
||||
|
||||
return _Assembly(methods, inlines, events, adaptations, messages) |
||||
|
||||
|
||||
def digest(service, control, pool): |
||||
"""Creates a TestServiceDigest from a TestService. |
||||
|
||||
Args: |
||||
service: A testing_service.TestService. |
||||
control: A testing_control.Control. |
||||
pool: If RPC methods should be serviced in a separate thread, a thread pool. |
||||
None if RPC methods should be serviced in the thread belonging to the |
||||
run-time that calls for their service. |
||||
|
||||
Returns: |
||||
A TestServiceDigest synthesized from the given service.TestService. |
||||
""" |
||||
names = set() |
||||
|
||||
unary_unary = _assemble( |
||||
service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, |
||||
_EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) |
||||
names.update(set(unary_unary.inlines)) |
||||
|
||||
unary_stream = _assemble( |
||||
service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, |
||||
_EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) |
||||
names.update(set(unary_stream.inlines)) |
||||
|
||||
stream_unary = _assemble( |
||||
service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, |
||||
_EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) |
||||
names.update(set(stream_unary.inlines)) |
||||
|
||||
stream_stream = _assemble( |
||||
service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, |
||||
_EventStreamStreamMethod, _IDENTITY, control, pool) |
||||
names.update(set(stream_stream.inlines)) |
||||
|
||||
methods = list(unary_unary.methods) |
||||
methods.extend(unary_stream.methods) |
||||
methods.extend(stream_unary.methods) |
||||
methods.extend(stream_stream.methods) |
||||
adaptations = dict(unary_unary.adaptations) |
||||
adaptations.update(unary_stream.adaptations) |
||||
adaptations.update(stream_unary.adaptations) |
||||
adaptations.update(stream_stream.adaptations) |
||||
inlines = dict(unary_unary.inlines) |
||||
inlines.update(unary_stream.inlines) |
||||
inlines.update(stream_unary.inlines) |
||||
inlines.update(stream_stream.inlines) |
||||
events = dict(unary_unary.events) |
||||
events.update(unary_stream.events) |
||||
events.update(stream_unary.events) |
||||
events.update(stream_stream.events) |
||||
|
||||
return TestServiceDigest( |
||||
service.name(), |
||||
methods, |
||||
inlines, |
||||
events, |
||||
_MultiMethodImplementation(adaptations, control, pool), |
||||
unary_unary.messages, |
||||
unary_stream.messages, |
||||
stream_unary.messages, |
||||
stream_stream.messages) |
@ -1,378 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""A test to verify an implementation of the Face layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
import unittest |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.face import interfaces |
||||
from tests.unit.framework.common import test_constants |
||||
from tests.unit.framework.face.testing import callback as testing_callback |
||||
from tests.unit.framework.face.testing import control |
||||
from tests.unit.framework.face.testing import coverage |
||||
from tests.unit.framework.face.testing import digest |
||||
from tests.unit.framework.face.testing import stock_service |
||||
from tests.unit.framework.face.testing import test_case |
||||
|
||||
|
||||
class EventInvocationSynchronousEventServiceTestCase( |
||||
six.with_metaclass(abc.ABCMeta, |
||||
test_case.FaceTestCase, coverage.FullCoverage)): |
||||
"""A test of the Face layer of RPC Framework. |
||||
|
||||
Concrete subclasses must also extend unittest.TestCase. |
||||
""" |
||||
|
||||
def setUp(self): |
||||
"""See unittest.TestCase.setUp for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.control = control.PauseFailControl() |
||||
self.digest = digest.digest( |
||||
stock_service.STOCK_TEST_SERVICE, self.control, None) |
||||
|
||||
self.stub, self.memo = self.set_up_implementation( |
||||
self.digest.name, self.digest.methods, |
||||
self.digest.event_method_implementations, None) |
||||
|
||||
def tearDown(self): |
||||
"""See unittest.TestCase.tearDown for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.tear_down_implementation(self.memo) |
||||
|
||||
def testSuccessfulUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
self.stub.event_value_in_value_out( |
||||
name, request, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
response = callback.response() |
||||
|
||||
test_messages.verify(request, response, self) |
||||
|
||||
def testSuccessfulUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
self.stub.event_value_in_stream_out( |
||||
name, request, callback, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
responses = callback.responses() |
||||
|
||||
test_messages.verify(request, responses, self) |
||||
|
||||
def testSuccessfulStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
unused_call, request_consumer = self.stub.event_stream_in_value_out( |
||||
name, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
request_consumer.terminate() |
||||
callback.block_until_terminated() |
||||
response = callback.response() |
||||
|
||||
test_messages.verify(requests, response, self) |
||||
|
||||
def testSuccessfulStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
unused_call, request_consumer = self.stub.event_stream_in_stream_out( |
||||
name, callback, callback.abort, test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
request_consumer.terminate() |
||||
callback.block_until_terminated() |
||||
responses = callback.responses() |
||||
|
||||
test_messages.verify(requests, responses, self) |
||||
|
||||
def testSequentialInvocations(self): |
||||
# pylint: disable=cell-var-from-loop |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
first_request = test_messages.request() |
||||
second_request = test_messages.request() |
||||
first_callback = testing_callback.Callback() |
||||
second_callback = testing_callback.Callback() |
||||
|
||||
def make_second_invocation(first_response): |
||||
first_callback.complete(first_response) |
||||
self.stub.event_value_in_value_out( |
||||
name, second_request, second_callback.complete, |
||||
second_callback.abort, test_constants.SHORT_TIMEOUT) |
||||
|
||||
self.stub.event_value_in_value_out( |
||||
name, first_request, make_second_invocation, first_callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
second_callback.block_until_terminated() |
||||
|
||||
first_response = first_callback.response() |
||||
second_response = second_callback.response() |
||||
test_messages.verify(first_request, first_response, self) |
||||
test_messages.verify(second_request, second_response, self) |
||||
|
||||
def testExpiredUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.pause(): |
||||
self.stub.event_value_in_value_out( |
||||
name, request, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) |
||||
|
||||
def testExpiredUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.pause(): |
||||
self.stub.event_value_in_stream_out( |
||||
name, request, callback, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) |
||||
|
||||
def testExpiredStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for unused_test_messages in test_messages_sequence: |
||||
callback = testing_callback.Callback() |
||||
|
||||
self.stub.event_stream_in_value_out( |
||||
name, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) |
||||
|
||||
def testExpiredStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
unused_call, request_consumer = self.stub.event_stream_in_stream_out( |
||||
name, callback, callback.abort, test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) |
||||
|
||||
def testFailedUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.fail(): |
||||
self.stub.event_value_in_value_out( |
||||
name, request, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, |
||||
callback.abortion()) |
||||
|
||||
def testFailedUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.fail(): |
||||
self.stub.event_value_in_stream_out( |
||||
name, request, callback, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, |
||||
callback.abortion()) |
||||
|
||||
def testFailedStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.fail(): |
||||
unused_call, request_consumer = self.stub.event_stream_in_value_out( |
||||
name, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
request_consumer.terminate() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, |
||||
callback.abortion()) |
||||
|
||||
def testFailedStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.fail(): |
||||
unused_call, request_consumer = self.stub.event_stream_in_stream_out( |
||||
name, callback, callback.abort, test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
request_consumer.terminate() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) |
||||
|
||||
def testParallelInvocations(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
first_request = test_messages.request() |
||||
first_callback = testing_callback.Callback() |
||||
second_request = test_messages.request() |
||||
second_callback = testing_callback.Callback() |
||||
|
||||
self.stub.event_value_in_value_out( |
||||
name, first_request, first_callback.complete, first_callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
self.stub.event_value_in_value_out( |
||||
name, second_request, second_callback.complete, |
||||
second_callback.abort, test_constants.SHORT_TIMEOUT) |
||||
first_callback.block_until_terminated() |
||||
second_callback.block_until_terminated() |
||||
|
||||
first_response = first_callback.response() |
||||
second_response = second_callback.response() |
||||
test_messages.verify(first_request, first_response, self) |
||||
test_messages.verify(second_request, second_response, self) |
||||
|
||||
@unittest.skip('TODO(nathaniel): implement.') |
||||
def testWaitingForSomeButNotAllParallelInvocations(self): |
||||
raise NotImplementedError() |
||||
|
||||
def testCancelledUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
with self.control.pause(): |
||||
call = self.stub.event_value_in_value_out( |
||||
name, request, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
call.cancel() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) |
||||
|
||||
def testCancelledUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
callback = testing_callback.Callback() |
||||
|
||||
call = self.stub.event_value_in_stream_out( |
||||
name, request, callback, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
call.cancel() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) |
||||
|
||||
def testCancelledStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
callback = testing_callback.Callback() |
||||
|
||||
call, request_consumer = self.stub.event_stream_in_value_out( |
||||
name, callback.complete, callback.abort, |
||||
test_constants.SHORT_TIMEOUT) |
||||
for request in requests: |
||||
request_consumer.consume(request) |
||||
call.cancel() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) |
||||
|
||||
def testCancelledStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for unused_test_messages in test_messages_sequence: |
||||
callback = testing_callback.Callback() |
||||
|
||||
call, unused_request_consumer = self.stub.event_stream_in_stream_out( |
||||
name, callback, callback.abort, test_constants.SHORT_TIMEOUT) |
||||
call.cancel() |
||||
callback.block_until_terminated() |
||||
|
||||
self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) |
@ -1,384 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""A test to verify an implementation of the Face layer of RPC Framework.""" |
||||
|
||||
import abc |
||||
import contextlib |
||||
import threading |
||||
import unittest |
||||
|
||||
import six |
||||
|
||||
from grpc.framework.face import exceptions |
||||
from grpc.framework.foundation import future |
||||
from grpc.framework.foundation import logging_pool |
||||
from tests.unit.framework.common import test_constants |
||||
from tests.unit.framework.face.testing import control |
||||
from tests.unit.framework.face.testing import coverage |
||||
from tests.unit.framework.face.testing import digest |
||||
from tests.unit.framework.face.testing import stock_service |
||||
from tests.unit.framework.face.testing import test_case |
||||
|
||||
_MAXIMUM_POOL_SIZE = 10 |
||||
|
||||
|
||||
class _PauseableIterator(object): |
||||
|
||||
def __init__(self, upstream): |
||||
self._upstream = upstream |
||||
self._condition = threading.Condition() |
||||
self._paused = False |
||||
|
||||
@contextlib.contextmanager |
||||
def pause(self): |
||||
with self._condition: |
||||
self._paused = True |
||||
yield |
||||
with self._condition: |
||||
self._paused = False |
||||
self._condition.notify_all() |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def __next__(self): |
||||
return self.next() |
||||
|
||||
def next(self): |
||||
with self._condition: |
||||
while self._paused: |
||||
self._condition.wait() |
||||
return next(self._upstream) |
||||
|
||||
|
||||
class FutureInvocationAsynchronousEventServiceTestCase( |
||||
six.with_metaclass(abc.ABCMeta, |
||||
test_case.FaceTestCase, coverage.FullCoverage)): |
||||
"""A test of the Face layer of RPC Framework. |
||||
|
||||
Concrete subclasses must also extend unittest.TestCase. |
||||
""" |
||||
|
||||
def setUp(self): |
||||
"""See unittest.TestCase.setUp for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.control = control.PauseFailControl() |
||||
self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) |
||||
self.digest = digest.digest( |
||||
stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) |
||||
|
||||
self.stub, self.memo = self.set_up_implementation( |
||||
self.digest.name, self.digest.methods, |
||||
self.digest.event_method_implementations, None) |
||||
|
||||
def tearDown(self): |
||||
"""See unittest.TestCase.tearDown for full specification. |
||||
|
||||
Overriding implementations must call this implementation. |
||||
""" |
||||
self.tear_down_implementation(self.memo) |
||||
self.digest_pool.shutdown(wait=True) |
||||
|
||||
def testSuccessfulUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
response_future = self.stub.future_value_in_value_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
response = response_future.result() |
||||
|
||||
test_messages.verify(request, response, self) |
||||
|
||||
def testSuccessfulUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
responses = list(response_iterator) |
||||
|
||||
test_messages.verify(request, responses, self) |
||||
|
||||
def testSuccessfulStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
request_iterator = _PauseableIterator(iter(requests)) |
||||
|
||||
# Use of a paused iterator of requests allows us to test that control is |
||||
# returned to calling code before the iterator yields any requests. |
||||
with request_iterator.pause(): |
||||
response_future = self.stub.future_stream_in_value_out( |
||||
name, request_iterator, test_constants.SHORT_TIMEOUT) |
||||
response = response_future.result() |
||||
|
||||
test_messages.verify(requests, response, self) |
||||
|
||||
def testSuccessfulStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
request_iterator = _PauseableIterator(iter(requests)) |
||||
|
||||
# Use of a paused iterator of requests allows us to test that control is |
||||
# returned to calling code before the iterator yields any requests. |
||||
with request_iterator.pause(): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, request_iterator, test_constants.SHORT_TIMEOUT) |
||||
responses = list(response_iterator) |
||||
|
||||
test_messages.verify(requests, responses, self) |
||||
|
||||
def testSequentialInvocations(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
first_request = test_messages.request() |
||||
second_request = test_messages.request() |
||||
|
||||
first_response_future = self.stub.future_value_in_value_out( |
||||
name, first_request, test_constants.SHORT_TIMEOUT) |
||||
first_response = first_response_future.result() |
||||
|
||||
test_messages.verify(first_request, first_response, self) |
||||
|
||||
second_response_future = self.stub.future_value_in_value_out( |
||||
name, second_request, test_constants.SHORT_TIMEOUT) |
||||
second_response = second_response_future.result() |
||||
|
||||
test_messages.verify(second_request, second_response, self) |
||||
|
||||
def testExpiredUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(): |
||||
multi_callable = self.stub.unary_unary_multi_callable(name) |
||||
response_future = multi_callable.future(request, |
||||
test_constants.SHORT_TIMEOUT) |
||||
self.assertIsInstance( |
||||
response_future.exception(), exceptions.ExpirationError) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
response_future.result() |
||||
|
||||
def testExpiredUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(): |
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
list(response_iterator) |
||||
|
||||
def testExpiredStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(): |
||||
multi_callable = self.stub.stream_unary_multi_callable(name) |
||||
response_future = multi_callable.future(iter(requests), |
||||
test_constants.SHORT_TIMEOUT) |
||||
self.assertIsInstance( |
||||
response_future.exception(), exceptions.ExpirationError) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
response_future.result() |
||||
|
||||
def testExpiredStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
list(response_iterator) |
||||
|
||||
def testFailedUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.fail(): |
||||
response_future = self.stub.future_value_in_value_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
|
||||
# Because the servicer fails outside of the thread from which the |
||||
# servicer-side runtime called into it its failure is |
||||
# indistinguishable from simply not having called its |
||||
# response_callback before the expiration of the RPC. |
||||
self.assertIsInstance( |
||||
response_future.exception(), exceptions.ExpirationError) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
response_future.result() |
||||
|
||||
def testFailedUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
# Because the servicer fails outside of the thread from which the |
||||
# servicer-side runtime called into it its failure is indistinguishable |
||||
# from simply not having called its response_consumer before the |
||||
# expiration of the RPC. |
||||
with self.control.fail(), self.assertRaises(exceptions.ExpirationError): |
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
||||
|
||||
def testFailedStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.fail(): |
||||
response_future = self.stub.future_stream_in_value_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
|
||||
# Because the servicer fails outside of the thread from which the |
||||
# servicer-side runtime called into it its failure is |
||||
# indistinguishable from simply not having called its |
||||
# response_callback before the expiration of the RPC. |
||||
self.assertIsInstance( |
||||
response_future.exception(), exceptions.ExpirationError) |
||||
with self.assertRaises(exceptions.ExpirationError): |
||||
response_future.result() |
||||
|
||||
def testFailedStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
# Because the servicer fails outside of the thread from which the |
||||
# servicer-side runtime called into it its failure is indistinguishable |
||||
# from simply not having called its response_consumer before the |
||||
# expiration of the RPC. |
||||
with self.control.fail(), self.assertRaises(exceptions.ExpirationError): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
list(response_iterator) |
||||
|
||||
def testParallelInvocations(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
first_request = test_messages.request() |
||||
second_request = test_messages.request() |
||||
|
||||
# TODO(bug 2039): use LONG_TIMEOUT instead |
||||
first_response_future = self.stub.future_value_in_value_out( |
||||
name, first_request, test_constants.SHORT_TIMEOUT) |
||||
second_response_future = self.stub.future_value_in_value_out( |
||||
name, second_request, test_constants.SHORT_TIMEOUT) |
||||
first_response = first_response_future.result() |
||||
second_response = second_response_future.result() |
||||
|
||||
test_messages.verify(first_request, first_response, self) |
||||
test_messages.verify(second_request, second_response, self) |
||||
|
||||
@unittest.skip('TODO(nathaniel): implement.') |
||||
def testWaitingForSomeButNotAllParallelInvocations(self): |
||||
raise NotImplementedError() |
||||
|
||||
def testCancelledUnaryRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(): |
||||
response_future = self.stub.future_value_in_value_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
cancel_method_return_value = response_future.cancel() |
||||
|
||||
self.assertFalse(cancel_method_return_value) |
||||
self.assertTrue(response_future.cancelled()) |
||||
|
||||
def testCancelledUnaryRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.unary_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
request = test_messages.request() |
||||
|
||||
with self.control.pause(): |
||||
response_iterator = self.stub.inline_value_in_stream_out( |
||||
name, request, test_constants.SHORT_TIMEOUT) |
||||
response_iterator.cancel() |
||||
|
||||
with self.assertRaises(future.CancelledError): |
||||
next(response_iterator) |
||||
|
||||
def testCancelledStreamRequestUnaryResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_unary_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(): |
||||
response_future = self.stub.future_stream_in_value_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
cancel_method_return_value = response_future.cancel() |
||||
|
||||
self.assertFalse(cancel_method_return_value) |
||||
self.assertTrue(response_future.cancelled()) |
||||
|
||||
def testCancelledStreamRequestStreamResponse(self): |
||||
for name, test_messages_sequence in ( |
||||
six.iteritems(self.digest.stream_stream_messages_sequences)): |
||||
for test_messages in test_messages_sequence: |
||||
requests = test_messages.requests() |
||||
|
||||
with self.control.pause(): |
||||
response_iterator = self.stub.inline_stream_in_stream_out( |
||||
name, iter(requests), test_constants.SHORT_TIMEOUT) |
||||
response_iterator.cancel() |
||||
|
||||
with self.assertRaises(future.CancelledError): |
||||
next(response_iterator) |
@ -1,118 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Interfaces implemented by data sets used in Face-layer tests.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# cardinality is referenced from specification in this module. |
||||
from grpc.framework.common import cardinality # pylint: disable=unused-import |
||||
|
||||
|
||||
class Method(six.with_metaclass(abc.ABCMeta)): |
||||
"""An RPC method to be used in tests of RPC implementations.""" |
||||
|
||||
@abc.abstractmethod |
||||
def name(self): |
||||
"""Identify the name of the method. |
||||
|
||||
Returns: |
||||
The name of the method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def cardinality(self): |
||||
"""Identify the cardinality of the method. |
||||
|
||||
Returns: |
||||
A cardinality.Cardinality value describing the streaming semantics of the |
||||
method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def request_class(self): |
||||
"""Identify the class used for the method's request objects. |
||||
|
||||
Returns: |
||||
The class object of the class to which the method's request objects |
||||
belong. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def response_class(self): |
||||
"""Identify the class used for the method's response objects. |
||||
|
||||
Returns: |
||||
The class object of the class to which the method's response objects |
||||
belong. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def serialize_request(self, request): |
||||
"""Serialize the given request object. |
||||
|
||||
Args: |
||||
request: A request object appropriate for this method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def deserialize_request(self, serialized_request): |
||||
"""Synthesize a request object from a given bytestring. |
||||
|
||||
Args: |
||||
serialized_request: A bytestring deserializable into a request object |
||||
appropriate for this method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def serialize_response(self, response): |
||||
"""Serialize the given response object. |
||||
|
||||
Args: |
||||
response: A response object appropriate for this method. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def deserialize_response(self, serialized_response): |
||||
"""Synthesize a response object from a given bytestring. |
||||
|
||||
Args: |
||||
serialized_response: A bytestring deserializable into a response object |
||||
appropriate for this method. |
||||
""" |
||||
raise NotImplementedError() |
@ -1,321 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Private interfaces implemented by data sets used in Face-layer tests.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# interfaces is referenced from specification in this module. |
||||
from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import |
||||
from tests.unit.framework.face.testing import interfaces |
||||
|
||||
|
||||
class UnaryUnaryTestMethodImplementation(six.with_metaclass(abc.ABCMeta, interfaces.Method)): |
||||
"""A controllable implementation of a unary-unary RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, request, response_callback, context, control): |
||||
"""Services an RPC that accepts one message and produces one message. |
||||
|
||||
Args: |
||||
request: The single request message for the RPC. |
||||
response_callback: A callback to be called to accept the response message |
||||
of the RPC. |
||||
context: An face_interfaces.RpcContext object. |
||||
control: A test_control.Control to control execution of this method. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: May or may not be raised when the RPC has been |
||||
aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryUnaryTestMessages(six.with_metaclass(abc.ABCMeta)): |
||||
"""A type for unary-request-unary-response message pairings.""" |
||||
|
||||
@abc.abstractmethod |
||||
def request(self): |
||||
"""Affords a request message. |
||||
|
||||
Implementations of this method should return a different message with each |
||||
call so that multiple test executions of the test method may be made with |
||||
different inputs. |
||||
|
||||
Returns: |
||||
A request message. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def verify(self, request, response, test_case): |
||||
"""Verifies that the computed response matches the given request. |
||||
|
||||
Args: |
||||
request: A request message. |
||||
response: A response message. |
||||
test_case: A unittest.TestCase object affording useful assertion methods. |
||||
|
||||
Raises: |
||||
AssertionError: If the request and response do not match, indicating that |
||||
there was some problem executing the RPC under test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryStreamTestMethodImplementation(six.with_metaclass(abc.ABCMeta, interfaces.Method)): |
||||
"""A controllable implementation of a unary-stream RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, request, response_consumer, context, control): |
||||
"""Services an RPC that takes one message and produces a stream of messages. |
||||
|
||||
Args: |
||||
request: The single request message for the RPC. |
||||
response_consumer: A stream.Consumer to be called to accept the response |
||||
messages of the RPC. |
||||
context: A face_interfaces.RpcContext object. |
||||
control: A test_control.Control to control execution of this method. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: May or may not be raised when the RPC has been |
||||
aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class UnaryStreamTestMessages(six.with_metaclass(abc.ABCMeta)): |
||||
"""A type for unary-request-stream-response message pairings.""" |
||||
|
||||
@abc.abstractmethod |
||||
def request(self): |
||||
"""Affords a request message. |
||||
|
||||
Implementations of this method should return a different message with each |
||||
call so that multiple test executions of the test method may be made with |
||||
different inputs. |
||||
|
||||
Returns: |
||||
A request message. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def verify(self, request, responses, test_case): |
||||
"""Verifies that the computed responses match the given request. |
||||
|
||||
Args: |
||||
request: A request message. |
||||
responses: A sequence of response messages. |
||||
test_case: A unittest.TestCase object affording useful assertion methods. |
||||
|
||||
Raises: |
||||
AssertionError: If the request and responses do not match, indicating that |
||||
there was some problem executing the RPC under test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamUnaryTestMethodImplementation(six.with_metaclass(abc.ABCMeta, interfaces.Method)): |
||||
"""A controllable implementation of a stream-unary RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, response_callback, context, control): |
||||
"""Services an RPC that takes a stream of messages and produces one message. |
||||
|
||||
Args: |
||||
response_callback: A callback to be called to accept the response message |
||||
of the RPC. |
||||
context: A face_interfaces.RpcContext object. |
||||
control: A test_control.Control to control execution of this method. |
||||
|
||||
Returns: |
||||
A stream.Consumer with which to accept the request messages of the RPC. |
||||
The consumer returned from this method may or may not be invoked to |
||||
completion: in the case of RPC abortion, RPC Framework will simply stop |
||||
passing messages to this object. Implementations must not assume that |
||||
this object will be called to completion of the request stream or even |
||||
called at all. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: May or may not be raised when the RPC has been |
||||
aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamUnaryTestMessages(six.with_metaclass(abc.ABCMeta)): |
||||
"""A type for stream-request-unary-response message pairings.""" |
||||
|
||||
@abc.abstractmethod |
||||
def requests(self): |
||||
"""Affords a sequence of request messages. |
||||
|
||||
Implementations of this method should return a different sequences with each |
||||
call so that multiple test executions of the test method may be made with |
||||
different inputs. |
||||
|
||||
Returns: |
||||
A sequence of request messages. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def verify(self, requests, response, test_case): |
||||
"""Verifies that the computed response matches the given requests. |
||||
|
||||
Args: |
||||
requests: A sequence of request messages. |
||||
response: A response message. |
||||
test_case: A unittest.TestCase object affording useful assertion methods. |
||||
|
||||
Raises: |
||||
AssertionError: If the requests and response do not match, indicating that |
||||
there was some problem executing the RPC under test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamStreamTestMethodImplementation(six.with_metaclass(abc.ABCMeta, interfaces.Method)): |
||||
"""A controllable implementation of a stream-stream RPC method.""" |
||||
|
||||
@abc.abstractmethod |
||||
def service(self, response_consumer, context, control): |
||||
"""Services an RPC that accepts and produces streams of messages. |
||||
|
||||
Args: |
||||
response_consumer: A stream.Consumer to be called to accept the response |
||||
messages of the RPC. |
||||
context: A face_interfaces.RpcContext object. |
||||
control: A test_control.Control to control execution of this method. |
||||
|
||||
Returns: |
||||
A stream.Consumer with which to accept the request messages of the RPC. |
||||
The consumer returned from this method may or may not be invoked to |
||||
completion: in the case of RPC abortion, RPC Framework will simply stop |
||||
passing messages to this object. Implementations must not assume that |
||||
this object will be called to completion of the request stream or even |
||||
called at all. |
||||
|
||||
Raises: |
||||
abandonment.Abandoned: May or may not be raised when the RPC has been |
||||
aborted. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class StreamStreamTestMessages(six.with_metaclass(abc.ABCMeta)): |
||||
"""A type for stream-request-stream-response message pairings.""" |
||||
|
||||
@abc.abstractmethod |
||||
def requests(self): |
||||
"""Affords a sequence of request messages. |
||||
|
||||
Implementations of this method should return a different sequences with each |
||||
call so that multiple test executions of the test method may be made with |
||||
different inputs. |
||||
|
||||
Returns: |
||||
A sequence of request messages. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def verify(self, requests, responses, test_case): |
||||
"""Verifies that the computed response matches the given requests. |
||||
|
||||
Args: |
||||
requests: A sequence of request messages. |
||||
responses: A sequence of response messages. |
||||
test_case: A unittest.TestCase object affording useful assertion methods. |
||||
|
||||
Raises: |
||||
AssertionError: If the requests and responses do not match, indicating |
||||
that there was some problem executing the RPC under test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class TestService(six.with_metaclass(abc.ABCMeta)): |
||||
"""A specification of implemented RPC methods to use in tests.""" |
||||
|
||||
@abc.abstractmethod |
||||
def name(self): |
||||
"""Identifies the RPC service name used during the test. |
||||
|
||||
Returns: |
||||
The RPC service name to be used for the test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def unary_unary_scenarios(self): |
||||
"""Affords unary-request-unary-response test methods and their messages. |
||||
|
||||
Returns: |
||||
A dict from method name to pair. The first element of the pair |
||||
is a UnaryUnaryTestMethodImplementation object and the second element |
||||
is a sequence of UnaryUnaryTestMethodMessages objects. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def unary_stream_scenarios(self): |
||||
"""Affords unary-request-stream-response test methods and their messages. |
||||
|
||||
Returns: |
||||
A dict from method name to pair. The first element of the pair is a |
||||
UnaryStreamTestMethodImplementation object and the second element is a |
||||
sequence of UnaryStreamTestMethodMessages objects. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def stream_unary_scenarios(self): |
||||
"""Affords stream-request-unary-response test methods and their messages. |
||||
|
||||
Returns: |
||||
A dict from method name to pair. The first element of the pair is a |
||||
StreamUnaryTestMethodImplementation object and the second element is a |
||||
sequence of StreamUnaryTestMethodMessages objects. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def stream_stream_scenarios(self): |
||||
"""Affords stream-request-stream-response test methods and their messages. |
||||
|
||||
Returns: |
||||
A dict from method name to pair. The first element of the pair is a |
||||
StreamStreamTestMethodImplementation object and the second element is a |
||||
sequence of StreamStreamTestMethodMessages objects. |
||||
""" |
||||
raise NotImplementedError() |
@ -1,374 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Examples of Python implementations of the stock.proto Stock service.""" |
||||
|
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.foundation import abandonment |
||||
from grpc.framework.foundation import stream |
||||
from grpc.framework.foundation import stream_util |
||||
from tests.unit.framework.face.testing import service |
||||
from tests.unit._junkdrawer import stock_pb2 |
||||
|
||||
SYMBOL_FORMAT = 'test symbol:%03d' |
||||
STREAM_LENGTH = 400 |
||||
|
||||
# A test-appropriate security-pricing function. :-P |
||||
_price = lambda symbol_name: float(hash(symbol_name) % 4096) |
||||
|
||||
|
||||
def _get_last_trade_price(stock_request, stock_reply_callback, control, active): |
||||
"""A unary-request, unary-response test method.""" |
||||
control.control() |
||||
if active(): |
||||
stock_reply_callback( |
||||
stock_pb2.StockReply( |
||||
symbol=stock_request.symbol, price=_price(stock_request.symbol))) |
||||
else: |
||||
raise abandonment.Abandoned() |
||||
|
||||
|
||||
def _get_last_trade_price_multiple(stock_reply_consumer, control, active): |
||||
"""A stream-request, stream-response test method.""" |
||||
def stock_reply_for_stock_request(stock_request): |
||||
control.control() |
||||
if active(): |
||||
return stock_pb2.StockReply( |
||||
symbol=stock_request.symbol, price=_price(stock_request.symbol)) |
||||
else: |
||||
raise abandonment.Abandoned() |
||||
return stream_util.TransformingConsumer( |
||||
stock_reply_for_stock_request, stock_reply_consumer) |
||||
|
||||
|
||||
def _watch_future_trades(stock_request, stock_reply_consumer, control, active): |
||||
"""A unary-request, stream-response test method.""" |
||||
base_price = _price(stock_request.symbol) |
||||
for index in range(stock_request.num_trades_to_watch): |
||||
control.control() |
||||
if active(): |
||||
stock_reply_consumer.consume( |
||||
stock_pb2.StockReply( |
||||
symbol=stock_request.symbol, price=base_price + index)) |
||||
else: |
||||
raise abandonment.Abandoned() |
||||
stock_reply_consumer.terminate() |
||||
|
||||
|
||||
def _get_highest_trade_price(stock_reply_callback, control, active): |
||||
"""A stream-request, unary-response test method.""" |
||||
|
||||
class StockRequestConsumer(stream.Consumer): |
||||
"""Keeps an ongoing record of the most valuable symbol yet consumed.""" |
||||
|
||||
def __init__(self): |
||||
self._symbol = None |
||||
self._price = None |
||||
|
||||
def consume(self, stock_request): |
||||
control.control() |
||||
if active(): |
||||
if self._price is None: |
||||
self._symbol = stock_request.symbol |
||||
self._price = _price(stock_request.symbol) |
||||
else: |
||||
candidate_price = _price(stock_request.symbol) |
||||
if self._price < candidate_price: |
||||
self._symbol = stock_request.symbol |
||||
self._price = candidate_price |
||||
|
||||
def terminate(self): |
||||
control.control() |
||||
if active(): |
||||
if self._symbol is None: |
||||
raise ValueError() |
||||
else: |
||||
stock_reply_callback( |
||||
stock_pb2.StockReply(symbol=self._symbol, price=self._price)) |
||||
self._symbol = None |
||||
self._price = None |
||||
|
||||
def consume_and_terminate(self, stock_request): |
||||
control.control() |
||||
if active(): |
||||
if self._price is None: |
||||
stock_reply_callback( |
||||
stock_pb2.StockReply( |
||||
symbol=stock_request.symbol, |
||||
price=_price(stock_request.symbol))) |
||||
else: |
||||
candidate_price = _price(stock_request.symbol) |
||||
if self._price < candidate_price: |
||||
stock_reply_callback( |
||||
stock_pb2.StockReply( |
||||
symbol=stock_request.symbol, price=candidate_price)) |
||||
else: |
||||
stock_reply_callback( |
||||
stock_pb2.StockReply( |
||||
symbol=self._symbol, price=self._price)) |
||||
|
||||
self._symbol = None |
||||
self._price = None |
||||
|
||||
return StockRequestConsumer() |
||||
|
||||
|
||||
class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): |
||||
"""GetLastTradePrice for use in tests.""" |
||||
|
||||
def name(self): |
||||
return 'GetLastTradePrice' |
||||
|
||||
def cardinality(self): |
||||
return cardinality.Cardinality.UNARY_UNARY |
||||
|
||||
def request_class(self): |
||||
return stock_pb2.StockRequest |
||||
|
||||
def response_class(self): |
||||
return stock_pb2.StockReply |
||||
|
||||
def serialize_request(self, request): |
||||
return request.SerializeToString() |
||||
|
||||
def deserialize_request(self, serialized_request): |
||||
return stock_pb2.StockRequest.FromString(serialized_request) |
||||
|
||||
def serialize_response(self, response): |
||||
return response.SerializeToString() |
||||
|
||||
def deserialize_response(self, serialized_response): |
||||
return stock_pb2.StockReply.FromString(serialized_response) |
||||
|
||||
def service(self, request, response_callback, context, control): |
||||
_get_last_trade_price( |
||||
request, response_callback, control, context.is_active) |
||||
|
||||
|
||||
class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): |
||||
|
||||
def __init__(self): |
||||
self._index = 0 |
||||
|
||||
def request(self): |
||||
symbol = SYMBOL_FORMAT % self._index |
||||
self._index += 1 |
||||
return stock_pb2.StockRequest(symbol=symbol) |
||||
|
||||
def verify(self, request, response, test_case): |
||||
test_case.assertEqual(request.symbol, response.symbol) |
||||
test_case.assertEqual(_price(request.symbol), response.price) |
||||
|
||||
|
||||
class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): |
||||
"""GetLastTradePriceMultiple for use in tests.""" |
||||
|
||||
def name(self): |
||||
return 'GetLastTradePriceMultiple' |
||||
|
||||
def cardinality(self): |
||||
return cardinality.Cardinality.STREAM_STREAM |
||||
|
||||
def request_class(self): |
||||
return stock_pb2.StockRequest |
||||
|
||||
def response_class(self): |
||||
return stock_pb2.StockReply |
||||
|
||||
def serialize_request(self, request): |
||||
return request.SerializeToString() |
||||
|
||||
def deserialize_request(self, serialized_request): |
||||
return stock_pb2.StockRequest.FromString(serialized_request) |
||||
|
||||
def serialize_response(self, response): |
||||
return response.SerializeToString() |
||||
|
||||
def deserialize_response(self, serialized_response): |
||||
return stock_pb2.StockReply.FromString(serialized_response) |
||||
|
||||
def service(self, response_consumer, context, control): |
||||
return _get_last_trade_price_multiple( |
||||
response_consumer, control, context.is_active) |
||||
|
||||
|
||||
class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): |
||||
"""Pairs of message streams for use with GetLastTradePriceMultiple.""" |
||||
|
||||
def __init__(self): |
||||
self._index = 0 |
||||
|
||||
def requests(self): |
||||
base_index = self._index |
||||
self._index += 1 |
||||
return [ |
||||
stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) |
||||
for index in range(STREAM_LENGTH)] |
||||
|
||||
def verify(self, requests, responses, test_case): |
||||
test_case.assertEqual(len(requests), len(responses)) |
||||
for stock_request, stock_reply in zip(requests, responses): |
||||
test_case.assertEqual(stock_request.symbol, stock_reply.symbol) |
||||
test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) |
||||
|
||||
|
||||
class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): |
||||
"""WatchFutureTrades for use in tests.""" |
||||
|
||||
def name(self): |
||||
return 'WatchFutureTrades' |
||||
|
||||
def cardinality(self): |
||||
return cardinality.Cardinality.UNARY_STREAM |
||||
|
||||
def request_class(self): |
||||
return stock_pb2.StockRequest |
||||
|
||||
def response_class(self): |
||||
return stock_pb2.StockReply |
||||
|
||||
def serialize_request(self, request): |
||||
return request.SerializeToString() |
||||
|
||||
def deserialize_request(self, serialized_request): |
||||
return stock_pb2.StockRequest.FromString(serialized_request) |
||||
|
||||
def serialize_response(self, response): |
||||
return response.SerializeToString() |
||||
|
||||
def deserialize_response(self, serialized_response): |
||||
return stock_pb2.StockReply.FromString(serialized_response) |
||||
|
||||
def service(self, request, response_consumer, context, control): |
||||
_watch_future_trades(request, response_consumer, control, context.is_active) |
||||
|
||||
|
||||
class WatchFutureTradesMessages(service.UnaryStreamTestMessages): |
||||
"""Pairs of a single request message and a sequence of response messages.""" |
||||
|
||||
def __init__(self): |
||||
self._index = 0 |
||||
|
||||
def request(self): |
||||
symbol = SYMBOL_FORMAT % self._index |
||||
self._index += 1 |
||||
return stock_pb2.StockRequest( |
||||
symbol=symbol, num_trades_to_watch=STREAM_LENGTH) |
||||
|
||||
def verify(self, request, responses, test_case): |
||||
test_case.assertEqual(STREAM_LENGTH, len(responses)) |
||||
base_price = _price(request.symbol) |
||||
for index, response in enumerate(responses): |
||||
test_case.assertEqual(base_price + index, response.price) |
||||
|
||||
|
||||
class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): |
||||
"""GetHighestTradePrice for use in tests.""" |
||||
|
||||
def name(self): |
||||
return 'GetHighestTradePrice' |
||||
|
||||
def cardinality(self): |
||||
return cardinality.Cardinality.STREAM_UNARY |
||||
|
||||
def request_class(self): |
||||
return stock_pb2.StockRequest |
||||
|
||||
def response_class(self): |
||||
return stock_pb2.StockReply |
||||
|
||||
def serialize_request(self, request): |
||||
return request.SerializeToString() |
||||
|
||||
def deserialize_request(self, serialized_request): |
||||
return stock_pb2.StockRequest.FromString(serialized_request) |
||||
|
||||
def serialize_response(self, response): |
||||
return response.SerializeToString() |
||||
|
||||
def deserialize_response(self, serialized_response): |
||||
return stock_pb2.StockReply.FromString(serialized_response) |
||||
|
||||
def service(self, response_callback, context, control): |
||||
return _get_highest_trade_price( |
||||
response_callback, control, context.is_active) |
||||
|
||||
|
||||
class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): |
||||
|
||||
def requests(self): |
||||
return [ |
||||
stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) |
||||
for index in range(STREAM_LENGTH)] |
||||
|
||||
def verify(self, requests, response, test_case): |
||||
price = None |
||||
symbol = None |
||||
for stock_request in requests: |
||||
current_symbol = stock_request.symbol |
||||
current_price = _price(current_symbol) |
||||
if price is None or price < current_price: |
||||
price = current_price |
||||
symbol = current_symbol |
||||
test_case.assertEqual(price, response.price) |
||||
test_case.assertEqual(symbol, response.symbol) |
||||
|
||||
|
||||
class StockTestService(service.TestService): |
||||
"""A corpus of test data with one method of each RPC cardinality.""" |
||||
|
||||
def name(self): |
||||
return 'Stock' |
||||
|
||||
def unary_unary_scenarios(self): |
||||
return { |
||||
'GetLastTradePrice': ( |
||||
GetLastTradePrice(), [GetLastTradePriceMessages()]), |
||||
} |
||||
|
||||
def unary_stream_scenarios(self): |
||||
return { |
||||
'WatchFutureTrades': ( |
||||
WatchFutureTrades(), [WatchFutureTradesMessages()]), |
||||
} |
||||
|
||||
def stream_unary_scenarios(self): |
||||
return { |
||||
'GetHighestTradePrice': ( |
||||
GetHighestTradePrice(), [GetHighestTradePriceMessages()]) |
||||
} |
||||
|
||||
def stream_stream_scenarios(self): |
||||
return { |
||||
'GetLastTradePriceMultiple': ( |
||||
GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), |
||||
} |
||||
|
||||
|
||||
STOCK_TEST_SERVICE = StockTestService() |
@ -1,81 +0,0 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Tools for creating tests of implementations of the Face layer.""" |
||||
|
||||
import abc |
||||
|
||||
import six |
||||
|
||||
# face_interfaces and interfaces are referenced in specification in this module. |
||||
from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import |
||||
from tests.unit.framework.face.testing import interfaces # pylint: disable=unused-import |
||||
|
||||
|
||||
class FaceTestCase(six.with_metaclass(abc.ABCMeta)): |
||||
"""Describes a test of the Face Layer of RPC Framework. |
||||
|
||||
Concrete subclasses must also inherit from unittest.TestCase and from at least |
||||
one class that defines test methods. |
||||
""" |
||||
|
||||
@abc.abstractmethod |
||||
def set_up_implementation( |
||||
self, name, methods, method_implementations, |
||||
multi_method_implementation): |
||||
"""Instantiates the Face Layer implementation under test. |
||||
|
||||
Args: |
||||
name: The service name to be used in the test. |
||||
methods: A sequence of interfaces.Method objects describing the RPC |
||||
methods that will be called during the test. |
||||
method_implementations: A dictionary from string RPC method name to |
||||
face_interfaces.MethodImplementation object specifying |
||||
implementation of an RPC method. |
||||
multi_method_implementation: An face_interfaces.MultiMethodImplementation |
||||
or None. |
||||
|
||||
Returns: |
||||
A sequence of length two the first element of which is a |
||||
face_interfaces.GenericStub (backed by the given method |
||||
implementations), and the second element of which is an arbitrary memo |
||||
object to be kept and passed to tearDownImplementation at the conclusion |
||||
of the test. |
||||
""" |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def tear_down_implementation(self, memo): |
||||
"""Destroys the Face layer implementation under test. |
||||
|
||||
Args: |
||||
memo: The object from the second position of the return value of |
||||
set_up_implementation. |
||||
""" |
||||
raise NotImplementedError() |
Loading…
Reference in new issue