diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397d073 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Files to ignore by git. + +# Back-up files +*~ +*.swp + +# Generic auto-generated build files +*.pyc +*.pyo + +# Specific auto-generated build files +.eggs +.tox +/__pycache__ +/build +/dist +/python_gflags.egg-info diff --git a/gflags/__init__.py b/gflags/__init__.py index 8a0ab13..5ae8896 100644 --- a/gflags/__init__.py +++ b/gflags/__init__.py @@ -414,7 +414,7 @@ def ADOPT_module_key_flags( # pylint: disable=g-bad-name # a different module. So, we can't use _GetKeyFlagsForModule. # Instead, we take all flags from _SPECIAL_FLAGS (a private # FlagValues, where no other module should register flags). - [f.name for f in _helpers.SPECIAL_FLAGS.FlagDict().itervalues()], + [f.name for f in iter(_helpers.SPECIAL_FLAGS.FlagDict().values())], flag_values=_helpers.SPECIAL_FLAGS, key_flag_values=flag_values) diff --git a/gflags/_helpers.py b/gflags/_helpers.py index 26427a1..a5f82fc 100644 --- a/gflags/_helpers.py +++ b/gflags/_helpers.py @@ -121,8 +121,7 @@ def GetCallingModuleObjectAndName(): 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()): + for depth in range(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 @@ -188,7 +187,7 @@ def GetHelpWidth(): return _DEFAULT_HELP_WIDTH try: data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234') - columns = struct.unpack('hh', data)[1] + 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: @@ -357,7 +356,7 @@ def FlagDictToArgs(flag_map): Yields: sequence of string suitable for a subprocess execution. """ - for key, value in flag_map.iteritems(): + for key, value in iter(flag_map.items()): if value is None: yield '--%s' % key elif isinstance(value, bool): diff --git a/gflags/_helpers_test.py b/gflags/_helpers_test.py index 5195fbe..ca43ca9 100644 --- a/gflags/_helpers_test.py +++ b/gflags/_helpers_test.py @@ -52,35 +52,35 @@ def setUp(self): 'ftree-ch'] def testDamerauLevenshteinId(self): - self.assertEquals(0, _helpers._DamerauLevenshtein('asdf', 'asdf')) + self.assertEqual(0, _helpers._DamerauLevenshtein('asdf', 'asdf')) def testDamerauLevenshteinEmpty(self): - self.assertEquals(5, _helpers._DamerauLevenshtein('', 'kites')) - self.assertEquals(6, _helpers._DamerauLevenshtein('kitten', '')) + self.assertEqual(5, _helpers._DamerauLevenshtein('', 'kites')) + self.assertEqual(6, _helpers._DamerauLevenshtein('kitten', '')) def testDamerauLevenshteinCommutative(self): - self.assertEquals(2, _helpers._DamerauLevenshtein('kitten', 'kites')) - self.assertEquals(2, _helpers._DamerauLevenshtein('kites', 'kitten')) + self.assertEqual(2, _helpers._DamerauLevenshtein('kitten', 'kites')) + self.assertEqual(2, _helpers._DamerauLevenshtein('kites', 'kitten')) def testDamerauLevenshteinTransposition(self): - self.assertEquals(1, _helpers._DamerauLevenshtein('kitten', 'ktiten')) + self.assertEqual(1, _helpers._DamerauLevenshtein('kitten', 'ktiten')) def testMispelledSuggestions(self): suggestions = _helpers.GetFlagSuggestions('fstack_protector_all', self.longopts) - self.assertEquals(['fstack-protector-all'], suggestions) + self.assertEqual(['fstack-protector-all'], suggestions) def testAmbiguousPrefixSuggestion(self): suggestions = _helpers.GetFlagSuggestions('fstack', self.longopts) - self.assertEquals(['fstack-protector', 'fstack-protector-all'], suggestions) + self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) def testMisspelledAmbiguousPrefixSuggestion(self): suggestions = _helpers.GetFlagSuggestions('stack', self.longopts) - self.assertEquals(['fstack-protector', 'fstack-protector-all'], suggestions) + self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) def testCrazySuggestion(self): suggestions = _helpers.GetFlagSuggestions('asdfasdgasdfa', self.longopts) - self.assertEquals([], suggestions) + self.assertEqual([], suggestions) def main(): diff --git a/gflags/argument_parser.py b/gflags/argument_parser.py index 5ce6c4d..9f016ea 100644 --- a/gflags/argument_parser.py +++ b/gflags/argument_parser.py @@ -30,8 +30,8 @@ """Contains base classes used to parse and convert arguments.""" -import cStringIO import csv +import io import string @@ -325,7 +325,7 @@ def __init__(self, list_sep): def Serialize(self, value): """Serialize a list as a string, if possible, or as a unicode string.""" - output = cStringIO.StringIO() + output = io.StringIO() writer = csv.writer(output) # csv.writer doesn't accept unicode, so we convert to UTF-8. @@ -382,7 +382,7 @@ def Parse(self, argument): else: try: return [s.strip() for s in list(csv.reader([argument], strict=True))[0]] - except csv.Error, e: + except csv.Error as e: # Provide a helpful report for case like # --listflag="$(printf 'hello,\nworld')" # IOW, list flag values containing naked newlines. This error diff --git a/gflags/flag.py b/gflags/flag.py index fd41fe7..f66de76 100644 --- a/gflags/flag.py +++ b/gflags/flag.py @@ -130,7 +130,7 @@ def Parse(self, argument): self.name, argument, self.value)) try: self.value = self.parser.Parse(argument) - except ValueError, e: # Recast ValueError as IllegalFlagValue. + except ValueError as e: # Recast ValueError as IllegalFlagValue. raise exceptions.IllegalFlagValue( 'flag --%s=%s: %s' % (self.name, argument, e)) self.present += 1 diff --git a/gflags/flags_formatting_test.py b/gflags/flags_formatting_test.py index 8532830..4d43f42 100644 --- a/gflags/flags_formatting_test.py +++ b/gflags/flags_formatting_test.py @@ -1,6 +1,18 @@ #!/usr/bin/env python +import struct +import sys import unittest +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 + import gflags from gflags import _helpers @@ -10,6 +22,25 @@ class FlagsUnitTest(unittest.TestCase): """Flags formatting Unit Test.""" + def setUp(self): + """Set up before running a test.""" + self._winsize_data = None + if fcntl and termios: + # The tests rely on the terminal width so we need to set it to + # a value smaller than the minimum help width used in the tests. + winsize_data = struct.pack('HHHH', 0, 0, 0, 0) + self._winsize_data = fcntl.ioctl( + sys.stdout, termios.TIOCGWINSZ, winsize_data) + winsize = struct.unpack('HHHH', self._winsize_data) + winsize_data = struct.pack( + 'HHHH', winsize[0], 0, winsize[2], winsize[3]) + fcntl.ioctl(sys.stdout, termios.TIOCSWINSZ, winsize_data) + + def tearDown(self): + """Clean up after running a test.""" + if fcntl and termios and self._winsize_data: + fcntl.ioctl(sys.stdout, termios.TIOCSWINSZ, self._winsize_data) + def testGetHelpWidth(self): """Verify that GetHelpWidth() reflects _help_width.""" default_help_width = _helpers._DEFAULT_HELP_WIDTH # Save. @@ -30,7 +61,7 @@ def testTextWrap(self): # Generate a string with length 40, no spaces text = '' expect = [] - for n in xrange(4): + for n in range(4): line = str(n) line += '123456789' text += line @@ -62,7 +93,7 @@ def testTextWrap(self): 13: ['a b c d e f g', 'h'], 14: ['a b c d e f g', 'h'], 15: ['a b c d e f g h']} - for width, exp in expect.iteritems(): + for width, exp in iter(expect.items()): self.assertEqual(exp, gflags.TextWrap(input_value, width).split('\n')) # We turn lines with only whitespace into empty lines @@ -178,18 +209,18 @@ def testDocToHelp_FlagValues(self): # Test the general outline of the converted docs lines = doc.splitlines() self.assertEqual(17, len(lines)) - empty_lines = [index for index in xrange(len(lines)) if not lines[index]] + empty_lines = [index for index in range(len(lines)) if not lines[index]] self.assertEqual([1, 3, 5, 8, 12, 15], empty_lines) # test that some starting prefix is kept - flags_lines = [index for index in xrange(len(lines)) + flags_lines = [index for index in range(len(lines)) if lines[index].startswith(' FLAGS')] self.assertEqual([7, 10, 11], flags_lines) # but other, especially common space has been removed - space_lines = [index for index in xrange(len(lines)) + space_lines = [index for index in range(len(lines)) if lines[index] and lines[index][0].isspace()] self.assertEqual([7, 10, 11, 14], space_lines) # No right space was kept - rspace_lines = [index for index in xrange(len(lines)) + rspace_lines = [index for index in range(len(lines)) if lines[index] != lines[index].rstrip()] self.assertEqual([], rspace_lines) # test double spaces are kept diff --git a/gflags/flags_modules_for_testing/module_bar.py b/gflags/flags_modules_for_testing/module_bar.py index 03b053f..91b4b6d 100644 --- a/gflags/flags_modules_for_testing/module_bar.py +++ b/gflags/flags_modules_for_testing/module_bar.py @@ -128,7 +128,7 @@ def ExecuteCode(code, global_dict): """ # Indeed, using exec generates a lint warning. But some user code # actually uses exec, and we have to test for it ... - exec code in global_dict # pylint: disable=exec-used + exec(code, global_dict) # pylint: disable=exec-used def DisclaimKeyFlags(): diff --git a/gflags/flags_unicode_literals_test.py b/gflags/flags_unicode_literals_test.py index cb20887..0a6e40c 100644 --- a/gflags/flags_unicode_literals_test.py +++ b/gflags/flags_unicode_literals_test.py @@ -14,6 +14,7 @@ class FlagsUnicodeLiteralsTest(unittest.TestCase): def testUnicodeFlagNameAndValueAreGood(self): + # This call triggers UnparsedFlagAccessError. alleged_mountain_lion = gflags.FLAGS.seen_in_crittenden self.assertTrue( isinstance(alleged_mountain_lion, type(u'')), diff --git a/gflags/flagvalues.py b/gflags/flagvalues.py index c1b769d..595e97a 100644 --- a/gflags/flagvalues.py +++ b/gflags/flagvalues.py @@ -244,7 +244,7 @@ def FindModuleDefiningFlag(self, flagname, default=None): If no such module exists (i.e. no flag with this name exists), we return default. """ - for module, flags in self.FlagsByModuleDict().iteritems(): + for module, flags in iter(self.FlagsByModuleDict().items()): for flag in flags: if flag.name == flagname or flag.short_name == flagname: return module @@ -263,7 +263,7 @@ def FindModuleIdDefiningFlag(self, flagname, default=None): If no such module exists (i.e. no flag with this name exists), we return default. """ - for module_id, flags in self.FlagsByModuleIdDict().iteritems(): + for module_id, flags in iter(self.FlagsByModuleIdDict().items()): for flag in flags: if flag.name == flagname or flag.short_name == flagname: return module_id @@ -275,7 +275,7 @@ def AppendFlagValues(self, flag_values): Args: flag_values: registry to copy from """ - for flag_name, flag in flag_values.FlagDict().iteritems(): + for flag_name, flag in iter(flag_values.FlagDict().items()): # Each flags with shortname appears here twice (once under its # normal name, and again with its short name). To prevent # problems (DuplicateFlagError) with double flag registration, we @@ -405,7 +405,7 @@ def __setattr__(self, name, value): def _AssertAllValidators(self): all_validators = set() - for flag in self.FlagDict().itervalues(): + for flag in iter(self.FlagDict().values()): for validator in flag.validators: all_validators.add(validator) self._AssertValidators(all_validators) @@ -425,7 +425,7 @@ def _AssertValidators(self, validators): validators, key=lambda validator: validator.insertion_index): try: validator.Verify(self) - except gflags_validators.Error, e: + except gflags_validators.Error as e: message = validator.PrintFlagsWithValues(self) raise exceptions.IllegalFlagValue('%s: %s' % (message, str(e))) @@ -500,7 +500,7 @@ def __RemoveFlagFromDictByModule(self, flags_by_module_dict, flag_obj): flags. flag_obj: A flag object. """ - for unused_module, flags_in_module in flags_by_module_dict.iteritems(): + for unused_module, flags_in_module in iter(flags_by_module_dict.items()): # while (as opposed to if) takes care of multiple occurrences of a # flag in the list for the same module. while flag_obj in flags_in_module: @@ -851,14 +851,14 @@ def ShortestUniquePrefixes(self, fl): # name (the latter check uses cached info from the previous loop). shortest_matches = {} prev_idx = 0 - for flag_idx in xrange(len(sorted_flags)): + for flag_idx in range(len(sorted_flags)): curr = sorted_flags[flag_idx] if flag_idx == (len(sorted_flags) - 1): next_flag = None else: next_flag = sorted_flags[flag_idx+1] next_flag_len = len(next_flag) - for curr_idx in xrange(len(curr)): + for curr_idx in range(len(curr)): if (next_flag is None or curr_idx >= next_flag_len or curr[curr_idx] != next_flag[curr_idx]): @@ -945,7 +945,7 @@ def __GetFlagFileLines(self, filename, parsed_file_stack=None): flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags. try: file_obj = open(filename, 'r') - except IOError, e_msg: + except IOError as e_msg: raise exceptions.CantOpenFlagFileError( 'ERROR:: Unable to open flagfile: %s' % e_msg) diff --git a/gflags/third_party/pep257/__init__.py b/gflags/third_party/pep257/__init__.py index 97136a4..3a2e49a 100644 --- a/gflags/third_party/pep257/__init__.py +++ b/gflags/third_party/pep257/__init__.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -import sys - def trim(docstring): """Removes indentation from triple-quoted strings. @@ -10,18 +8,21 @@ def trim(docstring): """ if not docstring: return '' + # Python 3 does not support sys.maxint so we use and arbitrary + # large integer instead. + maxint = 1 << 32 # Convert tabs to spaces (following the normal Python rules) # and split into a list of lines: lines = docstring.expandtabs().splitlines() # Determine minimum indentation (first line doesn't count): - indent = sys.maxint + indent = maxint for line in lines[1:]: stripped = line.lstrip() if stripped: indent = min(indent, len(line) - len(stripped)) # Remove indentation (first line is special): trimmed = [lines[0].strip()] - if indent < sys.maxint: + if indent < maxint: for line in lines[1:]: trimmed.append(line[indent:].rstrip()) # Strip off trailing and leading blank lines: diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..8db6d37 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py27, py34 + +[testenv] +setenv = + PYTHONPATH = {toxinidir} +commands = + python gflags/flags_formatting_test.py + python gflags/flags_unicode_literals_test.py + python gflags/_helpers_test.py