def AddDescriptorFromSchema(self, schema_name, schema): """Add a new MessageDescriptor named schema_name based on schema.""" # TODO(craigcitro): Is schema_name redundant? if self.__GetDescriptor(schema_name): return if schema.get('enum'): self.__DeclareEnum(schema_name, schema) return if schema.get('type') == 'any': self.__DeclareMessageAlias(schema, 'extra_types.JsonValue') return if schema.get('type') != 'object': raise ValueError('Cannot create message descriptors for type %s', schema.get('type')) message = extended_descriptor.ExtendedMessageDescriptor() message.name = self.__names.ClassName(schema['id']) message.description = util.CleanDescription( schema.get('description', 'A %s object.' % message.name)) self.__DeclareDescriptor(message.name) with self.__DescriptorEnv(message): properties = schema.get('properties', {}) for index, (name, attrs) in enumerate(sorted(properties.items())): field = self.__FieldDescriptorFromProperties( name, index + 1, attrs) message.fields.append(field) if field.name != name: message.field_mappings.append( extended_descriptor.ExtendedMessageDescriptor. JsonFieldMapping(python_name=field.name, json_name=name)) self.__AddImport('from %s import encoding' % self.__base_files_package) if 'additionalProperties' in schema: self.__AddAdditionalProperties(message, schema, properties) self.__RegisterDescriptor(message)
def __init__(self, client_info, names, description, root_package_dir, base_files_package): self.__names = names self.__client_info = client_info self.__package = client_info.package self.__description = util.CleanDescription(description) self.__root_package_dir = root_package_dir self.__base_files_package = base_files_package self.__file_descriptor = extended_descriptor.ExtendedFileDescriptor( package=self.__package, description=self.__description) # Add required imports self.__file_descriptor.additional_imports = [ 'from protorpc import messages', ] # Map from scoped names (i.e. Foo.Bar) to MessageDescriptors. self.__message_registry = collections.OrderedDict() # A set of types that we're currently adding (for cycle detection). self.__nascent_types = set() # A set of types for which we've seen a reference but no # definition; if this set is nonempty, validation fails. self.__unknown_types = set() # Used for tracking paths during message creation self.__current_path = [] # Where to register created messages self.__current_env = self.__file_descriptor
def __FieldDescriptorFromProperties(self, name, index, attrs): """Create a field descriptor for these attrs.""" field = descriptor.FieldDescriptor() field.name = self.__names.CleanName(name) field.number = index field.label = self.__ComputeLabel(attrs) new_type_name_hint = self.__names.ClassName( '%sValue' % self.__names.ClassName(name)) type_info = self.__GetTypeInfo(attrs, new_type_name_hint) field.type_name = type_info.type_name field.variant = type_info.variant if 'default' in attrs: # TODO(craigcitro): Correctly handle non-primitive default values. default = attrs['default'] if field.type_name != 'string' and field.variant != messages.Variant.ENUM: default = str(json.loads(default)) if field.variant == messages.Variant.ENUM: default = self.__names.NormalizeEnumName(default) field.default_value = default extended_field = extended_descriptor.ExtendedFieldDescriptor() extended_field.name = field.name extended_field.description = util.CleanDescription( attrs.get('description', 'A %s attribute.' % field.type_name)) extended_field.field_descriptor = field return extended_field
def __PrintDocstring(self, printer, method_info, method_name, name): """Print a docstring for a service method.""" if method_info.description: description = util.CleanDescription(method_info.description) first_line, newline, remaining = method_info.description.partition( '\n') if not first_line.endswith('.'): first_line = '%s.' % first_line description = '%s%s%s' % (first_line, newline, remaining) else: description = '%s method for the %s service.' % (method_name, name) with printer.CommentContext(): printer('"""%s' % description) printer() printer('Args:') printer(' request: (%s) input message', method_info.request_type_name) printer(' global_params: (StandardQueryParameters, default: None) ' 'global arguments') if method_info.upload_config: printer(' upload: (Upload, default: None) If present, upload') printer(' this stream with the request.') if method_info.supports_download: printer( ' download: (Download, default: None) If present, download') printer(' data from the request via this stream.') printer('Returns:') printer(' (%s) The response message.', method_info.response_type_name) printer('"""')
def __DeclareEnum(self, enum_name, attrs): description = util.CleanDescription(attrs.get('description', '')) enum_values = attrs['enum'] enum_descriptions = attrs.get('enumDescriptions', [''] * len(enum_values)) self.AddEnumDescriptor(enum_name, description, enum_values, enum_descriptions) self.__AddIfUnknown(enum_name) return TypeInfo(type_name=enum_name, variant=messages.Variant.ENUM)
def AddEnumDescriptor(self, name, description, enum_values, enum_descriptions): """Add a new EnumDescriptor named name with the given enum values.""" message = extended_descriptor.ExtendedEnumDescriptor() message.name = self.__names.ClassName(name) message.description = util.CleanDescription(description) self.__DeclareDescriptor(message.name) for index, (enum_name, enum_description) in enumerate( zip(enum_values, enum_descriptions)): enum_value = extended_descriptor.ExtendedEnumValueDescriptor() enum_value.name = self.__names.NormalizeEnumName(enum_name) if enum_value.name != enum_name: message.enum_mappings.append( extended_descriptor.ExtendedEnumDescriptor.JsonEnumMapping( python_name=enum_value.name, json_name=enum_name)) self.__AddImport('from %s import encoding' % self.__base_files_package) enum_value.number = index enum_value.description = util.CleanDescription( enum_description or '<no description>') message.values.append(enum_value) self.__RegisterDescriptor(message)
def __ComputeMethodInfo(self, method_description, request, response, request_field): """Compute the base_api.ApiMethodInfo for this method.""" relative_path = self.__names.NormalizeRelativePath( ''.join((self.__client_info.base_path, method_description['path']))) method_id = method_description['id'] ordered_params = [] for param_name in method_description.get('parameterOrder', []): param_info = method_description['parameters'][param_name] if param_info.get('required', False): ordered_params.append(param_name) method_info = base_api.ApiMethodInfo( relative_path=relative_path, method_id=method_id, http_method=method_description['httpMethod'], description=util.CleanDescription( method_description.get('description', '')), query_params=[], path_params=[], ordered_params=ordered_params, request_type_name=self.__names.ClassName(request), response_type_name=self.__names.ClassName(response), request_field=request_field, ) flat_path = method_description.get('flatPath', None) if flat_path is not None: flat_path = self.__names.NormalizeRelativePath( self.__client_info.base_path + flat_path) if flat_path != relative_path: method_info.flat_path = flat_path if method_description.get('supportsMediaUpload', False): method_info.upload_config = self.__ComputeUploadConfig( method_description.get('mediaUpload'), method_id) method_info.supports_download = method_description.get( 'supportsMediaDownload', False) self.__all_scopes.update(method_description.get('scopes', ())) for param, desc in method_description.get('parameters', {}).items(): param = self.__names.CleanName(param) location = desc['location'] if location == 'query': method_info.query_params.append(param) elif location == 'path': method_info.path_params.append(param) else: raise ValueError( 'Unknown parameter location %s for parameter %s' % ( location, param)) method_info.path_params.sort() method_info.query_params.sort() return method_info
def __AddAdditionalProperties(self, message, schema, properties): """Add an additionalProperties field to message.""" additional_properties_info = schema['additionalProperties'] entries_type_name = self.__AddAdditionalPropertyType( message.name, additional_properties_info) description = util.CleanDescription( additional_properties_info.get('description')) if description is None: description = 'Additional properties of type %s' % message.name attrs = { 'items': { '$ref': entries_type_name, }, 'description': description, 'type': 'array', } field_name = 'additionalProperties' message.fields.append(self.__FieldDescriptorFromProperties( field_name, len(properties) + 1, attrs)) self.__AddImport('from %s import encoding' % self.__base_files_package) message.decorators.append( 'encoding.MapUnrecognizedFields(%r)' % field_name)
def __init__(self, discovery_doc, client_info, names, root_package, outdir, base_package, protorpc_package, generate_cli=False, init_wildcards_file=True, use_proto2=False, unelidable_request_methods=None, apitools_version=''): self.__discovery_doc = discovery_doc self.__client_info = client_info self.__outdir = outdir self.__use_proto2 = use_proto2 self.__description = util.CleanDescription( self.__discovery_doc.get('description', '')) self.__package = self.__client_info.package self.__version = self.__client_info.version self.__revision = discovery_doc.get('revision', '1') self.__generate_cli = generate_cli self.__init_wildcards_file = init_wildcards_file self.__root_package = root_package self.__base_files_package = base_package self.__protorpc_package = protorpc_package self.__names = names # Order is important here: we need the schemas before we can # define the services. self.__message_registry = message_registry.MessageRegistry( self.__client_info, self.__names, self.__description, self.__root_package, self.__base_files_package, self.__protorpc_package) schemas = self.__discovery_doc.get('schemas', {}) for schema_name, schema in schemas.items(): self.__message_registry.AddDescriptorFromSchema( schema_name, schema) # We need to add one more message type for the global parameters. standard_query_schema = _StandardQueryParametersSchema( self.__discovery_doc) self.__message_registry.AddDescriptorFromSchema( standard_query_schema['id'], standard_query_schema) # Now that we know all the messages, we need to correct some # fields from MessageFields to EnumFields. self.__message_registry.FixupMessageFields() self.__command_registry = command_registry.CommandRegistry( self.__package, self.__version, self.__client_info, self.__message_registry, self.__root_package, self.__base_files_package, self.__protorpc_package, self.__names) self.__command_registry.AddGlobalParameters( self.__message_registry.LookupDescriptorOrDie( 'StandardQueryParameters')) self.__services_registry = service_registry.ServiceRegistry( self.__client_info, self.__message_registry, self.__command_registry, self.__names, self.__root_package, self.__base_files_package, unelidable_request_methods or []) services = self.__discovery_doc.get('resources', {}) for service_name, methods in sorted(services.items()): self.__services_registry.AddServiceFromResource( service_name, methods) # We might also have top-level methods. api_methods = self.__discovery_doc.get('methods', []) if api_methods: self.__services_registry.AddServiceFromResource( 'api', {'methods': api_methods}) # pylint: disable=protected-access self.__client_info = self.__client_info._replace( scopes=self.__services_registry.scopes) # The apitools version that will be used in prerequisites for the # generated packages. self.__apitools_version = (apitools_version if apitools_version else _ApitoolsVersion())
def __init__(self, discovery_doc, client_info, names, root_package, outdir, base_package, generate_cli=False, use_proto2=False, unelidable_request_methods=None): self.__discovery_doc = discovery_doc self.__client_info = client_info self.__outdir = outdir self.__use_proto2 = use_proto2 self.__description = util.CleanDescription( self.__discovery_doc.get('description', '')) self.__package = self.__client_info.package self.__version = self.__client_info.version self.__generate_cli = generate_cli self.__root_package = root_package self.__base_files_package = base_package self.__base_files_target = ( '//cloud/bigscience/apitools/base/py:apitools_base') self.__names = names self.__base_url, self.__base_path = _ComputePaths( self.__package, self.__client_info.url_version, self.__discovery_doc) # Order is important here: we need the schemas before we can # define the services. self.__message_registry = message_registry.MessageRegistry( self.__client_info, self.__names, self.__description, self.__root_package, self.__base_files_package) schemas = self.__discovery_doc.get('schemas', {}) for schema_name, schema in schemas.items(): self.__message_registry.AddDescriptorFromSchema( schema_name, schema) # We need to add one more message type for the global parameters. standard_query_schema = _StandardQueryParametersSchema( self.__discovery_doc) self.__message_registry.AddDescriptorFromSchema( standard_query_schema['id'], standard_query_schema) # Now that we know all the messages, we need to correct some # fields from MessageFields to EnumFields. self.__message_registry.FixupMessageFields() self.__command_registry = command_registry.CommandRegistry( self.__package, self.__version, self.__client_info, self.__message_registry, self.__root_package, self.__base_files_package, self.__base_url, self.__names) self.__command_registry.AddGlobalParameters( self.__message_registry.LookupDescriptorOrDie( 'StandardQueryParameters')) self.__services_registry = service_registry.ServiceRegistry( self.__client_info, self.__message_registry, self.__command_registry, self.__base_url, self.__base_path, self.__names, self.__root_package, self.__base_files_package, unelidable_request_methods or []) services = self.__discovery_doc.get('resources', {}) for service_name, methods in sorted(services.items()): self.__services_registry.AddServiceFromResource( service_name, methods) # We might also have top-level methods. api_methods = self.__discovery_doc.get('methods', []) if api_methods: self.__services_registry.AddServiceFromResource( 'api', {'methods': api_methods}) self.__client_info = self.__client_info._replace( scopes=self.__services_registry.scopes)