Skip to content
This repository was archived by the owner on Apr 19, 2026. It is now read-only.
6 changes: 3 additions & 3 deletions endpoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
# pylint: disable=wildcard-import
from __future__ import absolute_import

from protorpc import message_types
from protorpc import messages
from protorpc import remote
from .bundled.protorpc import message_types
from .bundled.protorpc import messages
from .bundled.protorpc import remote

from .api_config import api, method
from .api_config import AUTH_LEVEL, EMAIL_SCOPE
Expand Down
2 changes: 1 addition & 1 deletion endpoints/_endpointscfg_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def GenApiConfig(service_class_names, config_string_generator=None,
resolved_services.extend(service.get_api_classes())
elif (not isinstance(service, type) or
not issubclass(service, remote.Service)):
raise TypeError('%s is not a ProtoRPC service' % service_class_name)
raise TypeError('%s is not a subclass of endpoints.remote.Service' % service_class_name)
else:
resolved_services.append(service)

Expand Down
2 changes: 1 addition & 1 deletion endpoints/api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def entries_get(self, request):

import attr
import semver
from protorpc import util
from .bundled.protorpc import util

from . import api_exceptions
from . import constants
Expand Down
6 changes: 5 additions & 1 deletion endpoints/apiserving.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def list(self, request):

from endpoints_management.control import client as control_client
from endpoints_management.control import wsgi as control_wsgi
from protorpc.wsgi import service as wsgi_service
from .bundled.protorpc.wsgi import service as wsgi_service

from . import api_config
from . import api_exceptions
Expand Down Expand Up @@ -564,6 +564,10 @@ def api_server(api_services, **kwargs):
if 'protocols' in kwargs:
raise TypeError("__init__() got an unexpected keyword argument 'protocols'")

for service in api_services:
if not issubclass(service, remote.Service):
raise TypeError('%s is not a subclass of endpoints.remote.Service' % service)

# Construct the api serving app
apis_app = _ApiServer(api_services, **kwargs)
dispatcher = endpoints_dispatcher.EndpointsDispatcherMiddleware(apis_app)
Expand Down
15 changes: 15 additions & 0 deletions endpoints/bundled/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2018 Google Inc. All Rights Reserved.
#
# 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.

"""Embedded libraries."""
21 changes: 21 additions & 0 deletions endpoints/bundled/protorpc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python
#
# Copyright 2011 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.
#

"""Main module for ProtoRPC package."""

__author__ = 'rafek@google.com (Rafe Kaplan)'
__version__ = '1.0'
290 changes: 290 additions & 0 deletions endpoints/bundled/protorpc/definition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
#!/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.
#

"""Stub library."""
import six

__author__ = 'rafek@google.com (Rafe Kaplan)'

import sys
import types

from . import descriptor
from . import message_types
from . import messages
from . import protobuf
from . import remote
from . import util

__all__ = [
'define_enum',
'define_field',
'define_file',
'define_message',
'define_service',
'import_file',
'import_file_set',
]


# Map variant back to message field classes.
def _build_variant_map():
"""Map variants to fields.

Returns:
Dictionary mapping field variant to its associated field type.
"""
result = {}
for name in dir(messages):
value = getattr(messages, name)
if isinstance(value, type) and issubclass(value, messages.Field):
for variant in getattr(value, 'VARIANTS', []):
result[variant] = value
return result

_VARIANT_MAP = _build_variant_map()

_MESSAGE_TYPE_MAP = {
message_types.DateTimeMessage.definition_name(): message_types.DateTimeField,
}


def _get_or_define_module(full_name, modules):
"""Helper method for defining new modules.

Args:
full_name: Fully qualified name of module to create or return.
modules: Dictionary of all modules. Defaults to sys.modules.

Returns:
Named module if found in 'modules', else creates new module and inserts in
'modules'. Will also construct parent modules if necessary.
"""
module = modules.get(full_name)
if not module:
module = types.ModuleType(full_name)
modules[full_name] = module

split_name = full_name.rsplit('.', 1)
if len(split_name) > 1:
parent_module_name, sub_module_name = split_name
parent_module = _get_or_define_module(parent_module_name, modules)
setattr(parent_module, sub_module_name, module)

return module


def define_enum(enum_descriptor, module_name):
"""Define Enum class from descriptor.

Args:
enum_descriptor: EnumDescriptor to build Enum class from.
module_name: Module name to give new descriptor class.

Returns:
New messages.Enum sub-class as described by enum_descriptor.
"""
enum_values = enum_descriptor.values or []

class_dict = dict((value.name, value.number) for value in enum_values)
class_dict['__module__'] = module_name
return type(str(enum_descriptor.name), (messages.Enum,), class_dict)


def define_field(field_descriptor):
"""Define Field instance from descriptor.

Args:
field_descriptor: FieldDescriptor class to build field instance from.

Returns:
New field instance as described by enum_descriptor.
"""
field_class = _VARIANT_MAP[field_descriptor.variant]
params = {'number': field_descriptor.number,
'variant': field_descriptor.variant,
}

if field_descriptor.label == descriptor.FieldDescriptor.Label.REQUIRED:
params['required'] = True
elif field_descriptor.label == descriptor.FieldDescriptor.Label.REPEATED:
params['repeated'] = True

message_type_field = _MESSAGE_TYPE_MAP.get(field_descriptor.type_name)
if message_type_field:
return message_type_field(**params)
elif field_class in (messages.EnumField, messages.MessageField):
return field_class(field_descriptor.type_name, **params)
else:
if field_descriptor.default_value:
value = field_descriptor.default_value
try:
value = descriptor._DEFAULT_FROM_STRING_MAP[field_class](value)
except (TypeError, ValueError, KeyError):
pass # Let the value pass to the constructor.
params['default'] = value
return field_class(**params)


def define_message(message_descriptor, module_name):
"""Define Message class from descriptor.

Args:
message_descriptor: MessageDescriptor to describe message class from.
module_name: Module name to give to new descriptor class.

Returns:
New messages.Message sub-class as described by message_descriptor.
"""
class_dict = {'__module__': module_name}

for enum in message_descriptor.enum_types or []:
enum_instance = define_enum(enum, module_name)
class_dict[enum.name] = enum_instance

# TODO(rafek): support nested messages when supported by descriptor.

for field in message_descriptor.fields or []:
field_instance = define_field(field)
class_dict[field.name] = field_instance

class_name = message_descriptor.name.encode('utf-8')
return type(class_name, (messages.Message,), class_dict)


def define_service(service_descriptor, module):
"""Define a new service proxy.

Args:
service_descriptor: ServiceDescriptor class that describes the service.
module: Module to add service to. Request and response types are found
relative to this module.

Returns:
Service class proxy capable of communicating with a remote server.
"""
class_dict = {'__module__': module.__name__}
class_name = service_descriptor.name.encode('utf-8')

for method_descriptor in service_descriptor.methods or []:
request_definition = messages.find_definition(
method_descriptor.request_type, module)
response_definition = messages.find_definition(
method_descriptor.response_type, module)

method_name = method_descriptor.name.encode('utf-8')
def remote_method(self, request):
"""Actual service method."""
raise NotImplementedError('Method is not implemented')
remote_method.__name__ = method_name
remote_method_decorator = remote.method(request_definition,
response_definition)

class_dict[method_name] = remote_method_decorator(remote_method)

service_class = type(class_name, (remote.Service,), class_dict)
return service_class


def define_file(file_descriptor, module=None):
"""Define module from FileDescriptor.

Args:
file_descriptor: FileDescriptor instance to describe module from.
module: Module to add contained objects to. Module name overrides value
in file_descriptor.package. Definitions are added to existing
module if provided.

Returns:
If no module provided, will create a new module with its name set to the
file descriptor's package. If a module is provided, returns the same
module.
"""
if module is None:
module = types.ModuleType(file_descriptor.package)

for enum_descriptor in file_descriptor.enum_types or []:
enum_class = define_enum(enum_descriptor, module.__name__)
setattr(module, enum_descriptor.name, enum_class)

for message_descriptor in file_descriptor.message_types or []:
message_class = define_message(message_descriptor, module.__name__)
setattr(module, message_descriptor.name, message_class)

for service_descriptor in file_descriptor.service_types or []:
service_class = define_service(service_descriptor, module)
setattr(module, service_descriptor.name, service_class)

return module


@util.positional(1)
def import_file(file_descriptor, modules=None):
"""Import FileDescriptor in to module space.

This is like define_file except that a new module and any required parent
modules are created and added to the modules parameter or sys.modules if not
provided.

Args:
file_descriptor: FileDescriptor instance to describe module from.
modules: Dictionary of modules to update. Modules and their parents that
do not exist will be created. If an existing module is found that
matches file_descriptor.package, that module is updated with the
FileDescriptor contents.

Returns:
Module found in modules, else a new module.
"""
if not file_descriptor.package:
raise ValueError('File descriptor must have package name')

if modules is None:
modules = sys.modules

module = _get_or_define_module(file_descriptor.package.encode('utf-8'),
modules)

return define_file(file_descriptor, module)


@util.positional(1)
def import_file_set(file_set, modules=None, _open=open):
"""Import FileSet in to module space.

Args:
file_set: If string, open file and read serialized FileSet. Otherwise,
a FileSet instance to import definitions from.
modules: Dictionary of modules to update. Modules and their parents that
do not exist will be created. If an existing module is found that
matches file_descriptor.package, that module is updated with the
FileDescriptor contents.
_open: Used for dependency injection during tests.
"""
if isinstance(file_set, six.string_types):
encoded_file = _open(file_set, 'rb')
try:
encoded_file_set = encoded_file.read()
finally:
encoded_file.close()

file_set = protobuf.decode_message(descriptor.FileSet, encoded_file_set)

for file_descriptor in file_set.files:
# Do not reload built in protorpc classes.
if not file_descriptor.package.startswith('protorpc.'):
import_file(file_descriptor, modules=modules)
Loading