Skip to content

Commit 98aa690

Browse files
authored
fix: disambiguate field headers whose names are reserved python words (googleapis#1178)
* fix: disambiguate field headers whose names are reserved python words * Add FieldHeader class * update tests and templates
1 parent 246bfe2 commit 98aa690

File tree

7 files changed

+39
-15
lines changed

7 files changed

+39
-15
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
452452
gapic_v1.routing_header.to_grpc_metadata((
453453
{% for field_header in method.field_headers %}
454454
{% if not method.client_streaming %}
455-
("{{ field_header }}", request.{{ field_header }}),
455+
("{{ field_header.raw }}", request.{{ field_header.disambiguated }}),
456456
{% endif %}
457457
{% endfor %}
458458
)),

gapic/ads-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
@@ -595,7 +595,7 @@ def test_{{ method_name }}_field_headers():
595595
request = {{ method.input.ident }}()
596596

597597
{% for field_header in method.field_headers %}
598-
request.{{ field_header }} = '{{ field_header }}/value'
598+
request.{{ field_header.disambiguated }} = '{{ field_header.raw }}/value'
599599
{% endfor %}
600600

601601
# Mock the actual call within the gRPC stub, and fake the request.
@@ -623,7 +623,7 @@ def test_{{ method_name }}_field_headers():
623623
assert (
624624
'x-goog-request-params',
625625
'{% for field_header in method.field_headers -%}
626-
{{ field_header }}={{ field_header }}/value
626+
{{ field_header.raw }}={{ field_header.raw }}/value
627627
{%- if not loop.last %}&{% endif %}
628628
{%- endfor -%}',
629629
) in kw['metadata']
@@ -783,7 +783,7 @@ def test_{{ method_name }}_pager(transport_name: str = "grpc"):
783783
gapic_v1.routing_header.to_grpc_metadata((
784784
{% for field_header in method.field_headers %}
785785
{% if not method.client_streaming %}
786-
('{{ field_header }}', ''),
786+
('{{ field_header.raw }}', ''),
787787
{% endif %}
788788
{% endfor %}
789789
)),

gapic/schema/wrappers.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,15 @@ def with_context(
332332
)
333333

334334

335+
@dataclasses.dataclass(frozen=True)
336+
class FieldHeader:
337+
raw: str
338+
339+
@property
340+
def disambiguated(self) -> str:
341+
return self.raw + "_" if self.raw in utils.RESERVED_NAMES else self.raw
342+
343+
335344
@dataclasses.dataclass(frozen=True)
336345
class Oneof:
337346
"""Description of a field."""
@@ -1070,8 +1079,9 @@ def is_deprecated(self) -> bool:
10701079
# e.g. doesn't work with basic case of gRPC transcoding
10711080

10721081
@property
1073-
def field_headers(self) -> Sequence[str]:
1082+
def field_headers(self) -> Sequence[FieldHeader]:
10741083
"""Return the field headers defined for this method."""
1084+
10751085
http = self.options.Extensions[annotations_pb2.http]
10761086

10771087
pattern = re.compile(r'\{([a-z][\w\d_.]+)=')
@@ -1084,8 +1094,13 @@ def field_headers(self) -> Sequence[str]:
10841094
http.patch,
10851095
http.custom.path,
10861096
]
1087-
1088-
return next((tuple(pattern.findall(verb)) for verb in potential_verbs if verb), ())
1097+
field_headers = (
1098+
tuple(FieldHeader(field_header)
1099+
for field_header in pattern.findall(verb))
1100+
for verb in potential_verbs
1101+
if verb
1102+
)
1103+
return next(field_headers, ())
10891104

10901105
@property
10911106
def explicit_routing(self):

