Skip to content

Commit 1aa729c

Browse files
authored
feat: support for proto3 optional fields (#519)
Fields marked as 'optional' in proto files are given an 'optional=True' parameter in their constructor. Bumps required version of proto-plus in gapic surface to 1.1.0 Includes minor cleanups in template code that refers to oneofs.
1 parent 12e9bee commit 1aa729c

File tree

7 files changed

+12
-13
lines changed

7 files changed

+12
-13
lines changed

gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/pagers.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class {{ method.name }}Pager:
3838
method: Callable[..., {{ method.output.ident }}],
3939
request: {{ method.input.ident }},
4040
response: {{ method.output.ident }},
41-
metadata: Sequence[Tuple[str, str]] = ())):
41+
metadata: Sequence[Tuple[str, str]] = ()):
4242
"""Instantiate the pager.
4343

4444
Args:

gapic/ads-templates/%namespace/%name/%version/%sub/types/_message.py.j2

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ class {{ message.name }}({{ p }}.Message):
4242
{% endwith -%}
4343
{% else -%}
4444
{{ field.name }} = {{ p }}.{% if field.repeated %}Repeated{% endif %}Field(
45-
{{- p }}.{{ field.proto_type }}, number={{ field.number }}
46-
{% if field.oneof %}, oneof='{{ field.oneof }}'{% endif %}
45+
{{- p }}.{{ field.proto_type }}, number={{ field.number }}{% if field.proto3_optional %}, optional=True{% elif field.oneof %}, oneof='{{ field.oneof }}'{% endif %}
4746
{%- if field.enum or field.message %},
4847
{{ field.proto_type.lower() }}={{ field.type.ident.rel(message.ident) }},
4948
{% endif %})

gapic/ads-templates/setup.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ setuptools.setup(
1919
'google-api-core >= 1.17.0, < 2.0.0dev',
2020
'googleapis-common-protos >= 1.5.8',
2121
'grpcio >= 1.10.0',
22-
'proto-plus >= 0.4.0',
22+
'proto-plus >= 1.1.0',
2323
{%- if api.requires_package(('google', 'iam', 'v1')) %}
2424
'grpc-google-iam-v1',
2525
{%- endif %}

gapic/ads-templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,9 @@ def test_{{ method.name|snake_case }}(transport: str = 'grpc'):
221221
call.return_value = iter([{{ method.output.ident }}()])
222222
{% else -%}
223223
call.return_value = {{ method.output.ident }}(
224-
{%- for field in method.output.fields.values() | rejectattr('message') %}{%- for field in method.output.fields.values() | rejectattr('message')%}{% if not field.oneof or field.proto3_optional %}
224+
{%- for field in method.output.fields.values() | rejectattr('message')%}{% if not field.oneof or field.proto3_optional %}
225225
{{ field.name }}={{ field.mock_value }},
226-
{%- endfor %}
226+
{% endif %}{%- endfor %}
227227
{#- This is a hack to only pick one field #}
228228
{%- for oneof_fields in method.output.oneof_fields().values() %}
229229
{% with field = oneof_fields[0] %}
@@ -257,14 +257,15 @@ def test_{{ method.name|snake_case }}(transport: str = 'grpc'):
257257
assert isinstance(message, {{ method.output.ident }})
258258
{% else -%}
259259
assert isinstance(response, {{ method.client_output.ident }})
260-
{% for field in method.output.fields.values() | rejectattr('message') -%}
260+
{% for field in method.output.fields.values() | rejectattr('message') -%}{% if not field.oneof or field.proto3_optional %}
261261
{% if field.field_pb.type in [1, 2] -%} {# Use approx eq for floats -#}
262262
assert math.isclose(response.{{ field.name }}, {{ field.mock_value }}, rel_tol=1e-6)
263263
{% elif field.field_pb.type == 8 -%} {# Use 'is' for bools #}
264264
assert response.{{ field.name }} is {{ field.mock_value }}
265265
{% else -%}
266266
assert response.{{ field.name }} == {{ field.mock_value }}
267267
{% endif -%}
268+
{% endif -%} {# end oneof/optional #}
268269
{% endfor %}
269270
{% endif %}
270271

gapic/templates/%namespace/%name_%version/%sub/types/_message.py.j2

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ class {{ message.name }}({{ p }}.Message):
4242
{% endwith -%}
4343
{% else -%} {# field.map #}
4444
{{ field.name }} = {{ p }}.{% if field.repeated %}Repeated{% endif %}Field(
45-
{{- p }}.{{ field.proto_type }}, number={{ field.number }}
46-
{% if field.oneof %}, oneof='{{ field.oneof }}'{% endif %}
45+
{{- p }}.{{ field.proto_type }}, number={{ field.number }}{% if field.proto3_optional %}, optional=True{% elif field.oneof %}, oneof='{{ field.oneof }}'{% endif %}
4746
{%- if field.enum or field.message %},
4847
{{ field.proto_type.lower() }}={{ field.type.ident.rel(message.ident) }},
4948
{% endif %}) {# enum or message #}

gapic/templates/setup.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ setuptools.setup(
1818
install_requires=(
1919
'google-api-core[grpc] >= 1.21.0, < 2.0.0dev',
2020
'libcst >= 0.2.5',
21-
'proto-plus >= 0.4.0',
21+
'proto-plus >= 1.1.0',
2222
{%- if api.requires_package(('google', 'iam', 'v1')) %}
2323
'grpc-google-iam-v1',
2424
{%- endif %}

gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def test_{{ method.name|snake_case }}(transport: str = 'grpc'):
325325
assert isinstance(message, {{ method.output.ident }})
326326
{% else -%}
327327
assert isinstance(response, {{ method.client_output.ident }})
328-
{% for field in method.output.fields.values() | rejectattr('message') -%}{% if not (field.oneof and not field.proto3_optional) %}
328+
{% for field in method.output.fields.values() | rejectattr('message') -%}{% if not field.oneof or field.proto3_optional %}
329329
{% if field.field_pb.type in [1, 2] -%} {# Use approx eq for floats -#}
330330
assert math.isclose(response.{{ field.name }}, {{ field.mock_value }}, rel_tol=1e-6)
331331
{% elif field.field_pb.type == 8 -%} {# Use 'is' for bools #}
@@ -376,7 +376,7 @@ async def test_{{ method.name|snake_case }}_async(transport: str = 'grpc_asyncio
376376
{%- else -%}
377377
grpc_helpers_async.FakeStreamUnaryCall
378378
{%- endif -%}({{ method.output.ident }}(
379-
{%- for field in method.output.fields.values() | rejectattr('message') %}{% if not (field.oneof and not field.proto3_optional) %}
379+
{%- for field in method.output.fields.values() | rejectattr('message') %}{% if not field.oneof or field.proto3_optional %}
380380
{{ field.name }}={{ field.mock_value }},
381381
{%- endif %}
382382
{%- endfor %}
@@ -409,7 +409,7 @@ async def test_{{ method.name|snake_case }}_async(transport: str = 'grpc_asyncio
409409
assert isinstance(message, {{ method.output.ident }})
410410
{% else -%}
411411
assert isinstance(response, {{ method.client_output_async.ident }})
412-
{% for field in method.output.fields.values() | rejectattr('message') -%}{% if not (field.oneof and not field.proto3_optional) %}
412+
{% for field in method.output.fields.values() | rejectattr('message') -%}{% if not field.oneof or field.proto3_optional %}
413413
{% if field.field_pb.type in [1, 2] -%} {# Use approx eq for floats -#}
414414
assert math.isclose(response.{{ field.name }}, {{ field.mock_value }}, rel_tol=1e-6)
415415
{% elif field.field_pb.type == 8 -%} {# Use 'is' for bools #}

0 commit comments

Comments
 (0)