Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/third_party/protorpc/remote.py b/third_party/protorpc/remote.py
new file mode 100644
index 0000000..7983573
--- /dev/null
+++ b/third_party/protorpc/remote.py
@@ -0,0 +1,1248 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Remote service library.
+
+This module contains classes that are useful for building remote services that
+conform to a standard request and response model.  To conform to this model
+a service must be like the following class:
+
+  # Each service instance only handles a single request and is then discarded.
+  # Make these objects light weight.
+  class Service(object):
+
+    # It must be possible to construct service objects without any parameters.
+    # If your constructor needs extra information you should provide a
+    # no-argument factory function to create service instances.
+    def __init__(self):
+      ...
+
+    # Each remote method must use the 'method' decorator, passing the request
+    # and response message types.  The remote method itself must take a single
+    # parameter which is an instance of RequestMessage and return an instance
+    # of ResponseMessage.
+    @method(RequestMessage, ResponseMessage)
+    def remote_method(self, request):
+      # Return an instance of ResponseMessage.
+
+    # A service object may optionally implement an 'initialize_request_state'
+    # method that takes as a parameter a single instance of a RequestState.  If
+    # a service does not implement this method it will not receive the request
+    # state.
+    def initialize_request_state(self, state):
+      ...
+
+The 'Service' class is provided as a convenient base class that provides the
+above functionality.  It implements all required and optional methods for a
+service.  It also has convenience methods for creating factory functions that
+can pass persistent global state to a new service instance.
+
+The 'method' decorator is used to declare which methods of a class are
+meant to service RPCs.  While this decorator is not responsible for handling
+actual remote method invocations, such as handling sockets, handling various
+RPC protocols and checking messages for correctness, it does attach information
+to methods that responsible classes can examine and ensure the correctness
+of the RPC.
+
+When the method decorator is used on a method, the wrapper method will have a
+'remote' property associated with it.  The 'remote' property contains the
+request_type and response_type expected by the methods implementation.
+
+On its own, the method decorator does not provide any support for subclassing
+remote methods.  In order to extend a service, one would need to redecorate
+the sub-classes methods.  For example:
+
+  class MyService(Service):
+
+    @method(DoSomethingRequest, DoSomethingResponse)
+    def do_stuff(self, request):
+      ... implement do_stuff ...
+
+  class MyBetterService(MyService):
+
+    @method(DoSomethingRequest, DoSomethingResponse)
+    def do_stuff(self, request):
+      response = super(MyBetterService, self).do_stuff.remote.method(request)
+      ... do stuff with response ...
+      return response
+
+A Service subclass also has a Stub class that can be used with a transport for
+making RPCs.  When a stub is created, it is capable of doing both synchronous
+and asynchronous RPCs if the underlying transport supports it.  To make a stub
+using an HTTP transport do:
+
+  my_service = MyService.Stub(HttpTransport('<my service URL>'))
+
+For synchronous calls, just call the expected methods on the service stub:
+
+  request = DoSomethingRequest()
+  ...
+  response = my_service.do_something(request)
+
+Each stub instance has an async object that can be used for initiating
+asynchronous RPCs if the underlying protocol transport supports it.  To
+make an asynchronous call, do:
+
+  rpc = my_service.async_.do_something(request)
+  response = rpc.get_response()
+"""
+
+from __future__ import with_statement
+import six
+
+__author__ = 'rafek@google.com (Rafe Kaplan)'
+
+import functools
+import logging
+import sys
+import threading
+from wsgiref import headers as wsgi_headers
+
+from . import message_types
+from . import messages
+from . import protobuf
+from . import protojson
+from . import util
+
+
+__all__ = [
+    'ApplicationError',
+    'MethodNotFoundError',
+    'NetworkError',
+    'RequestError',
+    'RpcError',
+    'ServerError',
+    'ServiceConfigurationError',
+    'ServiceDefinitionError',
+
+    'HttpRequestState',
+    'ProtocolConfig',
+    'Protocols',
+    'RequestState',
+    'RpcState',
+    'RpcStatus',
+    'Service',
+    'StubBase',
+    'check_rpc_status',
+    'get_remote_method_info',
+    'is_error_status',
+    'method',
+    'remote',
+]
+
+
+class ServiceDefinitionError(messages.Error):
+  """Raised when a service is improperly defined."""
+
+
+class ServiceConfigurationError(messages.Error):
+  """Raised when a service is incorrectly configured."""
+
+
+# TODO: Use error_name to map to specific exception message types.
+class RpcStatus(messages.Message):
+  """Status of on-going or complete RPC.
+
+  Fields:
+    state: State of RPC.
+    error_name: Error name set by application.  Only set when
+      status is APPLICATION_ERROR.  For use by application to transmit
+      specific reason for error.
+    error_message: Error message associated with status.
+  """
+
+  class State(messages.Enum):
+    """Enumeration of possible RPC states.
+
+    Values:
+      OK: Completed successfully.
+      RUNNING: Still running, not complete.
+      REQUEST_ERROR: Request was malformed or incomplete.
+      SERVER_ERROR: Server experienced an unexpected error.
+      NETWORK_ERROR: An error occured on the network.
+      APPLICATION_ERROR: The application is indicating an error.
+        When in this state, RPC should also set application_error.
+    """
+    OK = 0
+    RUNNING = 1
+
+    REQUEST_ERROR = 2
+    SERVER_ERROR = 3
+    NETWORK_ERROR = 4
+    APPLICATION_ERROR = 5
+    METHOD_NOT_FOUND_ERROR = 6
+
+  state = messages.EnumField(State, 1, required=True)
+  error_message = messages.StringField(2)
+  error_name = messages.StringField(3)
+
+
+RpcState = RpcStatus.State
+
+
+class RpcError(messages.Error):
+  """Base class for RPC errors.
+
+  Each sub-class of RpcError is associated with an error value from RpcState
+  and has an attribute STATE that refers to that value.
+  """
+
+  def __init__(self, message, cause=None):
+    super(RpcError, self).__init__(message)
+    self.cause = cause
+
+  @classmethod
+  def from_state(cls, state):
+    """Get error class from RpcState.
+
+    Args:
+      state: RpcState value.  Can be enum value itself, string or int.
+
+    Returns:
+      Exception class mapped to value if state is an error.  Returns None
+      if state is OK or RUNNING.
+    """
+    return _RPC_STATE_TO_ERROR.get(RpcState(state))
+
+
+class RequestError(RpcError):
+  """Raised when wrong request objects received during method invocation."""
+
+  STATE = RpcState.REQUEST_ERROR
+
+
+class MethodNotFoundError(RequestError):
+  """Raised when unknown method requested by RPC."""
+
+  STATE = RpcState.METHOD_NOT_FOUND_ERROR
+
+
+class NetworkError(RpcError):
+  """Raised when network error occurs during RPC."""
+
+  STATE = RpcState.NETWORK_ERROR
+
+
+class ServerError(RpcError):
+  """Unexpected error occured on server."""
+
+  STATE = RpcState.SERVER_ERROR
+
+
+class ApplicationError(RpcError):
+  """Raised for application specific errors.
+
+  Attributes:
+    error_name: Application specific error name for exception.
+  """
+
+  STATE = RpcState.APPLICATION_ERROR
+
+  def __init__(self, message, error_name=None):
+    """Constructor.
+
+    Args:
+      message: Application specific error message.
+      error_name: Application specific error name.  Must be None, string
+      or unicode string.
+    """
+    super(ApplicationError, self).__init__(message)
+    self.error_name = error_name
+
+  def __str__(self):
+    return self.args[0] or ''
+
+  def __repr__(self):
+    if self.error_name is None:
+      error_format = ''
+    else:
+      error_format = ', %r' % self.error_name
+    return '%s(%r%s)' % (type(self).__name__, self.args[0], error_format)
+
+
+_RPC_STATE_TO_ERROR = {
+  RpcState.REQUEST_ERROR: RequestError,
+  RpcState.NETWORK_ERROR: NetworkError,
+  RpcState.SERVER_ERROR: ServerError,
+  RpcState.APPLICATION_ERROR: ApplicationError,
+  RpcState.METHOD_NOT_FOUND_ERROR: MethodNotFoundError,
+}
+
+class _RemoteMethodInfo(object):
+  """Object for encapsulating remote method information.
+
+  An instance of this method is associated with the 'remote' attribute
+  of the methods 'invoke_remote_method' instance.
+
+  Instances of this class are created by the remote decorator and should not
+  be created directly.
+  """
+
+  def __init__(self,
+               method,
+               request_type,
+               response_type):
+    """Constructor.
+
+    Args:
+      method: The method which implements the remote method.  This is a
+        function that will act as an instance method of a class definition
+        that is decorated by '@method'.  It must always take 'self' as its
+        first parameter.
+      request_type: Expected request type for the remote method.
+      response_type: Expected response type for the remote method.
+    """
+    self.__method = method
+    self.__request_type = request_type
+    self.__response_type = response_type
+
+  @property
+  def method(self):
+    """Original undecorated method."""
+    return self.__method
+
+  @property
+  def request_type(self):
+    """Expected request type for remote method."""
+    if isinstance(self.__request_type, six.string_types):
+      self.__request_type = messages.find_definition(
+        self.__request_type,
+        relative_to=sys.modules[self.__method.__module__])
+    return self.__request_type
+
+  @property
+  def response_type(self):
+    """Expected response type for remote method."""
+    if isinstance(self.__response_type, six.string_types):
+      self.__response_type = messages.find_definition(
+        self.__response_type,
+        relative_to=sys.modules[self.__method.__module__])
+    return self.__response_type
+
+
+def method(request_type=message_types.VoidMessage,
+           response_type=message_types.VoidMessage):
+  """Method decorator for creating remote methods.
+
+  Args:
+    request_type: Message type of expected request.
+    response_type: Message type of expected response.
+
+  Returns:
+    'remote_method_wrapper' function.
+
+  Raises:
+    TypeError: if the request_type or response_type parameters are not
+      proper subclasses of messages.Message.
+  """
+  if (not isinstance(request_type, six.string_types) and
+      (not isinstance(request_type, type) or
+       not issubclass(request_type, messages.Message) or
+       request_type is messages.Message)):
+    raise TypeError(
+        'Must provide message class for request-type.  Found %s',
+        request_type)
+
+  if (not isinstance(response_type, six.string_types) and
+      (not isinstance(response_type, type) or
+       not issubclass(response_type, messages.Message) or
+       response_type is messages.Message)):
+    raise TypeError(
+        'Must provide message class for response-type.  Found %s',
+        response_type)
+
+  def remote_method_wrapper(method):
+    """Decorator used to wrap method.
+
+    Args:
+      method: Original method being wrapped.
+
+    Returns:
+      'invoke_remote_method' function responsible for actual invocation.
+      This invocation function instance is assigned an attribute 'remote'
+      which contains information about the remote method:
+        request_type: Expected request type for remote method.
+        response_type: Response type returned from remote method.
+
+    Raises:
+      TypeError: If request_type or response_type is not a subclass of Message
+        or is the Message class itself.
+    """
+
+    @functools.wraps(method)
+    def invoke_remote_method(service_instance, request):
+      """Function used to replace original method.
+
+      Invoke wrapped remote method.  Checks to ensure that request and
+      response objects are the correct types.
+
+      Does not check whether messages are initialized.
+
+      Args:
+        service_instance: The service object whose method is being invoked.
+          This is passed to 'self' during the invocation of the original
+          method.
+        request: Request message.
+
+      Returns:
+        Results of calling wrapped remote method.
+
+      Raises:
+        RequestError: Request object is not of the correct type.
+        ServerError: Response object is not of the correct type.
+      """
+      if not isinstance(request, remote_method_info.request_type):
+        raise RequestError('Method %s.%s expected request type %s, '
+                           'received %s' %
+                           (type(service_instance).__name__,
+                            method.__name__,
+                            remote_method_info.request_type,
+                            type(request)))
+      response = method(service_instance, request)
+      if not isinstance(response, remote_method_info.response_type):
+        raise ServerError('Method %s.%s expected response type %s, '
+                          'sent %s' %
+                          (type(service_instance).__name__,
+                           method.__name__,
+                           remote_method_info.response_type,
+                           type(response)))
+      return response
+
+    remote_method_info = _RemoteMethodInfo(method,
+                                           request_type,
+                                           response_type)
+
+    invoke_remote_method.remote = remote_method_info
+    return invoke_remote_method
+
+  return remote_method_wrapper
+
+
+def remote(request_type, response_type):
+  """Temporary backward compatibility alias for method."""
+  logging.warning('The remote decorator has been renamed method.  It will be '
+                  'removed in very soon from future versions of ProtoRPC.')
+  return method(request_type, response_type)
+
+
+def get_remote_method_info(method):
+  """Get remote method info object from remote method.
+
+  Returns:
+    Remote method info object if method is a remote method, else None.
+  """
+  if not callable(method):
+    return None
+
+  try:
+    method_info = method.remote
+  except AttributeError:
+    return None
+
+  if not isinstance(method_info, _RemoteMethodInfo):
+    return None
+
+  return method_info
+
+
+class StubBase(object):
+  """Base class for client side service stubs.
+
+  The remote method stubs are created by the _ServiceClass meta-class
+  when a Service class is first created.  The resulting stub will
+  extend both this class and the service class it handles communications for.
+
+  Assume that there is a service:
+
+    class NewContactRequest(messages.Message):
+
+      name = messages.StringField(1, required=True)
+      phone = messages.StringField(2)
+      email = messages.StringField(3)
+
+    class NewContactResponse(message.Message):
+
+      contact_id = messages.StringField(1)
+
+    class AccountService(remote.Service):
+
+      @remote.method(NewContactRequest, NewContactResponse):
+      def new_contact(self, request):
+        ... implementation ...
+
+  A stub of this service can be called in two ways.  The first is to pass in a
+  correctly initialized NewContactRequest message:
+
+    request = NewContactRequest()
+    request.name = 'Bob Somebody'
+    request.phone = '+1 415 555 1234'
+
+    response = account_service_stub.new_contact(request)
+
+  The second way is to pass in keyword parameters that correspond with the root
+  request message type:
+
+      account_service_stub.new_contact(name='Bob Somebody',
+                                       phone='+1 415 555 1234')
+
+  The second form will create a request message of the appropriate type.
+  """
+
+  def __init__(self, transport):
+    """Constructor.
+
+    Args:
+      transport: Underlying transport to communicate with remote service.
+    """
+    self.__transport = transport
+
+  @property
+  def transport(self):
+    """Transport used to communicate with remote service."""
+    return self.__transport
+
+
+class _ServiceClass(type):
+  """Meta-class for service class."""
+
+  def __new_async_method(cls, remote):
+    """Create asynchronous method for Async handler.
+
+    Args:
+      remote: RemoteInfo to create method for.
+    """
+    def async_method(self, *args, **kwargs):
+      """Asynchronous remote method.
+
+      Args:
+        self: Instance of StubBase.Async subclass.
+
+        Stub methods either take a single positional argument when a full
+        request message is passed in, or keyword arguments, but not both.
+
+        See docstring for StubBase for more information on how to use remote
+        stub methods.
+
+      Returns:
+        Rpc instance used to represent asynchronous RPC.
+      """
+      if args and kwargs:
+        raise TypeError('May not provide both args and kwargs')
+
+      if not args:
+        # Construct request object from arguments.
+        request = remote.request_type()
+        for name, value in six.iteritems(kwargs):
+          setattr(request, name, value)
+      else:
+        # First argument is request object.
+        request = args[0]
+
+      return self.transport.send_rpc(remote, request)
+
+    async_method.__name__ = remote.method.__name__
+    async_method = util.positional(2)(async_method)
+    async_method.remote = remote
+    return async_method
+
+  def __new_sync_method(cls, async_method):
+    """Create synchronous method for stub.
+
+    Args:
+      async_method: asynchronous method to delegate calls to.
+    """
+    def sync_method(self, *args, **kwargs):
+      """Synchronous remote method.
+
+      Args:
+        self: Instance of StubBase.Async subclass.
+        args: Tuple (request,):
+          request: Request object.
+        kwargs: Field values for request.  Must be empty if request object
+          is provided.
+
+      Returns:
+        Response message from synchronized RPC.
+      """
+      return async_method(self.async_, *args, **kwargs).response
+    sync_method.__name__ = async_method.__name__
+    sync_method.remote = async_method.remote
+    return sync_method
+
+  def __create_async_methods(cls, remote_methods):
+    """Construct a dictionary of asynchronous methods based on remote methods.
+
+    Args:
+      remote_methods: Dictionary of methods with associated RemoteInfo objects.
+
+    Returns:
+      Dictionary of asynchronous methods with assocaited RemoteInfo objects.
+      Results added to AsyncStub subclass.
+    """
+    async_methods = {}
+    for method_name, method in remote_methods.items():
+      async_methods[method_name] = cls.__new_async_method(method.remote)
+    return async_methods
+
+  def __create_sync_methods(cls, async_methods):
+    """Construct a dictionary of synchronous methods based on remote methods.
+
+    Args:
+      async_methods: Dictionary of async methods to delegate calls to.
+
+    Returns:
+      Dictionary of synchronous methods with assocaited RemoteInfo objects.
+      Results added to Stub subclass.
+    """
+    sync_methods = {}
+    for method_name, async_method in async_methods.items():
+      sync_methods[method_name] = cls.__new_sync_method(async_method)
+    return sync_methods
+
+  def __new__(cls, name, bases, dct):
+    """Instantiate new service class instance."""
+    if StubBase not in bases:
+      # Collect existing remote methods.
+      base_methods = {}
+      for base in bases:
+        try:
+          remote_methods = base.__remote_methods
+        except AttributeError:
+          pass
+        else:
+          base_methods.update(remote_methods)
+
+      # Set this class private attribute so that base_methods do not have
+      # to be recacluated in __init__.
+      dct['_ServiceClass__base_methods'] = base_methods
+
+      for attribute, value in dct.items():
+        base_method = base_methods.get(attribute, None)
+        if base_method:
+          if not callable(value):
+            raise ServiceDefinitionError(
+              'Must override %s in %s with a method.' % (
+                attribute, name))
+
+          if get_remote_method_info(value):
+            raise ServiceDefinitionError(
+              'Do not use method decorator when overloading remote method %s '
+              'on service %s.' %
+              (attribute, name))
+
+          base_remote_method_info = get_remote_method_info(base_method)
+          remote_decorator = method(
+            base_remote_method_info.request_type,
+            base_remote_method_info.response_type)
+          new_remote_method = remote_decorator(value)
+          dct[attribute] = new_remote_method
+
+    return type.__new__(cls, name, bases, dct)
+
+  def __init__(cls, name, bases, dct):
+    """Create uninitialized state on new class."""
+    type.__init__(cls, name, bases, dct)
+
+    # Only service implementation classes should have remote methods and stub
+    # sub classes created.  Stub implementations have their own methods passed
+    # in to the type constructor.
+    if StubBase not in bases:
+      # Create list of remote methods.
+      cls.__remote_methods = dict(cls.__base_methods)
+
+      for attribute, value in dct.items():
+        value = getattr(cls, attribute)
+        remote_method_info = get_remote_method_info(value)
+        if remote_method_info:
+          cls.__remote_methods[attribute] = value
+
+      # Build asynchronous stub class.
+      stub_attributes = {'Service': cls}
+      async_methods = cls.__create_async_methods(cls.__remote_methods)
+      stub_attributes.update(async_methods)
+      async_class = type('AsyncStub', (StubBase, cls), stub_attributes)
+      cls.AsyncStub = async_class
+
+      # Constructor for synchronous stub class.
+      def __init__(self, transport):
+        """Constructor.
+
+        Args:
+          transport: Underlying transport to communicate with remote service.
+        """
+        super(cls.Stub, self).__init__(transport)
+        self.async_ = cls.AsyncStub(transport)
+
+      # Build synchronous stub class.
+      stub_attributes = {'Service': cls,
+                         '__init__': __init__}
+      stub_attributes.update(cls.__create_sync_methods(async_methods))
+
+      cls.Stub = type('Stub', (StubBase, cls), stub_attributes)
+
+  @staticmethod
+  def all_remote_methods(cls):
+    """Get all remote methods of service.
+
+    Returns:
+      Dict from method name to unbound method.
+    """
+    return dict(cls.__remote_methods)
+
+
+class RequestState(object):
+  """Request state information.
+
+  Properties:
+    remote_host: Remote host name where request originated.
+    remote_address: IP address where request originated.
+    server_host: Host of server within which service resides.
+    server_port: Post which service has recevied request from.
+  """
+
+  @util.positional(1)
+  def __init__(self,
+               remote_host=None,
+               remote_address=None,
+               server_host=None,
+               server_port=None):
+    """Constructor.
+
+    Args:
+      remote_host: Assigned to property.
+      remote_address: Assigned to property.
+      server_host: Assigned to property.
+      server_port: Assigned to property.
+    """
+    self.__remote_host = remote_host
+    self.__remote_address = remote_address
+    self.__server_host = server_host
+    self.__server_port = server_port
+
+  @property
+  def remote_host(self):
+    return self.__remote_host
+
+  @property
+  def remote_address(self):
+    return self.__remote_address
+
+  @property
+  def server_host(self):
+    return self.__server_host
+
+  @property
+  def server_port(self):
+    return self.__server_port
+
+  def _repr_items(self):
+    for name in ['remote_host',
+                 'remote_address',
+                 'server_host',
+                 'server_port']:
+      yield name, getattr(self, name)
+
+  def __repr__(self):
+    """String representation of state."""
+    state = [self.__class__.__name__]
+    for name, value in self._repr_items():
+      if value:
+        state.append('%s=%r' % (name, value))
+
+    return '<%s>' % (' '.join(state),)
+
+
+class HttpRequestState(RequestState):
+  """HTTP request state information.
+
+  NOTE: Does not attempt to represent certain types of information from the
+  request such as the query string as query strings are not permitted in
+  ProtoRPC URLs unless required by the underlying message format.
+
+  Properties:
+    headers: wsgiref.headers.Headers instance of HTTP request headers.
+    http_method: HTTP method as a string.
+    service_path: Path on HTTP service where service is mounted.  This path
+      will not include the remote method name.
+  """
+
+  @util.positional(1)
+  def __init__(self,
+               http_method=None,
+               service_path=None,
+               headers=None,
+               **kwargs):
+    """Constructor.
+
+    Args:
+      Same as RequestState, including:
+        http_method: Assigned to property.
+        service_path: Assigned to property.
+        headers: HTTP request headers.  If instance of Headers, assigned to
+          property without copying.  If dict, will convert to name value pairs
+          for use with Headers constructor.  Otherwise, passed as parameters to
+          Headers constructor.
+    """
+    super(HttpRequestState, self).__init__(**kwargs)
+
+    self.__http_method = http_method
+    self.__service_path = service_path
+
+    # Initialize headers.
+    if isinstance(headers, dict):
+      header_list = []
+      for key, value in sorted(headers.items()):
+        if not isinstance(value, list):
+          value = [value]
+        for item in value:
+          header_list.append((key, item))
+        headers = header_list
+    self.__headers = wsgi_headers.Headers(headers or [])
+
+  @property
+  def http_method(self):
+    return self.__http_method
+
+  @property
+  def service_path(self):
+    return self.__service_path
+
+  @property
+  def headers(self):
+    return self.__headers
+
+  def _repr_items(self):
+    for item in super(HttpRequestState, self)._repr_items():
+      yield item
+
+    for name in ['http_method', 'service_path']:
+      yield name, getattr(self, name)
+
+    yield 'headers', list(self.headers.items())
+
+
+class Service(six.with_metaclass(_ServiceClass, object)):
+  """Service base class.
+
+  Base class used for defining remote services.  Contains reflection functions,
+  useful helpers and built-in remote methods.
+
+  Services are expected to be constructed via either a constructor or factory
+  which takes no parameters.  However, it might be required that some state or
+  configuration is passed in to a service across multiple requests.
+
+  To do this, define parameters to the constructor of the service and use
+  the 'new_factory' class method to build a constructor that will transmit
+  parameters to the constructor.  For example:
+
+    class MyService(Service):
+
+      def __init__(self, configuration, state):
+        self.configuration = configuration
+        self.state = state
+
+    configuration = MyServiceConfiguration()
+    global_state = MyServiceState()
+
+    my_service_factory = MyService.new_factory(configuration,
+                                               state=global_state)
+
+  The contract with any service handler is that a new service object is created
+  to handle each user request, and that the construction does not take any
+  parameters.  The factory satisfies this condition:
+
+    new_instance = my_service_factory()
+    assert new_instance.state is global_state
+
+  Attributes:
+    request_state: RequestState set via initialize_request_state.
+  """
+
+  __request_state = None
+
+  @classmethod
+  def all_remote_methods(cls):
+    """Get all remote methods for service class.
+
+    Built-in methods do not appear in the dictionary of remote methods.
+
+    Returns:
+      Dictionary mapping method name to remote method.
+    """
+    return _ServiceClass.all_remote_methods(cls)
+
+  @classmethod
+  def new_factory(cls, *args, **kwargs):
+    """Create factory for service.
+
+    Useful for passing configuration or state objects to the service.  Accepts
+    arbitrary parameters and keywords, however, underlying service must accept
+    also accept not other parameters in its constructor.
+
+    Args:
+      args: Args to pass to service constructor.
+      kwargs: Keyword arguments to pass to service constructor.
+
+    Returns:
+      Factory function that will create a new instance and forward args and
+      keywords to the constructor.
+    """
+
+    def service_factory():
+      return cls(*args, **kwargs)
+
+    # Update docstring so that it is easier to debug.
+    full_class_name = '%s.%s' % (cls.__module__, cls.__name__)
+    service_factory.__doc__ = (
+        'Creates new instances of service %s.\n\n'
+        'Returns:\n'
+        '  New instance of %s.'
+        % (cls.__name__, full_class_name))
+
+    # Update name so that it is easier to debug the factory function.
+    service_factory.__name__ = '%s_service_factory' % cls.__name__
+
+    service_factory.service_class = cls
+
+    return service_factory
+
+  def initialize_request_state(self, request_state):
+    """Save request state for use in remote method.
+
+    Args:
+      request_state: RequestState instance.
+    """
+    self.__request_state = request_state
+
+  @classmethod
+  def definition_name(cls):
+    """Get definition name for Service class.
+
+    Package name is determined by the global 'package' attribute in the
+    module that contains the Service definition.  If no 'package' attribute
+    is available, uses module name.  If no module is found, just uses class
+    name as name.
+
+    Returns:
+      Fully qualified service name.
+    """
+    try:
+      return cls.__definition_name
+    except AttributeError:
+      outer_definition_name = cls.outer_definition_name()
+      if outer_definition_name is None:
+        cls.__definition_name = cls.__name__
+      else:
+        cls.__definition_name = '%s.%s' % (outer_definition_name, cls.__name__)
+
+      return cls.__definition_name
+
+  @classmethod
+  def outer_definition_name(cls):
+    """Get outer definition name.
+
+    Returns:
+      Package for service.  Services are never nested inside other definitions.
+    """
+    return cls.definition_package()
+
+  @classmethod
+  def definition_package(cls):
+    """Get package for service.
+
+    Returns:
+      Package name for service.
+    """
+    try:
+      return cls.__definition_package
+    except AttributeError:
+      cls.__definition_package = util.get_package_for_module(cls.__module__)
+
+    return cls.__definition_package
+
+  @property
+  def request_state(self):
+    """Request state associated with this Service instance."""
+    return self.__request_state
+
+
+def is_error_status(status):
+  """Function that determines whether the RPC status is an error.
+
+  Args:
+    status: Initialized RpcStatus message to check for errors.
+  """
+  status.check_initialized()
+  return RpcError.from_state(status.state) is not None
+
+
+def check_rpc_status(status):
+  """Function converts an error status to a raised exception.
+
+  Args:
+    status: Initialized RpcStatus message to check for errors.
+
+  Raises:
+    RpcError according to state set on status, if it is an error state.
+  """
+  status.check_initialized()
+  error_class = RpcError.from_state(status.state)
+  if error_class is not None:
+    if error_class is ApplicationError:
+      raise error_class(status.error_message, status.error_name)
+    else:
+      raise error_class(status.error_message)
+
+
+class ProtocolConfig(object):
+  """Configuration for single protocol mapping.
+
+  A read-only protocol configuration provides a given protocol implementation
+  with a name and a set of content-types that it recognizes.
+
+  Properties:
+    protocol: The protocol implementation for configuration (usually a module,
+      for example, protojson, protobuf, etc.).  This is an object that has the
+      following attributes:
+        CONTENT_TYPE: Used as the default content-type if default_content_type
+          is not set.
+        ALTERNATIVE_CONTENT_TYPES (optional): A list of alternative
+          content-types to the default that indicate the same protocol.
+        encode_message: Function that matches the signature of
+          ProtocolConfig.encode_message.  Used for encoding a ProtoRPC message.
+        decode_message: Function that matches the signature of
+          ProtocolConfig.decode_message.  Used for decoding a ProtoRPC message.
+    name: Name of protocol configuration.
+    default_content_type: The default content type for the protocol.  Overrides
+      CONTENT_TYPE defined on protocol.
+    alternative_content_types: A list of alternative content-types supported
+      by the protocol.  Must not contain the default content-type, nor
+      duplicates.  Overrides ALTERNATIVE_CONTENT_TYPE defined on protocol.
+    content_types: A list of all content-types supported by configuration.
+      Combination of default content-type and alternatives.
+  """
+
+  def __init__(self,
+               protocol,
+               name,
+               default_content_type=None,
+               alternative_content_types=None):
+    """Constructor.
+
+    Args:
+      protocol: The protocol implementation for configuration.
+      name: The name of the protocol configuration.
+      default_content_type: The default content-type for protocol.  If none
+        provided it will check protocol.CONTENT_TYPE.
+      alternative_content_types:  A list of content-types.  If none provided,
+        it will check protocol.ALTERNATIVE_CONTENT_TYPES.  If that attribute
+        does not exist, will be an empty tuple.
+
+    Raises:
+      ServiceConfigurationError if there are any duplicate content-types.
+    """
+    self.__protocol = protocol
+    self.__name = name
+    self.__default_content_type = (default_content_type or
+                                   protocol.CONTENT_TYPE).lower()
+    if alternative_content_types is None:
+      alternative_content_types = getattr(protocol,
+                                          'ALTERNATIVE_CONTENT_TYPES',
+                                          ())
+    self.__alternative_content_types = tuple(
+      content_type.lower() for content_type in alternative_content_types)
+    self.__content_types = (
+      (self.__default_content_type,) + self.__alternative_content_types)
+
+    # Detect duplicate content types in definition.
+    previous_type = None
+    for content_type in sorted(self.content_types):
+      if content_type == previous_type:
+        raise ServiceConfigurationError(
+          'Duplicate content-type %s' % content_type)
+      previous_type = content_type
+
+  @property
+  def protocol(self):
+    return self.__protocol
+
+  @property
+  def name(self):
+    return self.__name
+
+  @property
+  def default_content_type(self):
+    return self.__default_content_type
+
+  @property
+  def alternate_content_types(self):
+    return self.__alternative_content_types
+
+  @property
+  def content_types(self):
+    return self.__content_types
+
+  def encode_message(self, message):
+    """Encode message.
+
+    Args:
+      message: Message instance to encode.
+
+    Returns:
+      String encoding of Message instance encoded in protocol's format.
+    """
+    return self.__protocol.encode_message(message)
+
+  def decode_message(self, message_type, encoded_message):
+    """Decode buffer to Message instance.
+
+    Args:
+      message_type: Message type to decode data to.
+      encoded_message: Encoded version of message as string.
+
+    Returns:
+      Decoded instance of message_type.
+    """
+    return self.__protocol.decode_message(message_type, encoded_message)
+
+
+class Protocols(object):
+  """Collection of protocol configurations.
+
+  Used to describe a complete set of content-type mappings for multiple
+  protocol configurations.
+
+  Properties:
+    names: Sorted list of the names of registered protocols.
+    content_types: Sorted list of supported content-types.
+  """
+
+  __default_protocols = None
+  __lock = threading.Lock()
+
+  def __init__(self):
+    """Constructor."""
+    self.__by_name = {}
+    self.__by_content_type = {}
+
+  def add_protocol_config(self, config):
+    """Add a protocol configuration to protocol mapping.
+
+    Args:
+      config: A ProtocolConfig.
+
+    Raises:
+      ServiceConfigurationError if protocol.name is already registered
+        or any of it's content-types are already registered.
+    """
+    if config.name in self.__by_name:
+      raise ServiceConfigurationError(
+        'Protocol name %r is already in use' % config.name)
+    for content_type in config.content_types:
+      if content_type in self.__by_content_type:
+        raise ServiceConfigurationError(
+          'Content type %r is already in use' % content_type)
+
+    self.__by_name[config.name] = config
+    self.__by_content_type.update((t, config) for t in config.content_types)
+
+  def add_protocol(self, *args, **kwargs):
+    """Add a protocol configuration from basic parameters.
+
+    Simple helper method that creates and registeres a ProtocolConfig instance.
+    """
+    self.add_protocol_config(ProtocolConfig(*args, **kwargs))
+
+  @property
+  def names(self):
+    return tuple(sorted(self.__by_name))
+
+  @property
+  def content_types(self):
+    return tuple(sorted(self.__by_content_type))
+
+  def lookup_by_name(self, name):
+    """Look up a ProtocolConfig by name.
+
+    Args:
+      name: Name of protocol to look for.
+
+    Returns:
+      ProtocolConfig associated with name.
+
+    Raises:
+      KeyError if there is no protocol for name.
+    """
+    return self.__by_name[name.lower()]
+
+  def lookup_by_content_type(self, content_type):
+    """Look up a ProtocolConfig by content-type.
+
+    Args:
+      content_type: Content-type to find protocol configuration for.
+
+    Returns:
+      ProtocolConfig associated with content-type.
+
+    Raises:
+      KeyError if there is no protocol for content-type.
+    """
+    return self.__by_content_type[content_type.lower()]
+
+  @classmethod
+  def new_default(cls):
+    """Create default protocols configuration.
+
+    Returns:
+      New Protocols instance configured for protobuf and protorpc.
+    """
+    protocols = cls()
+    protocols.add_protocol(protobuf, 'protobuf')
+    protocols.add_protocol(protojson.ProtoJson.get_default(), 'protojson')
+    return protocols
+
+  @classmethod
+  def get_default(cls):
+    """Get the global default Protocols instance.
+
+    Returns:
+      Current global default Protocols instance.
+    """
+    default_protocols = cls.__default_protocols
+    if default_protocols is None:
+      with cls.__lock:
+        default_protocols = cls.__default_protocols
+        if default_protocols is None:
+          default_protocols = cls.new_default()
+          cls.__default_protocols = default_protocols
+    return default_protocols
+
+  @classmethod
+  def set_default(cls, protocols):
+    """Set the global default Protocols instance.
+
+    Args:
+      protocols: A Protocols instance.
+
+    Raises:
+      TypeError: If protocols is not an instance of Protocols.
+    """
+    if not isinstance(protocols, Protocols):
+      raise TypeError(
+        'Expected value of type "Protocols", found %r' % protocols)
+    with cls.__lock:
+      cls.__default_protocols = protocols