gapic/templates/%namespace/%name_%version/%sub/services/%service/async_client.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ class {{ service.async_client_name }}:
327327
gapic_v1.routing_header.to_grpc_metadata((
328328
{% for field_header in method.field_headers %}
329329
{% if not method.client_streaming %}
330-
("{{ field_header }}", request.{{ field_header }}),
330+
("{{ field_header.raw }}", request.{{ field_header.disambiguated }}),
331331
{% endif %}
332332
{% endfor %}
333333
)),

gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
509509
gapic_v1.routing_header.to_grpc_metadata((
510510
{% for field_header in method.field_headers %}
511511
{% if not method.client_streaming %}
512-
("{{ field_header }}", request.{{ field_header }}),
512+
("{{ field_header.raw }}", request.{{ field_header.disambiguated }}),
513513
{% endif %}
514514
{% endfor %}
515515
)),

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ def test_{{ method_name }}_field_headers():
805805
request = {{ method.input.ident }}()
806806

807807
{% for field_header in method.field_headers %}
808-
request.{{ field_header }} = '{{ field_header }}/value'
808+
request.{{ field_header.disambiguated }} = '{{ field_header.raw }}/value'
809809
{% endfor %}
810810

811811
# Mock the actual call within the gRPC stub, and fake the request.
@@ -833,7 +833,7 @@ def test_{{ method_name }}_field_headers():
833833
assert (
834834
'x-goog-request-params',
835835
'{% for field_header in method.field_headers -%}
836-
{{ field_header }}={{ field_header }}/value
836+
{{ field_header.raw }}={{ field_header.raw }}/value
837837
{%- if not loop.last %}&{% endif %}
838838
{%- endfor -%}',
839839
) in kw['metadata']
@@ -850,7 +850,7 @@ async def test_{{ method_name }}_field_headers_async():
850850
request = {{ method.input.ident }}()
851851

852852
{% for field_header in method.field_headers %}
853-
request.{{ field_header }} = '{{ field_header }}/value'
853+
request.{{ field_header.disambiguated }} = '{{ field_header.raw }}/value'
854854
{% endfor %}
855855

856856
# Mock the actual call within the gRPC stub, and fake the request.
@@ -879,7 +879,7 @@ async def test_{{ method_name }}_field_headers_async():
879879
assert (
880880
'x-goog-request-params',
881881
'{% for field_header in method.field_headers -%}
882-
{{ field_header }}={{ field_header }}/value
882+
{{ field_header.raw }}={{ field_header.raw }}/value
883883
{%- if not loop.last %}&{% endif %}
884884
{%- endfor -%}',
885885
) in kw['metadata']
@@ -1128,7 +1128,7 @@ def test_{{ method_name }}_pager(transport_name: str = "grpc"):
11281128
gapic_v1.routing_header.to_grpc_metadata((
11291129
{% for field_header in method.field_headers %}
11301130
{% if not method.client_streaming %}
1131-
('{{ field_header }}', ''),
1131+
('{{ field_header.raw }}', ''),
11321132
{% endif %}
11331133
{% endfor %}
11341134
)),

tests/unit/schema/wrappers/test_method.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,16 @@ def test_method_field_headers_present():
287287
for v in verbs:
288288
rule = http_pb2.HttpRule(**{v: '/v1/{parent=projects/*}/topics'})
289289
method = make_method('DoSomething', http_rule=rule)
290-
assert method.field_headers == ('parent',)
290+
assert method.field_headers == (wrappers.FieldHeader('parent'),)
291+
assert method.field_headers[0].raw == 'parent'
292+
assert method.field_headers[0].disambiguated == 'parent'
293+
294+
# test that reserved keyword in field header is disambiguated
295+
rule = http_pb2.HttpRule(**{v: '/v1/{object=objects/*}/topics'})
296+
method = make_method('DoSomething', http_rule=rule)
297+
assert method.field_headers == (wrappers.FieldHeader('object'),)
298+
assert method.field_headers[0].raw == 'object'
299+
assert method.field_headers[0].disambiguated == 'object_'
291300

292301

293302
def test_method_routing_rule():

0 commit comments

Comments
 (0)