Skip to content
This repository has been archived by the owner on Mar 20, 2018. It is now read-only.

Utility methods to write to dictionaries or protobuf Messages #183

Merged
merged 8 commits into from May 3, 2017

Conversation

lukesneeringer
Copy link
Contributor

This PR adds a set of utility methods for reading or writing values from objects that might be dictionaries and might be subclasses of google.protobuf.message.Message.

Needed for partial Veneers.

@lukesneeringer lukesneeringer self-assigned this May 3, 2017
@lukesneeringer
Copy link
Contributor Author

Assigning review to both @geigerj and @jonparrott.

This is actually pretty urgent because of the partial Veneer timeline for Vision, so I am merging it once either of you gives it a thumbs up.

Copy link
Contributor

@geigerj geigerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the motivation for this to support subcases of BatchAnnotateImages for Vision, or how do you anticipate this being used?

raise KeyError.

Args:
pb_or_dict: An object which may be either:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation for supporting dict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can basically always send a dictionary to any GAPIC function that requires a protobuf.message.Message subclass. This is huge for usability, because it is a lot more palatable to tell a user to issue:

request = {
    'image': {'source': {'image_uri': 'http://bogus.bogus/img.jpg'}},
    'features': ['FACE_DETECTION'],
}

...than it is to ask them to send the equivalent protobuf messages (the example above would require four imports).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that the case right now? I didn't realize that was possible currently.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! It has been possible at least since I joined Google. :-)

key (str): The key to retrieve from the object in question.
default (Any): If the key is not present on the object, and a default
is set, returns that default instead. A type-appropriate falsy
default is generally recommended, as protobuf messages almost
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as protobuf messages almost always have default values

To be more explicit, primitive-type protobuf fields always have default values. In contrast, message-type fields never do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect:

>>> from google.cloud.proto.vision.v1 import image_annotator_pb2
>>> request = image_annotator_pb2.AnnotateImageRequest()
>>> request.features
<google.protobuf.pyext._message.RepeatedCompositeContainer object at 0x7f862b64b2b8>
>>> bool(request.features)
False
>>> image_annotator_pb2.AnnotateImageRequest.mro()
[<class 'google.cloud.proto.vision.v1.image_annotator_pb2.AnnotateImageRequest'>, <class 'google.protobu
f.pyext._message.CMessage'>, <class 'google.protobuf.message.Message'>, <class 'object'>]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, message-type fields are not the same as repeated message-type fields. The latter defaults to an empty list.

I just meant to point out that proto3 has a distinction between default and unset values, and the semantics are predictable based on field type (https://developers.google.com/protocol-buffers/docs/proto3#default), whereas this comment was more hazy.

"""
subkey = None
if separator in key:
subkey = separator.join(key.split(separator)[1:])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional nit: seems simpler just to use index rather than twice splitting the string (and once re-joining most of it)

set(pb_or_dict, key, value)


def _resolve_subkeys(key, separator='.'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't appear to configure separator anywhere, so why provide it as a kwarg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy future-proofing that costs me nothing.

@lukesneeringer
Copy link
Contributor Author

Is the motivation for this to support subcases of BatchAnnotateImages for Vision, or how do you anticipate this being used?

You hit the nail on the head. :-) This is for use in partial GAPICs generally. A common partial GAPIC use case is likely going to be to take a portion of the required request and fill other information in.

In my Vision helper, the single-image annotation will ask for all features if you leave off that input:

protobuf.setdefault(request, 'features', self._get_all_features())

__all__ = ('get', 'set', 'setdefault')


SENTINEL = object()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this should be private?

if separator in key:
subkey = separator.join(key.split(separator)[1:])
key = key.split(separator)[0]
return (key, subkey)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need for ( and ) for tuple return types.

@lukesneeringer
Copy link
Contributor Author

Merging once CI goes green.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants