示例#1
0
def get_method(
    name: str,
    in_type: str,
    out_type: str,
    lro_payload_type: str = '',
    lro_metadata_type: str = '',
    http_rule: http_pb2.HttpRule = None,
) -> wrappers.Method:
    input_ = get_message(in_type)
    output = get_message(out_type)
    lro_payload = get_message(lro_payload_type) if lro_payload_type else None
    lro_meta = get_message(lro_metadata_type) if lro_metadata_type else None

    # Define a method descriptor. Set the field headers if appropriate.
    method_pb = descriptor_pb2.MethodDescriptorProto(
        name=name,
        input_type=input_.proto_path,
        output_type=output.proto_path,
    )
    if http_rule:
        ext_key = annotations_pb2.http
        method_pb.options.Extensions[ext_key].MergeFrom(http_rule)

    return wrappers.Method(
        method_pb=method_pb,
        input=input_,
        lro_metadata=lro_meta,
        lro_payload=lro_payload,
        output=output,
    )
示例#2
0
def get_method() -> wrappers.Method:
    # Create the address where this method lives, and the types live,
    # and make them distinct.
    method_addr = metadata.Address(package=['foo', 'bar'], module='baz')
    types_addr = metadata.Address(package=['foo', 'bar'], module='bacon')

    # Create the method pb2 and set an overload in it.
    method_pb = descriptor_pb2.MethodDescriptorProto(
        name='DoTheThings',
        input_type='foo.bar.Input',
        output_type='foo.bar.Output',
    )

    # Instantiate the wrapper class.
    return wrappers.Method(
        method_pb=method_pb,
        input=wrappers.MessageType(
            fields=[],
            message_pb=descriptor_pb2.DescriptorProto(name='Input'),
            meta=metadata.Metadata(address=types_addr),
        ),
        output=wrappers.MessageType(
            fields=[],
            message_pb=descriptor_pb2.DescriptorProto(name='Output'),
            meta=metadata.Metadata(address=types_addr),
        ),
        meta=metadata.Metadata(address=method_addr),
    )
def get_method(name: str,
        in_type: str,
        out_type: str,
        lro_response_type: str = '',
        lro_metadata_type: str = '', *,
        in_fields: typing.Tuple[descriptor_pb2.FieldDescriptorProto] = (),
        http_rule: http_pb2.HttpRule = None,
        method_signature: signature_pb2.MethodSignature = None,
        ) -> wrappers.Method:
    input_ = get_message(in_type, fields=in_fields)
    output = get_message(out_type)

    # Define a method descriptor. Set the field headers if appropriate.
    method_pb = descriptor_pb2.MethodDescriptorProto(
        name=name,
        input_type=input_.proto_path,
        output_type=output.proto_path,
    )
    if lro_response_type:
        output = wrappers.OperationType(
            lro_response=get_message(lro_response_type),
            lro_metadata=get_message(lro_metadata_type),
        )
    if http_rule:
        ext_key = annotations_pb2.http
        method_pb.options.Extensions[ext_key].MergeFrom(http_rule)
    if method_signature:
        ext_key = annotations_pb2.method_signature
        method_pb.options.Extensions[ext_key].MergeFrom(method_signature)

    return wrappers.Method(
        method_pb=method_pb,
        input=input_,
        output=output,
    )
