Пример #1
0
def test_proto_oneof():
    # 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='Bar',
                            fields=(
                                make_field_pb2(
                                    name='imported_message',
                                    number=1,
                                    type_name='.google.dep.ImportedMessage',
                                    oneof_index=0),
                                make_field_pb2(name='primitive',
                                               number=2,
                                               type=1,
                                               oneof_index=0),
                            ),
                            oneof_decl=(make_oneof_pb2(
                                name="value_type"), )))))

    # Create an API with those protos.
    api_schema = api.API.build(fd, package='google.example.v1')
    proto = api_schema.protos['foo.proto']
    assert proto.names == {'imported_message', 'Bar', 'primitive', 'Foo'}
    oneofs = proto.messages["google.example.v1.Bar"].oneofs
    assert len(oneofs) == 1
    assert "value_type" in oneofs.keys()
Пример #2
0
def test_proto_names():
    # 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='Bar', 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='Baz', fields=(
                    make_field_pb2(name='foo', number=1,
                                   type_name='.google.example.v1.Foo'),
                )),
            ),
        ),
    )

    # Create an API with those protos.
    api_schema = api.API.build(fd, package='google.example.v1')
    proto = api_schema.protos['foo.proto']
    assert proto.names == {'Foo', 'Bar', 'Baz', 'foo', 'imported_message',
                           'primitive'}
    assert proto.disambiguate('enum') == 'enum'
    assert proto.disambiguate('foo') == '_foo'
Пример #3
0
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'), )
Пример #4
0
def test_out_of_order_enums():
    # Enums can be referenced as field types before they
    # are defined in the proto file.
    # This happens when they're a nested type within a message.
    messages = (make_message_pb2(
        name='Squid',
        fields=(make_field_pb2(
            name='base_color',
            type_name='google.mollusca.Chromatophore.Color',
            number=1,
        ), ),
    ),
                make_message_pb2(
                    name='Chromatophore',
                    enum_type=(descriptor_pb2.EnumDescriptorProto(name='Color',
                                                                  value=()), ),
                ))
    fd = (make_file_pb2(
        name='squid.proto',
        package='google.mollusca',
        messages=messages,
        services=(descriptor_pb2.ServiceDescriptorProto(
            name='SquidService', ), ),
    ), )
    api_schema = api.API.build(fd, package='google.mollusca')
    field_type = (
        api_schema.messages['google.mollusca.Squid'].fields['base_color'].type)
    enum_type = api_schema.enums['google.mollusca.Chromatophore.Color']
    assert field_type == enum_type
Пример #5
0
def test_messages():
    L = descriptor_pb2.SourceCodeInfo.Location

    message_pb = make_message_pb2(name='Foo',
                                  fields=(make_field_pb2(name='bar',
                                                         type=3,
                                                         number=1), ))
    locations = (
        L(path=(4, 0), leading_comments='This is the Foo message.'),
        L(path=(4, 0, 2, 0), leading_comments='This is the bar field.'),
    )
    fdp = make_file_pb2(
        messages=(message_pb, ),
        locations=locations,
        package='google.example.v2',
    )

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

    # Get the message.
    assert len(proto.messages) == 1
    message = proto.messages['google.example.v2.Foo']
    assert isinstance(message, wrappers.MessageType)
    assert message.meta.doc == 'This is the Foo message.'
    assert len(message.fields) == 1
    assert message.fields['bar'].meta.doc == 'This is the bar field.'
Пример #6
0
def test_proto_names_import_collision():
    # Put together a couple of minimal protos.
    fd = (
        make_file_pb2(
            name='a/b/c/spam.proto',
            package='a.b.c',
            messages=(make_message_pb2(name='ImportedMessage', fields=()), ),
        ),
        make_file_pb2(
            name='x/y/z/spam.proto',
            package='x.y.z',
            messages=(make_message_pb2(name='OtherMessage', fields=()), ),
        ),
        make_file_pb2(
            name='foo.proto',
            package='google.example.v1',
            messages=(
                make_message_pb2(name='Foo', fields=()),
                make_message_pb2(
                    name='Bar',
                    fields=(
                        make_field_pb2(name='imported_message',
                                       number=1,
                                       type_name='.a.b.c.ImportedMessage'),
                        make_field_pb2(name='other_message',
                                       number=2,
                                       type_name='.x.y.z.OtherMessage'),
                        make_field_pb2(name='primitive', number=3, type=1),
                    )),
                make_message_pb2(name='Baz',
                                 fields=(make_field_pb2(
                                     name='foo',
                                     number=1,
                                     type_name='.google.example.v1.Foo'), )),
            ),
        ),
    )

    # Create an API with those protos.
    api_schema = api.API.build(fd, package='google.example.v1')
    proto = api_schema.protos['foo.proto']
    assert proto.names == {
        'Foo', 'Bar', 'Baz', 'foo', 'imported_message', 'other_message',
        'primitive', 'spam'
    }
Пример #7
0
def test_undefined_type():
    fd = (make_file_pb2(
        name='mollusc.proto',
        package='google.mollusca',
        messages=(make_message_pb2(name='Mollusc',
                                   fields=(make_field_pb2(
                                       name='class',
                                       type_name='google.mollusca.Class',
                                       number=1,
                                   ), )), ),
    ), )
    with pytest.raises(TypeError):
        api.API.build(fd, package='google.mollusca')
Пример #8
0
def test_not_target_file():
    """Establish that services are not ignored for untargeted protos."""
    message_pb = make_message_pb2(
        name='Foo', fields=(make_field_pb2(name='bar', type=3, number=1),)
    )
    service_pb = descriptor_pb2.ServiceDescriptorProto()
    fdp = make_file_pb2(messages=(message_pb,), services=(service_pb,))

    # Actually make the proto object.
    proto = api.Proto.build(fdp, file_to_generate=False, naming=make_naming())

    # The proto object should have the message, but no service.
    assert len(proto.messages) == 1
    assert len(proto.services) == 0
Пример #9
0
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
Пример #10
0
def test_messages_recursive():
    # Test that if a message is used inside itself, that things will still
    # work.
    message_pbs = (make_message_pb2(
        name='Foo',
        fields=(make_field_pb2(name='foo',
                               number=1,
                               type_name='.google.example.v3.Foo'), ),
    ), )
    fdp = make_file_pb2(
        messages=message_pbs,
        package='google.example.v3',
    )

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

    # Get the message.
    assert len(proto.messages) == 1
    Foo = proto.messages['google.example.v3.Foo']
    assert Foo.fields['foo'].message == proto.messages['google.example.v3.Foo']
Пример #11
0
def test_messages_reverse_declaration_order():
    # Test that if a message is used as a field higher in the same file,
    # that things still work.
    message_pbs = (
        make_message_pb2(name='Foo', fields=(
            make_field_pb2(name='bar', number=1,
                           type_name='.google.example.v3.Bar'),
        ),
        ),
        make_message_pb2(name='Bar'),
    )
    fdp = make_file_pb2(
        messages=message_pbs,
        package='google.example.v3',
    )

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

    # Get the message.
    assert len(proto.messages) == 2
    Foo = proto.messages['google.example.v3.Foo']
    assert Foo.fields['bar'].message == proto.messages['google.example.v3.Bar']
Пример #12
0
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,
    }
Пример #13
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='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
Пример #14
0
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
Пример #15
0
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