def test_lro(): # Set up a prior proto that mimics google/protobuf/empty.proto lro_proto = api.Proto.build(make_file_pb2( name='operations.proto', package='google.longrunning', messages=(make_message_pb2(name='Operation'), ), ), file_to_generate=False, naming=make_naming()) # Set up a method with LRO annotations. method_pb2 = descriptor_pb2.MethodDescriptorProto( name='AsyncDoThing', input_type='google.example.v3.AsyncDoThingRequest', output_type='google.longrunning.Operation', ) method_pb2.options.Extensions[operations_pb2.operation_info].MergeFrom( operations_pb2.OperationInfo( response_type='google.example.v3.AsyncDoThingResponse', metadata_type='google.example.v3.AsyncDoThingMetadata', ), ) # Set up the service with an RPC. service_pb = descriptor_pb2.ServiceDescriptorProto( name='LongRunningService', method=(method_pb2, ), ) # Set up the messages, including the annotated ones. messages = ( make_message_pb2(name='AsyncDoThingRequest', fields=()), make_message_pb2(name='AsyncDoThingResponse', fields=()), make_message_pb2(name='AsyncDoThingMetadata', fields=()), ) # Finally, set up the file that encompasses these. fdp = make_file_pb2( package='google.example.v3', messages=messages, services=(service_pb, ), ) # Make the proto object. proto = api.Proto.build(fdp, file_to_generate=True, prior_protos={ 'google/longrunning/operations.proto': lro_proto, }, naming=make_naming()) # Establish that our data looks correct. assert len(proto.services) == 1 assert len(proto.messages) == 3 assert len(lro_proto.messages) == 1
def test_cross_file_lro(): # Protobuf annotations for longrunning operations use strings to name types. # As far as the protobuf compiler is concerned they don't reference the # _types_ at all, so the corresponding proto file that owns the types # does not need to be imported. # This creates a potential issue when building rich structures around # LRO returning methods. This test is intended to verify that the issue # is handled correctly. # Set up a prior proto that mimics google/protobuf/empty.proto lro_proto = api.Proto.build(make_file_pb2( name='operations.proto', package='google.longrunning', messages=(make_message_pb2(name='Operation'), ), ), file_to_generate=False, naming=make_naming()) # Set up a method with LRO annotations. method_pb2 = descriptor_pb2.MethodDescriptorProto( name='AsyncDoThing', input_type='google.example.v3.AsyncDoThingRequest', output_type='google.longrunning.Operation', ) method_pb2.options.Extensions[operations_pb2.operation_info].MergeFrom( operations_pb2.OperationInfo( response_type='google.example.v3.AsyncDoThingResponse', metadata_type='google.example.v3.AsyncDoThingMetadata', ), ) # Set up the service with an RPC. service_file = make_file_pb2( name='service_file.proto', package='google.example.v3', messages=(make_message_pb2(name='AsyncDoThingRequest', fields=()), ), services=(descriptor_pb2.ServiceDescriptorProto( name='LongRunningService', method=(method_pb2, ), ), )) # Set up the messages, including the annotated ones. # This file is distinct and is not explicitly imported # into the file that defines the service. messages_file = make_file_pb2( name='messages_file.proto', package='google.example.v3', messages=( make_message_pb2(name='AsyncDoThingResponse', fields=()), make_message_pb2(name='AsyncDoThingMetadata', fields=()), ), ) api_schema = api.API.build( file_descriptors=( service_file, messages_file, ), package='google.example.v3', prior_protos={ 'google/longrunning/operations.proto': lro_proto, }, ) method = (api_schema.all_protos['service_file.proto'].services[ 'google.example.v3.LongRunningService'].methods['AsyncDoThing']) assert method.lro assert method.lro.response_type.name == 'AsyncDoThingResponse' assert method.lro.metadata_type.name == 'AsyncDoThingMetadata'
def test_proto_names_import_collision_flattening(): lro_proto = api.Proto.build(make_file_pb2( name='operations.proto', package='google.longrunning', messages=(make_message_pb2(name='Operation'), ), ), file_to_generate=False, naming=make_naming()) fd = ( make_file_pb2( name='mollusc.proto', package='google.animalia.mollusca', messages=( make_message_pb2(name='Mollusc', ), make_message_pb2(name='MolluscResponse', ), make_message_pb2(name='MolluscMetadata', ), ), ), make_file_pb2( name='squid.proto', package='google.animalia.mollusca', messages=( make_message_pb2( name='IdentifySquidRequest', fields=(make_field_pb2( name='mollusc', number=1, type_name='.google.animalia.mollusca.Mollusc'), ), ), make_message_pb2( name='IdentifySquidResponse', fields=(), ), ), services=(descriptor_pb2.ServiceDescriptorProto( name='SquidIdentificationService', method=(descriptor_pb2.MethodDescriptorProto( name='IdentifyMollusc', input_type='google.animalia.mollusca.IdentifySquidRequest', output_type='google.longrunning.Operation', ), ), ), ), ), ) method_options = fd[1].service[0].method[0].options # Notice that a signature field collides with the name of an imported module method_options.Extensions[client_pb2.method_signature].append('mollusc') method_options.Extensions[operations_pb2.operation_info].MergeFrom( operations_pb2.OperationInfo( response_type='google.animalia.mollusca.MolluscResponse', metadata_type='google.animalia.mollusca.MolluscMetadata', )) api_schema = api.API.build(fd, package='google.animalia.mollusca', prior_protos={ 'google/longrunning/operations.proto': lro_proto, }) actual_imports = { ref_type.ident.python_import for service in api_schema.services.values() for method in service.methods.values() for ref_type in method.ref_types } expected_imports = { imp.Import( package=('google', 'animalia', 'mollusca', 'types'), module='mollusc', alias='gam_mollusc', ), imp.Import( package=('google', 'animalia', 'mollusca', 'types'), module='squid', ), imp.Import( package=('google', 'api_core'), module='operation', ), imp.Import( package=('google', 'api_core'), module='operation_async', ), } assert expected_imports == actual_imports method = (api_schema. services['google.animalia.mollusca.SquidIdentificationService']. methods['IdentifyMollusc']) actual_response_import = method.lro.response_type.ident.python_import expected_response_import = imp.Import( package=('google', 'animalia', 'mollusca', 'types'), module='mollusc', alias='gam_mollusc', ) assert actual_response_import == expected_response_import
def make_method( name: str, input_message: wrappers.Message = None, output_message: wrappers.Message = None, client_streaming: bool = False, server_streaming: bool = False, signatures: Sequence[str] = (), lro_response_type: str = None, lro_metadata_type: str = None, http_method: str = "get", http_uri: str = None, http_body: str = None, messages_map: Dict[str, wrappers.Message] = {}, proto_file_name: str = "foo", locations: Sequence[desc.SourceCodeInfo.Location] = [], path: Tuple[int] = (), **kwargs, ) -> wrappers.Method: # Use default input and output messages if they are not provided. input_message = input_message or make_message("MethodInput") output_message = output_message or make_message("MethodOutput") method_pb = make_method_pb2( name=name, input_type=input_message.full_name, output_type=output_message.full_name, client_streaming=client_streaming, server_streaming=server_streaming, **kwargs, ) # If there are signatures, include them. for sig in signatures: ext_key = client_pb2.method_signature method_pb.options.Extensions[ext_key].append(sig) # If there are LRO annotations, include them. if lro_response_type and lro_metadata_type: method_pb.options.Extensions[operations_pb2.operation_info].MergeFrom( operations_pb2.OperationInfo( response_type=lro_response_type, metadata_type=lro_metadata_type, )) # If there are HTTP annotations, include them. if http_method and http_uri and http_body: http_annotation = method_pb.options.Extensions[annotations_pb2.http] http_annotation.get = http_uri http_annotation.body = http_body source_code_locations = { tuple(location.path): location for location in locations } # Instantiate the wrapper class. return wrappers.Method( method_pb=method_pb, messages_map=messages_map, proto_file_name=proto_file_name, source_code_locations=source_code_locations, path=path, )