Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions gapic/schema/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ def __str__(self) -> str:
Because we import modules as a whole, rather than individual
members from modules, this is consistently `module.Name`.
"""
# Edge case: For google.protobuf.Empty, use None instead.
if self.proto == 'google.protobuf.Empty':
return 'None'

# Most (but not all) types are in a module.
if self.module:
# If collisions are registered and conflict with our module,
# use the module alias instead.
Expand Down
9 changes: 8 additions & 1 deletion gapic/schema/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ def grpc_stub_type(self) -> str:
def ref_types(self) -> Sequence[Union[MessageType, EnumType]]:
"""Return types referenced by this method."""
# Begin with the input (request) and output (response) messages.
answer = [self.input, self.output]
answer = [self.input]
if not self.void:
answer.append(self.output)

# If this method has flattening that is honored, add its
# composite types.
Expand Down Expand Up @@ -458,6 +460,11 @@ def signatures(self) -> 'MethodSignatures':
# Done; return a tuple of signatures.
return MethodSignatures(all=tuple(answer))

@property
def void(self) -> bool:
"""Return True if this method has no return value, False otherwise."""
return self.output.ident.proto == 'google.protobuf.Empty'

def with_context(self, *, collisions: Set[str]) -> 'Method':
"""Return a derivative of this method with the provided context.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ class {{ service.name }}:
timeout (float): The timeout for this request.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
{%- if not method.void %}

Returns:
{{ method.output.ident.sphinx }}:
{{ method.output.meta.doc|wrap(width=72, indent=16) }}
{%- endif %}
"""
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
Expand All @@ -97,8 +99,12 @@ class {{ service.name }}:
{%- endif %}

# Send the request.
response = rpc(request, retry=retry,
timeout=timeout, metadata=metadata)
{% if not method.void %}response = {% endif %}rpc(
request,
retry=retry,
timeout=timeout,
metadata=metadata,
)
{%- if method.output.lro_response is defined %}

# Wrap the response in an operation future
Expand All @@ -111,9 +117,11 @@ class {{ service.name }}:
{%- endif %}
)
{%- endif %}
{%- if not method.void %}

# Done; return the response.
return response
{%- endif %}

{% for signature in method.signatures.single_dispatch -%}
@{{ method.name|snake_case }}.register
Expand All @@ -138,10 +146,12 @@ class {{ service.name }}:
timeout (float): The timeout for this request.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent alont with the request as metadata.
{%- if not method.void %}

Returns:
{{ method.output.ident.sphinx }}:
{{ method.output.meta.doc|wrap(width=72, indent=16) }}
{%- endif %}
"""
return self.{{ method.name|snake_case }}(
{{ method.input.ident }}(
Expand All @@ -158,7 +168,8 @@ class {{ service.name }}:

@classmethod
def get_transport_class(cls,
label: str = None) -> Type[{{ service.name }}Transport]:
label: str = None,
) -> Type[{{ service.name }}Transport]:
"""Return an appropriate transport class.

Args:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):

Returns:
Callable[[~.{{ method.input.name }}],
~.{{ method.output.name }}]:
{{ method.output.meta.doc|rst(width=72, indent=16) }}
{% if method.void %}None{% else %}~.{{ method.output.name }}{% endif %}]:
A function that, when called, will call the underlying RPC
on the server.
"""
# Generate a "stub function" on-the-fly which will actually make
# the request.
Expand All @@ -121,8 +122,8 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
if '{{ method.name|snake_case }}' not in self._stubs:
self._stubs['{{ method.name|snake_case }}'] = self.grpc_channel.{{ method.grpc_stub_type }}(
'/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
request_serializer={{ method.input.ident }}.serialize,
response_deserializer={{ method.output.ident }}.deserialize,
request_serializer={{ method.input.ident }}.{% if method.input.ident.module.endswith('_pb2') %}SerializeToString{% else %}serialize{% endif %},
response_deserializer={{ method.output.ident }}.{% if method.input.ident.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %},
)
return self._stubs['{{ method.name|snake_case }}']
{%- endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,23 @@ class {{ service.name }}HttpTransport({{ service.name }}Transport):
The request object.
{{ method.input.meta.doc|rst(width=72, indent=16) }}
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent alont with the request as metadata.
sent along with the request as metadata.
{%- if not method.void %}

Returns:
~.{{ method.output.ident }}:
{{ method.output.meta.doc|rst(width=72, indent=16) }}
{%- endif %}
"""
# Serialize the input.
{%- if method.input.ident.module.endswith('_pb2') %}
data = request.SerializeToString()
{%- else %}
data = {{ method.input.ident }}.serialize(request)
{%- endif %}

# Send the request.
response = self._session.post(
{% if not method.void %}response = {% endif %}self._session.post(
'https://{host}/$rpc/{package}.{service}/{method}'.format(
host=self._host,
method='{{ method.name }}',
Expand All @@ -103,11 +109,13 @@ class {{ service.name }}HttpTransport({{ service.name }}Transport):
'content-type': 'application/x-protobuf',
},
)
{%- if not method.void %}

# Return the response.
return {{ method.output.ident }}.FromString(
return {{ method.output.ident }}.{% if method.input.ident.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %}(
response.content,
)
{%- endif %}
{%- endfor %}


Expand Down
9 changes: 9 additions & 0 deletions tests/unit/schema/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ def test_address_str_parent():
assert str(addr) == 'baz.spam.eggs.Bacon'


def test_address_str_empty():
addr = metadata.Address(
package=('google', 'protobuf'),
module='empty_pb2',
name='Empty',
)
assert str(addr) == 'None'


def test_address_proto():
addr = metadata.Address(package=('foo', 'bar'), module='baz', name='Bacon')
assert addr.proto == 'foo.bar.Bacon'
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/schema/wrappers/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ def test_method_no_signature():
assert len(make_method('Ping').signatures) == 0


def test_method_void():
empty = make_message(name='Empty', package='google.protobuf')
method = make_method('Meh', output_message=empty)
assert method.void


def test_method_not_void():
not_empty = make_message(name='OutputMessage', package='foo.bar.v1')
method = make_method('Meh', output_message=not_empty)
assert not method.void


def test_method_field_headers_none():
method = make_method('DoSomething')
assert isinstance(method.field_headers, collections.Sequence)
Expand Down