Skip to content

Commit 2e7ea11

Browse files
authored
feat: update templates to permit enum aliases (googleapis#809)
Certain APIs like RecommendationEngine use multiple enum variant monikers to reference the same value. Achieving this in protos requires explicit support from proto-plus and the generated surface. Hand testing indicates compliance. Bump the min proto-plus version for generated clients.
1 parent 6a2622a commit 2e7ea11

File tree

7 files changed

+40
-7
lines changed

7 files changed

+40
-7
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
class {{ enum.name }}({{ p }}.Enum):
22
r"""{{ enum.meta.doc|rst(indent=4) }}"""
3+
{% if enum.enum_pb.HasField("options") -%}
4+
_pb_options = {{ enum.options_dict }}
5+
{% endif -%}
36
{% for enum_value in enum.values -%}
47
{{ enum_value.name }} = {{ enum_value.number }}
58
{% endfor -%}

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.22.2, < 2.0.0dev',
2020
'googleapis-common-protos >= 1.5.8',
2121
'grpcio >= 1.10.0',
22-
'proto-plus >= 1.4.0',
22+
'proto-plus >= 1.15.0',
2323
{%- if api.requires_package(('google', 'iam', 'v1')) %}
2424
'grpc-google-iam-v1',
2525
{%- endif %}

gapic/schema/wrappers.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from google.api import resource_pb2
4040
from google.api_core import exceptions # type: ignore
4141
from google.protobuf import descriptor_pb2 # type: ignore
42+
from google.protobuf.json_format import MessageToDict # type: ignore
4243

4344
from gapic import utils
4445
from gapic.schema import metadata
@@ -413,7 +414,7 @@ def get_field(self, *field_path: str,
413414
# Get the first field in the path.
414415
first_field = field_path[0]
415416
cursor = self.fields[first_field +
416-
('_' if first_field in utils.RESERVED_NAMES else '')]
417+
('_' if first_field in utils.RESERVED_NAMES else '')]
417418

418419
# Base case: If this is the last field in the path, return it outright.
419420
if len(field_path) == 1:
@@ -536,6 +537,18 @@ def with_context(self, *, collisions: FrozenSet[str]) -> 'EnumType':
536537
meta=self.meta.with_context(collisions=collisions),
537538
)
538539

540+
@property
541+
def options_dict(self) -> Dict:
542+
"""Return the EnumOptions (if present) as a dict.
543+
544+
This is a hack to support a pythonic structure representation for
545+
the generator templates.
546+
"""
547+
return MessageToDict(
548+
self.enum_pb.options,
549+
preserving_proto_field_name=True
550+
)
551+
539552

540553
@dataclasses.dataclass(frozen=True)
541554
class PythonType:
@@ -869,7 +882,7 @@ def paged_result_field(self) -> Optional[Field]:
869882

870883
# The request must have page_token and next_page_token as they keep track of pages
871884
for source, source_type, name in ((self.input, str, 'page_token'),
872-
(self.output, str, 'next_page_token')):
885+
(self.output, str, 'next_page_token')):
873886
field = source.fields.get(name, None)
874887
if not field or field.type != source_type:
875888
return None

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
class {{ enum.name }}({{ p }}.Enum):
22
r"""{{ enum.meta.doc|rst(indent=4) }}"""
3+
{% if enum.enum_pb.HasField("options") -%}
4+
_pb_options = {{ enum.options_dict }}
5+
{% endif -%}
36
{% for enum_value in enum.values -%}
47
{{ enum_value.name }} = {{ enum_value.number }}
58
{% endfor -%}

gapic/templates/setup.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ setuptools.setup(
2828
install_requires=(
2929
'google-api-core[grpc] >= 1.22.2, < 2.0.0dev',
3030
'libcst >= 0.2.5',
31-
'proto-plus >= 1.4.0',
31+
'proto-plus >= 1.15.0',
3232
{%- if api.requires_package(('google', 'iam', 'v1')) or opts.add_iam_methods %}
3333
'grpc-google-iam-v1',
3434
{%- endif %}

test_utils/test_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def make_enum(
290290
module: str = 'baz',
291291
values: typing.Tuple[str, int] = (),
292292
meta: metadata.Metadata = None,
293+
options: desc.EnumOptions = None,
293294
) -> wrappers.EnumType:
294295
enum_value_pbs = [
295296
desc.EnumValueDescriptorProto(name=i[0], number=i[1])
@@ -298,6 +299,7 @@ def make_enum(
298299
enum_pb = desc.EnumDescriptorProto(
299300
name=name,
300301
value=enum_value_pbs,
302+
options=options,
301303
)
302304
return wrappers.EnumType(
303305
enum_pb=enum_pb,

tests/unit/schema/wrappers/test_enums.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ def test_enum_value_properties():
3737

3838

3939
def test_enum_ident():
40-
message = make_enum('Baz', package='foo.v1', module='bar')
41-
assert str(message.ident) == 'bar.Baz'
42-
assert message.ident.sphinx == 'foo.v1.bar.Baz'
40+
enum = make_enum('Baz', package='foo.v1', module='bar')
41+
assert str(enum.ident) == 'bar.Baz'
42+
assert enum.ident.sphinx == 'foo.v1.bar.Baz'
43+
44+
45+
def test_enum_options_dict():
46+
cephalopod = make_enum("Cephalopod", package="animalia.v1",
47+
module="mollusca", options={"allow_alias": True})
48+
assert isinstance(cephalopod.enum_pb.options, descriptor_pb2.EnumOptions)
49+
assert cephalopod.options_dict == {"allow_alias": True}
50+
51+
bivalve = make_enum("Bivalve", package="animalia.v1",
52+
module="mollusca")
53+
assert isinstance(bivalve.enum_pb.options, descriptor_pb2.EnumOptions)
54+
assert bivalve.options_dict == {}

0 commit comments

Comments
 (0)