def make_method(
        name: str, input_message: wrappers.MessageType = None,
        output_message: wrappers.MessageType = None,
        package: str = 'foo.bar.v1', module: str = 'baz',
        **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')

    # Create the method pb2.
    method_pb = descriptor_pb2.MethodDescriptorProto(
        name=name,
        input_type=str(input_message.meta.address),
        output_type=str(output_message.meta.address),
    )

    # Instantiate the wrapper class.
    return wrappers.Method(
        method_pb=method_pb,
        input=input_message,
        output=output_message,
        meta=metadata.Metadata(address=metadata.Address(
            package=package,
            module=module,
        )),
    )
def test_python_modules_nested():
    fd = (
        make_file_pb2(
            name='dep.proto',
            package='google.dep',
            messages=(make_message_pb2(name='ImportedMessage', fields=()), ),
        ),
        make_file_pb2(
            name='common.proto',
            package='google.example.v1.common',
            messages=(make_message_pb2(name='Bar'), ),
        ),
        make_file_pb2(
            name='foo.proto',
            package='google.example.v1',
            messages=(
                make_message_pb2(
                    name='GetFooRequest',
                    fields=(
                        make_field_pb2(name='primitive', number=2, type=1),
                        make_field_pb2(
                            name='foo',
                            number=3,
                            type=1,
                            type_name='.google.example.v1.GetFooRequest.Foo',
                        ),
                    ),
                    nested_type=(make_message_pb2(
                        name='Foo',
                        fields=(make_field_pb2(
                            name='imported_message',
                            number=1,
                            type_name='.google.dep.ImportedMessage'), ),
                    ), ),
                ),
                make_message_pb2(
                    name='GetFooResponse',
                    fields=(make_field_pb2(
                        name='foo',
                        number=1,
                        type_name='.google.example.v1.GetFooRequest.Foo',
                    ), ),
                ),
            ),
            services=(descriptor_pb2.ServiceDescriptorProto(
                name='FooService',
                method=(descriptor_pb2.MethodDescriptorProto(
                    name='GetFoo',
                    input_type='google.example.v1.GetFooRequest',
                    output_type='google.example.v1.GetFooResponse',
                ), ),
            ), ),
        ),
    )

    api_schema = api.API.build(fd, package='google.example.v1')

    assert api_schema.protos['foo.proto'].python_modules == (imp.Import(
        package=('google', 'dep'), module='dep_pb2'), )
def make_method(
        name: str,
        input_message: wrappers.MessageType = None,
        output_message: wrappers.MessageType = None,
        package: typing.Union[typing.Tuple[str], str] = 'foo.bar.v1',
        module: str = 'baz',
        http_rule: http_pb2.HttpRule = None,
        signatures: typing.Sequence[str] = (),
        is_deprecated: bool = False,
        routing_rule: routing_pb2.RoutingRule = None,
        **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')

    # Create the method pb2.
    method_pb = desc.MethodDescriptorProto(
        name=name,
        input_type=str(input_message.meta.address),
        output_type=str(output_message.meta.address),
        **kwargs
    )

    if routing_rule:
        ext_key = routing_pb2.routing
        method_pb.options.Extensions[ext_key].MergeFrom(routing_rule)

    # If there is an HTTP rule, process it.
    if http_rule:
        ext_key = annotations_pb2.http
        method_pb.options.Extensions[ext_key].MergeFrom(http_rule)

    # 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 isinstance(package, str):
        package = tuple(package.split('.'))

    if is_deprecated:
        method_pb.options.deprecated = True

    # Instantiate the wrapper class.
    return wrappers.Method(
        method_pb=method_pb,
        input=input_message,
        output=output_message,
        meta=metadata.Metadata(address=metadata.Address(
            name=name,
            package=package,
            module=module,
            parent=(f'{name}Service',),
        )),
    )
def test_prior_protos():
    L = descriptor_pb2.SourceCodeInfo.Location

    # Set up a prior proto that mimics google/protobuf/empty.proto
    empty_proto = api.Proto.build(make_file_pb2(
        name='empty.proto',
        package='google.protobuf',
        messages=(make_message_pb2(name='Empty'), ),
    ),
                                  file_to_generate=False,
                                  naming=make_naming())

    # Set up the service with an RPC.
    service_pb = descriptor_pb2.ServiceDescriptorProto(
        name='PingService',
        method=(descriptor_pb2.MethodDescriptorProto(
            name='Ping',
            input_type='google.protobuf.Empty',
            output_type='google.protobuf.Empty',
        ), ),
    )

    # Fake-document our fake stuff.
    locations = (
        L(path=(6, 0), leading_comments='This is the PingService service.'),
        L(path=(6, 0, 2, 0), leading_comments='This is the Ping method.'),
    )

    # Finally, set up the file that encompasses these.
    fdp = make_file_pb2(
        package='google.example.v1',
        services=(service_pb, ),
        locations=locations,
    )

    # Make the proto object.
    proto = api.Proto.build(fdp,
                            file_to_generate=True,
                            prior_protos={
                                'google/protobuf/empty.proto': empty_proto,
                            },
                            naming=make_naming())

    # Establish that our data looks correct.
    assert len(proto.services) == 1
    assert len(empty_proto.messages) == 1
    assert len(proto.messages) == 0
    service = proto.services['google.example.v1.PingService']
    assert service.meta.doc == 'This is the PingService service.'
    assert len(service.methods) == 1
    method = service.methods['Ping']
    assert isinstance(method.input, wrappers.MessageType)
    assert isinstance(method.output, wrappers.MessageType)
    assert method.input.name == 'Empty'
    assert method.output.name == 'Empty'
    assert method.meta.doc == 'This is the Ping method.'
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
示例#9
0
def test_services():
    L = descriptor_pb2.SourceCodeInfo.Location

    # Set up messages for our RPC.
    request_message_pb = make_message_pb2(name='GetFooRequest',
                                          fields=(make_field_pb2(name='name',
                                                                 type=9,
                                                                 number=1), ))
    response_message_pb = make_message_pb2(name='GetFooResponse', fields=())

    # Set up the service with an RPC.
    service_pb = descriptor_pb2.ServiceDescriptorProto(
        name='FooService',
        method=(descriptor_pb2.MethodDescriptorProto(
            name='GetFoo',
            input_type='google.example.v2.GetFooRequest',
            output_type='google.example.v2.GetFooResponse',
        ), ),
    )

    # Fake-document our fake stuff.
    locations = (
        L(path=(6, 0), leading_comments='This is the FooService service.'),
        L(path=(6, 0, 2, 0), leading_comments='This is the GetFoo method.'),
        L(path=(4, 0), leading_comments='This is the GetFooRequest message.'),
        L(path=(4, 1), leading_comments='This is the GetFooResponse message.'),
    )

    # Finally, set up the file that encompasses these.
    fdp = make_file_pb2(
        package='google.example.v2',
        messages=(request_message_pb, response_message_pb),
        services=(service_pb, ),
        locations=locations,
    )

    # Make the proto object.
    proto = api.Proto.build(fdp, file_to_generate=True)

    # Establish that our data looks correct.
    assert len(proto.services) == 1
    assert len(proto.messages) == 2
    service = proto.services['google.example.v2.FooService']
    assert service.meta.doc == 'This is the FooService service.'
    assert len(service.methods) == 1
    method = service.methods['GetFoo']
    assert method.meta.doc == 'This is the GetFoo method.'
    assert isinstance(method.input, wrappers.MessageType)
    assert isinstance(method.output, wrappers.MessageType)
    assert method.input.name == 'GetFooRequest'
    assert method.input.meta.doc == 'This is the GetFooRequest message.'
    assert method.output.name == 'GetFooResponse'
    assert method.output.meta.doc == 'This is the GetFooResponse message.'
示例#10
0
def test_api_build():
    # Put together a couple of minimal protos.
    fd = (
        make_file_pb2(
            name='dep.proto',
            package='google.dep',
            messages=(make_message_pb2(name='ImportedMessage', fields=()), ),
        ),
        make_file_pb2(
            name='foo.proto',
            package='google.example.v1',
            messages=(
                make_message_pb2(name='Foo', fields=()),
                make_message_pb2(
                    name='GetFooRequest',
                    fields=(make_field_pb2(
                        name='imported_message',
                        number=1,
                        type_name='google.dep.ImportedMessasge'), )),
                make_message_pb2(name='GetFooResponse',
                                 fields=(make_field_pb2(
                                     name='foo',
                                     number=1,
                                     type_name='google.example.v1.Foo'), )),
            ),
            services=(descriptor_pb2.ServiceDescriptorProto(
                name='FooService',
                method=(descriptor_pb2.MethodDescriptorProto(
                    name='GetFoo',
                    input_type='google.example.v1.GetFooRequest',
                    output_type='google.example.v1.GetFooResponse',
                ), ),
            ), ),
        ),
    )

    # Create an API with those protos.
    api_schema = api.API.build(fd, package='google.example.v1')

    # Establish that the API has the data expected.
    assert isinstance(api_schema, api.API)
    assert len(api_schema.protos) == 2
    assert 'google.dep.ImportedMessage' in api_schema.messages
    assert 'google.example.v1.Foo' in api_schema.messages
    assert 'google.example.v1.GetFooRequest' in api_schema.messages
    assert 'google.example.v1.GetFooResponse' in api_schema.messages
    assert 'google.example.v1.FooService' in api_schema.services
    assert len(api_schema.enums) == 0
def make_method_pb2(
    name: str,
    input_type: str,
    output_type: str,
    client_streaming: bool = False,
    server_streaming: bool = False,
    **kwargs,
) -> desc.MethodDescriptorProto:
    # Create the method pb2.
    return desc.MethodDescriptorProto(
        name=name,
        input_type=input_type,
        output_type=output_type,
        client_streaming=client_streaming,
        server_streaming=server_streaming,
        **kwargs,
    )
示例#12
0
 def _get_methods_from_service(self, service_pb) -> Dict[str, MethodDescriptorProto]:
     services = service_pb.DESCRIPTOR.services_by_name
     methods = {}
     methods_to_generate = {}
     for service_name in services:
         service: ServiceDescriptor = services[service_name]
         for method in service.methods:
             fqn = "{}.{}.{}".format(
                 service_pb.DESCRIPTOR.package, service.name, method.name)
             methods[fqn] = method
     for rule in self.service_yaml_config.http.rules:
         if rule.selector in methods:
             m = methods[rule.selector]
             x = descriptor_pb2.MethodDescriptorProto()
             m.CopyToProto(x)
             x.options.Extensions[annotations_pb2.http].CopyFrom(rule)
             methods_to_generate[x.name] = x
     return methods_to_generate
def test_resources_referenced_but_not_typed(reference_attr="type"):
    fdp = make_file_pb2(
        name="nomenclature.proto",
        package="nomenclature.linneaen.v1",
        messages=(
            make_message_pb2(name="Species", ),
            make_message_pb2(
                name="CreateSpeciesRequest",
                fields=(make_field_pb2(name='species', number=1, type=9), ),
            ),
            make_message_pb2(name="CreateSpeciesResponse", ),
        ),
        services=(descriptor_pb2.ServiceDescriptorProto(
            name="SpeciesService",
            method=(descriptor_pb2.MethodDescriptorProto(
                name="CreateSpecies",
                input_type="nomenclature.linneaen.v1.CreateSpeciesRequest",
                output_type="nomenclature.linneaen.v1.CreateSpeciesResponse",
            ), ),
        ), ),
    )

    # Set up the resource
    species_resource_opts = fdp.message_type[0].options.Extensions[
        resource_pb2.resource]
    species_resource_opts.type = "nomenclature.linnaen.com/Species"
    species_resource_opts.pattern.append(
        "families/{family}/genera/{genus}/species/{species}")

    # Set up the reference
    name_resource_opts = fdp.message_type[1].field[0].options.Extensions[
        resource_pb2.resource_reference]
    if reference_attr == "type":
        name_resource_opts.type = species_resource_opts.type
    else:
        name_resource_opts.child_type = species_resource_opts.type

    api_schema = api.API.build([fdp], package="nomenclature.linneaen.v1")
    expected = {api_schema.messages["nomenclature.linneaen.v1.Species"]}
    actual = api_schema.services[
        "nomenclature.linneaen.v1.SpeciesService"].resource_messages

    assert actual == expected
示例#14
0
def make_method(name: str,
                input_message: wrappers.MessageType = None,
                output_message: wrappers.MessageType = None,
                package: str = 'foo.bar.v1',
                module: str = 'baz',
                http_rule: http_pb2.HttpRule = None,
                signatures: Sequence[str] = (),
                **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')

    # Create the method pb2.
    method_pb = descriptor_pb2.MethodDescriptorProto(
        name=name,
        input_type=str(input_message.meta.address),
        output_type=str(output_message.meta.address),
        **kwargs)

    # If there is an HTTP rule, process it.
    if http_rule:
        ext_key = annotations_pb2.http
        method_pb.options.Extensions[ext_key].MergeFrom(http_rule)

    # If there are signatures, include them.
    for sig in signatures:
        ext_key = client_pb2.method_signature
        method_pb.options.Extensions[ext_key].append(sig)

    # Instantiate the wrapper class.
    return wrappers.Method(
        method_pb=method_pb,
        input=input_message,
        output=output_message,
        meta=metadata.Metadata(address=metadata.Address(
            name=name,
            package=package,
            module=module,
            parent=(f'{name}Service', ),
        )),
    )
def test_lro_missing_annotation():
    # 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 an LRO but no annotation.
    method_pb2 = descriptor_pb2.MethodDescriptorProto(
        name='AsyncDoThing',
        input_type='google.example.v3.AsyncDoThingRequest',
        output_type='google.longrunning.Operation',
    )

    # 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=()), )

    # 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.
    with pytest.raises(TypeError):
        api.Proto.build(fdp,
                        file_to_generate=True,
                        prior_protos={
                            'google/longrunning/operations.proto': lro_proto,
                        },
                        naming=make_naming())
def test_api_build():
    # Put together a couple of minimal protos.
    fd = (
        make_file_pb2(
            name='dep.proto',
            package='google.dep',
            messages=(make_message_pb2(name='ImportedMessage', fields=()), ),
        ),
        make_file_pb2(
            name='common.proto',
            package='google.example.v1.common',
            messages=(make_message_pb2(name='Bar'), ),
        ),
        make_file_pb2(
            name='foo.proto',
            package='google.example.v1',
            messages=(
                make_message_pb2(name='Foo', fields=()),
                make_message_pb2(
                    name='GetFooRequest',
                    fields=(
                        make_field_pb2(
                            name='imported_message',
                            number=1,
                            type_name='.google.dep.ImportedMessage'),
                        make_field_pb2(name='primitive', number=2, type=1),
                    )),
                make_message_pb2(name='GetFooResponse',
                                 fields=(make_field_pb2(
                                     name='foo',
                                     number=1,
                                     type_name='.google.example.v1.Foo'), )),
            ),
            services=(descriptor_pb2.ServiceDescriptorProto(
                name='FooService',
                method=(descriptor_pb2.MethodDescriptorProto(
                    name='GetFoo',
                    input_type='google.example.v1.GetFooRequest',
                    output_type='google.example.v1.GetFooResponse',
                ), ),
            ), ),
        ),
    )

    # Create an API with those protos.
    api_schema = api.API.build(fd, package='google.example.v1')

    # Establish that the API has the data expected.
    assert isinstance(api_schema, api.API)
    assert len(api_schema.all_protos) == 3
    assert len(api_schema.protos) == 2
    assert 'google.dep.ImportedMessage' not in api_schema.messages
    assert 'google.example.v1.common.Bar' in api_schema.messages
    assert 'google.example.v1.Foo' in api_schema.messages
    assert 'google.example.v1.GetFooRequest' in api_schema.messages
    assert 'google.example.v1.GetFooResponse' in api_schema.messages
    assert 'google.example.v1.FooService' in api_schema.services
    assert len(api_schema.enums) == 0
    assert api_schema.protos['foo.proto'].python_modules == (imp.Import(
        package=('google', 'dep'), module='dep_pb2'), )

    assert api_schema.requires_package(('google', 'example', 'v1'))
    assert not api_schema.requires_package(('elgoog', 'example', 'v1'))

    # Establish that the subpackages work.
    assert 'common' in api_schema.subpackages
    sub = api_schema.subpackages['common']
    assert len(sub.protos) == 1
    assert 'google.example.v1.common.Bar' in sub.messages
    assert 'google.example.v1.Foo' not in sub.messages
def test_file_level_resources():
    fdp = make_file_pb2(
        name="nomenclature.proto",
        package="nomenclature.linneaen.v1",
        messages=(
            make_message_pb2(
                name="CreateSpeciesRequest",
                fields=(make_field_pb2(name='species', number=1, type=9), ),
            ),
            make_message_pb2(name="CreateSpeciesResponse", ),
        ),
        services=(descriptor_pb2.ServiceDescriptorProto(
            name="SpeciesService",
            method=(descriptor_pb2.MethodDescriptorProto(
                name="CreateSpecies",
                input_type="nomenclature.linneaen.v1.CreateSpeciesRequest",
                output_type="nomenclature.linneaen.v1.CreateSpeciesResponse",
            ), ),
        ), ),
    )
    res_pb2 = fdp.options.Extensions[resource_pb2.resource_definition]
    definitions = [
        ("nomenclature.linnaen.com/Species",
         "families/{family}/genera/{genus}/species/{species}"),
        ("nomenclature.linnaen.com/Phylum",
         "kingdoms/{kingdom}/phyla/{phylum}"),
    ]
    for type_, pattern in definitions:
        resource_definition = res_pb2.add()
        resource_definition.type = type_
        resource_definition.pattern.append(pattern)

    species_field = fdp.message_type[0].field[0]
    resource_reference = species_field.options.Extensions[
        resource_pb2.resource_reference]
    resource_reference.type = "nomenclature.linnaen.com/Species"

    api_schema = api.API.build([fdp], package='nomenclature.linneaen.v1')
    actual = api_schema.protos['nomenclature.proto'].resource_messages
    expected = collections.OrderedDict((
        ("nomenclature.linnaen.com/Species",
         wrappers.CommonResource(
             type_name="nomenclature.linnaen.com/Species",
             pattern="families/{family}/genera/{genus}/species/{species}").
         message_type),
        ("nomenclature.linnaen.com/Phylum",
         wrappers.CommonResource(
             type_name="nomenclature.linnaen.com/Phylum",
             pattern="kingdoms/{kingdom}/phyla/{phylum}").message_type),
    ))

    assert actual == expected

    # The proto file _owns_ the file level resources, but the service needs to
    # see them too because the client class owns all the helper methods.
    service = api_schema.services["nomenclature.linneaen.v1.SpeciesService"]
    actual = service.visible_resources
    assert actual == expected

    # The service doesn't own any method that owns a message that references
    # Phylum, so the service doesn't count it among its resource messages.
    expected.pop("nomenclature.linnaen.com/Phylum")
    expected = frozenset(expected.values())
    actual = service.resource_messages

    assert actual == expected
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 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_services():
    L = descriptor_pb2.SourceCodeInfo.Location

    # Make a silly helper method to not repeat some of the structure.
    def _n(method_name: str):
        return {
            'service': 'google.example.v2.FooService',
            'method': method_name,
        }

    # Set up retry information.
    opts = Options(
        retry={
            'methodConfig': [
                {
                    'name': [_n('TimeoutableGetFoo')],
                    'timeout': '30s'
                },
                {
                    'name': [_n('RetryableGetFoo')],
                    'retryPolicy': {
                        'maxAttempts': 3,
                        'initialBackoff': '%dn' % 1e6,
                        'maxBackoff': '60s',
                        'backoffMultiplier': 1.5,
                        'retryableStatusCodes': ['UNAVAILABLE', 'ABORTED'],
                    }
                },
            ]
        })

    # Set up messages for our RPC.
    request_message_pb = make_message_pb2(name='GetFooRequest',
                                          fields=(make_field_pb2(name='name',
                                                                 type=9,
                                                                 number=1), ))
    response_message_pb = make_message_pb2(name='GetFooResponse', fields=())

    # Set up the service with an RPC.
    service_pb = descriptor_pb2.ServiceDescriptorProto(
        name='FooService',
        method=(
            descriptor_pb2.MethodDescriptorProto(
                name='GetFoo',
                input_type='google.example.v2.GetFooRequest',
                output_type='google.example.v2.GetFooResponse',
            ),
            descriptor_pb2.MethodDescriptorProto(
                name='TimeoutableGetFoo',
                input_type='google.example.v2.GetFooRequest',
                output_type='google.example.v2.GetFooResponse',
            ),
            descriptor_pb2.MethodDescriptorProto(
                name='RetryableGetFoo',
                input_type='google.example.v2.GetFooRequest',
                output_type='google.example.v2.GetFooResponse',
            ),
        ),
    )

    # Fake-document our fake stuff.
    locations = (
        L(path=(6, 0), leading_comments='This is the FooService service.'),
        L(path=(6, 0, 2, 0), leading_comments='This is the GetFoo method.'),
        L(path=(4, 0), leading_comments='This is the GetFooRequest message.'),
        L(path=(4, 1), leading_comments='This is the GetFooResponse message.'),
    )

    # Finally, set up the file that encompasses these.
    fdp = make_file_pb2(
        name='test.proto',
        package='google.example.v2',
        messages=(request_message_pb, response_message_pb),
        services=(service_pb, ),
        locations=locations,
    )

    # Make the proto object.
    proto = api.API.build(
        [fdp],
        'google.example.v2',
        opts=opts,
    ).protos['test.proto']

    # Establish that our data looks correct.
    assert len(proto.services) == 1
    assert len(proto.messages) == 2
    service = proto.services['google.example.v2.FooService']
    assert service.meta.doc == 'This is the FooService service.'
    assert len(service.methods) == 3
    method = service.methods['GetFoo']
    assert method.meta.doc == 'This is the GetFoo method.'
    assert isinstance(method.input, wrappers.MessageType)
    assert isinstance(method.output, wrappers.MessageType)
    assert method.input.name == 'GetFooRequest'
    assert method.input.meta.doc == 'This is the GetFooRequest message.'
    assert method.output.name == 'GetFooResponse'
    assert method.output.meta.doc == 'This is the GetFooResponse message.'
    assert not method.timeout
    assert not method.retry

    # Establish that the retry information on a timeout-able method also
    # looks correct.
    timeout_method = service.methods['TimeoutableGetFoo']
    assert timeout_method.timeout == pytest.approx(30.0)
    assert not timeout_method.retry

    # Establish that the retry information on the retryable method also
    # looks correct.
    retry_method = service.methods['RetryableGetFoo']
    assert retry_method.timeout is None
    assert retry_method.retry.max_attempts == 3
    assert retry_method.retry.initial_backoff == pytest.approx(0.001)
    assert retry_method.retry.backoff_multiplier == pytest.approx(1.5)
    assert retry_method.retry.max_backoff == pytest.approx(60.0)
    assert retry_method.retry.retryable_exceptions == {
        exceptions.ServiceUnavailable,
        exceptions.Aborted,
    }