Skip to content

Commit bba7f68

Browse files
landritoJon Wayne Parrott
authored andcommitted
Port gax proto helper methods (#4249)
1 parent e7dad83 commit bba7f68

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

google/api_core/protobuf_helpers.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414

1515
"""Helpers for :mod:`protobuf`."""
1616

17+
import collections
18+
import inspect
19+
20+
from google.protobuf.message import Message
21+
1722

1823
def from_any_pb(pb_type, any_pb):
1924
"""Converts an ``Any`` protobuf to the specified message type.
@@ -36,3 +41,38 @@ def from_any_pb(pb_type, any_pb):
3641
any_pb.__class__.__name__, pb_type.__name__))
3742

3843
return msg
44+
45+
46+
def check_oneof(**kwargs):
47+
"""Raise ValueError if more than one keyword argument is not none.
48+
Args:
49+
kwargs (dict): The keyword arguments sent to the function.
50+
Raises:
51+
ValueError: If more than one entry in kwargs is not none.
52+
"""
53+
# Sanity check: If no keyword arguments were sent, this is fine.
54+
if not kwargs:
55+
return
56+
57+
not_nones = [val for val in kwargs.values() if val is not None]
58+
if len(not_nones) > 1:
59+
raise ValueError('Only one of {fields} should be set.'.format(
60+
fields=', '.join(sorted(kwargs.keys())),
61+
))
62+
63+
64+
def get_messages(module):
65+
"""Return a dictionary of message names and objects.
66+
Args:
67+
module (module): A Python module; dir() will be run against this
68+
module to find Message subclasses.
69+
Returns:
70+
dict[str, Message]: A dictionary with the Message class names as
71+
keys, and the Message subclasses themselves as values.
72+
"""
73+
answer = collections.OrderedDict()
74+
for name in dir(module):
75+
candidate = getattr(module, name)
76+
if inspect.isclass(candidate) and issubclass(candidate, Message):
77+
answer[name] = candidate
78+
return answer

tests/unit/test_protobuf_helpers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from google.api_core import protobuf_helpers
1818
from google.protobuf import any_pb2
19+
from google.protobuf.message import Message
1920
from google.type import date_pb2
2021
from google.type import timeofday_pb2
2122

@@ -35,3 +36,32 @@ def test_from_any_pb_failure():
3536

3637
with pytest.raises(TypeError):
3738
protobuf_helpers.from_any_pb(timeofday_pb2.TimeOfDay, in_message)
39+
40+
41+
def test_check_protobuf_helpers_ok():
42+
assert protobuf_helpers.check_oneof() is None
43+
assert protobuf_helpers.check_oneof(foo='bar') is None
44+
assert protobuf_helpers.check_oneof(foo='bar', baz=None) is None
45+
assert protobuf_helpers.check_oneof(foo=None, baz='bacon') is None
46+
assert (protobuf_helpers.check_oneof(foo='bar', spam=None, eggs=None)
47+
is None)
48+
49+
50+
def test_check_protobuf_helpers_failures():
51+
with pytest.raises(ValueError):
52+
protobuf_helpers.check_oneof(foo='bar', spam='eggs')
53+
with pytest.raises(ValueError):
54+
protobuf_helpers.check_oneof(foo='bar', baz='bacon', spam='eggs')
55+
with pytest.raises(ValueError):
56+
protobuf_helpers.check_oneof(foo='bar', spam=0, eggs=None)
57+
58+
59+
def test_get_messages():
60+
answer = protobuf_helpers.get_messages(date_pb2)
61+
62+
# Ensure that Date was exported properly.
63+
assert answer['Date'] is date_pb2.Date
64+
65+
# Ensure that no non-Message objects were exported.
66+
for value in answer.values():
67+
assert issubclass(value, Message)

0 commit comments

Comments
 (0)