mirror of https://github.com/grpc/grpc.git
parent
1f1919c8cf
commit
4c8288ec01
14 changed files with 1630 additions and 18 deletions
@ -0,0 +1,30 @@ |
|||||||
|
# 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. |
||||||
|
|
||||||
|
|
@ -0,0 +1,204 @@ |
|||||||
|
# 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.""" |
||||||
|
|
||||||
|
from grpc.framework.crust import _control |
||||||
|
from grpc.framework.interfaces.base import utilities |
||||||
|
from grpc.framework.interfaces.face import face |
||||||
|
|
||||||
|
_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' |
||||||
|
|
||||||
|
_EMPTY_COMPLETION = utilities.completion(None, None, None) |
||||||
|
|
||||||
|
|
||||||
|
def _invoke(end, group, method, timeout, initial_metadata, payload, complete): |
||||||
|
rendezvous = _control.Rendezvous(None, None) |
||||||
|
operation_context, operator = end.operate( |
||||||
|
group, method, utilities.full_subscription(rendezvous), timeout, |
||||||
|
initial_metadata=initial_metadata, payload=payload, |
||||||
|
completion=_EMPTY_COMPLETION if complete else None) |
||||||
|
rendezvous.set_operator_and_context(operator, operation_context) |
||||||
|
outcome = operation_context.add_termination_callback(rendezvous.set_outcome) |
||||||
|
if outcome is not None: |
||||||
|
rendezvous.set_outcome(outcome) |
||||||
|
return rendezvous, operation_context, outcome |
||||||
|
|
||||||
|
|
||||||
|
def _event_return_unary( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool): |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
abortion = rendezvous.add_abortion_callback(abortion_callback) |
||||||
|
if abortion is None: |
||||||
|
try: |
||||||
|
receiver.initial_metadata(rendezvous.initial_metadata()) |
||||||
|
receiver.response(next(rendezvous)) |
||||||
|
receiver.complete( |
||||||
|
rendezvous.terminal_metadata(), rendezvous.code(), |
||||||
|
rendezvous.details()) |
||||||
|
except face.AbortionError: |
||||||
|
pass |
||||||
|
else: |
||||||
|
abortion_callback(abortion) |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context)) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def _event_return_stream( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool): |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
abortion = rendezvous.add_abortion_callback(abortion_callback) |
||||||
|
if abortion is None: |
||||||
|
try: |
||||||
|
receiver.initial_metadata(rendezvous.initial_metadata()) |
||||||
|
for response in rendezvous: |
||||||
|
receiver.response(response) |
||||||
|
receiver.complete( |
||||||
|
rendezvous.terminal_metadata(), rendezvous.code(), |
||||||
|
rendezvous.details()) |
||||||
|
except face.AbortionError: |
||||||
|
pass |
||||||
|
else: |
||||||
|
abortion_callback(abortion) |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context)) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def blocking_unary_unary( |
||||||
|
end, group, method, timeout, with_call, initial_metadata, payload): |
||||||
|
"""Services in a blocking fashion a unary-unary servicer method.""" |
||||||
|
rendezvous, unused_operation_context, unused_outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, payload, True) |
||||||
|
if with_call: |
||||||
|
return next(rendezvous, rendezvous) |
||||||
|
else: |
||||||
|
return next(rendezvous) |
||||||
|
|
||||||
|
|
||||||
|
def future_unary_unary(end, group, method, timeout, initial_metadata, payload): |
||||||
|
"""Services a value-in value-out servicer method by returning a Future.""" |
||||||
|
rendezvous, unused_operation_context, unused_outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, payload, True) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def inline_unary_stream(end, group, method, timeout, initial_metadata, payload): |
||||||
|
"""Services a value-in stream-out servicer method.""" |
||||||
|
rendezvous, unused_operation_context, unused_outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, payload, True) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def blocking_stream_unary( |
||||||
|
end, group, method, timeout, with_call, initial_metadata, payload_iterator, |
||||||
|
pool): |
||||||
|
"""Services in a blocking fashion a stream-in value-out servicer method.""" |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, None, False) |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
for payload in payload_iterator: |
||||||
|
rendezvous.consume(payload) |
||||||
|
rendezvous.terminate() |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context)) |
||||||
|
if with_call: |
||||||
|
return next(rendezvous), rendezvous |
||||||
|
else: |
||||||
|
return next(rendezvous) |
||||||
|
else: |
||||||
|
if with_call: |
||||||
|
return next(rendezvous), rendezvous |
||||||
|
else: |
||||||
|
return next(rendezvous) |
||||||
|
|
||||||
|
|
||||||
|
def future_stream_unary( |
||||||
|
end, group, method, timeout, initial_metadata, payload_iterator, pool): |
||||||
|
"""Services a stream-in value-out servicer method by returning a Future.""" |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, None, False) |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
for payload in payload_iterator: |
||||||
|
rendezvous.consume(payload) |
||||||
|
rendezvous.terminate() |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context)) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def inline_stream_stream( |
||||||
|
end, group, method, timeout, initial_metadata, payload_iterator, pool): |
||||||
|
"""Services a stream-in stream-out servicer method.""" |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, None, False) |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
for payload in payload_iterator: |
||||||
|
rendezvous.consume(payload) |
||||||
|
rendezvous.terminate() |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context)) |
||||||
|
return rendezvous |
||||||
|
|
||||||
|
|
||||||
|
def event_unary_unary( |
||||||
|
end, group, method, timeout, initial_metadata, payload, receiver, |
||||||
|
abortion_callback, pool): |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, payload, True) |
||||||
|
return _event_return_unary( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool) |
||||||
|
|
||||||
|
|
||||||
|
def event_unary_stream( |
||||||
|
end, group, method, timeout, initial_metadata, payload, |
||||||
|
receiver, abortion_callback, pool): |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, payload, True) |
||||||
|
return _event_return_stream( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool) |
||||||
|
|
||||||
|
|
||||||
|
def event_stream_unary( |
||||||
|
end, group, method, timeout, initial_metadata, receiver, abortion_callback, |
||||||
|
pool): |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, None, False) |
||||||
|
return _event_return_unary( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool) |
||||||
|
|
||||||
|
|
||||||
|
def event_stream_stream( |
||||||
|
end, group, method, timeout, initial_metadata, receiver, abortion_callback, |
||||||
|
pool): |
||||||
|
rendezvous, operation_context, outcome = _invoke( |
||||||
|
end, group, method, timeout, initial_metadata, None, False) |
||||||
|
return _event_return_stream( |
||||||
|
receiver, abortion_callback, rendezvous, operation_context, outcome, pool) |
@ -0,0 +1,545 @@ |
|||||||
|
# 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 collections |
||||||
|
import enum |
||||||
|
import sys |
||||||
|
import threading |
||||||
|
import time |
||||||
|
|
||||||
|
from grpc.framework.foundation import abandonment |
||||||
|
from grpc.framework.foundation import callable_util |
||||||
|
from grpc.framework.foundation import future |
||||||
|
from grpc.framework.foundation import stream |
||||||
|
from grpc.framework.interfaces.base import base |
||||||
|
from grpc.framework.interfaces.base import utilities |
||||||
|
from grpc.framework.interfaces.face import face |
||||||
|
|
||||||
|
_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' |
||||||
|
_INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Crust) Internal Error! )-:' |
||||||
|
|
||||||
|
_CANNOT_SET_INITIAL_METADATA = ( |
||||||
|
'Could not set initial metadata - has it already been set, or has a ' + |
||||||
|
'payload already been sent?') |
||||||
|
_CANNOT_SET_TERMINAL_METADATA = ( |
||||||
|
'Could not set terminal metadata - has it already been set, or has RPC ' + |
||||||
|
'completion already been indicated?') |
||||||
|
_CANNOT_SET_CODE = ( |
||||||
|
'Could not set code - has it already been set, or has RPC completion ' + |
||||||
|
'already been indicated?') |
||||||
|
_CANNOT_SET_DETAILS = ( |
||||||
|
'Could not set details - has it already been set, or has RPC completion ' + |
||||||
|
'already been indicated?') |
||||||
|
|
||||||
|
|
||||||
|
class _DummyOperator(base.Operator): |
||||||
|
|
||||||
|
def advance( |
||||||
|
self, initial_metadata=None, payload=None, completion=None, |
||||||
|
allowance=None): |
||||||
|
pass |
||||||
|
|
||||||
|
_DUMMY_OPERATOR = _DummyOperator() |
||||||
|
|
||||||
|
|
||||||
|
class _Awaited( |
||||||
|
collections.namedtuple('_Awaited', ('kind', 'value',))): |
||||||
|
|
||||||
|
@enum.unique |
||||||
|
class Kind(enum.Enum): |
||||||
|
NOT_YET_ARRIVED = 'not yet arrived' |
||||||
|
ARRIVED = 'arrived' |
||||||
|
|
||||||
|
_NOT_YET_ARRIVED = _Awaited(_Awaited.Kind.NOT_YET_ARRIVED, None) |
||||||
|
_ARRIVED_AND_NONE = _Awaited(_Awaited.Kind.ARRIVED, None) |
||||||
|
|
||||||
|
|
||||||
|
class _Transitory( |
||||||
|
collections.namedtuple('_Transitory', ('kind', 'value',))): |
||||||
|
|
||||||
|
@enum.unique |
||||||
|
class Kind(enum.Enum): |
||||||
|
NOT_YET_SEEN = 'not yet seen' |
||||||
|
PRESENT = 'present' |
||||||
|
GONE = 'gone' |
||||||
|
|
||||||
|
_NOT_YET_SEEN = _Transitory(_Transitory.Kind.NOT_YET_SEEN, None) |
||||||
|
_GONE = _Transitory(_Transitory.Kind.GONE, None) |
||||||
|
|
||||||
|
|
||||||
|
class _Termination( |
||||||
|
collections.namedtuple( |
||||||
|
'_Termination', ('terminated', 'abortion', 'abortion_error',))): |
||||||
|
"""Values indicating whether and how an RPC has terminated. |
||||||
|
|
||||||
|
Attributes: |
||||||
|
terminated: A boolean indicating whether or not the RPC has terminated. |
||||||
|
abortion: A face.Abortion value describing the RPC's abortion or None if the |
||||||
|
RPC did not abort. |
||||||
|
abortion_error: A face.AbortionError describing the RPC's abortion or None |
||||||
|
if the RPC did not abort. |
||||||
|
""" |
||||||
|
|
||||||
|
_NOT_TERMINATED = _Termination(False, None, None) |
||||||
|
|
||||||
|
_OPERATION_OUTCOME_TO_TERMINATION_CONSTRUCTOR = { |
||||||
|
base.Outcome.COMPLETED: lambda *unused_args: _Termination(True, None, None), |
||||||
|
base.Outcome.CANCELLED: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.CANCELLED, *args), |
||||||
|
face.CancellationError(*args)), |
||||||
|
base.Outcome.EXPIRED: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.EXPIRED, *args), |
||||||
|
face.ExpirationError(*args)), |
||||||
|
base.Outcome.LOCAL_SHUTDOWN: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.LOCAL_SHUTDOWN, *args), |
||||||
|
face.LocalShutdownError(*args)), |
||||||
|
base.Outcome.REMOTE_SHUTDOWN: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.REMOTE_SHUTDOWN, *args), |
||||||
|
face.RemoteShutdownError(*args)), |
||||||
|
base.Outcome.RECEPTION_FAILURE: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), |
||||||
|
face.NetworkError(*args)), |
||||||
|
base.Outcome.TRANSMISSION_FAILURE: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), |
||||||
|
face.NetworkError(*args)), |
||||||
|
base.Outcome.LOCAL_FAILURE: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.LOCAL_FAILURE, *args), |
||||||
|
face.LocalError(*args)), |
||||||
|
base.Outcome.REMOTE_FAILURE: lambda *args: _Termination( |
||||||
|
True, face.Abortion(face.Abortion.Kind.REMOTE_FAILURE, *args), |
||||||
|
face.RemoteError(*args)), |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
def _wait_once_until(condition, until): |
||||||
|
if until is None: |
||||||
|
condition.wait() |
||||||
|
else: |
||||||
|
remaining = until - time.time() |
||||||
|
if remaining < 0: |
||||||
|
raise future.TimeoutError() |
||||||
|
else: |
||||||
|
condition.wait(timeout=remaining) |
||||||
|
|
||||||
|
|
||||||
|
def _done_callback_as_operation_termination_callback( |
||||||
|
done_callback, rendezvous): |
||||||
|
def operation_termination_callback(operation_outcome): |
||||||
|
rendezvous.set_outcome(operation_outcome) |
||||||
|
done_callback(rendezvous) |
||||||
|
return operation_termination_callback |
||||||
|
|
||||||
|
|
||||||
|
def _abortion_callback_as_operation_termination_callback( |
||||||
|
rpc_abortion_callback, rendezvous_set_outcome): |
||||||
|
def operation_termination_callback(operation_outcome): |
||||||
|
termination = rendezvous_set_outcome(operation_outcome) |
||||||
|
if termination.abortion is not None: |
||||||
|
rpc_abortion_callback(termination.abortion) |
||||||
|
return operation_termination_callback |
||||||
|
|
||||||
|
|
||||||
|
class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): |
||||||
|
"""A rendez-vous for the threads of an operation. |
||||||
|
|
||||||
|
Instances of this object present iterator and stream.Consumer interfaces for |
||||||
|
interacting with application code and present a base.Operator interface and |
||||||
|
maintain a base.Operator internally for interacting with base interface code. |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, operator, operation_context): |
||||||
|
self._condition = threading.Condition() |
||||||
|
|
||||||
|
self._operator = operator |
||||||
|
self._operation_context = operation_context |
||||||
|
|
||||||
|
self._up_initial_metadata = _NOT_YET_ARRIVED |
||||||
|
self._up_payload = None |
||||||
|
self._up_allowance = 1 |
||||||
|
self._up_completion = _NOT_YET_ARRIVED |
||||||
|
self._down_initial_metadata = _NOT_YET_SEEN |
||||||
|
self._down_payload = None |
||||||
|
self._down_allowance = 1 |
||||||
|
self._down_terminal_metadata = _NOT_YET_SEEN |
||||||
|
self._down_code = _NOT_YET_SEEN |
||||||
|
self._down_details = _NOT_YET_SEEN |
||||||
|
|
||||||
|
self._termination = _NOT_TERMINATED |
||||||
|
|
||||||
|
# The semantics of future.Future.cancel and future.Future.cancelled are |
||||||
|
# slightly wonky, so they have to be tracked separately from the rest of the |
||||||
|
# result of the RPC. This field tracks whether cancellation was requested |
||||||
|
# prior to termination of the RPC |
||||||
|
self._cancelled = False |
||||||
|
|
||||||
|
def set_operator_and_context(self, operator, operation_context): |
||||||
|
with self._condition: |
||||||
|
self._operator = operator |
||||||
|
self._operation_context = operation_context |
||||||
|
|
||||||
|
def _down_completion(self): |
||||||
|
if self._down_terminal_metadata.kind is _Transitory.Kind.NOT_YET_SEEN: |
||||||
|
terminal_metadata = None |
||||||
|
self._down_terminal_metadata = _GONE |
||||||
|
elif self._down_terminal_metadata.kind is _Transitory.Kind.PRESENT: |
||||||
|
terminal_metadata = self._down_terminal_metadata.value |
||||||
|
self._down_terminal_metadata = _GONE |
||||||
|
else: |
||||||
|
terminal_metadata = None |
||||||
|
if self._down_code.kind is _Transitory.Kind.NOT_YET_SEEN: |
||||||
|
code = None |
||||||
|
self._down_code = _GONE |
||||||
|
elif self._down_code.kind is _Transitory.Kind.PRESENT: |
||||||
|
code = self._down_code.value |
||||||
|
self._down_code = _GONE |
||||||
|
else: |
||||||
|
code = None |
||||||
|
if self._down_details.kind is _Transitory.Kind.NOT_YET_SEEN: |
||||||
|
details = None |
||||||
|
self._down_details = _GONE |
||||||
|
elif self._down_details.kind is _Transitory.Kind.PRESENT: |
||||||
|
details = self._down_details.value |
||||||
|
self._down_details = _GONE |
||||||
|
else: |
||||||
|
details = None |
||||||
|
return utilities.completion(terminal_metadata, code, details) |
||||||
|
|
||||||
|
def _set_outcome(self, outcome): |
||||||
|
if not self._termination.terminated: |
||||||
|
self._operator = _DUMMY_OPERATOR |
||||||
|
self._operation_context = None |
||||||
|
self._down_initial_metadata = _GONE |
||||||
|
self._down_payload = None |
||||||
|
self._down_terminal_metadata = _GONE |
||||||
|
self._down_code = _GONE |
||||||
|
self._down_details = _GONE |
||||||
|
|
||||||
|
if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: |
||||||
|
initial_metadata = None |
||||||
|
else: |
||||||
|
initial_metadata = self._up_initial_metadata.value |
||||||
|
if self._up_completion.kind is _Awaited.Kind.NOT_YET_ARRIVED: |
||||||
|
terminal_metadata, code, details = None, None, None |
||||||
|
else: |
||||||
|
terminal_metadata = self._up_completion.value.terminal_metadata |
||||||
|
code = self._up_completion.value.code |
||||||
|
details = self._up_completion.value.message |
||||||
|
self._termination = _OPERATION_OUTCOME_TO_TERMINATION_CONSTRUCTOR[ |
||||||
|
outcome](initial_metadata, terminal_metadata, code, details) |
||||||
|
|
||||||
|
self._condition.notify_all() |
||||||
|
|
||||||
|
return self._termination |
||||||
|
|
||||||
|
def advance( |
||||||
|
self, initial_metadata=None, payload=None, completion=None, |
||||||
|
allowance=None): |
||||||
|
with self._condition: |
||||||
|
if initial_metadata is not None: |
||||||
|
self._up_initial_metadata = _Awaited( |
||||||
|
_Awaited.Kind.ARRIVED, initial_metadata) |
||||||
|
if payload is not None: |
||||||
|
if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: |
||||||
|
self._up_initial_metadata = _ARRIVED_AND_NONE |
||||||
|
self._up_payload = payload |
||||||
|
self._up_allowance -= 1 |
||||||
|
if completion is not None: |
||||||
|
if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: |
||||||
|
self._up_initial_metadata = _ARRIVED_AND_NONE |
||||||
|
self._up_completion = _Awaited( |
||||||
|
_Awaited.Kind.ARRIVED, completion) |
||||||
|
if allowance is not None: |
||||||
|
if self._down_payload is not None: |
||||||
|
self._operator.advance(payload=self._down_payload) |
||||||
|
self._down_payload = None |
||||||
|
self._down_allowance += allowance - 1 |
||||||
|
else: |
||||||
|
self._down_allowance += allowance |
||||||
|
self._condition.notify_all() |
||||||
|
|
||||||
|
def cancel(self): |
||||||
|
with self._condition: |
||||||
|
if self._operation_context is not None: |
||||||
|
self._operation_context.cancel() |
||||||
|
self._cancelled = True |
||||||
|
return False |
||||||
|
|
||||||
|
def cancelled(self): |
||||||
|
with self._condition: |
||||||
|
return self._cancelled |
||||||
|
|
||||||
|
def running(self): |
||||||
|
with self._condition: |
||||||
|
return not self._termination.terminated |
||||||
|
|
||||||
|
def done(self): |
||||||
|
with self._condition: |
||||||
|
return self._termination.terminated |
||||||
|
|
||||||
|
def result(self, timeout=None): |
||||||
|
until = None if timeout is None else time.time() + timeout |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.terminated: |
||||||
|
if self._termination.abortion is None: |
||||||
|
return self._up_payload |
||||||
|
elif self._termination.abortion.kind is face.Abortion.Kind.CANCELLED: |
||||||
|
raise future.CancelledError() |
||||||
|
else: |
||||||
|
raise self._termination.abortion_error # pylint: disable=raising-bad-type |
||||||
|
else: |
||||||
|
_wait_once_until(self._condition, until) |
||||||
|
|
||||||
|
def exception(self, timeout=None): |
||||||
|
until = None if timeout is None else time.time() + timeout |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.terminated: |
||||||
|
if self._termination.abortion is None: |
||||||
|
return None |
||||||
|
else: |
||||||
|
return self._termination.abortion_error |
||||||
|
else: |
||||||
|
_wait_once_until(self._condition, until) |
||||||
|
|
||||||
|
def traceback(self, timeout=None): |
||||||
|
until = None if timeout is None else time.time() + timeout |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.terminated: |
||||||
|
if self._termination.abortion_error is None: |
||||||
|
return None |
||||||
|
else: |
||||||
|
abortion_error = self._termination.abortion_error |
||||||
|
break |
||||||
|
else: |
||||||
|
_wait_once_until(self._condition, until) |
||||||
|
|
||||||
|
try: |
||||||
|
raise abortion_error |
||||||
|
except face.AbortionError: |
||||||
|
return sys.exc_info()[2] |
||||||
|
|
||||||
|
def add_done_callback(self, fn): |
||||||
|
with self._condition: |
||||||
|
if self._operation_context is not None: |
||||||
|
outcome = self._operation_context.add_termination_callback( |
||||||
|
_done_callback_as_operation_termination_callback(fn, self)) |
||||||
|
if outcome is None: |
||||||
|
return |
||||||
|
else: |
||||||
|
self._set_outcome(outcome) |
||||||
|
|
||||||
|
fn(self) |
||||||
|
|
||||||
|
def consume(self, value): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.terminated: |
||||||
|
return |
||||||
|
elif 0 < self._down_allowance: |
||||||
|
self._operator.advance(payload=value) |
||||||
|
self._down_allowance -= 1 |
||||||
|
return |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def terminate(self): |
||||||
|
with self._condition: |
||||||
|
if self._termination.terminated: |
||||||
|
return |
||||||
|
elif self._down_code.kind is _Transitory.Kind.GONE: |
||||||
|
# Conform to specified idempotence of terminate by ignoring extra calls. |
||||||
|
return |
||||||
|
else: |
||||||
|
completion = self._down_completion() |
||||||
|
self._operator.advance(completion=completion) |
||||||
|
|
||||||
|
def consume_and_terminate(self, value): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.terminated: |
||||||
|
return |
||||||
|
elif 0 < self._down_allowance: |
||||||
|
completion = self._down_completion() |
||||||
|
self._operator.advance(payload=value, completion=completion) |
||||||
|
return |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return self |
||||||
|
|
||||||
|
def next(self): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._termination.abortion_error is not None: |
||||||
|
raise self._termination.abortion_error |
||||||
|
elif self._up_payload is not None: |
||||||
|
payload = self._up_payload |
||||||
|
self._up_payload = None |
||||||
|
if self._up_completion.kind is _Awaited.Kind.NOT_YET_ARRIVED: |
||||||
|
self._operator.advance(allowance=1) |
||||||
|
return payload |
||||||
|
elif self._up_completion.kind is _Awaited.Kind.ARRIVED: |
||||||
|
raise StopIteration() |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def is_active(self): |
||||||
|
with self._condition: |
||||||
|
return not self._termination.terminated |
||||||
|
|
||||||
|
def time_remaining(self): |
||||||
|
if self._operation_context is None: |
||||||
|
return 0 |
||||||
|
else: |
||||||
|
return self._operation_context.time_remaining() |
||||||
|
|
||||||
|
def add_abortion_callback(self, abortion_callback): |
||||||
|
with self._condition: |
||||||
|
if self._operation_context is None: |
||||||
|
return self._termination.abortion |
||||||
|
else: |
||||||
|
outcome = self._operation_context.add_termination_callback( |
||||||
|
_abortion_callback_as_operation_termination_callback( |
||||||
|
abortion_callback, self.set_outcome)) |
||||||
|
if outcome is not None: |
||||||
|
return self._set_outcome(outcome).abortion |
||||||
|
else: |
||||||
|
return self._termination.abortion |
||||||
|
|
||||||
|
def initial_metadata(self): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._up_initial_metadata.kind is _Awaited.Kind.ARRIVED: |
||||||
|
return self._up_initial_metadata.value |
||||||
|
elif self._termination.terminated: |
||||||
|
return None |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def terminal_metadata(self): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._up_completion.kind is _Awaited.Kind.ARRIVED: |
||||||
|
return self._up_completion.value.terminal_metadata |
||||||
|
elif self._termination.terminated: |
||||||
|
return None |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def code(self): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._up_completion.kind is _Awaited.Kind.ARRIVED: |
||||||
|
return self._up_completion.value.code |
||||||
|
elif self._termination.terminated: |
||||||
|
return None |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def details(self): |
||||||
|
with self._condition: |
||||||
|
while True: |
||||||
|
if self._up_completion.kind is _Awaited.Kind.ARRIVED: |
||||||
|
return self._up_completion.value.message |
||||||
|
elif self._termination.terminated: |
||||||
|
return None |
||||||
|
else: |
||||||
|
self._condition.wait() |
||||||
|
|
||||||
|
def set_initial_metadata(self, initial_metadata): |
||||||
|
with self._condition: |
||||||
|
if (self._down_initial_metadata.kind is not |
||||||
|
_Transitory.Kind.NOT_YET_SEEN): |
||||||
|
raise ValueError(_CANNOT_SET_INITIAL_METADATA) |
||||||
|
else: |
||||||
|
self._down_initial_metadata = _GONE |
||||||
|
self._operator.advance(initial_metadata=initial_metadata) |
||||||
|
|
||||||
|
def set_terminal_metadata(self, terminal_metadata): |
||||||
|
with self._condition: |
||||||
|
if (self._down_terminal_metadata.kind is not |
||||||
|
_Transitory.Kind.NOT_YET_SEEN): |
||||||
|
raise ValueError(_CANNOT_SET_TERMINAL_METADATA) |
||||||
|
else: |
||||||
|
self._down_terminal_metadata = _Transitory( |
||||||
|
_Transitory.Kind.PRESENT, terminal_metadata) |
||||||
|
|
||||||
|
def set_code(self, code): |
||||||
|
with self._condition: |
||||||
|
if self._down_code.kind is not _Transitory.Kind.NOT_YET_SEEN: |
||||||
|
raise ValueError(_CANNOT_SET_CODE) |
||||||
|
else: |
||||||
|
self._down_code = _Transitory(_Transitory.Kind.PRESENT, code) |
||||||
|
|
||||||
|
def set_details(self, details): |
||||||
|
with self._condition: |
||||||
|
if self._down_details.kind is not _Transitory.Kind.NOT_YET_SEEN: |
||||||
|
raise ValueError(_CANNOT_SET_DETAILS) |
||||||
|
else: |
||||||
|
self._down_details = _Transitory(_Transitory.Kind.PRESENT, details) |
||||||
|
|
||||||
|
def set_outcome(self, outcome): |
||||||
|
with self._condition: |
||||||
|
return self._set_outcome(outcome) |
||||||
|
|
||||||
|
|
||||||
|
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, |
||||||
|
face.CancellationError, |
||||||
|
face.ExpirationError, |
||||||
|
face.LocalShutdownError, |
||||||
|
face.RemoteShutdownError, |
||||||
|
face.NetworkError, |
||||||
|
face.RemoteError, |
||||||
|
) as e: |
||||||
|
if operation_context.outcome() is None: |
||||||
|
operation_context.fail(e) |
||||||
|
except Exception as e: |
||||||
|
operation_context.fail(e) |
||||||
|
return callable_util.with_exceptions_logged( |
||||||
|
translation, _INTERNAL_ERROR_LOG_MESSAGE) |
@ -0,0 +1,166 @@ |
|||||||
|
# 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.""" |
||||||
|
|
||||||
|
from grpc.framework.crust import _control |
||||||
|
from grpc.framework.foundation import abandonment |
||||||
|
from grpc.framework.interfaces.base import utilities |
||||||
|
from grpc.framework.interfaces.face import face |
||||||
|
|
||||||
|
|
||||||
|
class _ServicerContext(face.ServicerContext): |
||||||
|
|
||||||
|
def __init__(self, rendezvous): |
||||||
|
self._rendezvous = rendezvous |
||||||
|
|
||||||
|
def is_active(self): |
||||||
|
return self._rendezvous.is_active() |
||||||
|
|
||||||
|
def time_remaining(self): |
||||||
|
return self._rendezvous.time_remaining() |
||||||
|
|
||||||
|
def add_abortion_callback(self, abortion_callback): |
||||||
|
return self._rendezvous.add_abortion_callback(abortion_callback) |
||||||
|
|
||||||
|
def cancel(self): |
||||||
|
self._rendezvous.cancel() |
||||||
|
|
||||||
|
def invocation_metadata(self): |
||||||
|
return self._rendezvous.initial_metadata() |
||||||
|
|
||||||
|
def initial_metadata(self, initial_metadata): |
||||||
|
self._rendezvous.set_initial_metadata(initial_metadata) |
||||||
|
|
||||||
|
def terminal_metadata(self, terminal_metadata): |
||||||
|
self._rendezvous.set_terminal_metadata(terminal_metadata) |
||||||
|
|
||||||
|
def code(self, code): |
||||||
|
self._rendezvous.set_code(code) |
||||||
|
|
||||||
|
def details(self, details): |
||||||
|
self._rendezvous.set_details(details) |
||||||
|
|
||||||
|
|
||||||
|
def _adaptation(pool, in_pool): |
||||||
|
def adaptation(operator, operation_context): |
||||||
|
rendezvous = _control.Rendezvous(operator, operation_context) |
||||||
|
outcome = operation_context.add_termination_callback(rendezvous.set_outcome) |
||||||
|
if outcome is None: |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) |
||||||
|
return utilities.full_subscription(rendezvous) |
||||||
|
else: |
||||||
|
raise abandonment.Abandoned() |
||||||
|
return adaptation |
||||||
|
|
||||||
|
|
||||||
|
def adapt_inline_unary_unary(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request = next(rendezvous) |
||||||
|
response = method(request, _ServicerContext(rendezvous)) |
||||||
|
rendezvous.consume_and_terminate(response) |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_inline_unary_stream(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request = next(rendezvous) |
||||||
|
response_iterator = method(request, _ServicerContext(rendezvous)) |
||||||
|
for response in response_iterator: |
||||||
|
rendezvous.consume(response) |
||||||
|
rendezvous.terminate() |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_inline_stream_unary(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
response = method(rendezvous, _ServicerContext(rendezvous)) |
||||||
|
rendezvous.consume_and_terminate(response) |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_inline_stream_stream(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
response_iterator = method(rendezvous, _ServicerContext(rendezvous)) |
||||||
|
for response in response_iterator: |
||||||
|
rendezvous.consume(response) |
||||||
|
rendezvous.terminate() |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_event_unary_unary(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request = next(rendezvous) |
||||||
|
method( |
||||||
|
request, rendezvous.consume_and_terminate, _ServicerContext(rendezvous)) |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_event_unary_stream(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request = next(rendezvous) |
||||||
|
method(request, rendezvous, _ServicerContext(rendezvous)) |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_event_stream_unary(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request_consumer = method( |
||||||
|
rendezvous.consume_and_terminate, _ServicerContext(rendezvous)) |
||||||
|
for request in rendezvous: |
||||||
|
request_consumer.consume(request) |
||||||
|
request_consumer.terminate() |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_event_stream_stream(method, pool): |
||||||
|
def in_pool(rendezvous): |
||||||
|
request_consumer = method(rendezvous, _ServicerContext(rendezvous)) |
||||||
|
for request in rendezvous: |
||||||
|
request_consumer.consume(request) |
||||||
|
request_consumer.terminate() |
||||||
|
return _adaptation(pool, in_pool) |
||||||
|
|
||||||
|
|
||||||
|
def adapt_multi_method(multi_method, pool): |
||||||
|
def adaptation(group, method, operator, operation_context): |
||||||
|
rendezvous = _control.Rendezvous(operator, operation_context) |
||||||
|
outcome = operation_context.add_termination_callback(rendezvous.set_outcome) |
||||||
|
if outcome is None: |
||||||
|
def in_pool(): |
||||||
|
request_consumer = multi_method( |
||||||
|
group, method, rendezvous, _ServicerContext(rendezvous)) |
||||||
|
for request in rendezvous: |
||||||
|
request_consumer.consume(request) |
||||||
|
request_consumer.terminate() |
||||||
|
pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) |
||||||
|
return utilities.full_subscription(rendezvous) |
||||||
|
else: |
||||||
|
raise abandonment.Abandoned() |
||||||
|
return adaptation |
@ -0,0 +1,352 @@ |
|||||||
|
# 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 Crust layer of RPC Framework.""" |
||||||
|
|
||||||
|
from grpc.framework.common import cardinality |
||||||
|
from grpc.framework.common import style |
||||||
|
from grpc.framework.crust import _calls |
||||||
|
from grpc.framework.crust import _service |
||||||
|
from grpc.framework.interfaces.base import base |
||||||
|
from grpc.framework.interfaces.face import face |
||||||
|
|
||||||
|
|
||||||
|
class _BaseServicer(base.Servicer): |
||||||
|
|
||||||
|
def __init__(self, adapted_methods, adapted_multi_method): |
||||||
|
self._adapted_methods = adapted_methods |
||||||
|
self._adapted_multi_method = adapted_multi_method |
||||||
|
|
||||||
|
def service(self, group, method, context, output_operator): |
||||||
|
adapted_method = self._adapted_methods.get((group, method), None) |
||||||
|
if adapted_method is not None: |
||||||
|
return adapted_method(output_operator, context) |
||||||
|
elif self._adapted_multi_method is not None: |
||||||
|
try: |
||||||
|
return self._adapted_multi_method.service( |
||||||
|
group, method, output_operator, context) |
||||||
|
except face.NoSuchMethodError: |
||||||
|
raise base.NoSuchMethodError() |
||||||
|
else: |
||||||
|
raise base.NoSuchMethodError() |
||||||
|
|
||||||
|
|
||||||
|
class _UnaryUnaryMultiCallable(face.UnaryUnaryMultiCallable): |
||||||
|
|
||||||
|
def __init__(self, end, group, method, pool): |
||||||
|
self._end = end |
||||||
|
self._group = group |
||||||
|
self._method = method |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def __call__( |
||||||
|
self, request, timeout, metadata=None, with_call=False): |
||||||
|
return _calls.blocking_unary_unary( |
||||||
|
self._end, self._group, self._method, timeout, with_call, |
||||||
|
metadata, request) |
||||||
|
|
||||||
|
def future(self, request, timeout, metadata=None): |
||||||
|
return _calls.future_unary_unary( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request) |
||||||
|
|
||||||
|
def event( |
||||||
|
self, request, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_unary_unary( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request, receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
|
||||||
|
class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable): |
||||||
|
|
||||||
|
def __init__(self, end, group, method, pool): |
||||||
|
self._end = end |
||||||
|
self._group = group |
||||||
|
self._method = method |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def __call__(self, request, timeout, metadata=None): |
||||||
|
return _calls.inline_unary_stream( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request) |
||||||
|
|
||||||
|
def event( |
||||||
|
self, request, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_unary_stream( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request, receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
|
||||||
|
class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable): |
||||||
|
|
||||||
|
def __init__(self, end, group, method, pool): |
||||||
|
self._end = end |
||||||
|
self._group = group |
||||||
|
self._method = method |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def __call__( |
||||||
|
self, request_iterator, timeout, metadata=None, |
||||||
|
with_call=False): |
||||||
|
return _calls.blocking_stream_unary( |
||||||
|
self._end, self._group, self._method, timeout, with_call, |
||||||
|
metadata, request_iterator, self._pool) |
||||||
|
|
||||||
|
def future(self, request_iterator, timeout, metadata=None): |
||||||
|
return _calls.future_stream_unary( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request_iterator, self._pool) |
||||||
|
|
||||||
|
def event( |
||||||
|
self, receiver, abortion_callback, timeout, metadata=None): |
||||||
|
return _calls.event_stream_unary( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
|
||||||
|
class _StreamStreamMultiCallable(face.StreamStreamMultiCallable): |
||||||
|
|
||||||
|
def __init__(self, end, group, method, pool): |
||||||
|
self._end = end |
||||||
|
self._group = group |
||||||
|
self._method = method |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def __call__(self, request_iterator, timeout, metadata=None): |
||||||
|
return _calls.inline_stream_stream( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
request_iterator, self._pool) |
||||||
|
|
||||||
|
def event( |
||||||
|
self, receiver, abortion_callback, timeout, metadata=None): |
||||||
|
return _calls.event_stream_stream( |
||||||
|
self._end, self._group, self._method, timeout, metadata, |
||||||
|
receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
|
||||||
|
class _GenericStub(face.GenericStub): |
||||||
|
"""An face.GenericStub implementation.""" |
||||||
|
|
||||||
|
def __init__(self, end, pool): |
||||||
|
self._end = end |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def blocking_unary_unary( |
||||||
|
self, group, method, request, timeout, metadata=None, |
||||||
|
with_call=None): |
||||||
|
return _calls.blocking_unary_unary( |
||||||
|
self._end, group, method, timeout, with_call, metadata, |
||||||
|
request) |
||||||
|
|
||||||
|
def future_unary_unary( |
||||||
|
self, group, method, request, timeout, metadata=None): |
||||||
|
return _calls.future_unary_unary( |
||||||
|
self._end, group, method, timeout, metadata, request) |
||||||
|
|
||||||
|
def inline_unary_stream( |
||||||
|
self, group, method, request, timeout, metadata=None): |
||||||
|
return _calls.inline_unary_stream( |
||||||
|
self._end, group, method, timeout, metadata, request) |
||||||
|
|
||||||
|
def blocking_stream_unary( |
||||||
|
self, group, method, request_iterator, timeout, metadata=None, |
||||||
|
with_call=None): |
||||||
|
return _calls.blocking_stream_unary( |
||||||
|
self._end, group, method, timeout, with_call, metadata, |
||||||
|
request_iterator, self._pool) |
||||||
|
|
||||||
|
def future_stream_unary( |
||||||
|
self, group, method, request_iterator, timeout, metadata=None): |
||||||
|
return _calls.future_stream_unary( |
||||||
|
self._end, group, method, timeout, metadata, |
||||||
|
request_iterator, self._pool) |
||||||
|
|
||||||
|
def inline_stream_stream( |
||||||
|
self, group, method, request_iterator, timeout, metadata=None): |
||||||
|
return _calls.inline_stream_stream( |
||||||
|
self._end, group, method, timeout, metadata, |
||||||
|
request_iterator, self._pool) |
||||||
|
|
||||||
|
def event_unary_unary( |
||||||
|
self, group, method, request, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_unary_unary( |
||||||
|
self._end, group, method, timeout, metadata, request, |
||||||
|
receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
def event_unary_stream( |
||||||
|
self, group, method, request, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_unary_stream( |
||||||
|
self._end, group, method, timeout, metadata, request, |
||||||
|
receiver, abortion_callback, self._pool) |
||||||
|
|
||||||
|
def event_stream_unary( |
||||||
|
self, group, method, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_stream_unary( |
||||||
|
self._end, group, method, timeout, metadata, receiver, |
||||||
|
abortion_callback, self._pool) |
||||||
|
|
||||||
|
def event_stream_stream( |
||||||
|
self, group, method, receiver, abortion_callback, timeout, |
||||||
|
metadata=None): |
||||||
|
return _calls.event_stream_stream( |
||||||
|
self._end, group, method, timeout, metadata, receiver, |
||||||
|
abortion_callback, self._pool) |
||||||
|
|
||||||
|
def unary_unary(self, group, method): |
||||||
|
return _UnaryUnaryMultiCallable(self._end, group, method, self._pool) |
||||||
|
|
||||||
|
def unary_stream(self, group, method): |
||||||
|
return _UnaryStreamMultiCallable(self._end, group, method, self._pool) |
||||||
|
|
||||||
|
def stream_unary(self, group, method): |
||||||
|
return _StreamUnaryMultiCallable(self._end, group, method, self._pool) |
||||||
|
|
||||||
|
def stream_stream(self, group, method): |
||||||
|
return _StreamStreamMultiCallable(self._end, group, method, self._pool) |
||||||
|
|
||||||
|
|
||||||
|
class _DynamicStub(face.DynamicStub): |
||||||
|
"""An face.DynamicStub implementation.""" |
||||||
|
|
||||||
|
def __init__(self, end, group, cardinalities, pool): |
||||||
|
self._end = end |
||||||
|
self._group = group |
||||||
|
self._cardinalities = cardinalities |
||||||
|
self._pool = pool |
||||||
|
|
||||||
|
def __getattr__(self, attr): |
||||||
|
method_cardinality = self._cardinalities.get(attr) |
||||||
|
if method_cardinality is cardinality.Cardinality.UNARY_UNARY: |
||||||
|
return _UnaryUnaryMultiCallable(self._end, self._group, attr, self._pool) |
||||||
|
elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||||
|
return _UnaryStreamMultiCallable(self._end, self._group, attr, self._pool) |
||||||
|
elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||||
|
return _StreamUnaryMultiCallable(self._end, self._group, attr, self._pool) |
||||||
|
elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||||
|
return _StreamStreamMultiCallable( |
||||||
|
self._end, self._group, 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 method_implementations.iteritems(): |
||||||
|
if method_implementation.style is style.Service.INLINE: |
||||||
|
if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: |
||||||
|
adapted_implementations[name] = _service.adapt_inline_unary_unary( |
||||||
|
method_implementation.unary_unary_inline, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||||
|
adapted_implementations[name] = _service.adapt_inline_unary_stream( |
||||||
|
method_implementation.unary_stream_inline, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||||
|
adapted_implementations[name] = _service.adapt_inline_stream_unary( |
||||||
|
method_implementation.stream_unary_inline, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||||
|
adapted_implementations[name] = _service.adapt_inline_stream_stream( |
||||||
|
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_unary_unary( |
||||||
|
method_implementation.unary_unary_event, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: |
||||||
|
adapted_implementations[name] = _service.adapt_event_unary_stream( |
||||||
|
method_implementation.unary_stream_event, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: |
||||||
|
adapted_implementations[name] = _service.adapt_event_stream_unary( |
||||||
|
method_implementation.stream_unary_event, pool) |
||||||
|
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: |
||||||
|
adapted_implementations[name] = _service.adapt_event_stream_stream( |
||||||
|
method_implementation.stream_stream_event, pool) |
||||||
|
return adapted_implementations |
||||||
|
|
||||||
|
|
||||||
|
def servicer(method_implementations, multi_method_implementation, pool): |
||||||
|
"""Creates a base.Servicer. |
||||||
|
|
||||||
|
It is guaranteed that any passed face.MultiMethodImplementation will |
||||||
|
only be called to service an RPC if there is no |
||||||
|
face.MethodImplementation for the RPC method in the passed |
||||||
|
method_implementations dictionary. |
||||||
|
|
||||||
|
Args: |
||||||
|
method_implementations: A dictionary from RPC method name to |
||||||
|
face.MethodImplementation object to be used to service the named |
||||||
|
RPC method. |
||||||
|
multi_method_implementation: An face.MultiMethodImplementation to be |
||||||
|
used to service any RPCs not serviced by the |
||||||
|
face.MethodImplementations given in the method_implementations |
||||||
|
dictionary, or None. |
||||||
|
pool: A thread pool. |
||||||
|
|
||||||
|
Returns: |
||||||
|
A base.Servicer that services RPCs via the given implementations. |
||||||
|
""" |
||||||
|
adapted_implementations = _adapt_method_implementations( |
||||||
|
method_implementations, pool) |
||||||
|
adapted_multi_method_implementation = _service.adapt_multi_method( |
||||||
|
multi_method_implementation, pool) |
||||||
|
return _BaseServicer( |
||||||
|
adapted_implementations, adapted_multi_method_implementation) |
||||||
|
|
||||||
|
|
||||||
|
def generic_stub(end, pool): |
||||||
|
"""Creates an face.GenericStub. |
||||||
|
|
||||||
|
Args: |
||||||
|
end: A base.End. |
||||||
|
pool: A futures.ThreadPoolExecutor. |
||||||
|
|
||||||
|
Returns: |
||||||
|
A face.GenericStub that performs RPCs via the given base.End. |
||||||
|
""" |
||||||
|
return _GenericStub(end, pool) |
||||||
|
|
||||||
|
|
||||||
|
def dynamic_stub(end, group, cardinalities, pool): |
||||||
|
"""Creates an face.DynamicStub. |
||||||
|
|
||||||
|
Args: |
||||||
|
end: A base.End. |
||||||
|
group: The group identifier for all RPCs to be made with the created |
||||||
|
face.DynamicStub. |
||||||
|
cardinalities: A dict from method identifier to cardinality.Cardinality |
||||||
|
value identifying the cardinality of every RPC method to be supported by |
||||||
|
the created face.DynamicStub. |
||||||
|
pool: A futures.ThreadPoolExecutor. |
||||||
|
|
||||||
|
Returns: |
||||||
|
A face.DynamicStub that performs RPCs via the given base.End. |
||||||
|
""" |
||||||
|
return _DynamicStub(end, group, cardinalities, pool) |
@ -0,0 +1,160 @@ |
|||||||
|
# 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. |
||||||
|
|
||||||
|
"""Tests Face compliance of the crust-over-core-over-gRPC-links stack.""" |
||||||
|
|
||||||
|
import collections |
||||||
|
import unittest |
||||||
|
|
||||||
|
from grpc._adapter import _intermediary_low |
||||||
|
from grpc._links import invocation |
||||||
|
from grpc._links import service |
||||||
|
from grpc.framework.core import implementations as core_implementations |
||||||
|
from grpc.framework.crust import implementations as crust_implementations |
||||||
|
from grpc.framework.foundation import logging_pool |
||||||
|
from grpc.framework.interfaces.links import utilities |
||||||
|
from grpc_test import test_common |
||||||
|
from grpc_test.framework.common import test_constants |
||||||
|
from grpc_test.framework.interfaces.face import test_cases |
||||||
|
from grpc_test.framework.interfaces.face import test_interfaces |
||||||
|
from grpc_test.framework.interfaces.links import test_utilities |
||||||
|
|
||||||
|
|
||||||
|
class _SerializationBehaviors( |
||||||
|
collections.namedtuple( |
||||||
|
'_SerializationBehaviors', |
||||||
|
('request_serializers', 'request_deserializers', 'response_serializers', |
||||||
|
'response_deserializers',))): |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
def _serialization_behaviors_from_test_methods(test_methods): |
||||||
|
request_serializers = {} |
||||||
|
request_deserializers = {} |
||||||
|
response_serializers = {} |
||||||
|
response_deserializers = {} |
||||||
|
for (group, method), test_method in test_methods.iteritems(): |
||||||
|
request_serializers[group, method] = test_method.serialize_request |
||||||
|
request_deserializers[group, method] = test_method.deserialize_request |
||||||
|
response_serializers[group, method] = test_method.serialize_response |
||||||
|
response_deserializers[group, method] = test_method.deserialize_response |
||||||
|
return _SerializationBehaviors( |
||||||
|
request_serializers, request_deserializers, response_serializers, |
||||||
|
response_deserializers) |
||||||
|
|
||||||
|
|
||||||
|
class _Implementation(test_interfaces.Implementation): |
||||||
|
|
||||||
|
def instantiate( |
||||||
|
self, methods, method_implementations, multi_method_implementation): |
||||||
|
pool = logging_pool.pool(test_constants.POOL_SIZE) |
||||||
|
servicer = crust_implementations.servicer( |
||||||
|
method_implementations, multi_method_implementation, pool) |
||||||
|
serialization_behaviors = _serialization_behaviors_from_test_methods( |
||||||
|
methods) |
||||||
|
invocation_end_link = core_implementations.invocation_end_link() |
||||||
|
service_end_link = core_implementations.service_end_link( |
||||||
|
servicer, test_constants.DEFAULT_TIMEOUT, |
||||||
|
test_constants.MAXIMUM_TIMEOUT) |
||||||
|
service_grpc_link = service.service_link( |
||||||
|
serialization_behaviors.request_deserializers, |
||||||
|
serialization_behaviors.response_serializers) |
||||||
|
port = service_grpc_link.add_port(0, None) |
||||||
|
channel = _intermediary_low.Channel('localhost:%d' % port, None) |
||||||
|
invocation_grpc_link = invocation.invocation_link( |
||||||
|
channel, b'localhost', |
||||||
|
serialization_behaviors.request_serializers, |
||||||
|
serialization_behaviors.response_deserializers) |
||||||
|
|
||||||
|
invocation_end_link.join_link(invocation_grpc_link) |
||||||
|
invocation_grpc_link.join_link(invocation_end_link) |
||||||
|
service_grpc_link.join_link(service_end_link) |
||||||
|
service_end_link.join_link(service_grpc_link) |
||||||
|
service_end_link.start() |
||||||
|
invocation_end_link.start() |
||||||
|
invocation_grpc_link.start() |
||||||
|
service_grpc_link.start() |
||||||
|
|
||||||
|
generic_stub = crust_implementations.generic_stub(invocation_end_link, pool) |
||||||
|
# TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. |
||||||
|
group = next(iter(methods))[0] |
||||||
|
# TODO(nathaniel): Add a "cardinalities_by_group" attribute to |
||||||
|
# _digest.TestServiceDigest. |
||||||
|
cardinalities = { |
||||||
|
method: method_object.cardinality() |
||||||
|
for (group, method), method_object in methods.iteritems()} |
||||||
|
dynamic_stub = crust_implementations.dynamic_stub( |
||||||
|
invocation_end_link, group, cardinalities, pool) |
||||||
|
|
||||||
|
return generic_stub, {group: dynamic_stub}, ( |
||||||
|
invocation_end_link, invocation_grpc_link, service_grpc_link, |
||||||
|
service_end_link, pool) |
||||||
|
|
||||||
|
def destantiate(self, memo): |
||||||
|
(invocation_end_link, invocation_grpc_link, service_grpc_link, |
||||||
|
service_end_link, pool) = memo |
||||||
|
invocation_end_link.stop(0).wait() |
||||||
|
invocation_grpc_link.stop() |
||||||
|
service_grpc_link.stop_gracefully() |
||||||
|
service_end_link.stop(0).wait() |
||||||
|
invocation_end_link.join_link(utilities.NULL_LINK) |
||||||
|
invocation_grpc_link.join_link(utilities.NULL_LINK) |
||||||
|
service_grpc_link.join_link(utilities.NULL_LINK) |
||||||
|
service_end_link.join_link(utilities.NULL_LINK) |
||||||
|
pool.shutdown(wait=True) |
||||||
|
|
||||||
|
def invocation_metadata(self): |
||||||
|
return test_common.INVOCATION_INITIAL_METADATA |
||||||
|
|
||||||
|
def initial_metadata(self): |
||||||
|
return test_common.SERVICE_INITIAL_METADATA |
||||||
|
|
||||||
|
def terminal_metadata(self): |
||||||
|
return test_common.SERVICE_TERMINAL_METADATA |
||||||
|
|
||||||
|
def code(self): |
||||||
|
return _intermediary_low.Code.OK |
||||||
|
|
||||||
|
def details(self): |
||||||
|
return test_common.DETAILS |
||||||
|
|
||||||
|
def metadata_transmitted(self, original_metadata, transmitted_metadata): |
||||||
|
return original_metadata is None or grpc_test_common.metadata_transmitted( |
||||||
|
original_metadata, transmitted_metadata) |
||||||
|
|
||||||
|
|
||||||
|
def load_tests(loader, tests, pattern): |
||||||
|
return unittest.TestSuite( |
||||||
|
tests=tuple( |
||||||
|
loader.loadTestsFromTestCase(test_case_class) |
||||||
|
for test_case_class in test_cases.test_cases(_Implementation()))) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main(verbosity=2) |
@ -0,0 +1,111 @@ |
|||||||
|
# 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. |
||||||
|
|
||||||
|
"""Tests Face interface compliance of the crust-over-core stack.""" |
||||||
|
|
||||||
|
import collections |
||||||
|
import unittest |
||||||
|
|
||||||
|
from grpc.framework.core import implementations as core_implementations |
||||||
|
from grpc.framework.crust import implementations as crust_implementations |
||||||
|
from grpc.framework.foundation import logging_pool |
||||||
|
from grpc.framework.interfaces.links import utilities |
||||||
|
from grpc_test.framework.common import test_constants |
||||||
|
from grpc_test.framework.interfaces.face import test_cases |
||||||
|
from grpc_test.framework.interfaces.face import test_interfaces |
||||||
|
from grpc_test.framework.interfaces.links import test_utilities |
||||||
|
|
||||||
|
|
||||||
|
class _Implementation(test_interfaces.Implementation): |
||||||
|
|
||||||
|
def instantiate( |
||||||
|
self, methods, method_implementations, multi_method_implementation): |
||||||
|
pool = logging_pool.pool(test_constants.POOL_SIZE) |
||||||
|
servicer = crust_implementations.servicer( |
||||||
|
method_implementations, multi_method_implementation, pool) |
||||||
|
|
||||||
|
service_end_link = core_implementations.service_end_link( |
||||||
|
servicer, test_constants.DEFAULT_TIMEOUT, |
||||||
|
test_constants.MAXIMUM_TIMEOUT) |
||||||
|
invocation_end_link = core_implementations.invocation_end_link() |
||||||
|
invocation_end_link.join_link(service_end_link) |
||||||
|
service_end_link.join_link(invocation_end_link) |
||||||
|
service_end_link.start() |
||||||
|
invocation_end_link.start() |
||||||
|
|
||||||
|
generic_stub = crust_implementations.generic_stub(invocation_end_link, pool) |
||||||
|
# TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. |
||||||
|
group = next(iter(methods))[0] |
||||||
|
# TODO(nathaniel): Add a "cardinalities_by_group" attribute to |
||||||
|
# _digest.TestServiceDigest. |
||||||
|
cardinalities = { |
||||||
|
method: method_object.cardinality() |
||||||
|
for (group, method), method_object in methods.iteritems()} |
||||||
|
dynamic_stub = crust_implementations.dynamic_stub( |
||||||
|
invocation_end_link, group, cardinalities, pool) |
||||||
|
|
||||||
|
return generic_stub, {group: dynamic_stub}, ( |
||||||
|
invocation_end_link, service_end_link, pool) |
||||||
|
|
||||||
|
def destantiate(self, memo): |
||||||
|
invocation_end_link, service_end_link, pool = memo |
||||||
|
invocation_end_link.stop(0).wait() |
||||||
|
service_end_link.stop(0).wait() |
||||||
|
invocation_end_link.join_link(utilities.NULL_LINK) |
||||||
|
service_end_link.join_link(utilities.NULL_LINK) |
||||||
|
pool.shutdown(wait=True) |
||||||
|
|
||||||
|
def invocation_metadata(self): |
||||||
|
return object() |
||||||
|
|
||||||
|
def initial_metadata(self): |
||||||
|
return object() |
||||||
|
|
||||||
|
def terminal_metadata(self): |
||||||
|
return object() |
||||||
|
|
||||||
|
def code(self): |
||||||
|
return object() |
||||||
|
|
||||||
|
def details(self): |
||||||
|
return object() |
||||||
|
|
||||||
|
def metadata_transmitted(self, original_metadata, transmitted_metadata): |
||||||
|
return original_metadata is transmitted_metadata |
||||||
|
|
||||||
|
|
||||||
|
def load_tests(loader, tests, pattern): |
||||||
|
return unittest.TestSuite( |
||||||
|
tests=tuple( |
||||||
|
loader.loadTestsFromTestCase(test_case_class) |
||||||
|
for test_case_class in test_cases.test_cases(_Implementation()))) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main(verbosity=2) |
@ -0,0 +1,37 @@ |
|||||||
|
# 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 constant working around issue 3069.""" |
||||||
|
|
||||||
|
# test_constants is referenced from specification in this module. |
||||||
|
from grpc_test.framework.common import test_constants # pylint: disable=unused-import |
||||||
|
|
||||||
|
# TODO(issue 3069): Replace uses of this constant with |
||||||
|
# test_constants.SHORT_TIMEOUT. |
||||||
|
REALLY_SHORT_TIMEOUT = 0.1 |
Loading…
Reference in new issue