1# Copyright 2018 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16class MessageRule:
17 """A marshal for converting between a descriptor and proto.Message."""
18
19 def __init__(self, descriptor: type, wrapper: type):
20 self._descriptor = descriptor
21 self._wrapper = wrapper
22
23 def to_python(self, value, *, absent: bool = None):
24 if isinstance(value, self._descriptor):
25 return self._wrapper.wrap(value)
26 return value
27
28 def to_proto(self, value):
29 if isinstance(value, self._wrapper):
30 return self._wrapper.pb(value)
31 if isinstance(value, dict) and not self.is_map:
32 # We need to use the wrapper's marshaling to handle
33 # potentially problematic nested messages.
34 try:
35 # Try the fast path first.
36 return self._descriptor(**value)
37 except (TypeError, ValueError, AttributeError) as ex:
38 # If we have a TypeError, ValueError or AttributeError,
39 # try the slow path in case the error
40 # was:
41 # - an int64/string issue.
42 # - a missing key issue in case a key only exists with a `_` suffix.
43 # See related issue: https://github.com/googleapis/python-api-core/issues/227.
44 # - a missing key issue due to nested struct. See: https://github.com/googleapis/proto-plus-python/issues/424.
45 # - a missing key issue due to nested duration. See: https://github.com/googleapis/google-cloud-python/issues/13350.
46 return self._wrapper(value)._pb
47 return value
48
49 @property
50 def is_map(self):
51 """Return True if the descriptor is a map entry, False otherwise."""
52 desc = self._descriptor.DESCRIPTOR
53 return desc.has_options and desc.GetOptions().map_entry