Skip to content

Endpoints 2.0.0b7 '_ApiDecorator' object has no attribute 'base_path' #31

@lhmatt

Description

@lhmatt

Discovered while attempting to modify the EchoApi example code from https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/standard/endpoints-frameworks-v2/echo to support multiple api classes.

I don't know if this is officially supported yet or not, but #27 seems to imply that it should work.

# [START imports]
import endpoints
from protorpc import message_types
from protorpc import messages
from protorpc import remote
# [END imports]


# [START messages]
class EchoRequest(messages.Message):
    content = messages.StringField(1)


class EchoResponse(messages.Message):
    """A proto Message that contains a simple string field."""
    content = messages.StringField(1)


ECHO_RESOURCE = endpoints.ResourceContainer(
    EchoRequest,
    n=messages.IntegerField(2, default=1))
# [END messages]


# [START echo_api]
api_collection = endpoints.api(
    name='library',
    version='v1')


@api_collection.api_class(resource_name='echo')
class Echo(remote.Service):

    @endpoints.method(
        # This method takes a ResourceContainer defined above.
        ECHO_RESOURCE,
        # This method returns an Echo message.
        EchoResponse,
        path='echo',
        http_method='POST',
        name='echo')
    def echo(self, request):
        output_content = ' '.join([request.content] * request.n)
        return EchoResponse(content=output_content)

    @endpoints.method(
        # This method takes a ResourceContainer defined above.
        ECHO_RESOURCE,
        # This method returns an Echo message.
        EchoResponse,
        path='echo/{n}',
        http_method='POST',
        name='echo_path_parameter')
    def echo_path_parameter(self, request):
        output_content = ' '.join([request.content] * request.n)
        return EchoResponse(content=output_content)

    @endpoints.method(
        # This method takes a ResourceContainer defined above.
        message_types.VoidMessage,
        # This method returns an Echo message.
        EchoResponse,
        path='echo/getApiKey',
        http_method='GET',
        name='echo_api_key')
    def echo_api_key(self, request):
        return EchoResponse(content=request.get_unrecognized_field_info('key'))

    @endpoints.method(
        # This method takes an empty request body.
        message_types.VoidMessage,
        # This method returns an Echo message.
        EchoResponse,
        path='echo/getUserEmail',
        http_method='GET',
        # Require auth tokens to have the following scopes to access this API.
        scopes=[endpoints.EMAIL_SCOPE],
        # OAuth2 audiences allowed in incoming tokens.
        audiences=['your-oauth-client-id.com'])
    def get_user_email(self, request):
        user = endpoints.get_current_user()
        # If there's no user defined, the request was unauthenticated, so we
        # raise 401 Unauthorized.
        if not user:
            raise endpoints.UnauthorizedException
        return EchoResponse(content=user.email())
# [END echo_api]


# [START api_server]
api = endpoints.api_server([api_collection])
# [END api_server]

Running the endpointscfg.py script results in an AttributeError:

Traceback (most recent call last):
  File "thirdparty/endpoints/endpointscfg.py", line 625, in <module>
    main(sys.argv)
  File "thirdparty/endpoints/endpointscfg.py", line 621, in main
    args.callback(args)
  File "thirdparty/endpoints/endpointscfg.py", line 479, in _GenOpenApiSpecCallback
    application_path=args.application)
  File "thirdparty/endpoints/endpointscfg.py", line 324, in _GenOpenApiSpec
    application_path=application_path)
  File "thirdparty/endpoints/endpointscfg.py", line 181, in GenApiConfig
    module = __import__(module_name, fromlist=base_service_class_name)
  File "/Users/matt/PycharmProjects/gff-api/main.py", line 113, in <module>
    api = endpoints.api_server([api_collection])
  File "/Users/matt/PycharmProjects/gff-api/thirdparty/endpoints/apiserving.py", line 485, in api_server
    apis_app = _ApiServer(api_services, **kwargs)
  File "/Users/matt/PycharmProjects/gff-api/thirdparty/endpoints/apiserving.py", line 241, in __init__
    self.base_paths.add(entry.base_path)
AttributeError: '_ApiDecorator' object has no attribute 'base_path'

I played around with the _ApiServer.__init__ method in endpoints/apiserving.py so that it checks the _ApiDecorator__common_info if it can't fetch base_path directly:

  def __init__(self, api_services, **kwargs):
    self.base_paths = set()

    for entry in api_services[:]:
      # pylint: disable=protected-access
      if isinstance(entry, api_config._ApiDecorator):
        api_services.remove(entry)
        api_services.extend(entry.get_api_classes())
        try:
            self.base_paths.add(entry.base_path)
        except AttributeError:
            self.base_paths.add(entry._ApiDecorator__common_info.base_path)

    # Record the API services for quick discovery doc generation
    self.api_services = api_services
    ...

This gets further through the process but results in a new error:

No handlers could be found for logger "endpoints.apiserving"
Traceback (most recent call last):
  File "thirdparty/endpoints/endpointscfg.py", line 625, in <module>
    main(sys.argv)
  File "thirdparty/endpoints/endpointscfg.py", line 621, in main
    args.callback(args)
  File "thirdparty/endpoints/endpointscfg.py", line 479, in _GenOpenApiSpecCallback
    application_path=args.application)
  File "thirdparty/endpoints/endpointscfg.py", line 324, in _GenOpenApiSpec
    application_path=application_path)
  File "thirdparty/endpoints/endpointscfg.py", line 184, in GenApiConfig
    raise TypeError('%s is not a ProtoRPC service' % service_class_name)
TypeError: main.api_collection is not a ProtoRPC service

I decided not to follow the rabbit hole any further -- it doesn't seem like a small job to fix. Any ideas?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions