| Index: tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/util.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/util.py b/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/util.py
|
| deleted file mode 100755
|
| index 50893c9e14b642a861745825b97537d13808a119..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/util.py
|
| +++ /dev/null
|
| @@ -1,492 +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.
|
| -#
|
| -
|
| -"""Common utility library."""
|
| -
|
| -from __future__ import with_statement
|
| -import six
|
| -
|
| -__author__ = ['rafek@google.com (Rafe Kaplan)',
|
| - 'guido@google.com (Guido van Rossum)',
|
| -]
|
| -
|
| -import cgi
|
| -import datetime
|
| -import inspect
|
| -import os
|
| -import re
|
| -import sys
|
| -
|
| -__all__ = ['AcceptItem',
|
| - 'AcceptError',
|
| - 'Error',
|
| - 'choose_content_type',
|
| - 'decode_datetime',
|
| - 'get_package_for_module',
|
| - 'pad_string',
|
| - 'parse_accept_header',
|
| - 'positional',
|
| - 'PROTORPC_PROJECT_URL',
|
| - 'TimeZoneOffset',
|
| - 'total_seconds',
|
| -]
|
| -
|
| -
|
| -class Error(Exception):
|
| - """Base class for protorpc exceptions."""
|
| -
|
| -
|
| -class AcceptError(Error):
|
| - """Raised when there is an error parsing the accept header."""
|
| -
|
| -
|
| -PROTORPC_PROJECT_URL = 'http://code.google.com/p/google-protorpc'
|
| -
|
| -_TIME_ZONE_RE_STRING = r"""
|
| - # Examples:
|
| - # +01:00
|
| - # -05:30
|
| - # Z12:00
|
| - ((?P<z>Z) | (?P<sign>[-+])
|
| - (?P<hours>\d\d) :
|
| - (?P<minutes>\d\d))$
|
| -"""
|
| -_TIME_ZONE_RE = re.compile(_TIME_ZONE_RE_STRING, re.IGNORECASE | re.VERBOSE)
|
| -
|
| -
|
| -def pad_string(string):
|
| - """Pad a string for safe HTTP error responses.
|
| -
|
| - Prevents Internet Explorer from displaying their own error messages
|
| - when sent as the content of error responses.
|
| -
|
| - Args:
|
| - string: A string.
|
| -
|
| - Returns:
|
| - Formatted string left justified within a 512 byte field.
|
| - """
|
| - return string.ljust(512)
|
| -
|
| -
|
| -def positional(max_positional_args):
|
| - """A decorator to declare that only the first N arguments may be positional.
|
| -
|
| - This decorator makes it easy to support Python 3 style keyword-only
|
| - parameters. For example, in Python 3 it is possible to write:
|
| -
|
| - def fn(pos1, *, kwonly1=None, kwonly1=None):
|
| - ...
|
| -
|
| - All named parameters after * must be a keyword:
|
| -
|
| - fn(10, 'kw1', 'kw2') # Raises exception.
|
| - fn(10, kwonly1='kw1') # Ok.
|
| -
|
| - Example:
|
| - To define a function like above, do:
|
| -
|
| - @positional(1)
|
| - def fn(pos1, kwonly1=None, kwonly2=None):
|
| - ...
|
| -
|
| - If no default value is provided to a keyword argument, it becomes a required
|
| - keyword argument:
|
| -
|
| - @positional(0)
|
| - def fn(required_kw):
|
| - ...
|
| -
|
| - This must be called with the keyword parameter:
|
| -
|
| - fn() # Raises exception.
|
| - fn(10) # Raises exception.
|
| - fn(required_kw=10) # Ok.
|
| -
|
| - When defining instance or class methods always remember to account for
|
| - 'self' and 'cls':
|
| -
|
| - class MyClass(object):
|
| -
|
| - @positional(2)
|
| - def my_method(self, pos1, kwonly1=None):
|
| - ...
|
| -
|
| - @classmethod
|
| - @positional(2)
|
| - def my_method(cls, pos1, kwonly1=None):
|
| - ...
|
| -
|
| - One can omit the argument to 'positional' altogether, and then no
|
| - arguments with default values may be passed positionally. This
|
| - would be equivalent to placing a '*' before the first argument
|
| - with a default value in Python 3. If there are no arguments with
|
| - default values, and no argument is given to 'positional', an error
|
| - is raised.
|
| -
|
| - @positional
|
| - def fn(arg1, arg2, required_kw1=None, required_kw2=0):
|
| - ...
|
| -
|
| - fn(1, 3, 5) # Raises exception.
|
| - fn(1, 3) # Ok.
|
| - fn(1, 3, required_kw1=5) # Ok.
|
| -
|
| - Args:
|
| - max_positional_arguments: Maximum number of positional arguments. All
|
| - parameters after the this index must be keyword only.
|
| -
|
| - Returns:
|
| - A decorator that prevents using arguments after max_positional_args from
|
| - being used as positional parameters.
|
| -
|
| - Raises:
|
| - TypeError if a keyword-only argument is provided as a positional parameter.
|
| - ValueError if no maximum number of arguments is provided and the function
|
| - has no arguments with default values.
|
| - """
|
| - def positional_decorator(wrapped):
|
| - def positional_wrapper(*args, **kwargs):
|
| - if len(args) > max_positional_args:
|
| - plural_s = ''
|
| - if max_positional_args != 1:
|
| - plural_s = 's'
|
| - raise TypeError('%s() takes at most %d positional argument%s '
|
| - '(%d given)' % (wrapped.__name__,
|
| - max_positional_args,
|
| - plural_s, len(args)))
|
| - return wrapped(*args, **kwargs)
|
| - return positional_wrapper
|
| -
|
| - if isinstance(max_positional_args, six.integer_types):
|
| - return positional_decorator
|
| - else:
|
| - args, _, _, defaults = inspect.getargspec(max_positional_args)
|
| - if defaults is None:
|
| - raise ValueError(
|
| - 'Functions with no keyword arguments must specify '
|
| - 'max_positional_args')
|
| - return positional(len(args) - len(defaults))(max_positional_args)
|
| -
|
| -
|
| -# TODO(rafek): Support 'level' from the Accept header standard.
|
| -class AcceptItem(object):
|
| - """Encapsulate a single entry of an Accept header.
|
| -
|
| - Parses and extracts relevent values from an Accept header and implements
|
| - a sort order based on the priority of each requested type as defined
|
| - here:
|
| -
|
| - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
| -
|
| - Accept headers are normally a list of comma separated items. Each item
|
| - has the format of a normal HTTP header. For example:
|
| -
|
| - Accept: text/plain, text/html, text/*, */*
|
| -
|
| - This header means to prefer plain text over HTML, HTML over any other
|
| - kind of text and text over any other kind of supported format.
|
| -
|
| - This class does not attempt to parse the list of items from the Accept header.
|
| - The constructor expects the unparsed sub header and the index within the
|
| - Accept header that the fragment was found.
|
| -
|
| - Properties:
|
| - index: The index that this accept item was found in the Accept header.
|
| - main_type: The main type of the content type.
|
| - sub_type: The sub type of the content type.
|
| - q: The q value extracted from the header as a float. If there is no q
|
| - value, defaults to 1.0.
|
| - values: All header attributes parsed form the sub-header.
|
| - sort_key: A tuple (no_main_type, no_sub_type, q, no_values, index):
|
| - no_main_type: */* has the least priority.
|
| - no_sub_type: Items with no sub-type have less priority.
|
| - q: Items with lower q value have less priority.
|
| - no_values: Items with no values have less priority.
|
| - index: Index of item in accept header is the last priority.
|
| - """
|
| -
|
| - __CONTENT_TYPE_REGEX = re.compile(r'^([^/]+)/([^/]+)$')
|
| -
|
| - def __init__(self, accept_header, index):
|
| - """Parse component of an Accept header.
|
| -
|
| - Args:
|
| - accept_header: Unparsed sub-expression of accept header.
|
| - index: The index that this accept item was found in the Accept header.
|
| - """
|
| - accept_header = accept_header.lower()
|
| - content_type, values = cgi.parse_header(accept_header)
|
| - match = self.__CONTENT_TYPE_REGEX.match(content_type)
|
| - if not match:
|
| - raise AcceptError('Not valid Accept header: %s' % accept_header)
|
| - self.__index = index
|
| - self.__main_type = match.group(1)
|
| - self.__sub_type = match.group(2)
|
| - self.__q = float(values.get('q', 1))
|
| - self.__values = values
|
| -
|
| - if self.__main_type == '*':
|
| - self.__main_type = None
|
| -
|
| - if self.__sub_type == '*':
|
| - self.__sub_type = None
|
| -
|
| - self.__sort_key = (not self.__main_type,
|
| - not self.__sub_type,
|
| - -self.__q,
|
| - not self.__values,
|
| - self.__index)
|
| -
|
| - @property
|
| - def index(self):
|
| - return self.__index
|
| -
|
| - @property
|
| - def main_type(self):
|
| - return self.__main_type
|
| -
|
| - @property
|
| - def sub_type(self):
|
| - return self.__sub_type
|
| -
|
| - @property
|
| - def q(self):
|
| - return self.__q
|
| -
|
| - @property
|
| - def values(self):
|
| - """Copy the dictionary of values parsed from the header fragment."""
|
| - return dict(self.__values)
|
| -
|
| - @property
|
| - def sort_key(self):
|
| - return self.__sort_key
|
| -
|
| - def match(self, content_type):
|
| - """Determine if the given accept header matches content type.
|
| -
|
| - Args:
|
| - content_type: Unparsed content type string.
|
| -
|
| - Returns:
|
| - True if accept header matches content type, else False.
|
| - """
|
| - content_type, _ = cgi.parse_header(content_type)
|
| - match = self.__CONTENT_TYPE_REGEX.match(content_type.lower())
|
| - if not match:
|
| - return False
|
| -
|
| - main_type, sub_type = match.group(1), match.group(2)
|
| - if not(main_type and sub_type):
|
| - return False
|
| -
|
| - return ((self.__main_type is None or self.__main_type == main_type) and
|
| - (self.__sub_type is None or self.__sub_type == sub_type))
|
| -
|
| -
|
| - def __cmp__(self, other):
|
| - """Comparison operator based on sort keys."""
|
| - if not isinstance(other, AcceptItem):
|
| - return NotImplemented
|
| - return cmp(self.sort_key, other.sort_key)
|
| -
|
| - def __str__(self):
|
| - """Rebuilds Accept header."""
|
| - content_type = '%s/%s' % (self.__main_type or '*', self.__sub_type or '*')
|
| - values = self.values
|
| -
|
| - if values:
|
| - value_strings = ['%s=%s' % (i, v) for i, v in values.items()]
|
| - return '%s; %s' % (content_type, '; '.join(value_strings))
|
| - else:
|
| - return content_type
|
| -
|
| - def __repr__(self):
|
| - return 'AcceptItem(%r, %d)' % (str(self), self.__index)
|
| -
|
| -
|
| -def parse_accept_header(accept_header):
|
| - """Parse accept header.
|
| -
|
| - Args:
|
| - accept_header: Unparsed accept header. Does not include name of header.
|
| -
|
| - Returns:
|
| - List of AcceptItem instances sorted according to their priority.
|
| - """
|
| - accept_items = []
|
| - for index, header in enumerate(accept_header.split(',')):
|
| - accept_items.append(AcceptItem(header, index))
|
| - return sorted(accept_items)
|
| -
|
| -
|
| -def choose_content_type(accept_header, supported_types):
|
| - """Choose most appropriate supported type based on what client accepts.
|
| -
|
| - Args:
|
| - accept_header: Unparsed accept header. Does not include name of header.
|
| - supported_types: List of content-types supported by the server. The index
|
| - of the supported types determines which supported type is prefered by
|
| - the server should the accept header match more than one at the same
|
| - priority.
|
| -
|
| - Returns:
|
| - The preferred supported type if the accept header matches any, else None.
|
| - """
|
| - for accept_item in parse_accept_header(accept_header):
|
| - for supported_type in supported_types:
|
| - if accept_item.match(supported_type):
|
| - return supported_type
|
| - return None
|
| -
|
| -
|
| -@positional(1)
|
| -def get_package_for_module(module):
|
| - """Get package name for a module.
|
| -
|
| - Helper calculates the package name of a module.
|
| -
|
| - Args:
|
| - module: Module to get name for. If module is a string, try to find
|
| - module in sys.modules.
|
| -
|
| - Returns:
|
| - If module contains 'package' attribute, uses that as package name.
|
| - Else, if module is not the '__main__' module, the module __name__.
|
| - Else, the base name of the module file name. Else None.
|
| - """
|
| - if isinstance(module, six.string_types):
|
| - try:
|
| - module = sys.modules[module]
|
| - except KeyError:
|
| - return None
|
| -
|
| - try:
|
| - return six.text_type(module.package)
|
| - except AttributeError:
|
| - if module.__name__ == '__main__':
|
| - try:
|
| - file_name = module.__file__
|
| - except AttributeError:
|
| - pass
|
| - else:
|
| - base_name = os.path.basename(file_name)
|
| - split_name = os.path.splitext(base_name)
|
| - if len(split_name) == 1:
|
| - return six.text_type(base_name)
|
| - else:
|
| - return u'.'.join(split_name[:-1])
|
| -
|
| - return six.text_type(module.__name__)
|
| -
|
| -
|
| -def total_seconds(offset):
|
| - """Backport of offset.total_seconds() from python 2.7+."""
|
| - seconds = offset.days * 24 * 60 * 60 + offset.seconds
|
| - microseconds = seconds * 10**6 + offset.microseconds
|
| - return microseconds / (10**6 * 1.0)
|
| -
|
| -
|
| -class TimeZoneOffset(datetime.tzinfo):
|
| - """Time zone information as encoded/decoded for DateTimeFields."""
|
| -
|
| - def __init__(self, offset):
|
| - """Initialize a time zone offset.
|
| -
|
| - Args:
|
| - offset: Integer or timedelta time zone offset, in minutes from UTC. This
|
| - can be negative.
|
| - """
|
| - super(TimeZoneOffset, self).__init__()
|
| - if isinstance(offset, datetime.timedelta):
|
| - offset = total_seconds(offset) / 60
|
| - self.__offset = offset
|
| -
|
| - def utcoffset(self, dt):
|
| - """Get the a timedelta with the time zone's offset from UTC.
|
| -
|
| - Returns:
|
| - The time zone offset from UTC, as a timedelta.
|
| - """
|
| - return datetime.timedelta(minutes=self.__offset)
|
| -
|
| - def dst(self, dt):
|
| - """Get the daylight savings time offset.
|
| -
|
| - The formats that ProtoRPC uses to encode/decode time zone information don't
|
| - contain any information about daylight savings time. So this always
|
| - returns a timedelta of 0.
|
| -
|
| - Returns:
|
| - A timedelta of 0.
|
| - """
|
| - return datetime.timedelta(0)
|
| -
|
| -
|
| -def decode_datetime(encoded_datetime):
|
| - """Decode a DateTimeField parameter from a string to a python datetime.
|
| -
|
| - Args:
|
| - encoded_datetime: A string in RFC 3339 format.
|
| -
|
| - Returns:
|
| - A datetime object with the date and time specified in encoded_datetime.
|
| -
|
| - Raises:
|
| - ValueError: If the string is not in a recognized format.
|
| - """
|
| - # Check if the string includes a time zone offset. Break out the
|
| - # part that doesn't include time zone info. Convert to uppercase
|
| - # because all our comparisons should be case-insensitive.
|
| - time_zone_match = _TIME_ZONE_RE.search(encoded_datetime)
|
| - if time_zone_match:
|
| - time_string = encoded_datetime[:time_zone_match.start(1)].upper()
|
| - else:
|
| - time_string = encoded_datetime.upper()
|
| -
|
| - if '.' in time_string:
|
| - format_string = '%Y-%m-%dT%H:%M:%S.%f'
|
| - else:
|
| - format_string = '%Y-%m-%dT%H:%M:%S'
|
| -
|
| - decoded_datetime = datetime.datetime.strptime(time_string, format_string)
|
| -
|
| - if not time_zone_match:
|
| - return decoded_datetime
|
| -
|
| - # Time zone info was included in the parameter. Add a tzinfo
|
| - # object to the datetime. Datetimes can't be changed after they're
|
| - # created, so we'll need to create a new one.
|
| - if time_zone_match.group('z'):
|
| - offset_minutes = 0
|
| - else:
|
| - sign = time_zone_match.group('sign')
|
| - hours, minutes = [int(value) for value in
|
| - time_zone_match.group('hours', 'minutes')]
|
| - offset_minutes = hours * 60 + minutes
|
| - if sign == '-':
|
| - offset_minutes *= -1
|
| -
|
| - return datetime.datetime(decoded_datetime.year,
|
| - decoded_datetime.month,
|
| - decoded_datetime.day,
|
| - decoded_datetime.hour,
|
| - decoded_datetime.minute,
|
| - decoded_datetime.second,
|
| - decoded_datetime.microsecond,
|
| - TimeZoneOffset(offset_minutes))
|
|
|