structure saas with tools
This commit is contained in:
18
.venv/lib/python3.10/site-packages/proto/marshal/__init__.py
Normal file
18
.venv/lib/python3.10/site-packages/proto/marshal/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .marshal import Marshal
|
||||
|
||||
|
||||
__all__ = ("Marshal",)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .maps import MapComposite
|
||||
from .repeated import Repeated
|
||||
from .repeated import RepeatedComposite
|
||||
|
||||
|
||||
__all__ = (
|
||||
"MapComposite",
|
||||
"Repeated",
|
||||
"RepeatedComposite",
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,82 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
|
||||
from proto.utils import cached_property
|
||||
from google.protobuf.message import Message
|
||||
|
||||
|
||||
class MapComposite(collections.abc.MutableMapping):
|
||||
"""A view around a mutable sequence in protocol buffers.
|
||||
|
||||
This implements the full Python MutableMapping interface, but all methods
|
||||
modify the underlying field container directly.
|
||||
"""
|
||||
|
||||
@cached_property
|
||||
def _pb_type(self):
|
||||
"""Return the protocol buffer type for this sequence."""
|
||||
# Huzzah, another hack. Still less bad than RepeatedComposite.
|
||||
return type(self.pb.GetEntryClass()().value)
|
||||
|
||||
def __init__(self, sequence, *, marshal):
|
||||
"""Initialize a wrapper around a protobuf map.
|
||||
|
||||
Args:
|
||||
sequence: A protocol buffers map.
|
||||
marshal (~.MarshalRegistry): An instantiated marshal, used to
|
||||
convert values going to and from this map.
|
||||
"""
|
||||
self._pb = sequence
|
||||
self._marshal = marshal
|
||||
|
||||
def __contains__(self, key):
|
||||
# Protocol buffers is so permissive that querying for the existence
|
||||
# of a key will in of itself create it.
|
||||
#
|
||||
# By taking a tuple of the keys and querying that, we avoid sending
|
||||
# the lookup to protocol buffers and therefore avoid creating the key.
|
||||
return key in tuple(self.keys())
|
||||
|
||||
def __getitem__(self, key):
|
||||
# We handle raising KeyError ourselves, because otherwise protocol
|
||||
# buffers will create the key if it does not exist.
|
||||
if key not in self:
|
||||
raise KeyError(key)
|
||||
return self._marshal.to_python(self._pb_type, self.pb[key])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
pb_value = self._marshal.to_proto(self._pb_type, value, strict=True)
|
||||
# Directly setting a key is not allowed; however, protocol buffers
|
||||
# is so permissive that querying for the existence of a key will in
|
||||
# of itself create it.
|
||||
#
|
||||
# Therefore, we create a key that way (clearing any fields that may
|
||||
# be set) and then merge in our values.
|
||||
self.pb[key].Clear()
|
||||
self.pb[key].MergeFrom(pb_value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.pb.pop(key)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.pb)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.pb)
|
||||
|
||||
@property
|
||||
def pb(self):
|
||||
return self._pb
|
||||
@@ -0,0 +1,189 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
import copy
|
||||
from typing import Iterable
|
||||
|
||||
from proto.utils import cached_property
|
||||
|
||||
|
||||
class Repeated(collections.abc.MutableSequence):
|
||||
"""A view around a mutable sequence in protocol buffers.
|
||||
|
||||
This implements the full Python MutableSequence interface, but all methods
|
||||
modify the underlying field container directly.
|
||||
"""
|
||||
|
||||
def __init__(self, sequence, *, marshal, proto_type=None):
|
||||
"""Initialize a wrapper around a protobuf repeated field.
|
||||
|
||||
Args:
|
||||
sequence: A protocol buffers repeated field.
|
||||
marshal (~.MarshalRegistry): An instantiated marshal, used to
|
||||
convert values going to and from this map.
|
||||
"""
|
||||
self._pb = sequence
|
||||
self._marshal = marshal
|
||||
self._proto_type = proto_type
|
||||
|
||||
def __copy__(self):
|
||||
"""Copy this object and return the copy."""
|
||||
return type(self)(self.pb[:], marshal=self._marshal)
|
||||
|
||||
def __delitem__(self, key):
|
||||
"""Delete the given item."""
|
||||
del self.pb[key]
|
||||
|
||||
def __eq__(self, other):
|
||||
if hasattr(other, "pb"):
|
||||
return tuple(self.pb) == tuple(other.pb)
|
||||
return tuple(self.pb) == tuple(other) if isinstance(other, Iterable) else False
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return the given item."""
|
||||
return self.pb[key]
|
||||
|
||||
def __len__(self):
|
||||
"""Return the length of the sequence."""
|
||||
return len(self.pb)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return repr([*self])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.pb[key] = value
|
||||
|
||||
def insert(self, index: int, value):
|
||||
"""Insert ``value`` in the sequence before ``index``."""
|
||||
self.pb.insert(index, value)
|
||||
|
||||
def sort(self, *, key: str = None, reverse: bool = False):
|
||||
"""Stable sort *IN PLACE*."""
|
||||
self.pb.sort(key=key, reverse=reverse)
|
||||
|
||||
@property
|
||||
def pb(self):
|
||||
return self._pb
|
||||
|
||||
|
||||
class RepeatedComposite(Repeated):
|
||||
"""A view around a mutable sequence of messages in protocol buffers.
|
||||
|
||||
This implements the full Python MutableSequence interface, but all methods
|
||||
modify the underlying field container directly.
|
||||
"""
|
||||
|
||||
@cached_property
|
||||
def _pb_type(self):
|
||||
"""Return the protocol buffer type for this sequence."""
|
||||
# Provide the marshal-given proto_type, if any.
|
||||
# Used for RepeatedComposite of Enum.
|
||||
if self._proto_type is not None:
|
||||
return self._proto_type
|
||||
|
||||
# There is no public-interface mechanism to determine the type
|
||||
# of what should go in the list (and the C implementation seems to
|
||||
# have no exposed mechanism at all).
|
||||
#
|
||||
# If the list has members, use the existing list members to
|
||||
# determine the type.
|
||||
if len(self.pb) > 0:
|
||||
return type(self.pb[0])
|
||||
|
||||
# We have no members in the list, so we get the type from the attributes.
|
||||
if hasattr(self.pb, "_message_descriptor") and hasattr(
|
||||
self.pb._message_descriptor, "_concrete_class"
|
||||
):
|
||||
return self.pb._message_descriptor._concrete_class
|
||||
|
||||
# Fallback logic in case attributes are not available
|
||||
# In order to get the type, we create a throw-away copy and add a
|
||||
# blank member to it.
|
||||
canary = copy.deepcopy(self.pb).add()
|
||||
return type(canary)
|
||||
|
||||
def __eq__(self, other):
|
||||
if super().__eq__(other):
|
||||
return True
|
||||
return (
|
||||
tuple([i for i in self]) == tuple(other)
|
||||
if isinstance(other, Iterable)
|
||||
else False
|
||||
)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._marshal.to_python(self._pb_type, self.pb[key])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# The underlying protocol buffer does not define __setitem__, so we
|
||||
# have to implement all the operations on our own.
|
||||
|
||||
# If ``key`` is an integer, as in list[index] = value:
|
||||
if isinstance(key, int):
|
||||
if -len(self) <= key < len(self):
|
||||
self.pop(key) # Delete the old item.
|
||||
self.insert(key, value) # Insert the new item in its place.
|
||||
else:
|
||||
raise IndexError("list assignment index out of range")
|
||||
|
||||
# If ``key`` is a slice object, as in list[start:stop:step] = [values]:
|
||||
elif isinstance(key, slice):
|
||||
start, stop, step = key.indices(len(self))
|
||||
|
||||
if not isinstance(value, collections.abc.Iterable):
|
||||
raise TypeError("can only assign an iterable")
|
||||
|
||||
if step == 1: # Is not an extended slice.
|
||||
# Assign all the new values to the sliced part, replacing the
|
||||
# old values, if any, and unconditionally inserting those
|
||||
# values whose indices already exceed the slice length.
|
||||
for index, item in enumerate(value):
|
||||
if start + index < stop:
|
||||
self.pop(start + index)
|
||||
self.insert(start + index, item)
|
||||
|
||||
# If there are less values than the length of the slice, remove
|
||||
# the remaining elements so that the slice adapts to the
|
||||
# newly provided values.
|
||||
for _ in range(stop - start - len(value)):
|
||||
self.pop(start + len(value))
|
||||
|
||||
else: # Is an extended slice.
|
||||
indices = range(start, stop, step)
|
||||
|
||||
if len(value) != len(indices): # XXX: Use PEP 572 on 3.8+
|
||||
raise ValueError(
|
||||
f"attempt to assign sequence of size "
|
||||
f"{len(value)} to extended slice of size "
|
||||
f"{len(indices)}"
|
||||
)
|
||||
|
||||
# Assign each value to its index, calling this function again
|
||||
# with individual integer indexes that get processed above.
|
||||
for index, item in zip(indices, value):
|
||||
self[index] = item
|
||||
|
||||
else:
|
||||
raise TypeError(
|
||||
f"list indices must be integers or slices, not {type(key).__name__}"
|
||||
)
|
||||
|
||||
def insert(self, index: int, value):
|
||||
"""Insert ``value`` in the sequence before ``index``."""
|
||||
pb_value = self._marshal.to_proto(self._pb_type, value)
|
||||
self.pb.insert(index, pb_value)
|
||||
64
.venv/lib/python3.10/site-packages/proto/marshal/compat.py
Normal file
64
.venv/lib/python3.10/site-packages/proto/marshal/compat.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file pulls in the container types from internal protocol buffers,
|
||||
# and exports the types available.
|
||||
#
|
||||
# If the C extensions were not installed, then their container types will
|
||||
# not be included.
|
||||
|
||||
from google.protobuf.internal import containers
|
||||
|
||||
# Import all message types to ensure that pyext types are recognized
|
||||
# when upb types exist. Conda's protobuf defaults to pyext despite upb existing.
|
||||
# See https://github.com/googleapis/proto-plus-python/issues/470
|
||||
try:
|
||||
from google._upb import _message as _message_upb
|
||||
except ImportError:
|
||||
_message_upb = None
|
||||
|
||||
try:
|
||||
from google.protobuf.pyext import _message as _message_pyext
|
||||
except ImportError:
|
||||
_message_pyext = None
|
||||
|
||||
|
||||
repeated_composite_types = (containers.RepeatedCompositeFieldContainer,)
|
||||
repeated_scalar_types = (containers.RepeatedScalarFieldContainer,)
|
||||
map_composite_types = (containers.MessageMap,)
|
||||
|
||||
# In `proto/marshal.py`, for compatibility with protobuf 5.x,
|
||||
# we'll use `map_composite_type_names` to check whether
|
||||
# the name of the class of a protobuf type is
|
||||
# `MessageMapContainer`, and, if `True`, return a MapComposite.
|
||||
# See https://github.com/protocolbuffers/protobuf/issues/16596
|
||||
map_composite_type_names = ("MessageMapContainer",)
|
||||
|
||||
for message in [_message_upb, _message_pyext]:
|
||||
if message:
|
||||
repeated_composite_types += (message.RepeatedCompositeContainer,)
|
||||
repeated_scalar_types += (message.RepeatedScalarContainer,)
|
||||
|
||||
try:
|
||||
map_composite_types += (message.MessageMapContainer,)
|
||||
except AttributeError:
|
||||
# The `MessageMapContainer` attribute is not available in Protobuf 5.x+
|
||||
pass
|
||||
|
||||
__all__ = (
|
||||
"repeated_composite_types",
|
||||
"repeated_scalar_types",
|
||||
"map_composite_types",
|
||||
"map_composite_type_names",
|
||||
)
|
||||
297
.venv/lib/python3.10/site-packages/proto/marshal/marshal.py
Normal file
297
.venv/lib/python3.10/site-packages/proto/marshal/marshal.py
Normal file
@@ -0,0 +1,297 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
import enum
|
||||
|
||||
from google.protobuf import message
|
||||
from google.protobuf import duration_pb2
|
||||
from google.protobuf import timestamp_pb2
|
||||
from google.protobuf import field_mask_pb2
|
||||
from google.protobuf import struct_pb2
|
||||
from google.protobuf import wrappers_pb2
|
||||
|
||||
from proto.marshal import compat
|
||||
from proto.marshal.collections import MapComposite
|
||||
from proto.marshal.collections import Repeated
|
||||
from proto.marshal.collections import RepeatedComposite
|
||||
|
||||
from proto.marshal.rules import bytes as pb_bytes
|
||||
from proto.marshal.rules import stringy_numbers
|
||||
from proto.marshal.rules import dates
|
||||
from proto.marshal.rules import struct
|
||||
from proto.marshal.rules import wrappers
|
||||
from proto.marshal.rules import field_mask
|
||||
from proto.primitives import ProtoType
|
||||
|
||||
|
||||
class Rule(abc.ABC):
|
||||
"""Abstract class definition for marshal rules."""
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, C):
|
||||
if hasattr(C, "to_python") and hasattr(C, "to_proto"):
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class BaseMarshal:
|
||||
"""The base class to translate between protobuf and Python classes.
|
||||
|
||||
Protocol buffers defines many common types (e.g. Timestamp, Duration)
|
||||
which also exist in the Python standard library. The marshal essentially
|
||||
translates between these: it keeps a registry of common protocol buffers
|
||||
and their Python representations, and translates back and forth.
|
||||
|
||||
The protocol buffer class is always the "key" in this relationship; when
|
||||
presenting a message, the declared field types are used to determine
|
||||
whether a value should be transformed into another class. Similarly,
|
||||
when accepting a Python value (when setting a field, for example),
|
||||
the declared field type is still used. This means that, if appropriate,
|
||||
multiple protocol buffer types may use the same Python type.
|
||||
|
||||
The primary implementation of this is :class:`Marshal`, which should
|
||||
usually be used instead of this class directly.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._rules = {}
|
||||
self._noop = NoopRule()
|
||||
self.reset()
|
||||
|
||||
def register(self, proto_type: type, rule: Rule = None):
|
||||
"""Register a rule against the given ``proto_type``.
|
||||
|
||||
This function expects a ``proto_type`` (the descriptor class) and
|
||||
a ``rule``; an object with a ``to_python`` and ``to_proto`` method.
|
||||
Each method should return the appropriate Python or protocol buffer
|
||||
type, and be idempotent (e.g. accept either type as input).
|
||||
|
||||
This function can also be used as a decorator::
|
||||
|
||||
@marshal.register(timestamp_pb2.Timestamp)
|
||||
class TimestampRule:
|
||||
...
|
||||
|
||||
In this case, the class will be initialized for you with zero
|
||||
arguments.
|
||||
|
||||
Args:
|
||||
proto_type (type): A protocol buffer message type.
|
||||
rule: A marshal object
|
||||
"""
|
||||
# If a rule was provided, register it and be done.
|
||||
if rule:
|
||||
# Ensure the rule implements Rule.
|
||||
if not isinstance(rule, Rule):
|
||||
raise TypeError(
|
||||
"Marshal rule instances must implement "
|
||||
"`to_proto` and `to_python` methods."
|
||||
)
|
||||
|
||||
# Register the rule.
|
||||
self._rules[proto_type] = rule
|
||||
return
|
||||
|
||||
# Create an inner function that will register an instance of the
|
||||
# marshal class to this object's registry, and return it.
|
||||
def register_rule_class(rule_class: type):
|
||||
# Ensure the rule class is a valid rule.
|
||||
if not issubclass(rule_class, Rule):
|
||||
raise TypeError(
|
||||
"Marshal rule subclasses must implement "
|
||||
"`to_proto` and `to_python` methods."
|
||||
)
|
||||
|
||||
# Register the rule class.
|
||||
self._rules[proto_type] = rule_class()
|
||||
return rule_class
|
||||
|
||||
return register_rule_class
|
||||
|
||||
def reset(self):
|
||||
"""Reset the registry to its initial state."""
|
||||
self._rules.clear()
|
||||
|
||||
# Register date and time wrappers.
|
||||
self.register(timestamp_pb2.Timestamp, dates.TimestampRule())
|
||||
self.register(duration_pb2.Duration, dates.DurationRule())
|
||||
|
||||
# Register FieldMask wrappers.
|
||||
self.register(field_mask_pb2.FieldMask, field_mask.FieldMaskRule())
|
||||
|
||||
# Register nullable primitive wrappers.
|
||||
self.register(wrappers_pb2.BoolValue, wrappers.BoolValueRule())
|
||||
self.register(wrappers_pb2.BytesValue, wrappers.BytesValueRule())
|
||||
self.register(wrappers_pb2.DoubleValue, wrappers.DoubleValueRule())
|
||||
self.register(wrappers_pb2.FloatValue, wrappers.FloatValueRule())
|
||||
self.register(wrappers_pb2.Int32Value, wrappers.Int32ValueRule())
|
||||
self.register(wrappers_pb2.Int64Value, wrappers.Int64ValueRule())
|
||||
self.register(wrappers_pb2.StringValue, wrappers.StringValueRule())
|
||||
self.register(wrappers_pb2.UInt32Value, wrappers.UInt32ValueRule())
|
||||
self.register(wrappers_pb2.UInt64Value, wrappers.UInt64ValueRule())
|
||||
|
||||
# Register the google.protobuf.Struct wrappers.
|
||||
#
|
||||
# These are aware of the marshal that created them, because they
|
||||
# create RepeatedComposite and MapComposite instances directly and
|
||||
# need to pass the marshal to them.
|
||||
self.register(struct_pb2.Value, struct.ValueRule(marshal=self))
|
||||
self.register(struct_pb2.ListValue, struct.ListValueRule(marshal=self))
|
||||
self.register(struct_pb2.Struct, struct.StructRule(marshal=self))
|
||||
|
||||
# Special case for bytes to allow base64 encode/decode
|
||||
self.register(ProtoType.BYTES, pb_bytes.BytesRule())
|
||||
|
||||
# Special case for int64 from strings because of dict round trip.
|
||||
# See https://github.com/protocolbuffers/protobuf/issues/2679
|
||||
for rule_class in stringy_numbers.STRINGY_NUMBER_RULES:
|
||||
self.register(rule_class._proto_type, rule_class())
|
||||
|
||||
def get_rule(self, proto_type):
|
||||
# Rules are needed to convert values between proto-plus and pb.
|
||||
# Retrieve the rule for the specified proto type.
|
||||
# The NoopRule will be used when a rule is not found.
|
||||
rule = self._rules.get(proto_type, self._noop)
|
||||
|
||||
# If we don't find a rule, also check under `_instances`
|
||||
# in case there is a rule in another package.
|
||||
# See https://github.com/googleapis/proto-plus-python/issues/349
|
||||
if rule == self._noop and hasattr(self, "_instances"):
|
||||
for _, instance in self._instances.items():
|
||||
rule = instance._rules.get(proto_type, self._noop)
|
||||
if rule != self._noop:
|
||||
break
|
||||
return rule
|
||||
|
||||
def to_python(self, proto_type, value, *, absent: bool = None):
|
||||
# Internal protobuf has its own special type for lists of values.
|
||||
# Return a view around it that implements MutableSequence.
|
||||
value_type = type(value) # Minor performance boost over isinstance
|
||||
if value_type in compat.repeated_composite_types:
|
||||
return RepeatedComposite(value, marshal=self)
|
||||
if value_type in compat.repeated_scalar_types:
|
||||
if isinstance(proto_type, type):
|
||||
return RepeatedComposite(value, marshal=self, proto_type=proto_type)
|
||||
else:
|
||||
return Repeated(value, marshal=self)
|
||||
|
||||
# Same thing for maps of messages.
|
||||
# See https://github.com/protocolbuffers/protobuf/issues/16596
|
||||
# We need to look up the name of the type in compat.map_composite_type_names
|
||||
# as class `MessageMapContainer` is no longer exposed
|
||||
# This is done to avoid taking a breaking change in proto-plus.
|
||||
if (
|
||||
value_type in compat.map_composite_types
|
||||
or value_type.__name__ in compat.map_composite_type_names
|
||||
):
|
||||
return MapComposite(value, marshal=self)
|
||||
return self.get_rule(proto_type=proto_type).to_python(value, absent=absent)
|
||||
|
||||
def to_proto(self, proto_type, value, *, strict: bool = False):
|
||||
# The protos in google/protobuf/struct.proto are exceptional cases,
|
||||
# because they can and should represent themselves as lists and dicts.
|
||||
# These cases are handled in their rule classes.
|
||||
if proto_type not in (
|
||||
struct_pb2.Value,
|
||||
struct_pb2.ListValue,
|
||||
struct_pb2.Struct,
|
||||
):
|
||||
# For our repeated and map view objects, simply return the
|
||||
# underlying pb.
|
||||
if isinstance(value, (Repeated, MapComposite)):
|
||||
return value.pb
|
||||
|
||||
# Convert lists and tuples recursively.
|
||||
if isinstance(value, (list, tuple)):
|
||||
return type(value)(self.to_proto(proto_type, i) for i in value)
|
||||
|
||||
# Convert dictionaries recursively when the proto type is a map.
|
||||
# This is slightly more complicated than converting a list or tuple
|
||||
# because we have to step through the magic that protocol buffers does.
|
||||
#
|
||||
# Essentially, a type of map<string, Foo> will show up here as
|
||||
# a FoosEntry with a `key` field, `value` field, and a `map_entry`
|
||||
# annotation. We need to do the conversion based on the `value`
|
||||
# field's type.
|
||||
if isinstance(value, dict) and (
|
||||
proto_type.DESCRIPTOR.has_options
|
||||
and proto_type.DESCRIPTOR.GetOptions().map_entry
|
||||
):
|
||||
recursive_type = type(proto_type().value)
|
||||
return {k: self.to_proto(recursive_type, v) for k, v in value.items()}
|
||||
|
||||
pb_value = self.get_rule(proto_type=proto_type).to_proto(value)
|
||||
|
||||
# Sanity check: If we are in strict mode, did we get the value we want?
|
||||
if strict and not isinstance(pb_value, proto_type):
|
||||
raise TypeError(
|
||||
"Parameter must be instance of the same class; "
|
||||
"expected {expected}, got {got}".format(
|
||||
expected=proto_type.__name__,
|
||||
got=pb_value.__class__.__name__,
|
||||
),
|
||||
)
|
||||
# Return the final value.
|
||||
return pb_value
|
||||
|
||||
|
||||
class Marshal(BaseMarshal):
|
||||
"""The translator between protocol buffer and Python instances.
|
||||
|
||||
The bulk of the implementation is in :class:`BaseMarshal`. This class
|
||||
adds identity tracking: multiple instantiations of :class:`Marshal` with
|
||||
the same name will provide the same instance.
|
||||
"""
|
||||
|
||||
_instances = {}
|
||||
|
||||
def __new__(cls, *, name: str):
|
||||
"""Create a marshal instance.
|
||||
|
||||
Args:
|
||||
name (str): The name of the marshal. Instantiating multiple
|
||||
marshals with the same ``name`` argument will provide the
|
||||
same marshal each time.
|
||||
"""
|
||||
klass = cls._instances.get(name)
|
||||
if klass is None:
|
||||
klass = cls._instances[name] = super().__new__(cls)
|
||||
|
||||
return klass
|
||||
|
||||
def __init__(self, *, name: str):
|
||||
"""Instantiate a marshal.
|
||||
|
||||
Args:
|
||||
name (str): The name of the marshal. Instantiating multiple
|
||||
marshals with the same ``name`` argument will provide the
|
||||
same marshal each time.
|
||||
"""
|
||||
self._name = name
|
||||
if not hasattr(self, "_rules"):
|
||||
super().__init__()
|
||||
|
||||
|
||||
class NoopRule:
|
||||
"""A catch-all rule that does nothing."""
|
||||
|
||||
def to_python(self, pb_value, *, absent: bool = None):
|
||||
return pb_value
|
||||
|
||||
def to_proto(self, value):
|
||||
return value
|
||||
|
||||
|
||||
__all__ = ("Marshal",)
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,44 @@
|
||||
# Copyright (C) 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import base64
|
||||
|
||||
|
||||
class BytesRule:
|
||||
"""A marshal between Python strings and protobuf bytes.
|
||||
|
||||
Note: this conversion is asymmetric because Python does have a bytes type.
|
||||
It is sometimes necessary to convert proto bytes fields to strings, e.g. for
|
||||
JSON encoding, marshalling a message to a dict. Because bytes fields can
|
||||
represent arbitrary data, bytes fields are base64 encoded when they need to
|
||||
be represented as strings.
|
||||
|
||||
It is necessary to have the conversion be bidirectional, i.e.
|
||||
my_message == MyMessage(MyMessage.to_dict(my_message))
|
||||
|
||||
To accomplish this, we need to intercept assignments from strings and
|
||||
base64 decode them back into bytes.
|
||||
"""
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
if isinstance(value, str):
|
||||
value = value.encode("utf-8")
|
||||
value += b"=" * (4 - len(value) % 4) # padding
|
||||
value = base64.urlsafe_b64decode(value)
|
||||
|
||||
return value
|
||||
@@ -0,0 +1,85 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import timezone
|
||||
|
||||
from google.protobuf import duration_pb2
|
||||
from google.protobuf import timestamp_pb2
|
||||
from proto import datetime_helpers, utils
|
||||
|
||||
|
||||
class TimestampRule:
|
||||
"""A marshal between Python datetimes and protobuf timestamps.
|
||||
|
||||
Note: Python datetimes are less precise than protobuf datetimes
|
||||
(microsecond vs. nanosecond level precision). If nanosecond-level
|
||||
precision matters, it is recommended to interact with the internal
|
||||
proto directly.
|
||||
"""
|
||||
|
||||
def to_python(
|
||||
self, value, *, absent: bool = None
|
||||
) -> datetime_helpers.DatetimeWithNanoseconds:
|
||||
if isinstance(value, timestamp_pb2.Timestamp):
|
||||
if absent:
|
||||
return None
|
||||
return datetime_helpers.DatetimeWithNanoseconds.from_timestamp_pb(value)
|
||||
return value
|
||||
|
||||
def to_proto(self, value) -> timestamp_pb2.Timestamp:
|
||||
if isinstance(value, datetime_helpers.DatetimeWithNanoseconds):
|
||||
return value.timestamp_pb()
|
||||
if isinstance(value, datetime):
|
||||
return timestamp_pb2.Timestamp(
|
||||
seconds=int(value.timestamp()),
|
||||
nanos=value.microsecond * 1000,
|
||||
)
|
||||
if isinstance(value, str):
|
||||
timestamp_value = timestamp_pb2.Timestamp()
|
||||
timestamp_value.FromJsonString(value=value)
|
||||
return timestamp_value
|
||||
return value
|
||||
|
||||
|
||||
class DurationRule:
|
||||
"""A marshal between Python timedeltas and protobuf durations.
|
||||
|
||||
Note: Python timedeltas are less precise than protobuf durations
|
||||
(microsecond vs. nanosecond level precision). If nanosecond-level
|
||||
precision matters, it is recommended to interact with the internal
|
||||
proto directly.
|
||||
"""
|
||||
|
||||
def to_python(self, value, *, absent: bool = None) -> timedelta:
|
||||
if isinstance(value, duration_pb2.Duration):
|
||||
return timedelta(
|
||||
days=value.seconds // 86400,
|
||||
seconds=value.seconds % 86400,
|
||||
microseconds=value.nanos // 1000,
|
||||
)
|
||||
return value
|
||||
|
||||
def to_proto(self, value) -> duration_pb2.Duration:
|
||||
if isinstance(value, timedelta):
|
||||
return duration_pb2.Duration(
|
||||
seconds=value.days * 86400 + value.seconds,
|
||||
nanos=value.microseconds * 1000,
|
||||
)
|
||||
if isinstance(value, str):
|
||||
duration_value = duration_pb2.Duration()
|
||||
duration_value.FromJsonString(value=value)
|
||||
return duration_value
|
||||
return value
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Type
|
||||
import enum
|
||||
import warnings
|
||||
|
||||
|
||||
class EnumRule:
|
||||
"""A marshal for converting between integer values and enum values."""
|
||||
|
||||
def __init__(self, enum_class: Type[enum.IntEnum]):
|
||||
self._enum = enum_class
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
if isinstance(value, int) and not isinstance(value, self._enum):
|
||||
try:
|
||||
# Coerce the int on the wire to the enum value.
|
||||
return self._enum(value)
|
||||
except ValueError:
|
||||
# Since it is possible to add values to enums, we do
|
||||
# not want to flatly error on this.
|
||||
#
|
||||
# However, it is useful to make some noise about it so
|
||||
# the user realizes that an unexpected value came along.
|
||||
warnings.warn(
|
||||
"Unrecognized {name} enum value: {value}".format(
|
||||
name=self._enum.__name__,
|
||||
value=value,
|
||||
)
|
||||
)
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
# Accept enum values and coerce to the pure integer.
|
||||
# This is not strictly necessary (protocol buffers can take these
|
||||
# objects as they subclass int) but nevertheless seems like the
|
||||
# right thing to do.
|
||||
if isinstance(value, self._enum):
|
||||
return value.value
|
||||
|
||||
# If a string is provided that matches an enum value, coerce it
|
||||
# to the enum value.
|
||||
if isinstance(value, str):
|
||||
return self._enum[value].value
|
||||
|
||||
# We got a pure integer; pass it on.
|
||||
return value
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from google.protobuf import field_mask_pb2
|
||||
|
||||
|
||||
class FieldMaskRule:
|
||||
"""A marshal between FieldMask and strings.
|
||||
|
||||
See https://github.com/googleapis/proto-plus-python/issues/333
|
||||
and
|
||||
https://developers.google.com/protocol-buffers/docs/proto3#json
|
||||
for more details.
|
||||
"""
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
if isinstance(value, str):
|
||||
field_mask_value = field_mask_pb2.FieldMask()
|
||||
field_mask_value.FromJsonString(value=value)
|
||||
return field_mask_value
|
||||
|
||||
return value
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
class MessageRule:
|
||||
"""A marshal for converting between a descriptor and proto.Message."""
|
||||
|
||||
def __init__(self, descriptor: type, wrapper: type):
|
||||
self._descriptor = descriptor
|
||||
self._wrapper = wrapper
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
if isinstance(value, self._descriptor):
|
||||
return self._wrapper.wrap(value)
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
if isinstance(value, self._wrapper):
|
||||
return self._wrapper.pb(value)
|
||||
if isinstance(value, dict) and not self.is_map:
|
||||
# We need to use the wrapper's marshaling to handle
|
||||
# potentially problematic nested messages.
|
||||
try:
|
||||
# Try the fast path first.
|
||||
return self._descriptor(**value)
|
||||
except (TypeError, ValueError, AttributeError) as ex:
|
||||
# If we have a TypeError, ValueError or AttributeError,
|
||||
# try the slow path in case the error
|
||||
# was:
|
||||
# - an int64/string issue.
|
||||
# - a missing key issue in case a key only exists with a `_` suffix.
|
||||
# See related issue: https://github.com/googleapis/python-api-core/issues/227.
|
||||
# - a missing key issue due to nested struct. See: https://github.com/googleapis/proto-plus-python/issues/424.
|
||||
# - a missing key issue due to nested duration. See: https://github.com/googleapis/google-cloud-python/issues/13350.
|
||||
return self._wrapper(value)._pb
|
||||
return value
|
||||
|
||||
@property
|
||||
def is_map(self):
|
||||
"""Return True if the descriptor is a map entry, False otherwise."""
|
||||
desc = self._descriptor.DESCRIPTOR
|
||||
return desc.has_options and desc.GetOptions().map_entry
|
||||
@@ -0,0 +1,71 @@
|
||||
# Copyright (C) 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from proto.primitives import ProtoType
|
||||
|
||||
|
||||
class StringyNumberRule:
|
||||
"""A marshal between certain numeric types and strings
|
||||
|
||||
This is a necessary hack to allow round trip conversion
|
||||
from messages to dicts back to messages.
|
||||
|
||||
See https://github.com/protocolbuffers/protobuf/issues/2679
|
||||
and
|
||||
https://developers.google.com/protocol-buffers/docs/proto3#json
|
||||
for more details.
|
||||
"""
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
if value is not None:
|
||||
return self._python_type(value)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class Int64Rule(StringyNumberRule):
|
||||
_python_type = int
|
||||
_proto_type = ProtoType.INT64
|
||||
|
||||
|
||||
class UInt64Rule(StringyNumberRule):
|
||||
_python_type = int
|
||||
_proto_type = ProtoType.UINT64
|
||||
|
||||
|
||||
class SInt64Rule(StringyNumberRule):
|
||||
_python_type = int
|
||||
_proto_type = ProtoType.SINT64
|
||||
|
||||
|
||||
class Fixed64Rule(StringyNumberRule):
|
||||
_python_type = int
|
||||
_proto_type = ProtoType.FIXED64
|
||||
|
||||
|
||||
class SFixed64Rule(StringyNumberRule):
|
||||
_python_type = int
|
||||
_proto_type = ProtoType.SFIXED64
|
||||
|
||||
|
||||
STRINGY_NUMBER_RULES = [
|
||||
Int64Rule,
|
||||
UInt64Rule,
|
||||
SInt64Rule,
|
||||
Fixed64Rule,
|
||||
SFixed64Rule,
|
||||
]
|
||||
143
.venv/lib/python3.10/site-packages/proto/marshal/rules/struct.py
Normal file
143
.venv/lib/python3.10/site-packages/proto/marshal/rules/struct.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import collections.abc
|
||||
|
||||
from google.protobuf import struct_pb2
|
||||
|
||||
from proto.marshal.collections import maps
|
||||
from proto.marshal.collections import repeated
|
||||
|
||||
|
||||
class ValueRule:
|
||||
"""A rule to marshal between google.protobuf.Value and Python values."""
|
||||
|
||||
def __init__(self, *, marshal):
|
||||
self._marshal = marshal
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
"""Coerce the given value to the appropriate Python type.
|
||||
|
||||
Note that both NullValue and absent fields return None.
|
||||
In order to disambiguate between these two options,
|
||||
use containment check,
|
||||
E.g.
|
||||
"value" in foo
|
||||
which is True for NullValue and False for an absent value.
|
||||
"""
|
||||
kind = value.WhichOneof("kind")
|
||||
if kind == "null_value" or absent:
|
||||
return None
|
||||
if kind == "bool_value":
|
||||
return bool(value.bool_value)
|
||||
if kind == "number_value":
|
||||
return float(value.number_value)
|
||||
if kind == "string_value":
|
||||
return str(value.string_value)
|
||||
if kind == "struct_value":
|
||||
return self._marshal.to_python(
|
||||
struct_pb2.Struct,
|
||||
value.struct_value,
|
||||
absent=False,
|
||||
)
|
||||
if kind == "list_value":
|
||||
return self._marshal.to_python(
|
||||
struct_pb2.ListValue,
|
||||
value.list_value,
|
||||
absent=False,
|
||||
)
|
||||
# If more variants are ever added, we want to fail loudly
|
||||
# instead of tacitly returning None.
|
||||
raise ValueError("Unexpected kind: %s" % kind) # pragma: NO COVER
|
||||
|
||||
def to_proto(self, value) -> struct_pb2.Value:
|
||||
"""Return a protobuf Value object representing this value."""
|
||||
if isinstance(value, struct_pb2.Value):
|
||||
return value
|
||||
if value is None:
|
||||
return struct_pb2.Value(null_value=0)
|
||||
if isinstance(value, bool):
|
||||
return struct_pb2.Value(bool_value=value)
|
||||
if isinstance(value, (int, float)):
|
||||
return struct_pb2.Value(number_value=float(value))
|
||||
if isinstance(value, str):
|
||||
return struct_pb2.Value(string_value=value)
|
||||
if isinstance(value, collections.abc.Sequence):
|
||||
return struct_pb2.Value(
|
||||
list_value=self._marshal.to_proto(struct_pb2.ListValue, value),
|
||||
)
|
||||
if isinstance(value, collections.abc.Mapping):
|
||||
return struct_pb2.Value(
|
||||
struct_value=self._marshal.to_proto(struct_pb2.Struct, value),
|
||||
)
|
||||
raise ValueError("Unable to coerce value: %r" % value)
|
||||
|
||||
|
||||
class ListValueRule:
|
||||
"""A rule translating google.protobuf.ListValue and list-like objects."""
|
||||
|
||||
def __init__(self, *, marshal):
|
||||
self._marshal = marshal
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
"""Coerce the given value to a Python sequence."""
|
||||
return (
|
||||
None
|
||||
if absent
|
||||
else repeated.RepeatedComposite(value.values, marshal=self._marshal)
|
||||
)
|
||||
|
||||
def to_proto(self, value) -> struct_pb2.ListValue:
|
||||
# We got a proto, or else something we sent originally.
|
||||
# Preserve the instance we have.
|
||||
if isinstance(value, struct_pb2.ListValue):
|
||||
return value
|
||||
if isinstance(value, repeated.RepeatedComposite):
|
||||
return struct_pb2.ListValue(values=[v for v in value.pb])
|
||||
|
||||
# We got a list (or something list-like); convert it.
|
||||
return struct_pb2.ListValue(
|
||||
values=[self._marshal.to_proto(struct_pb2.Value, v) for v in value]
|
||||
)
|
||||
|
||||
|
||||
class StructRule:
|
||||
"""A rule translating google.protobuf.Struct and dict-like objects."""
|
||||
|
||||
def __init__(self, *, marshal):
|
||||
self._marshal = marshal
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
"""Coerce the given value to a Python mapping."""
|
||||
return (
|
||||
None if absent else maps.MapComposite(value.fields, marshal=self._marshal)
|
||||
)
|
||||
|
||||
def to_proto(self, value) -> struct_pb2.Struct:
|
||||
# We got a proto, or else something we sent originally.
|
||||
# Preserve the instance we have.
|
||||
if isinstance(value, struct_pb2.Struct):
|
||||
return value
|
||||
if isinstance(value, maps.MapComposite):
|
||||
return struct_pb2.Struct(
|
||||
fields={k: v for k, v in value.pb.items()},
|
||||
)
|
||||
|
||||
# We got a dict (or something dict-like); convert it.
|
||||
answer = struct_pb2.Struct(
|
||||
fields={
|
||||
k: self._marshal.to_proto(struct_pb2.Value, v) for k, v in value.items()
|
||||
}
|
||||
)
|
||||
return answer
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from google.protobuf import wrappers_pb2
|
||||
|
||||
|
||||
class WrapperRule:
|
||||
"""A marshal for converting the protobuf wrapper classes to Python.
|
||||
|
||||
This class converts between ``google.protobuf.BoolValue``,
|
||||
``google.protobuf.StringValue``, and their siblings to the appropriate
|
||||
Python equivalents.
|
||||
|
||||
These are effectively similar to the protobuf primitives except
|
||||
that None becomes a possible value.
|
||||
"""
|
||||
|
||||
def to_python(self, value, *, absent: bool = None):
|
||||
if isinstance(value, self._proto_type):
|
||||
if absent:
|
||||
return None
|
||||
return value.value
|
||||
return value
|
||||
|
||||
def to_proto(self, value):
|
||||
if isinstance(value, self._python_type):
|
||||
return self._proto_type(value=value)
|
||||
return value
|
||||
|
||||
|
||||
class DoubleValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.DoubleValue
|
||||
_python_type = float
|
||||
|
||||
|
||||
class FloatValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.FloatValue
|
||||
_python_type = float
|
||||
|
||||
|
||||
class Int64ValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.Int64Value
|
||||
_python_type = int
|
||||
|
||||
|
||||
class UInt64ValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.UInt64Value
|
||||
_python_type = int
|
||||
|
||||
|
||||
class Int32ValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.Int32Value
|
||||
_python_type = int
|
||||
|
||||
|
||||
class UInt32ValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.UInt32Value
|
||||
_python_type = int
|
||||
|
||||
|
||||
class BoolValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.BoolValue
|
||||
_python_type = bool
|
||||
|
||||
|
||||
class StringValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.StringValue
|
||||
_python_type = str
|
||||
|
||||
|
||||
class BytesValueRule(WrapperRule):
|
||||
_proto_type = wrappers_pb2.BytesValue
|
||||
_python_type = bytes
|
||||
Reference in New Issue
Block a user