| Index: tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/protourlencode.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/protourlencode.py b/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/protourlencode.py
|
| deleted file mode 100755
|
| index 9f6059e090178f6770a9c84f2a2b7af0a1171585..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/protourlencode.py
|
| +++ /dev/null
|
| @@ -1,563 +0,0 @@
|
| -#!/usr/bin/env python
|
| -#
|
| -# Copyright 2010 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.
|
| -#
|
| -
|
| -"""URL encoding support for messages types.
|
| -
|
| -Protocol support for URL encoded form parameters.
|
| -
|
| -Nested Fields:
|
| - Nested fields are repesented by dot separated names. For example, consider
|
| - the following messages:
|
| -
|
| - class WebPage(Message):
|
| -
|
| - title = StringField(1)
|
| - tags = StringField(2, repeated=True)
|
| -
|
| - class WebSite(Message):
|
| -
|
| - name = StringField(1)
|
| - home = MessageField(WebPage, 2)
|
| - pages = MessageField(WebPage, 3, repeated=True)
|
| -
|
| - And consider the object:
|
| -
|
| - page = WebPage()
|
| - page.title = 'Welcome to NewSite 2010'
|
| -
|
| - site = WebSite()
|
| - site.name = 'NewSite 2010'
|
| - site.home = page
|
| -
|
| - The URL encoded representation of this constellation of objects is.
|
| -
|
| - name=NewSite+2010&home.title=Welcome+to+NewSite+2010
|
| -
|
| - An object that exists but does not have any state can be represented with
|
| - a reference to its name alone with no value assigned to it. For example:
|
| -
|
| - page = WebSite()
|
| - page.name = 'My Empty Site'
|
| - page.home = WebPage()
|
| -
|
| - is represented as:
|
| -
|
| - name=My+Empty+Site&home=
|
| -
|
| - This represents a site with an empty uninitialized home page.
|
| -
|
| -Repeated Fields:
|
| - Repeated fields are represented by the name of and the index of each value
|
| - separated by a dash. For example, consider the following message:
|
| -
|
| - home = Page()
|
| - home.title = 'Nome'
|
| -
|
| - news = Page()
|
| - news.title = 'News'
|
| - news.tags = ['news', 'articles']
|
| -
|
| - instance = WebSite()
|
| - instance.name = 'Super fun site'
|
| - instance.pages = [home, news, preferences]
|
| -
|
| - An instance of this message can be represented as:
|
| -
|
| - name=Super+fun+site&page-0.title=Home&pages-1.title=News&...
|
| - pages-1.tags-0=new&pages-1.tags-1=articles
|
| -
|
| -Helper classes:
|
| -
|
| - URLEncodedRequestBuilder: Used for encapsulating the logic used for building
|
| - a request message from a URL encoded RPC.
|
| -"""
|
| -import six
|
| -
|
| -__author__ = 'rafek@google.com (Rafe Kaplan)'
|
| -
|
| -import cgi
|
| -import re
|
| -import urllib
|
| -
|
| -from . import message_types
|
| -from . import messages
|
| -from . import util
|
| -
|
| -__all__ = ['CONTENT_TYPE',
|
| - 'URLEncodedRequestBuilder',
|
| - 'encode_message',
|
| - 'decode_message',
|
| - ]
|
| -
|
| -CONTENT_TYPE = 'application/x-www-form-urlencoded'
|
| -
|
| -_FIELD_NAME_REGEX = re.compile(r'^([a-zA-Z_][a-zA-Z_0-9]*)(?:-([0-9]+))?$')
|
| -
|
| -
|
| -class URLEncodedRequestBuilder(object):
|
| - """Helper that encapsulates the logic used for building URL encoded messages.
|
| -
|
| - This helper is used to map query parameters from a URL encoded RPC to a
|
| - message instance.
|
| - """
|
| -
|
| - @util.positional(2)
|
| - def __init__(self, message, prefix=''):
|
| - """Constructor.
|
| -
|
| - Args:
|
| - message: Message instance to build from parameters.
|
| - prefix: Prefix expected at the start of valid parameters.
|
| - """
|
| - self.__parameter_prefix = prefix
|
| -
|
| - # The empty tuple indicates the root message, which has no path.
|
| - # __messages is a full cache that makes it very easy to look up message
|
| - # instances by their paths. See make_path for details about what a path
|
| - # is.
|
| - self.__messages = {(): message}
|
| -
|
| - # This is a cache that stores paths which have been checked for
|
| - # correctness. Correctness means that an index is present for repeated
|
| - # fields on the path and absent for non-repeated fields. The cache is
|
| - # also used to check that indexes are added in the right order so that
|
| - # dicontiguous ranges of indexes are ignored.
|
| - self.__checked_indexes = set([()])
|
| -
|
| - def make_path(self, parameter_name):
|
| - """Parse a parameter name and build a full path to a message value.
|
| -
|
| - The path of a method is a tuple of 2-tuples describing the names and
|
| - indexes within repeated fields from the root message (the message being
|
| - constructed by the builder) to an arbitrarily nested message within it.
|
| -
|
| - Each 2-tuple node of a path (name, index) is:
|
| - name: The name of the field that refers to the message instance.
|
| - index: The index within a repeated field that refers to the message
|
| - instance, None if not a repeated field.
|
| -
|
| - For example, consider:
|
| -
|
| - class VeryInner(messages.Message):
|
| - ...
|
| -
|
| - class Inner(messages.Message):
|
| -
|
| - very_inner = messages.MessageField(VeryInner, 1, repeated=True)
|
| -
|
| - class Outer(messages.Message):
|
| -
|
| - inner = messages.MessageField(Inner, 1)
|
| -
|
| - If this builder is building an instance of Outer, that instance is
|
| - referred to in the URL encoded parameters without a path. Therefore
|
| - its path is ().
|
| -
|
| - The child 'inner' is referred to by its path (('inner', None)).
|
| -
|
| - The first child of repeated field 'very_inner' on the Inner instance
|
| - is referred to by (('inner', None), ('very_inner', 0)).
|
| -
|
| - Examples:
|
| - # Correct reference to model where nation is a Message, district is
|
| - # repeated Message and county is any not repeated field type.
|
| - >>> make_path('nation.district-2.county')
|
| - (('nation', None), ('district', 2), ('county', None))
|
| -
|
| - # Field is not part of model.
|
| - >>> make_path('nation.made_up_field')
|
| - None
|
| -
|
| - # nation field is not repeated and index provided.
|
| - >>> make_path('nation-1')
|
| - None
|
| -
|
| - # district field is repeated and no index provided.
|
| - >>> make_path('nation.district')
|
| - None
|
| -
|
| - Args:
|
| - parameter_name: Name of query parameter as passed in from the request.
|
| - in order to make a path, this parameter_name must point to a valid
|
| - field within the message structure. Nodes of the path that refer to
|
| - repeated fields must be indexed with a number, non repeated nodes must
|
| - not have an index.
|
| -
|
| - Returns:
|
| - Parsed version of the parameter_name as a tuple of tuples:
|
| - attribute: Name of attribute associated with path.
|
| - index: Postitive integer index when it is a repeated field, else None.
|
| - Will return None if the parameter_name does not have the right prefix,
|
| - does not point to a field within the message structure, does not have
|
| - an index if it is a repeated field or has an index but is not a repeated
|
| - field.
|
| - """
|
| - if parameter_name.startswith(self.__parameter_prefix):
|
| - parameter_name = parameter_name[len(self.__parameter_prefix):]
|
| - else:
|
| - return None
|
| -
|
| - path = []
|
| - name = []
|
| - message_type = type(self.__messages[()]) # Get root message.
|
| -
|
| - for item in parameter_name.split('.'):
|
| - # This will catch sub_message.real_message_field.not_real_field
|
| - if not message_type:
|
| - return None
|
| -
|
| - item_match = _FIELD_NAME_REGEX.match(item)
|
| - if not item_match:
|
| - return None
|
| - attribute = item_match.group(1)
|
| - index = item_match.group(2)
|
| - if index:
|
| - index = int(index)
|
| -
|
| - try:
|
| - field = message_type.field_by_name(attribute)
|
| - except KeyError:
|
| - return None
|
| -
|
| - if field.repeated != (index is not None):
|
| - return None
|
| -
|
| - if isinstance(field, messages.MessageField):
|
| - message_type = field.message_type
|
| - else:
|
| - message_type = None
|
| -
|
| - # Path is valid so far. Append node and continue.
|
| - path.append((attribute, index))
|
| -
|
| - return tuple(path)
|
| -
|
| - def __check_index(self, parent_path, name, index):
|
| - """Check correct index use and value relative to a given path.
|
| -
|
| - Check that for a given path the index is present for repeated fields
|
| - and that it is in range for the existing list that it will be inserted
|
| - in to or appended to.
|
| -
|
| - Args:
|
| - parent_path: Path to check against name and index.
|
| - name: Name of field to check for existance.
|
| - index: Index to check. If field is repeated, should be a number within
|
| - range of the length of the field, or point to the next item for
|
| - appending.
|
| - """
|
| - # Don't worry about non-repeated fields.
|
| - # It's also ok if index is 0 because that means next insert will append.
|
| - if not index:
|
| - return True
|
| -
|
| - parent = self.__messages.get(parent_path, None)
|
| - value_list = getattr(parent, name, None)
|
| - # If the list does not exist then the index should be 0. Since it is
|
| - # not, path is not valid.
|
| - if not value_list:
|
| - return False
|
| -
|
| - # The index must either point to an element of the list or to the tail.
|
| - return len(value_list) >= index
|
| -
|
| - def __check_indexes(self, path):
|
| - """Check that all indexes are valid and in the right order.
|
| -
|
| - This method must iterate over the path and check that all references
|
| - to indexes point to an existing message or to the end of the list, meaning
|
| - the next value should be appended to the repeated field.
|
| -
|
| - Args:
|
| - path: Path to check indexes for. Tuple of 2-tuples (name, index). See
|
| - make_path for more information.
|
| -
|
| - Returns:
|
| - True if all the indexes of the path are within range, else False.
|
| - """
|
| - if path in self.__checked_indexes:
|
| - return True
|
| -
|
| - # Start with the root message.
|
| - parent_path = ()
|
| -
|
| - for name, index in path:
|
| - next_path = parent_path + ((name, index),)
|
| - # First look in the checked indexes cache.
|
| - if next_path not in self.__checked_indexes:
|
| - if not self.__check_index(parent_path, name, index):
|
| - return False
|
| - self.__checked_indexes.add(next_path)
|
| -
|
| - parent_path = next_path
|
| -
|
| - return True
|
| -
|
| - def __get_or_create_path(self, path):
|
| - """Get a message from the messages cache or create it and add it.
|
| -
|
| - This method will also create any parent messages based on the path.
|
| -
|
| - When a new instance of a given message is created, it is stored in
|
| - __message by its path.
|
| -
|
| - Args:
|
| - path: Path of message to get. Path must be valid, in other words
|
| - __check_index(path) returns true. Tuple of 2-tuples (name, index).
|
| - See make_path for more information.
|
| -
|
| - Returns:
|
| - Message instance if the field being pointed to by the path is a
|
| - message, else will return None for non-message fields.
|
| - """
|
| - message = self.__messages.get(path, None)
|
| - if message:
|
| - return message
|
| -
|
| - parent_path = ()
|
| - parent = self.__messages[()] # Get the root object
|
| -
|
| - for name, index in path:
|
| - field = parent.field_by_name(name)
|
| - next_path = parent_path + ((name, index),)
|
| - next_message = self.__messages.get(next_path, None)
|
| - if next_message is None:
|
| - next_message = field.message_type()
|
| - self.__messages[next_path] = next_message
|
| - if not field.repeated:
|
| - setattr(parent, field.name, next_message)
|
| - else:
|
| - list_value = getattr(parent, field.name, None)
|
| - if list_value is None:
|
| - setattr(parent, field.name, [next_message])
|
| - else:
|
| - list_value.append(next_message)
|
| -
|
| - parent_path = next_path
|
| - parent = next_message
|
| -
|
| - return parent
|
| -
|
| - def add_parameter(self, parameter, values):
|
| - """Add a single parameter.
|
| -
|
| - Adds a single parameter and its value to the request message.
|
| -
|
| - Args:
|
| - parameter: Query string parameter to map to request.
|
| - values: List of values to assign to request message.
|
| -
|
| - Returns:
|
| - True if parameter was valid and added to the message, else False.
|
| -
|
| - Raises:
|
| - DecodeError if the parameter refers to a valid field, and the values
|
| - parameter does not have one and only one value. Non-valid query
|
| - parameters may have multiple values and should not cause an error.
|
| - """
|
| - path = self.make_path(parameter)
|
| -
|
| - if not path:
|
| - return False
|
| -
|
| - # Must check that all indexes of all items in the path are correct before
|
| - # instantiating any of them. For example, consider:
|
| - #
|
| - # class Repeated(object):
|
| - # ...
|
| - #
|
| - # class Inner(object):
|
| - #
|
| - # repeated = messages.MessageField(Repeated, 1, repeated=True)
|
| - #
|
| - # class Outer(object):
|
| - #
|
| - # inner = messages.MessageField(Inner, 1)
|
| - #
|
| - # instance = Outer()
|
| - # builder = URLEncodedRequestBuilder(instance)
|
| - # builder.add_parameter('inner.repeated')
|
| - #
|
| - # assert not hasattr(instance, 'inner')
|
| - #
|
| - # The check is done relative to the instance of Outer pass in to the
|
| - # constructor of the builder. This instance is not referred to at all
|
| - # because all names are assumed to be relative to it.
|
| - #
|
| - # The 'repeated' part of the path is not correct because it is missing an
|
| - # index. Because it is missing an index, it should not create an instance
|
| - # of Repeated. In this case add_parameter will return False and have no
|
| - # side effects.
|
| - #
|
| - # A correct path that would cause a new Inner instance to be inserted at
|
| - # instance.inner and a new Repeated instance to be appended to the
|
| - # instance.inner.repeated list would be 'inner.repeated-0'.
|
| - if not self.__check_indexes(path):
|
| - return False
|
| -
|
| - # Ok to build objects.
|
| - parent_path = path[:-1]
|
| - parent = self.__get_or_create_path(parent_path)
|
| - name, index = path[-1]
|
| - field = parent.field_by_name(name)
|
| -
|
| - if len(values) != 1:
|
| - raise messages.DecodeError(
|
| - 'Found repeated values for field %s.' % field.name)
|
| -
|
| - value = values[0]
|
| -
|
| - if isinstance(field, messages.IntegerField):
|
| - converted_value = int(value)
|
| - elif isinstance(field, message_types.DateTimeField):
|
| - try:
|
| - converted_value = util.decode_datetime(value)
|
| - except ValueError as e:
|
| - raise messages.DecodeError(e)
|
| - elif isinstance(field, messages.MessageField):
|
| - # Just make sure it's instantiated. Assignment to field or
|
| - # appending to list is done in __get_or_create_path.
|
| - self.__get_or_create_path(path)
|
| - return True
|
| - elif isinstance(field, messages.StringField):
|
| - converted_value = value.decode('utf-8')
|
| - elif isinstance(field, messages.BooleanField):
|
| - converted_value = value.lower() == 'true' and True or False
|
| - else:
|
| - try:
|
| - converted_value = field.type(value)
|
| - except TypeError:
|
| - raise messages.DecodeError('Invalid enum value "%s"' % value)
|
| -
|
| - if field.repeated:
|
| - value_list = getattr(parent, field.name, None)
|
| - if value_list is None:
|
| - setattr(parent, field.name, [converted_value])
|
| - else:
|
| - if index == len(value_list):
|
| - value_list.append(converted_value)
|
| - else:
|
| - # Index should never be above len(value_list) because it was
|
| - # verified during the index check above.
|
| - value_list[index] = converted_value
|
| - else:
|
| - setattr(parent, field.name, converted_value)
|
| -
|
| - return True
|
| -
|
| -
|
| -@util.positional(1)
|
| -def encode_message(message, prefix=''):
|
| - """Encode Message instance to url-encoded string.
|
| -
|
| - Args:
|
| - message: Message instance to encode in to url-encoded string.
|
| - prefix: Prefix to append to field names of contained values.
|
| -
|
| - Returns:
|
| - String encoding of Message in URL encoded format.
|
| -
|
| - Raises:
|
| - messages.ValidationError if message is not initialized.
|
| - """
|
| - message.check_initialized()
|
| -
|
| - parameters = []
|
| - def build_message(parent, prefix):
|
| - """Recursively build parameter list for URL response.
|
| -
|
| - Args:
|
| - parent: Message to build parameters for.
|
| - prefix: Prefix to append to field names of contained values.
|
| -
|
| - Returns:
|
| - True if some value of parent was added to the parameters list,
|
| - else False, meaning the object contained no values.
|
| - """
|
| - has_any_values = False
|
| - for field in sorted(parent.all_fields(), key=lambda f: f.number):
|
| - next_value = parent.get_assigned_value(field.name)
|
| - if next_value is None:
|
| - continue
|
| -
|
| - # Found a value. Ultimate return value should be True.
|
| - has_any_values = True
|
| -
|
| - # Normalize all values in to a list.
|
| - if not field.repeated:
|
| - next_value = [next_value]
|
| -
|
| - for index, item in enumerate(next_value):
|
| - # Create a name with an index if it is a repeated field.
|
| - if field.repeated:
|
| - field_name = '%s%s-%s' % (prefix, field.name, index)
|
| - else:
|
| - field_name = prefix + field.name
|
| -
|
| - if isinstance(field, message_types.DateTimeField):
|
| - # DateTimeField stores its data as a RFC 3339 compliant string.
|
| - parameters.append((field_name, item.isoformat()))
|
| - elif isinstance(field, messages.MessageField):
|
| - # Message fields must be recursed in to in order to construct
|
| - # their component parameter values.
|
| - if not build_message(item, field_name + '.'):
|
| - # The nested message is empty. Append an empty value to
|
| - # represent it.
|
| - parameters.append((field_name, ''))
|
| - elif isinstance(field, messages.BooleanField):
|
| - parameters.append((field_name, item and 'true' or 'false'))
|
| - else:
|
| - if isinstance(item, six.text_type):
|
| - item = item.encode('utf-8')
|
| - parameters.append((field_name, str(item)))
|
| -
|
| - return has_any_values
|
| -
|
| - build_message(message, prefix)
|
| -
|
| - # Also add any unrecognized values from the decoded string.
|
| - for key in message.all_unrecognized_fields():
|
| - values, _ = message.get_unrecognized_field_info(key)
|
| - if not isinstance(values, (list, tuple)):
|
| - values = (values,)
|
| - for value in values:
|
| - parameters.append((key, value))
|
| -
|
| - return urllib.urlencode(parameters)
|
| -
|
| -
|
| -def decode_message(message_type, encoded_message, **kwargs):
|
| - """Decode urlencoded content to message.
|
| -
|
| - Args:
|
| - message_type: Message instance to merge URL encoded content into.
|
| - encoded_message: URL encoded message.
|
| - prefix: Prefix to append to field names of contained values.
|
| -
|
| - Returns:
|
| - Decoded instance of message_type.
|
| - """
|
| - message = message_type()
|
| - builder = URLEncodedRequestBuilder(message, **kwargs)
|
| - arguments = cgi.parse_qs(encoded_message, keep_blank_values=True)
|
| - for argument, values in sorted(six.iteritems(arguments)):
|
| - added = builder.add_parameter(argument, values)
|
| - # Save off any unknown values, so they're still accessible.
|
| - if not added:
|
| - message.set_unrecognized_field(argument, values, messages.Variant.STRING)
|
| - message.check_initialized()
|
| - return message
|
|
|