diff options
Diffstat (limited to 'third_party/py/gflags/gflags/_helpers.py')
-rw-r--r-- | third_party/py/gflags/gflags/_helpers.py | 430 |
1 files changed, 0 insertions, 430 deletions
diff --git a/third_party/py/gflags/gflags/_helpers.py b/third_party/py/gflags/gflags/_helpers.py deleted file mode 100644 index f740506fdd..0000000000 --- a/third_party/py/gflags/gflags/_helpers.py +++ /dev/null @@ -1,430 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 Google Inc. All Rights Reserved. -# -# 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. - - -"""Helper functions for //gflags.""" - -import collections -import os -import re -import struct -import sys -import textwrap -try: - import fcntl # pylint: disable=g-import-not-at-top -except ImportError: - fcntl = None -try: - # Importing termios will fail on non-unix platforms. - import termios # pylint: disable=g-import-not-at-top -except ImportError: - termios = None - -# pylint: disable=g-import-not-at-top -import third_party.pep257 as pep257 -import six - - -_DEFAULT_HELP_WIDTH = 80 # Default width of help output. -_MIN_HELP_WIDTH = 40 # Minimal "sane" width of help output. We assume that any - # value below 40 is unreasonable. - -# Define the allowed error rate in an input string to get suggestions. -# -# We lean towards a high threshold because we tend to be matching a phrase, -# and the simple algorithm used here is geared towards correcting word -# spellings. -# -# For manual testing, consider "<command> --list" which produced a large number -# of spurious suggestions when we used "least_errors > 0.5" instead of -# "least_erros >= 0.5". -_SUGGESTION_ERROR_RATE_THRESHOLD = 0.50 - -# Characters that cannot appear or are highly discouraged in an XML 1.0 -# document. (See http://www.w3.org/TR/REC-xml/#charsets or -# https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0) -_ILLEGAL_XML_CHARS_REGEX = re.compile( - u'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]') - -# This is a set of module ids for the modules that disclaim key flags. -# This module is explicitly added to this set so that we never consider it to -# define key flag. -disclaim_module_ids = set([id(sys.modules[__name__])]) - - - -# Define special flags here so that help may be generated for them. -# NOTE: Please do NOT use SPECIAL_FLAGS from outside flags module. -# Initialized inside flagvalues.py. -SPECIAL_FLAGS = None - - -class _ModuleObjectAndName( - collections.namedtuple('_ModuleObjectAndName', 'module module_name')): - """Module object and name. - - Fields: - - module: object, module object. - - module_name: str, module name. - """ - - -def GetModuleObjectAndName(globals_dict): - """Returns the module that defines a global environment, and its name. - - Args: - globals_dict: A dictionary that should correspond to an environment - providing the values of the globals. - - Returns: - _ModuleObjectAndName - pair of module object & module name. - Returns (None, None) if the module could not be identified. - """ - name = globals_dict.get('__name__', None) - module = sys.modules.get(name, None) - # Pick a more informative name for the main module. - return _ModuleObjectAndName(module, - (sys.argv[0] if name == '__main__' else name)) - - -def GetCallingModuleObjectAndName(): - """Returns the module that's calling into this module. - - We generally use this function to get the name of the module calling a - DEFINE_foo... function. - - Returns: - The module object that called into this one. - - Raises: - AssertionError: if no calling module could be identified. - """ - range_func = range if sys.version_info[0] >= 3 else xrange - for depth in range_func(1, sys.getrecursionlimit()): - # sys._getframe is the right thing to use here, as it's the best - # way to walk up the call stack. - globals_for_frame = sys._getframe(depth).f_globals # pylint: disable=protected-access - module, module_name = GetModuleObjectAndName(globals_for_frame) - if id(module) not in disclaim_module_ids and module_name is not None: - return _ModuleObjectAndName(module, module_name) - raise AssertionError('No module was found') - - -def GetCallingModule(): - """Returns the name of the module that's calling into this module.""" - return GetCallingModuleObjectAndName().module_name - - -def StrOrUnicode(value): - """Converts a value to a python string. - - Behavior of this function is intentionally different in Python2/3. - - In Python2, the given value is attempted to convert to a str (byte string). - If it contains non-ASCII characters, it is converted to a unicode instead. - - In Python3, the given value is always converted to a str (unicode string). - - This behavior reflects the (bad) practice in Python2 to try to represent - a string as str as long as it contains ASCII characters only. - - Args: - value: An object to be converted to a string. - - Returns: - A string representation of the given value. See the description above - for its type. - """ - try: - return str(value) - except UnicodeEncodeError: - return unicode(value) # Python3 should never come here - - -def CreateXMLDOMElement(doc, name, value): - """Returns an XML DOM element with name and text value. - - Args: - doc: A minidom.Document, the DOM document it should create nodes from. - name: A string, the tag of XML element. - value: A Python object, whose string representation will be used - as the value of the XML element. Illegal or highly discouraged xml 1.0 - characters are stripped. - - Returns: - An instance of minidom.Element. - """ - s = StrOrUnicode(value) - if six.PY2 and not isinstance(s, unicode): - # Get a valid unicode string. - s = s.decode('utf-8', 'ignore') - if isinstance(value, bool): - # Display boolean values as the C++ flag library does: no caps. - s = s.lower() - # Remove illegal xml characters. - s = _ILLEGAL_XML_CHARS_REGEX.sub(u'', s) - - e = doc.createElement(name) - e.appendChild(doc.createTextNode(s)) - return e - - -def GetHelpWidth(): - """Returns: an integer, the width of help lines that is used in TextWrap.""" - if not sys.stdout.isatty() or termios is None or fcntl is None: - return _DEFAULT_HELP_WIDTH - try: - data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234') - columns = struct.unpack('hh', data)[1] - # Emacs mode returns 0. - # Here we assume that any value below 40 is unreasonable. - if columns >= _MIN_HELP_WIDTH: - return columns - # Returning an int as default is fine, int(int) just return the int. - return int(os.getenv('COLUMNS', _DEFAULT_HELP_WIDTH)) - - except (TypeError, IOError, struct.error): - return _DEFAULT_HELP_WIDTH - - -def GetFlagSuggestions(attempt, longopt_list): - """Get helpful similar matches for an invalid flag.""" - # Don't suggest on very short strings, or if no longopts are specified. - if len(attempt) <= 2 or not longopt_list: - return [] - - option_names = [v.split('=')[0] for v in longopt_list] - - # Find close approximations in flag prefixes. - # This also handles the case where the flag is spelled right but ambiguous. - distances = [(_DamerauLevenshtein(attempt, option[0:len(attempt)]), option) - for option in option_names] - distances.sort(key=lambda t: t[0]) - - least_errors, _ = distances[0] - # Don't suggest excessively bad matches. - if least_errors >= _SUGGESTION_ERROR_RATE_THRESHOLD * len(attempt): - return [] - - suggestions = [] - for errors, name in distances: - if errors == least_errors: - suggestions.append(name) - else: - break - return suggestions - - -def _DamerauLevenshtein(a, b): - """Damerau-Levenshtein edit distance from a to b.""" - memo = {} - - def Distance(x, y): - """Recursively defined string distance with memoization.""" - if (x, y) in memo: - return memo[x, y] - if not x: - d = len(y) - elif not y: - d = len(x) - else: - d = min( - Distance(x[1:], y) + 1, # correct an insertion error - Distance(x, y[1:]) + 1, # correct a deletion error - Distance(x[1:], y[1:]) + (x[0] != y[0])) # correct a wrong character - if len(x) >= 2 and len(y) >= 2 and x[0] == y[1] and x[1] == y[0]: - # Correct a transposition. - t = Distance(x[2:], y[2:]) + 1 - if d > t: - d = t - - memo[x, y] = d - return d - return Distance(a, b) - - -def TextWrap(text, length=None, indent='', firstline_indent=None): - """Wraps a given text to a maximum line length and returns it. - - It turns lines that only contain whitespace into empty lines, keeps new lines, - and expands tabs using 4 spaces. - - Args: - text: str, Text to wrap. - length: int, Maximum length of a line, includes indentation. - If this is None then use GetHelpWidth() - indent: str, Indent for all but first line. - firstline_indent: str, Indent for first line; if None, fall back to indent. - - Returns: - Wrapped text. - - Raises: - ValueError: if indent or firstline_indent not shorter than length. - """ - # Get defaults where callee used None - if length is None: - length = GetHelpWidth() - if indent is None: - indent = '' - if firstline_indent is None: - firstline_indent = indent - - if len(indent) >= length: - raise ValueError('Length of indent exceeds length') - if len(firstline_indent) >= length: - raise ValueError('Length of first line indent exceeds length') - - text = text.expandtabs(4) - - result = [] - # Create one wrapper for the first paragraph and one for subsequent - # paragraphs that does not have the initial wrapping. - wrapper = textwrap.TextWrapper( - width=length, initial_indent=firstline_indent, subsequent_indent=indent) - subsequent_wrapper = textwrap.TextWrapper( - width=length, initial_indent=indent, subsequent_indent=indent) - - # textwrap does not have any special treatment for newlines. From the docs: - # "...newlines may appear in the middle of a line and cause strange output. - # For this reason, text should be split into paragraphs (using - # str.splitlines() or similar) which are wrapped separately." - for paragraph in (p.strip() for p in text.splitlines()): - if paragraph: - result.extend(wrapper.wrap(paragraph)) - else: - result.append('') # Keep empty lines. - # Replace initial wrapper with wrapper for subsequent paragraphs. - wrapper = subsequent_wrapper - - return '\n'.join(result) - - -def FlagDictToArgs(flag_map): - """Convert a dict of values into process call parameters. - - This method is used to convert a dictionary into a sequence of parameters - for a binary that parses arguments using this module. - - Args: - flag_map: a mapping where the keys are flag names (strings). - values are treated according to their type: - * If value is None, then only the name is emitted. - * If value is True, then only the name is emitted. - * If value is False, then only the name prepended with 'no' is emitted. - * If value is a string then --name=value is emitted. - * If value is a collection, this will emit --name=value1,value2,value3. - * Everything else is converted to string an passed as such. - Yields: - sequence of string suitable for a subprocess execution. - """ - for key, value in six.iteritems(flag_map): - if value is None: - yield '--%s' % key - elif isinstance(value, bool): - if value: - yield '--%s' % key - else: - yield '--no%s' % key - elif isinstance(value, (bytes, type(u''))): - # We don't want strings to be handled like python collections. - yield '--%s=%s' % (key, value) - else: - # Now we attempt to deal with collections. - try: - yield '--%s=%s' % (key, ','.join(str(item) for item in value)) - except TypeError: - # Default case. - yield '--%s=%s' % (key, value) - - -def DocToHelp(doc): - """Takes a __doc__ string and reformats it as help.""" - - # Get rid of starting and ending white space. Using lstrip() or even - # strip() could drop more than maximum of first line and right space - # of last line. - doc = doc.strip() - - # Get rid of all empty lines. - whitespace_only_line = re.compile('^[ \t]+$', re.M) - doc = whitespace_only_line.sub('', doc) - - # Cut out common space at line beginnings. - doc = pep257.trim(doc) - - # Just like this module's comment, comments tend to be aligned somehow. - # In other words they all start with the same amount of white space. - # 1) keep double new lines; - # 2) keep ws after new lines if not empty line; - # 3) all other new lines shall be changed to a space; - # Solution: Match new lines between non white space and replace with space. - doc = re.sub(r'(?<=\S)\n(?=\S)', ' ', doc, flags=re.M) - - return doc - - -def IsRunningTest(): - """Tries to detect whether we are inside of the test.""" - modules = set(sys.modules) - test_modules = { - 'unittest', - 'unittest2', - 'pytest', - } - return bool(test_modules & modules) - - -# TODO(b/31830082): Migrate all users to PEP8-style methods and remove this. -def define_both_methods(class_name, class_dict, old_name, new_name): # pylint: disable=invalid-name - """Function to help CamelCase to PEP8 style class methods migration. - - For any class definition: - 1. Assert it does not define both old and new methods, - otherwise it does not work. - 2. If it defines the old method, create the same new method. - 3. If it defines the new method, create the same old method. - - Args: - class_name: the class name. - class_dict: the class dictionary. - old_name: old method's name. - new_name: new method's name. - - Raises: - AssertionError: raised when the class defines both the old_name and - new_name. - """ - assert old_name not in class_dict or new_name not in class_dict, ( - 'Class "{}" cannot define both "{}" and "{}" methods.'.format( - class_name, old_name, new_name)) - if old_name in class_dict: - class_dict[new_name] = class_dict[old_name] - elif new_name in class_dict: - class_dict[old_name] = class_dict[new_name] |