def __api_openapi_descriptor(self, services, hostname=None, x_google_api_name=False):
merged_api_info = self.__get_merged_api_info(services)<EOL>descriptor = self.get_descriptor_defaults(merged_api_info,<EOL>hostname=hostname,<EOL>x_google_api_name=x_google_api_name)<EOL>description = merged_api_info.description<EOL>if not description and len(services) == <NUM_LIT:1>:<EOL><INDENT>description = services[<NUM_LIT:0>].__doc__<EOL><DEDENT>if description:<EOL><INDENT>descriptor['<STR_LIT:info>']['<STR_LIT:description>'] = description<EOL><DEDENT>security_definitions = self.__security_definitions_descriptor(<EOL>merged_api_info.issuers)<EOL>method_map = {}<EOL>method_collision_tracker = {}<EOL>rest_collision_tracker = {}<EOL>for service in services:<EOL><INDENT>remote_methods = service.all_remote_methods()<EOL>for protorpc_meth_name in sorted(remote_methods.iterkeys()):<EOL><INDENT>protorpc_meth_info = remote_methods[protorpc_meth_name]<EOL>method_info = getattr(protorpc_meth_info, '<STR_LIT>', None)<EOL>if method_info is None:<EOL><INDENT>continue<EOL><DEDENT>method_id = method_info.method_id(service.api_info)<EOL>is_api_key_required = method_info.is_api_key_required(service.api_info)<EOL>path = '<STR_LIT>'.format(merged_api_info.name,<EOL>merged_api_info.path_version,<EOL>method_info.get_path(service.api_info))<EOL>verb = method_info.http_method.lower()<EOL>if path not in method_map:<EOL><INDENT>method_map[path] = {}<EOL><DEDENT>if is_api_key_required and _API_KEY not in security_definitions:<EOL><INDENT>security_definitions[_API_KEY] = {<EOL>'<STR_LIT:type>': '<STR_LIT>',<EOL>'<STR_LIT:name>': _API_KEY_PARAM,<EOL>'<STR_LIT>': '<STR_LIT>'<EOL>}<EOL><DEDENT>operation_id = self._construct_operation_id(<EOL>service.__name__, protorpc_meth_name)<EOL>method_map[path][verb] = self.__method_descriptor(<EOL>service, method_info, operation_id, protorpc_meth_info,<EOL>security_definitions)<EOL>if method_id in method_collision_tracker:<EOL><INDENT>raise api_exceptions.ApiConfigurationError(<EOL>'<STR_LIT>' %<EOL>(method_id, method_collision_tracker[method_id],<EOL>service.__name__))<EOL><DEDENT>else:<EOL><INDENT>method_collision_tracker[method_id] = service.__name__<EOL><DEDENT>rest_identifier = (method_info.http_method,<EOL>method_info.get_path(service.api_info))<EOL>if rest_identifier in rest_collision_tracker:<EOL><INDENT>raise api_exceptions.ApiConfigurationError(<EOL>'<STR_LIT>' %<EOL>(method_info.http_method, method_info.get_path(service.api_info),<EOL>rest_collision_tracker[rest_identifier],<EOL>service.__name__))<EOL><DEDENT>else:<EOL><INDENT>rest_collision_tracker[rest_identifier] = service.__name__<EOL><DEDENT><DEDENT><DEDENT>if method_map:<EOL><INDENT>descriptor['<STR_LIT>'] = method_map<EOL><DEDENT>definitions = self.__definitions_descriptor()<EOL>if definitions:<EOL><INDENT>descriptor['<STR_LIT>'] = definitions<EOL><DEDENT>descriptor['<STR_LIT>'] = security_definitions<EOL>limit_definitions = self.__x_google_quota_definitions_descriptor(<EOL>merged_api_info.limit_definitions)<EOL>if limit_definitions:<EOL><INDENT>descriptor['<STR_LIT>'] = limit_definitions<EOL><DEDENT>return descriptor<EOL>
Builds an OpenAPI description of an API.

        Args:
          services: List of protorpc.remote.Service instances implementing an
            api/version.
          hostname: string, Hostname of the API, to override the value set on the
            current service. Defaults to None.

        Returns:
          A dictionary that can be deserialized into JSON and stored as an API
          description document in OpenAPI format.

        Raises:
          ApiConfigurationError: If there's something wrong with the API
            configuration, such as a multiclass API decorated with different API
            descriptors (see the docstring for api()), or a repeated method
            signature.