Remove Python alpha/early_adopter implementation

This code has been unsupported for more than six months.
pull/6331/head
Nathaniel Manista 9 years ago
parent e2fd65343e
commit cab9470ee8
  1. 363
      src/python/grpcio/grpc/_adapter/fore.py
  2. 395
      src/python/grpcio/grpc/_adapter/rear.py
  3. 35
      src/python/grpcio/grpc/early_adopter/__init__.py
  4. 262
      src/python/grpcio/grpc/early_adopter/implementations.py
  5. 35
      src/python/grpcio/grpc/framework/alpha/__init__.py
  6. 183
      src/python/grpcio/grpc/framework/alpha/_face_utilities.py
  7. 205
      src/python/grpcio/grpc/framework/alpha/_reexport.py
  8. 47
      src/python/grpcio/grpc/framework/alpha/exceptions.py
  9. 384
      src/python/grpcio/grpc/framework/alpha/interfaces.py
  10. 269
      src/python/grpcio/grpc/framework/alpha/utilities.py
  11. 35
      src/python/grpcio/grpc/framework/base/__init__.py
  12. 64
      src/python/grpcio/grpc/framework/base/_cancellation.py
  13. 32
      src/python/grpcio/grpc/framework/base/_constants.py
  14. 99
      src/python/grpcio/grpc/framework/base/_context.py
  15. 125
      src/python/grpcio/grpc/framework/base/_emission.py
  16. 399
      src/python/grpcio/grpc/framework/base/_ends.py
  17. 158
      src/python/grpcio/grpc/framework/base/_expiration.py
  18. 443
      src/python/grpcio/grpc/framework/base/_ingestion.py
  19. 266
      src/python/grpcio/grpc/framework/base/_interfaces.py
  20. 400
      src/python/grpcio/grpc/framework/base/_reception.py
  21. 204
      src/python/grpcio/grpc/framework/base/_termination.py
  22. 429
      src/python/grpcio/grpc/framework/base/_transmission.py
  23. 34
      src/python/grpcio/grpc/framework/base/exceptions.py
  24. 77
      src/python/grpcio/grpc/framework/base/implementations.py
  25. 108
      src/python/grpcio/grpc/framework/base/in_memory.py
  26. 353
      src/python/grpcio/grpc/framework/base/interfaces.py
  27. 56
      src/python/grpcio/grpc/framework/base/null.py
  28. 94
      src/python/grpcio/grpc/framework/base/util.py
  29. 35
      src/python/grpcio/grpc/framework/face/__init__.py
  30. 422
      src/python/grpcio/grpc/framework/face/_calls.py
  31. 201
      src/python/grpcio/grpc/framework/face/_control.py
  32. 187
      src/python/grpcio/grpc/framework/face/_service.py
  33. 118
      src/python/grpcio/grpc/framework/face/demonstration.py
  34. 78
      src/python/grpcio/grpc/framework/face/exceptions.py
  35. 320
      src/python/grpcio/grpc/framework/face/implementations.py
  36. 634
      src/python/grpcio/grpc/framework/face/interfaces.py
  37. 177
      src/python/grpcio/grpc/framework/face/utilities.py
  38. 30
      src/python/grpcio/tests/unit/framework/face/__init__.py
  39. 30
      src/python/grpcio/tests/unit/framework/face/testing/__init__.py
  40. 102
      src/python/grpcio/tests/unit/framework/face/testing/base_util.py
  41. 224
      src/python/grpcio/tests/unit/framework/face/testing/blocking_invocation_inline_service_test_case.py
  42. 94
      src/python/grpcio/tests/unit/framework/face/testing/callback.py
  43. 87
      src/python/grpcio/tests/unit/framework/face/testing/control.py
  44. 121
      src/python/grpcio/tests/unit/framework/face/testing/coverage.py
  45. 452
      src/python/grpcio/tests/unit/framework/face/testing/digest.py
  46. 378
      src/python/grpcio/tests/unit/framework/face/testing/event_invocation_synchronous_event_service_test_case.py
  47. 384
      src/python/grpcio/tests/unit/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py
  48. 118
      src/python/grpcio/tests/unit/framework/face/testing/interfaces.py
  49. 321
      src/python/grpcio/tests/unit/framework/face/testing/service.py
  50. 374
      src/python/grpcio/tests/unit/framework/face/testing/stock_service.py
  51. 81
      src/python/grpcio/tests/unit/framework/face/testing/test_case.py

@ -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…
Cancel
Save