parent
eb6ebf9dd0
commit
23f1481386
100 changed files with 2088 additions and 10482 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,333 @@ |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Contains FieldMask class.""" |
||||
|
||||
from google.protobuf.descriptor import FieldDescriptor |
||||
|
||||
|
||||
class FieldMask(object): |
||||
"""Class for FieldMask message type.""" |
||||
|
||||
__slots__ = () |
||||
|
||||
def ToJsonString(self): |
||||
"""Converts FieldMask to string according to proto3 JSON spec.""" |
||||
camelcase_paths = [] |
||||
for path in self.paths: |
||||
camelcase_paths.append(_SnakeCaseToCamelCase(path)) |
||||
return ','.join(camelcase_paths) |
||||
|
||||
def FromJsonString(self, value): |
||||
"""Converts string to FieldMask according to proto3 JSON spec.""" |
||||
if not isinstance(value, str): |
||||
raise ValueError('FieldMask JSON value not a string: {!r}'.format(value)) |
||||
self.Clear() |
||||
if value: |
||||
for path in value.split(','): |
||||
self.paths.append(_CamelCaseToSnakeCase(path)) |
||||
|
||||
def IsValidForDescriptor(self, message_descriptor): |
||||
"""Checks whether the FieldMask is valid for Message Descriptor.""" |
||||
for path in self.paths: |
||||
if not _IsValidPath(message_descriptor, path): |
||||
return False |
||||
return True |
||||
|
||||
def AllFieldsFromDescriptor(self, message_descriptor): |
||||
"""Gets all direct fields of Message Descriptor to FieldMask.""" |
||||
self.Clear() |
||||
for field in message_descriptor.fields: |
||||
self.paths.append(field.name) |
||||
|
||||
def CanonicalFormFromMask(self, mask): |
||||
"""Converts a FieldMask to the canonical form. |
||||
|
||||
Removes paths that are covered by another path. For example, |
||||
"foo.bar" is covered by "foo" and will be removed if "foo" |
||||
is also in the FieldMask. Then sorts all paths in alphabetical order. |
||||
|
||||
Args: |
||||
mask: The original FieldMask to be converted. |
||||
""" |
||||
tree = _FieldMaskTree(mask) |
||||
tree.ToFieldMask(self) |
||||
|
||||
def Union(self, mask1, mask2): |
||||
"""Merges mask1 and mask2 into this FieldMask.""" |
||||
_CheckFieldMaskMessage(mask1) |
||||
_CheckFieldMaskMessage(mask2) |
||||
tree = _FieldMaskTree(mask1) |
||||
tree.MergeFromFieldMask(mask2) |
||||
tree.ToFieldMask(self) |
||||
|
||||
def Intersect(self, mask1, mask2): |
||||
"""Intersects mask1 and mask2 into this FieldMask.""" |
||||
_CheckFieldMaskMessage(mask1) |
||||
_CheckFieldMaskMessage(mask2) |
||||
tree = _FieldMaskTree(mask1) |
||||
intersection = _FieldMaskTree() |
||||
for path in mask2.paths: |
||||
tree.IntersectPath(path, intersection) |
||||
intersection.ToFieldMask(self) |
||||
|
||||
def MergeMessage( |
||||
self, source, destination, |
||||
replace_message_field=False, replace_repeated_field=False): |
||||
"""Merges fields specified in FieldMask from source to destination. |
||||
|
||||
Args: |
||||
source: Source message. |
||||
destination: The destination message to be merged into. |
||||
replace_message_field: Replace message field if True. Merge message |
||||
field if False. |
||||
replace_repeated_field: Replace repeated field if True. Append |
||||
elements of repeated field if False. |
||||
""" |
||||
tree = _FieldMaskTree(self) |
||||
tree.MergeMessage( |
||||
source, destination, replace_message_field, replace_repeated_field) |
||||
|
||||
|
||||
def _IsValidPath(message_descriptor, path): |
||||
"""Checks whether the path is valid for Message Descriptor.""" |
||||
parts = path.split('.') |
||||
last = parts.pop() |
||||
for name in parts: |
||||
field = message_descriptor.fields_by_name.get(name) |
||||
if (field is None or |
||||
field.label == FieldDescriptor.LABEL_REPEATED or |
||||
field.type != FieldDescriptor.TYPE_MESSAGE): |
||||
return False |
||||
message_descriptor = field.message_type |
||||
return last in message_descriptor.fields_by_name |
||||
|
||||
|
||||
def _CheckFieldMaskMessage(message): |
||||
"""Raises ValueError if message is not a FieldMask.""" |
||||
message_descriptor = message.DESCRIPTOR |
||||
if (message_descriptor.name != 'FieldMask' or |
||||
message_descriptor.file.name != 'google/protobuf/field_mask.proto'): |
||||
raise ValueError('Message {0} is not a FieldMask.'.format( |
||||
message_descriptor.full_name)) |
||||
|
||||
|
||||
def _SnakeCaseToCamelCase(path_name): |
||||
"""Converts a path name from snake_case to camelCase.""" |
||||
result = [] |
||||
after_underscore = False |
||||
for c in path_name: |
||||
if c.isupper(): |
||||
raise ValueError( |
||||
'Fail to print FieldMask to Json string: Path name ' |
||||
'{0} must not contain uppercase letters.'.format(path_name)) |
||||
if after_underscore: |
||||
if c.islower(): |
||||
result.append(c.upper()) |
||||
after_underscore = False |
||||
else: |
||||
raise ValueError( |
||||
'Fail to print FieldMask to Json string: The ' |
||||
'character after a "_" must be a lowercase letter ' |
||||
'in path name {0}.'.format(path_name)) |
||||
elif c == '_': |
||||
after_underscore = True |
||||
else: |
||||
result += c |
||||
|
||||
if after_underscore: |
||||
raise ValueError('Fail to print FieldMask to Json string: Trailing "_" ' |
||||
'in path name {0}.'.format(path_name)) |
||||
return ''.join(result) |
||||
|
||||
|
||||
def _CamelCaseToSnakeCase(path_name): |
||||
"""Converts a field name from camelCase to snake_case.""" |
||||
result = [] |
||||
for c in path_name: |
||||
if c == '_': |
||||
raise ValueError('Fail to parse FieldMask: Path name ' |
||||
'{0} must not contain "_"s.'.format(path_name)) |
||||
if c.isupper(): |
||||
result += '_' |
||||
result += c.lower() |
||||
else: |
||||
result += c |
||||
return ''.join(result) |
||||
|
||||
|
||||
class _FieldMaskTree(object): |
||||
"""Represents a FieldMask in a tree structure. |
||||
|
||||
For example, given a FieldMask "foo.bar,foo.baz,bar.baz", |
||||
the FieldMaskTree will be: |
||||
[_root] -+- foo -+- bar |
||||
| | |
||||
| +- baz |
||||
| |
||||
+- bar --- baz |
||||
In the tree, each leaf node represents a field path. |
||||
""" |
||||
|
||||
__slots__ = ('_root',) |
||||
|
||||
def __init__(self, field_mask=None): |
||||
"""Initializes the tree by FieldMask.""" |
||||
self._root = {} |
||||
if field_mask: |
||||
self.MergeFromFieldMask(field_mask) |
||||
|
||||
def MergeFromFieldMask(self, field_mask): |
||||
"""Merges a FieldMask to the tree.""" |
||||
for path in field_mask.paths: |
||||
self.AddPath(path) |
||||
|
||||
def AddPath(self, path): |
||||
"""Adds a field path into the tree. |
||||
|
||||
If the field path to add is a sub-path of an existing field path |
||||
in the tree (i.e., a leaf node), it means the tree already matches |
||||
the given path so nothing will be added to the tree. If the path |
||||
matches an existing non-leaf node in the tree, that non-leaf node |
||||
will be turned into a leaf node with all its children removed because |
||||
the path matches all the node's children. Otherwise, a new path will |
||||
be added. |
||||
|
||||
Args: |
||||
path: The field path to add. |
||||
""" |
||||
node = self._root |
||||
for name in path.split('.'): |
||||
if name not in node: |
||||
node[name] = {} |
||||
elif not node[name]: |
||||
# Pre-existing empty node implies we already have this entire tree. |
||||
return |
||||
node = node[name] |
||||
# Remove any sub-trees we might have had. |
||||
node.clear() |
||||
|
||||
def ToFieldMask(self, field_mask): |
||||
"""Converts the tree to a FieldMask.""" |
||||
field_mask.Clear() |
||||
_AddFieldPaths(self._root, '', field_mask) |
||||
|
||||
def IntersectPath(self, path, intersection): |
||||
"""Calculates the intersection part of a field path with this tree. |
||||
|
||||
Args: |
||||
path: The field path to calculates. |
||||
intersection: The out tree to record the intersection part. |
||||
""" |
||||
node = self._root |
||||
for name in path.split('.'): |
||||
if name not in node: |
||||
return |
||||
elif not node[name]: |
||||
intersection.AddPath(path) |
||||
return |
||||
node = node[name] |
||||
intersection.AddLeafNodes(path, node) |
||||
|
||||
def AddLeafNodes(self, prefix, node): |
||||
"""Adds leaf nodes begin with prefix to this tree.""" |
||||
if not node: |
||||
self.AddPath(prefix) |
||||
for name in node: |
||||
child_path = prefix + '.' + name |
||||
self.AddLeafNodes(child_path, node[name]) |
||||
|
||||
def MergeMessage( |
||||
self, source, destination, |
||||
replace_message, replace_repeated): |
||||
"""Merge all fields specified by this tree from source to destination.""" |
||||
_MergeMessage( |
||||
self._root, source, destination, replace_message, replace_repeated) |
||||
|
||||
|
||||
def _StrConvert(value): |
||||
"""Converts value to str if it is not.""" |
||||
# This file is imported by c extension and some methods like ClearField |
||||
# requires string for the field name. py2/py3 has different text |
||||
# type and may use unicode. |
||||
if not isinstance(value, str): |
||||
return value.encode('utf-8') |
||||
return value |
||||
|
||||
|
||||
def _MergeMessage( |
||||
node, source, destination, replace_message, replace_repeated): |
||||
"""Merge all fields specified by a sub-tree from source to destination.""" |
||||
source_descriptor = source.DESCRIPTOR |
||||
for name in node: |
||||
child = node[name] |
||||
field = source_descriptor.fields_by_name[name] |
||||
if field is None: |
||||
raise ValueError('Error: Can\'t find field {0} in message {1}.'.format( |
||||
name, source_descriptor.full_name)) |
||||
if child: |
||||
# Sub-paths are only allowed for singular message fields. |
||||
if (field.label == FieldDescriptor.LABEL_REPEATED or |
||||
field.cpp_type != FieldDescriptor.CPPTYPE_MESSAGE): |
||||
raise ValueError('Error: Field {0} in message {1} is not a singular ' |
||||
'message field and cannot have sub-fields.'.format( |
||||
name, source_descriptor.full_name)) |
||||
if source.HasField(name): |
||||
_MergeMessage( |
||||
child, getattr(source, name), getattr(destination, name), |
||||
replace_message, replace_repeated) |
||||
continue |
||||
if field.label == FieldDescriptor.LABEL_REPEATED: |
||||
if replace_repeated: |
||||
destination.ClearField(_StrConvert(name)) |
||||
repeated_source = getattr(source, name) |
||||
repeated_destination = getattr(destination, name) |
||||
repeated_destination.MergeFrom(repeated_source) |
||||
else: |
||||
if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: |
||||
if replace_message: |
||||
destination.ClearField(_StrConvert(name)) |
||||
if source.HasField(name): |
||||
getattr(destination, name).MergeFrom(getattr(source, name)) |
||||
else: |
||||
setattr(destination, name, getattr(source, name)) |
||||
|
||||
|
||||
def _AddFieldPaths(node, prefix, field_mask): |
||||
"""Adds the field paths descended from node to field_mask.""" |
||||
if not node and prefix: |
||||
field_mask.paths.append(prefix) |
||||
return |
||||
for name in sorted(node): |
||||
if prefix: |
||||
child_path = prefix + '.' + name |
||||
else: |
||||
child_path = name |
||||
_AddFieldPaths(node[name], child_path, field_mask) |
@ -0,0 +1,400 @@ |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Test for google.protobuf.internal.well_known_types.""" |
||||
|
||||
import unittest |
||||
|
||||
from google.protobuf import field_mask_pb2 |
||||
from google.protobuf import map_unittest_pb2 |
||||
from google.protobuf import unittest_pb2 |
||||
from google.protobuf.internal import field_mask |
||||
from google.protobuf.internal import test_util |
||||
from google.protobuf import descriptor |
||||
|
||||
|
||||
class FieldMaskTest(unittest.TestCase): |
||||
|
||||
def testStringFormat(self): |
||||
mask = field_mask_pb2.FieldMask() |
||||
self.assertEqual('', mask.ToJsonString()) |
||||
mask.paths.append('foo') |
||||
self.assertEqual('foo', mask.ToJsonString()) |
||||
mask.paths.append('bar') |
||||
self.assertEqual('foo,bar', mask.ToJsonString()) |
||||
|
||||
mask.FromJsonString('') |
||||
self.assertEqual('', mask.ToJsonString()) |
||||
mask.FromJsonString('foo') |
||||
self.assertEqual(['foo'], mask.paths) |
||||
mask.FromJsonString('foo,bar') |
||||
self.assertEqual(['foo', 'bar'], mask.paths) |
||||
|
||||
# Test camel case |
||||
mask.Clear() |
||||
mask.paths.append('foo_bar') |
||||
self.assertEqual('fooBar', mask.ToJsonString()) |
||||
mask.paths.append('bar_quz') |
||||
self.assertEqual('fooBar,barQuz', mask.ToJsonString()) |
||||
|
||||
mask.FromJsonString('') |
||||
self.assertEqual('', mask.ToJsonString()) |
||||
self.assertEqual([], mask.paths) |
||||
mask.FromJsonString('fooBar') |
||||
self.assertEqual(['foo_bar'], mask.paths) |
||||
mask.FromJsonString('fooBar,barQuz') |
||||
self.assertEqual(['foo_bar', 'bar_quz'], mask.paths) |
||||
|
||||
def testDescriptorToFieldMask(self): |
||||
mask = field_mask_pb2.FieldMask() |
||||
msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR |
||||
mask.AllFieldsFromDescriptor(msg_descriptor) |
||||
self.assertEqual(76, len(mask.paths)) |
||||
self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) |
||||
for field in msg_descriptor.fields: |
||||
self.assertTrue(field.name in mask.paths) |
||||
|
||||
def testIsValidForDescriptor(self): |
||||
msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR |
||||
# Empty mask |
||||
mask = field_mask_pb2.FieldMask() |
||||
self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# All fields from descriptor |
||||
mask.AllFieldsFromDescriptor(msg_descriptor) |
||||
self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Child under optional message |
||||
mask.paths.append('optional_nested_message.bb') |
||||
self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Repeated field is only allowed in the last position of path |
||||
mask.paths.append('repeated_nested_message.bb') |
||||
self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Invalid top level field |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.paths.append('xxx') |
||||
self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Invalid field in root |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.paths.append('xxx.zzz') |
||||
self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Invalid field in internal node |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.paths.append('optional_nested_message.xxx.zzz') |
||||
self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) |
||||
# Invalid field in leaf |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.paths.append('optional_nested_message.xxx') |
||||
self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) |
||||
|
||||
def testCanonicalFrom(self): |
||||
mask = field_mask_pb2.FieldMask() |
||||
out_mask = field_mask_pb2.FieldMask() |
||||
# Paths will be sorted. |
||||
mask.FromJsonString('baz.quz,bar,foo') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('bar,baz.quz,foo', out_mask.ToJsonString()) |
||||
# Duplicated paths will be removed. |
||||
mask.FromJsonString('foo,bar,foo') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('bar,foo', out_mask.ToJsonString()) |
||||
# Sub-paths of other paths will be removed. |
||||
mask.FromJsonString('foo.b1,bar.b1,foo.b2,bar') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('bar,foo.b1,foo.b2', out_mask.ToJsonString()) |
||||
|
||||
# Test more deeply nested cases. |
||||
mask.FromJsonString( |
||||
'foo.bar.baz1,foo.bar.baz2.quz,foo.bar.baz2') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('foo.bar.baz1,foo.bar.baz2', |
||||
out_mask.ToJsonString()) |
||||
mask.FromJsonString( |
||||
'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('foo.bar.baz1,foo.bar.baz2', |
||||
out_mask.ToJsonString()) |
||||
mask.FromJsonString( |
||||
'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo.bar') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('foo.bar', out_mask.ToJsonString()) |
||||
mask.FromJsonString( |
||||
'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo') |
||||
out_mask.CanonicalFormFromMask(mask) |
||||
self.assertEqual('foo', out_mask.ToJsonString()) |
||||
|
||||
def testUnion(self): |
||||
mask1 = field_mask_pb2.FieldMask() |
||||
mask2 = field_mask_pb2.FieldMask() |
||||
out_mask = field_mask_pb2.FieldMask() |
||||
mask1.FromJsonString('foo,baz') |
||||
mask2.FromJsonString('bar,quz') |
||||
out_mask.Union(mask1, mask2) |
||||
self.assertEqual('bar,baz,foo,quz', out_mask.ToJsonString()) |
||||
# Overlap with duplicated paths. |
||||
mask1.FromJsonString('foo,baz.bb') |
||||
mask2.FromJsonString('baz.bb,quz') |
||||
out_mask.Union(mask1, mask2) |
||||
self.assertEqual('baz.bb,foo,quz', out_mask.ToJsonString()) |
||||
# Overlap with paths covering some other paths. |
||||
mask1.FromJsonString('foo.bar.baz,quz') |
||||
mask2.FromJsonString('foo.bar,bar') |
||||
out_mask.Union(mask1, mask2) |
||||
self.assertEqual('bar,foo.bar,quz', out_mask.ToJsonString()) |
||||
src = unittest_pb2.TestAllTypes() |
||||
with self.assertRaises(ValueError): |
||||
out_mask.Union(src, mask2) |
||||
|
||||
def testIntersect(self): |
||||
mask1 = field_mask_pb2.FieldMask() |
||||
mask2 = field_mask_pb2.FieldMask() |
||||
out_mask = field_mask_pb2.FieldMask() |
||||
# Test cases without overlapping. |
||||
mask1.FromJsonString('foo,baz') |
||||
mask2.FromJsonString('bar,quz') |
||||
out_mask.Intersect(mask1, mask2) |
||||
self.assertEqual('', out_mask.ToJsonString()) |
||||
self.assertEqual(len(out_mask.paths), 0) |
||||
self.assertEqual(out_mask.paths, []) |
||||
# Overlap with duplicated paths. |
||||
mask1.FromJsonString('foo,baz.bb') |
||||
mask2.FromJsonString('baz.bb,quz') |
||||
out_mask.Intersect(mask1, mask2) |
||||
self.assertEqual('baz.bb', out_mask.ToJsonString()) |
||||
# Overlap with paths covering some other paths. |
||||
mask1.FromJsonString('foo.bar.baz,quz') |
||||
mask2.FromJsonString('foo.bar,bar') |
||||
out_mask.Intersect(mask1, mask2) |
||||
self.assertEqual('foo.bar.baz', out_mask.ToJsonString()) |
||||
mask1.FromJsonString('foo.bar,bar') |
||||
mask2.FromJsonString('foo.bar.baz,quz') |
||||
out_mask.Intersect(mask1, mask2) |
||||
self.assertEqual('foo.bar.baz', out_mask.ToJsonString()) |
||||
# Intersect '' with '' |
||||
mask1.Clear() |
||||
mask2.Clear() |
||||
mask1.paths.append('') |
||||
mask2.paths.append('') |
||||
self.assertEqual(mask1.paths, ['']) |
||||
self.assertEqual('', mask1.ToJsonString()) |
||||
out_mask.Intersect(mask1, mask2) |
||||
self.assertEqual(out_mask.paths, []) |
||||
|
||||
def testMergeMessageWithoutMapFields(self): |
||||
# Test merge one field. |
||||
src = unittest_pb2.TestAllTypes() |
||||
test_util.SetAllFields(src) |
||||
for field in src.DESCRIPTOR.fields: |
||||
if field.containing_oneof: |
||||
continue |
||||
field_name = field.name |
||||
dst = unittest_pb2.TestAllTypes() |
||||
# Only set one path to mask. |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.paths.append(field_name) |
||||
mask.MergeMessage(src, dst) |
||||
# The expected result message. |
||||
msg = unittest_pb2.TestAllTypes() |
||||
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: |
||||
repeated_src = getattr(src, field_name) |
||||
repeated_msg = getattr(msg, field_name) |
||||
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
||||
for item in repeated_src: |
||||
repeated_msg.add().CopyFrom(item) |
||||
else: |
||||
repeated_msg.extend(repeated_src) |
||||
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
||||
getattr(msg, field_name).CopyFrom(getattr(src, field_name)) |
||||
else: |
||||
setattr(msg, field_name, getattr(src, field_name)) |
||||
# Only field specified in mask is merged. |
||||
self.assertEqual(msg, dst) |
||||
|
||||
# Test merge nested fields. |
||||
nested_src = unittest_pb2.NestedTestAllTypes() |
||||
nested_dst = unittest_pb2.NestedTestAllTypes() |
||||
nested_src.child.payload.optional_int32 = 1234 |
||||
nested_src.child.child.payload.optional_int32 = 5678 |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.FromJsonString('child.payload') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(1234, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(0, nested_dst.child.child.payload.optional_int32) |
||||
|
||||
mask.FromJsonString('child.child.payload') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(1234, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) |
||||
|
||||
nested_dst.Clear() |
||||
mask.FromJsonString('child.child.payload') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(0, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) |
||||
|
||||
nested_dst.Clear() |
||||
mask.FromJsonString('child') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(1234, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) |
||||
|
||||
# Test MergeOptions. |
||||
nested_dst.Clear() |
||||
nested_dst.child.payload.optional_int64 = 4321 |
||||
# Message fields will be merged by default. |
||||
mask.FromJsonString('child.payload') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(1234, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(4321, nested_dst.child.payload.optional_int64) |
||||
# Change the behavior to replace message fields. |
||||
mask.FromJsonString('child.payload') |
||||
mask.MergeMessage(nested_src, nested_dst, True, False) |
||||
self.assertEqual(1234, nested_dst.child.payload.optional_int32) |
||||
self.assertEqual(0, nested_dst.child.payload.optional_int64) |
||||
|
||||
# By default, fields missing in source are not cleared in destination. |
||||
nested_dst.payload.optional_int32 = 1234 |
||||
self.assertTrue(nested_dst.HasField('payload')) |
||||
mask.FromJsonString('payload') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertTrue(nested_dst.HasField('payload')) |
||||
# But they are cleared when replacing message fields. |
||||
nested_dst.Clear() |
||||
nested_dst.payload.optional_int32 = 1234 |
||||
mask.FromJsonString('payload') |
||||
mask.MergeMessage(nested_src, nested_dst, True, False) |
||||
self.assertFalse(nested_dst.HasField('payload')) |
||||
|
||||
nested_src.payload.repeated_int32.append(1234) |
||||
nested_dst.payload.repeated_int32.append(5678) |
||||
# Repeated fields will be appended by default. |
||||
mask.FromJsonString('payload.repeatedInt32') |
||||
mask.MergeMessage(nested_src, nested_dst) |
||||
self.assertEqual(2, len(nested_dst.payload.repeated_int32)) |
||||
self.assertEqual(5678, nested_dst.payload.repeated_int32[0]) |
||||
self.assertEqual(1234, nested_dst.payload.repeated_int32[1]) |
||||
# Change the behavior to replace repeated fields. |
||||
mask.FromJsonString('payload.repeatedInt32') |
||||
mask.MergeMessage(nested_src, nested_dst, False, True) |
||||
self.assertEqual(1, len(nested_dst.payload.repeated_int32)) |
||||
self.assertEqual(1234, nested_dst.payload.repeated_int32[0]) |
||||
|
||||
# Test Merge oneof field. |
||||
new_msg = unittest_pb2.TestOneof2() |
||||
dst = unittest_pb2.TestOneof2() |
||||
dst.foo_message.moo_int = 1 |
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.FromJsonString('fooMessage,fooLazyMessage.mooInt') |
||||
mask.MergeMessage(new_msg, dst) |
||||
self.assertTrue(dst.HasField('foo_message')) |
||||
self.assertFalse(dst.HasField('foo_lazy_message')) |
||||
|
||||
def testMergeMessageWithMapField(self): |
||||
empty_map = map_unittest_pb2.TestRecursiveMapMessage() |
||||
src_level_2 = map_unittest_pb2.TestRecursiveMapMessage() |
||||
src_level_2.a['src level 2'].CopyFrom(empty_map) |
||||
src = map_unittest_pb2.TestRecursiveMapMessage() |
||||
src.a['common key'].CopyFrom(src_level_2) |
||||
src.a['src level 1'].CopyFrom(src_level_2) |
||||
|
||||
dst_level_2 = map_unittest_pb2.TestRecursiveMapMessage() |
||||
dst_level_2.a['dst level 2'].CopyFrom(empty_map) |
||||
dst = map_unittest_pb2.TestRecursiveMapMessage() |
||||
dst.a['common key'].CopyFrom(dst_level_2) |
||||
dst.a['dst level 1'].CopyFrom(empty_map) |
||||
|
||||
mask = field_mask_pb2.FieldMask() |
||||
mask.FromJsonString('a') |
||||
mask.MergeMessage(src, dst) |
||||
|
||||
# map from dst is replaced with map from src. |
||||
self.assertEqual(dst.a['common key'], src_level_2) |
||||
self.assertEqual(dst.a['src level 1'], src_level_2) |
||||
self.assertEqual(dst.a['dst level 1'], empty_map) |
||||
|
||||
def testMergeErrors(self): |
||||
src = unittest_pb2.TestAllTypes() |
||||
dst = unittest_pb2.TestAllTypes() |
||||
mask = field_mask_pb2.FieldMask() |
||||
test_util.SetAllFields(src) |
||||
mask.FromJsonString('optionalInt32.field') |
||||
with self.assertRaises(ValueError) as e: |
||||
mask.MergeMessage(src, dst) |
||||
self.assertEqual('Error: Field optional_int32 in message ' |
||||
'protobuf_unittest.TestAllTypes is not a singular ' |
||||
'message field and cannot have sub-fields.', |
||||
str(e.exception)) |
||||
|
||||
def testSnakeCaseToCamelCase(self): |
||||
self.assertEqual('fooBar', |
||||
field_mask._SnakeCaseToCamelCase('foo_bar')) |
||||
self.assertEqual('FooBar', |
||||
field_mask._SnakeCaseToCamelCase('_foo_bar')) |
||||
self.assertEqual('foo3Bar', |
||||
field_mask._SnakeCaseToCamelCase('foo3_bar')) |
||||
|
||||
# No uppercase letter is allowed. |
||||
self.assertRaisesRegex( |
||||
ValueError, |
||||
'Fail to print FieldMask to Json string: Path name Foo must ' |
||||
'not contain uppercase letters.', |
||||
field_mask._SnakeCaseToCamelCase, 'Foo') |
||||
# Any character after a "_" must be a lowercase letter. |
||||
# 1. "_" cannot be followed by another "_". |
||||
# 2. "_" cannot be followed by a digit. |
||||
# 3. "_" cannot appear as the last character. |
||||
self.assertRaisesRegex( |
||||
ValueError, |
||||
'Fail to print FieldMask to Json string: The character after a ' |
||||
'"_" must be a lowercase letter in path name foo__bar.', |
||||
field_mask._SnakeCaseToCamelCase, 'foo__bar') |
||||
self.assertRaisesRegex( |
||||
ValueError, |
||||
'Fail to print FieldMask to Json string: The character after a ' |
||||
'"_" must be a lowercase letter in path name foo_3bar.', |
||||
field_mask._SnakeCaseToCamelCase, 'foo_3bar') |
||||
self.assertRaisesRegex( |
||||
ValueError, |
||||
'Fail to print FieldMask to Json string: Trailing "_" in path ' |
||||
'name foo_bar_.', field_mask._SnakeCaseToCamelCase, 'foo_bar_') |
||||
|
||||
def testCamelCaseToSnakeCase(self): |
||||
self.assertEqual('foo_bar', |
||||
field_mask._CamelCaseToSnakeCase('fooBar')) |
||||
self.assertEqual('_foo_bar', |
||||
field_mask._CamelCaseToSnakeCase('FooBar')) |
||||
self.assertEqual('foo3_bar', |
||||
field_mask._CamelCaseToSnakeCase('foo3Bar')) |
||||
self.assertRaisesRegex( |
||||
ValueError, |
||||
'Fail to parse FieldMask: Path name foo_bar must not contain "_"s.', |
||||
field_mask._CamelCaseToSnakeCase, 'foo_bar') |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
File diff suppressed because it is too large
Load Diff
@ -1,143 +0,0 @@ |
||||
#!/usr/bin/python2.4 |
||||
# |
||||
# Copyright 2008 Google Inc. |
||||
# |
||||
# 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. |
||||
|
||||
# This file is used for testing. The original is at: |
||||
# http://code.google.com/p/pymox/ |
||||
|
||||
import inspect |
||||
|
||||
|
||||
class StubOutForTesting: |
||||
"""Sample Usage: |
||||
You want os.path.exists() to always return true during testing. |
||||
|
||||
stubs = StubOutForTesting() |
||||
stubs.Set(os.path, 'exists', lambda x: 1) |
||||
... |
||||
stubs.UnsetAll() |
||||
|
||||
The above changes os.path.exists into a lambda that returns 1. Once |
||||
the ... part of the code finishes, the UnsetAll() looks up the old value |
||||
of os.path.exists and restores it. |
||||
|
||||
""" |
||||
def __init__(self): |
||||
self.cache = [] |
||||
self.stubs = [] |
||||
|
||||
def __del__(self): |
||||
self.SmartUnsetAll() |
||||
self.UnsetAll() |
||||
|
||||
def SmartSet(self, obj, attr_name, new_attr): |
||||
"""Replace obj.attr_name with new_attr. This method is smart and works |
||||
at the module, class, and instance level while preserving proper |
||||
inheritance. It will not stub out C types however unless that has been |
||||
explicitly allowed by the type. |
||||
|
||||
This method supports the case where attr_name is a staticmethod or a |
||||
classmethod of obj. |
||||
|
||||
Notes: |
||||
- If obj is an instance, then it is its class that will actually be |
||||
stubbed. Note that the method Set() does not do that: if obj is |
||||
an instance, it (and not its class) will be stubbed. |
||||
- The stubbing is using the builtin getattr and setattr. So, the __get__ |
||||
and __set__ will be called when stubbing (TODO: A better idea would |
||||
probably be to manipulate obj.__dict__ instead of getattr() and |
||||
setattr()). |
||||
|
||||
Raises AttributeError if the attribute cannot be found. |
||||
""" |
||||
if (inspect.ismodule(obj) or |
||||
(not inspect.isclass(obj) and obj.__dict__.has_key(attr_name))): |
||||
orig_obj = obj |
||||
orig_attr = getattr(obj, attr_name) |
||||
|
||||
else: |
||||
if not inspect.isclass(obj): |
||||
mro = list(inspect.getmro(obj.__class__)) |
||||
else: |
||||
mro = list(inspect.getmro(obj)) |
||||
|
||||
mro.reverse() |
||||
|
||||
orig_attr = None |
||||
|
||||
for cls in mro: |
||||
try: |
||||
orig_obj = cls |
||||
orig_attr = getattr(obj, attr_name) |
||||
except AttributeError: |
||||
continue |
||||
|
||||
if orig_attr is None: |
||||
raise AttributeError("Attribute not found.") |
||||
|
||||
# Calling getattr() on a staticmethod transforms it to a 'normal' function. |
||||
# We need to ensure that we put it back as a staticmethod. |
||||
old_attribute = obj.__dict__.get(attr_name) |
||||
if old_attribute is not None and isinstance(old_attribute, staticmethod): |
||||
orig_attr = staticmethod(orig_attr) |
||||
|
||||
self.stubs.append((orig_obj, attr_name, orig_attr)) |
||||
setattr(orig_obj, attr_name, new_attr) |
||||
|
||||
def SmartUnsetAll(self): |
||||
"""Reverses all the SmartSet() calls, restoring things to their original |
||||
definition. Its okay to call SmartUnsetAll() repeatedly, as later calls |
||||
have no effect if no SmartSet() calls have been made. |
||||
|
||||
""" |
||||
self.stubs.reverse() |
||||
|
||||
for args in self.stubs: |
||||
setattr(*args) |
||||
|
||||
self.stubs = [] |
||||
|
||||
def Set(self, parent, child_name, new_child): |
||||
"""Replace child_name's old definition with new_child, in the context |
||||
of the given parent. The parent could be a module when the child is a |
||||
function at module scope. Or the parent could be a class when a class' |
||||
method is being replaced. The named child is set to new_child, while |
||||
the prior definition is saved away for later, when UnsetAll() is called. |
||||
|
||||
This method supports the case where child_name is a staticmethod or a |
||||
classmethod of parent. |
||||
""" |
||||
old_child = getattr(parent, child_name) |
||||
|
||||
old_attribute = parent.__dict__.get(child_name) |
||||
if old_attribute is not None and isinstance(old_attribute, staticmethod): |
||||
old_child = staticmethod(old_child) |
||||
|
||||
self.cache.append((parent, old_child, child_name)) |
||||
setattr(parent, child_name, new_child) |
||||
|
||||
def UnsetAll(self): |
||||
"""Reverses all the Set() calls, restoring things to their original |
||||
definition. Its okay to call UnsetAll() repeatedly, as later calls have |
||||
no effect if no Set() calls have been made. |
||||
|
||||
""" |
||||
# Undo calls to Set() in reverse order, in case Set() was called on the |
||||
# same arguments repeatedly (want the original call to be last one undone) |
||||
self.cache.reverse() |
||||
|
||||
for (parent, old_child, child_name) in self.cache: |
||||
setattr(parent, child_name, old_child) |
||||
self.cache = [] |
@ -1,68 +0,0 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// from google3/util/gtl/stl_util.h
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__ |
||||
#define GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__ |
||||
|
||||
#include <algorithm> |
||||
|
||||
#include "google/protobuf/stubs/common.h" |
||||
|
||||
// Must be last.
|
||||
#include "google/protobuf/port_def.inc" // NOLINT |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
|
||||
// Return a mutable char* pointing to a string's internal buffer,
|
||||
// which may not be null-terminated. Writing through this pointer will
|
||||
// modify the string.
|
||||
//
|
||||
// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
|
||||
// next call to a string method that invalidates iterators.
|
||||
//
|
||||
// As of 2006-04, there is no standard-blessed way of getting a
|
||||
// mutable reference to a string's internal buffer. However, issue 530
|
||||
// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)
|
||||
// proposes this as the method. According to Matt Austern, this should
|
||||
// already work on all current implementations.
|
||||
inline char* string_as_array(std::string* str) { |
||||
// DO NOT USE const_cast<char*>(str->data())! See the unittest for why.
|
||||
return str->empty() ? nullptr : &*str->begin(); |
||||
} |
||||
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#include "google/protobuf/port_undef.inc" // NOLINT |
||||
|
||||
#endif // GOOGLE_PROTOBUF_STUBS_STL_UTIL_H__
|
@ -0,0 +1 @@ |
||||
Subproject commit 9059f5cad030ba11d37818847443a53918c327b1 |
@ -0,0 +1,37 @@ |
||||
licenses(["unencumbered"]) # Public Domain or MIT |
||||
|
||||
exports_files(["LICENSE"]) |
||||
|
||||
cc_library( |
||||
name = "jsoncpp", |
||||
srcs = [ |
||||
"src/lib_json/json_reader.cpp", |
||||
"src/lib_json/json_tool.h", |
||||
"src/lib_json/json_value.cpp", |
||||
"src/lib_json/json_writer.cpp", |
||||
], |
||||
hdrs = [ |
||||
"include/json/allocator.h", |
||||
"include/json/assertions.h", |
||||
"include/json/config.h", |
||||
"include/json/json_features.h", |
||||
"include/json/forwards.h", |
||||
"include/json/json.h", |
||||
"include/json/reader.h", |
||||
"include/json/value.h", |
||||
"include/json/version.h", |
||||
"include/json/writer.h", |
||||
], |
||||
copts = [ |
||||
"-DJSON_USE_EXCEPTION=0", |
||||
"-DJSON_HAS_INT64", |
||||
], |
||||
includes = ["include"], |
||||
visibility = ["//visibility:public"], |
||||
deps = [":private"], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "private", |
||||
textual_hdrs = ["src/lib_json/json_valueiterator.inl"], |
||||
) |
Loading…
Reference in new issue