File python-textfsm-no-python2.patch of Package python-textfsm

From c8843d69daa9b565fea99a0283ad13c324d5b563 Mon Sep 17 00:00:00 2001
From: Daniel Harrison <harro@users.noreply.github.com>
Date: Mon, 29 Jan 2024 12:48:00 +1100
Subject: [PATCH] Remove python2 support. (#121)

Change triggers release renumber to v2.0.0  (which is to be done when we pull into the stable branch)
---
 setup.py                            |  60 ++-
 tests/clitable_test.py              |  21 +-
 tests/copyable_regex_object_test.py |  42 --
 tests/terminal_test.py              |  11 +-
 tests/textfsm_test.py               | 641 ++++++++++++++++------------
 tests/texttable_test.py             |  21 +-
 textfsm/clitable.py                 |  40 +-
 textfsm/copyable_regex_object.py    |  41 --
 textfsm/parser.py                   | 188 ++++----
 textfsm/terminal.py                 | 159 +++----
 textfsm/texttable.py                | 152 +++----
 11 files changed, 689 insertions(+), 687 deletions(-)
 delete mode 100755 tests/copyable_regex_object_test.py
 delete mode 100755 textfsm/copyable_regex_object.py

Index: textfsm-1.1.3/setup.py
===================================================================
--- textfsm-1.1.3.orig/setup.py
+++ textfsm-1.1.3/setup.py
@@ -16,42 +16,40 @@
 
 """Setup script."""
 
-from setuptools import setup, find_packages
-import textfsm
 # To use a consistent encoding
 from codecs import open
 from os import path
+from setuptools import find_packages, setup
+import textfsm
 
 here = path.abspath(path.dirname(__file__))
 
 # Get the long description from the README file
-with open(path.join(here, 'README.md'), encoding="utf8") as f:
-    long_description = f.read()
+with open(path.join(here, 'README.md'), encoding='utf8') as f:
+  long_description = f.read()
 
-setup(name='textfsm',
-      maintainer='Google',
-      maintainer_email='textfsm-dev@googlegroups.com',
-      version=textfsm.__version__,
-      description='Python module for parsing semi-structured text into python tables.',
-      long_description=long_description,
-      long_description_content_type='text/markdown',
-      url='https://github.com/google/textfsm',
-      license='Apache License, Version 2.0',
-      classifiers=[
-          'Development Status :: 5 - Production/Stable',
-          'Intended Audience :: Developers',
-          'License :: OSI Approved :: Apache Software License',
-          'Operating System :: OS Independent',
-          'Programming Language :: Python :: 2',
-          'Programming Language :: Python :: 3',
-          'Topic :: Software Development :: Libraries'],
-      packages=['textfsm'],
-      entry_points={
-        'console_scripts': [
-            'textfsm=textfsm.parser:main'
-        ]
-      },      
-      include_package_data=True,
-      package_data={'textfsm': ['../testdata/*']},
-      install_requires=['six', 'future'],
-     )
+setup(
+    name='textfsm',
+    maintainer='Google',
+    maintainer_email='textfsm-dev@googlegroups.com',
+    version=textfsm.__version__,
+    description=(
+        'Python module for parsing semi-structured text into python tables.'
+    ),
+    long_description=long_description,
+    long_description_content_type='text/markdown',
+    url='https://github.com/google/textfsm',
+    license='Apache License, Version 2.0',
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Apache Software License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python :: 3',
+        'Topic :: Software Development :: Libraries',
+    ],
+    packages=['textfsm'],
+    entry_points={'console_scripts': ['textfsm=textfsm.parser:main']},
+    include_package_data=True,
+    package_data={'textfsm': ['../testdata/*']},
+)
Index: textfsm-1.1.3/tests/clitable_test.py
===================================================================
--- textfsm-1.1.3.orig/tests/clitable_test.py
+++ textfsm-1.1.3/tests/clitable_test.py
@@ -16,19 +16,12 @@
 
 """Unittest for clitable script."""
 
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
 import copy
+import io
 import os
 import re
 import unittest
-
-from io import StringIO
 from textfsm import clitable
-from textfsm import copyable_regex_object
 
 
 class UnitTestIndexTable(unittest.TestCase):
@@ -47,8 +40,7 @@ class UnitTestIndexTable(unittest.TestCa
 
     self.assertEqual(indx.compiled.size, 3)
     for col in ('Command', 'Vendor', 'Template', 'Hostname'):
-      self.assertTrue(isinstance(indx.compiled[1][col],
-                                 copyable_regex_object.CopyableRegexObject))
+      self.assertIsInstance(indx.compiled[1][col], re.Pattern)
 
     self.assertTrue(indx.compiled[1]['Hostname'].match('random string'))
 
@@ -66,8 +58,7 @@ class UnitTestIndexTable(unittest.TestCa
     indx = clitable.IndexTable(_PreParse, _PreCompile, file_path)
     self.assertEqual(indx.index[2]['Template'], 'CLITABLE_TEMPLATEC')
     self.assertEqual(indx.index[1]['Command'], 'sh[[ow]] ve[[rsion]]')
-    self.assertTrue(isinstance(indx.compiled[1]['Hostname'],
-                               copyable_regex_object.CopyableRegexObject))
+    self.assertIsInstance(indx.compiled[1]['Hostname'], re.Pattern)
     self.assertFalse(indx.compiled[1]['Command'])
 
   def testGetRowMatch(self):
@@ -101,7 +92,7 @@ class UnitTestCliTable(unittest.TestCase
                      'Start\n'
                      '  ^${Col1} ${Col2} ${Col3} -> Record\n'
                      '\n')
-    self.template_file = StringIO(self.template)
+    self.template_file = io.StringIO(self.template)
 
   def testCompletion(self):
     """Tests '[[]]' syntax replacement."""
@@ -123,7 +114,7 @@ class UnitTestCliTable(unittest.TestCase
 
     self.assertEqual('sh(o(w)?)? ve(r(s(i(o(n)?)?)?)?)?',
                      self.clitable.index.index[1]['Command'])
-    self.assertEqual(None, self.clitable.index.compiled[1]['Template'])
+    self.assertIsNone(self.clitable.index.compiled[1]['Template'])
     self.assertTrue(
         self.clitable.index.compiled[1]['Command'].match('sho vers'))
 
@@ -267,7 +258,7 @@ class UnitTestCliTable(unittest.TestCase
                      'Start\n'
                      '  ^${Col1} ${Col2} ${Col3} -> Record\n'
                      '\n')
-    self.template_file = StringIO(self.template)
+    self.template_file = io.StringIO(self.template)
     self.clitable._TemplateNamesToFiles = lambda t: [self.template_file]
     self.clitable.ParseCmd(self.input_data + input_data2,
                            attributes={'Command': 'sh ver'})
Index: textfsm-1.1.3/tests/textfsm_test.py
===================================================================
--- textfsm-1.1.3.orig/tests/textfsm_test.py
+++ textfsm-1.1.3/tests/textfsm_test.py
@@ -16,16 +16,9 @@
 # permissions and limitations under the License.
 
 """Unittest for textfsm module."""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
 
-from builtins import str
+import io
 import unittest
-from io import StringIO
-
-
 
 import textfsm
 
@@ -60,27 +53,27 @@ class UnitTestFSM(unittest.TestCase):
     self.assertEqual(v.OptionNames(), ['Required'])
 
     # regex must be bounded by parenthesis.
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      'Value beer (boo(hoo)))boo')
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      'Value beer boo(boo(hoo)))')
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      'Value beer (boo)hoo)')
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, 'Value beer (boo(hoo)))boo'
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, 'Value beer boo(boo(hoo)))'
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, 'Value beer (boo)hoo)'
+    )
 
     # Escaped parentheses don't count.
     v = textfsm.TextFSMValue(options_class=textfsm.TextFSMOptions)
     v.Parse(r'Value beer (boo\)hoo)')
     self.assertEqual(v.name, 'beer')
     self.assertEqual(v.regex, r'(boo\)hoo)')
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      r'Value beer (boohoo\)')
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      r'Value beer (boo)hoo\)')
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, r'Value beer (boohoo\)'
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, r'Value beer (boo)hoo\)'
+    )
 
     # Unbalanced parenthesis can exist if within square "[]" braces.
     v = textfsm.TextFSMValue(options_class=textfsm.TextFSMOptions)
@@ -89,17 +82,16 @@ class UnitTestFSM(unittest.TestCase):
     self.assertEqual(v.regex, '(boo[(]hoo)')
 
     # Escaped braces don't count.
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      v.Parse,
-                      r'Value beer (boo\[)\]hoo)')
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, v.Parse, r'Value beer (boo\[)\]hoo)'
+    )
 
     # String function.
     v = textfsm.TextFSMValue(options_class=textfsm.TextFSMOptions)
     v.Parse('Value Required beer (boo(hoo))')
     self.assertEqual(str(v), 'Value Required beer (boo(hoo))')
     v = textfsm.TextFSMValue(options_class=textfsm.TextFSMOptions)
-    v.Parse(
-        r'Value Required,Filldown beer (bo\S+(hoo))')
+    v.Parse(r'Value Required,Filldown beer (bo\S+(hoo))')
     self.assertEqual(str(v), r'Value Required,Filldown beer (bo\S+(hoo))')
 
   def testFSMRule(self):
@@ -144,144 +136,174 @@ class UnitTestFSM(unittest.TestCase):
     self.assertEqual(r.record_op, 'NoRecord')
 
     # Bad syntax tests.
-    self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule,
-                      '  ^A beer called ${beer} -> Next Next Next')
-    self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule,
-                      '  ^A beer called ${beer} -> Boo.hoo')
-    self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule,
-                      '  ^A beer called ${beer} -> Continue.Record $Hi')
+    self.assertRaises(
+        textfsm.TextFSMTemplateError,
+        textfsm.TextFSMRule,
+        '  ^A beer called ${beer} -> Next Next Next',
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError,
+        textfsm.TextFSMRule,
+        '  ^A beer called ${beer} -> Boo.hoo',
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError,
+        textfsm.TextFSMRule,
+        '  ^A beer called ${beer} -> Continue.Record $Hi',
+    )
 
   def testRulePrefixes(self):
     """Test valid and invalid rule prefixes."""
 
     # Bad syntax tests.
     for prefix in (' ', '.^', ' \t', ''):
-      f = StringIO('Value unused (.)\n\nStart\n' + prefix + 'A simple string.')
+      f = io.StringIO(
+          'Value unused (.)\n\nStart\n' + prefix + 'A simple string.'
+      )
       self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, f)
 
     # Good syntax tests.
     for prefix in (' ^', '  ^', '\t^'):
-      f = StringIO('Value unused (.)\n\nStart\n' + prefix + 'A simple string.')
+      f = io.StringIO(
+          'Value unused (.)\n\nStart\n' + prefix + 'A simple string.'
+      )
       self.assertIsNotNone(textfsm.TextFSM(f))
 
   def testImplicitDefaultRules(self):
 
-    for line in ('  ^A beer called ${beer} -> Record End',
-                 '  ^A beer called ${beer} -> End',
-                 '  ^A beer called ${beer} -> Next.NoRecord End',
-                 '  ^A beer called ${beer} -> Clear End',
-                 '  ^A beer called ${beer} -> Error "Hello World"'):
+    for line in (
+        '  ^A beer called ${beer} -> Record End',
+        '  ^A beer called ${beer} -> End',
+        '  ^A beer called ${beer} -> Next.NoRecord End',
+        '  ^A beer called ${beer} -> Clear End',
+        '  ^A beer called ${beer} -> Error "Hello World"',
+    ):
       r = textfsm.TextFSMRule(line)
       self.assertEqual(str(r), line)
 
-    for line in ('  ^A beer called ${beer} -> Next "Hello World"',
-                 '  ^A beer called ${beer} -> Record.Next',
-                 '  ^A beer called ${beer} -> Continue End',
-                 '  ^A beer called ${beer} -> Beer End'):
-      self.assertRaises(textfsm.TextFSMTemplateError,
-                        textfsm.TextFSMRule, line)
+    for line in (
+        '  ^A beer called ${beer} -> Next "Hello World"',
+        '  ^A beer called ${beer} -> Record.Next',
+        '  ^A beer called ${beer} -> Continue End',
+        '  ^A beer called ${beer} -> Beer End',
+    ):
+      self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule, line)
 
   def testSpacesAroundAction(self):
-    for line in ('  ^Hello World -> Boo',
-                 '  ^Hello World ->  Boo',
-                 '  ^Hello World ->   Boo'):
-      self.assertEqual(
-          str(textfsm.TextFSMRule(line)), '  ^Hello World -> Boo')
+    for line in (
+        '  ^Hello World -> Boo',
+        '  ^Hello World ->  Boo',
+        '  ^Hello World ->   Boo',
+    ):
+      self.assertEqual(str(textfsm.TextFSMRule(line)), '  ^Hello World -> Boo')
 
     # A '->' without a leading space is considered part of the matching line.
-    self.assertEqual('  A simple line-> Boo -> Next',
-                     str(textfsm.TextFSMRule('  A simple line-> Boo -> Next')))
+    self.assertEqual(
+        '  A simple line-> Boo -> Next',
+        str(textfsm.TextFSMRule('  A simple line-> Boo -> Next')),
+    )
 
   def testParseFSMVariables(self):
     # Trivial template to initiate object.
-    f = StringIO('Value unused (.)\n\nStart\n')
+    f = io.StringIO('Value unused (.)\n\nStart\n')
     t = textfsm.TextFSM(f)
 
     # Trivial entry
     buf = 'Value Filldown Beer (beer)\n\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._ParseFSMVariables(f)
 
     # Single variable with commented header.
     buf = '# Headline\nValue Filldown Beer (beer)\n\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._ParseFSMVariables(f)
     self.assertEqual(str(t._GetValue('Beer')), 'Value Filldown Beer (beer)')
 
     # Multiple variables.
-    buf = ('# Headline\n'
-           'Value Filldown Beer (beer)\n'
-           'Value Required Spirits (whiskey)\n'
-           'Value Filldown Wine (claret)\n'
-           '\n')
+    buf = (
+        '# Headline\n'
+        'Value Filldown Beer (beer)\n'
+        'Value Required Spirits (whiskey)\n'
+        'Value Filldown Wine (claret)\n'
+        '\n'
+    )
     t._line_num = 0
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._ParseFSMVariables(f)
     self.assertEqual(str(t._GetValue('Beer')), 'Value Filldown Beer (beer)')
     self.assertEqual(
-        str(t._GetValue('Spirits')), 'Value Required Spirits (whiskey)')
+        str(t._GetValue('Spirits')), 'Value Required Spirits (whiskey)'
+    )
     self.assertEqual(str(t._GetValue('Wine')), 'Value Filldown Wine (claret)')
 
     # Multiple variables.
-    buf = ('# Headline\n'
-           'Value Filldown Beer (beer)\n'
-           ' # A comment\n'
-           'Value Spirits ()\n'
-           'Value Filldown,Required Wine ((c|C)laret)\n'
-           '\n')
+    buf = (
+        '# Headline\n'
+        'Value Filldown Beer (beer)\n'
+        ' # A comment\n'
+        'Value Spirits ()\n'
+        'Value Filldown,Required Wine ((c|C)laret)\n'
+        '\n'
+    )
 
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._ParseFSMVariables(f)
     self.assertEqual(str(t._GetValue('Beer')), 'Value Filldown Beer (beer)')
+    self.assertEqual(str(t._GetValue('Spirits')), 'Value Spirits ()')
     self.assertEqual(
-        str(t._GetValue('Spirits')), 'Value Spirits ()')
-    self.assertEqual(str(t._GetValue('Wine')),
-                     'Value Filldown,Required Wine ((c|C)laret)')
+        str(t._GetValue('Wine')), 'Value Filldown,Required Wine ((c|C)laret)'
+    )
 
     # Malformed variables.
     buf = 'Value Beer (beer) beer'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMVariables, f)
 
     buf = 'Value Filldown, Required Spirits ()'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMVariables, f)
     buf = 'Value filldown,Required Wine ((c|C)laret)'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMVariables, f)
 
     # Values that look bad but are OK.
-    buf = ('# Headline\n'
-           'Value Filldown Beer (bee(r), (and) (M)ead$)\n'
-           '# A comment\n'
-           'Value Spirits,and,some ()\n'
-           'Value Filldown,Required Wine ((c|C)laret)\n'
-           '\n')
-    f = StringIO(buf)
+    buf = (
+        '# Headline\n'
+        'Value Filldown Beer (bee(r), (and) (M)ead$)\n'
+        '# A comment\n'
+        'Value Spirits,and,some ()\n'
+        'Value Filldown,Required Wine ((c|C)laret)\n'
+        '\n'
+    )
+    f = io.StringIO(buf)
     t._ParseFSMVariables(f)
-    self.assertEqual(str(t._GetValue('Beer')),
-                     'Value Filldown Beer (bee(r), (and) (M)ead$)')
     self.assertEqual(
-        str(t._GetValue('Spirits,and,some')), 'Value Spirits,and,some ()')
-    self.assertEqual(str(t._GetValue('Wine')),
-                     'Value Filldown,Required Wine ((c|C)laret)')
+        str(t._GetValue('Beer')), 'Value Filldown Beer (bee(r), (and) (M)ead$)'
+    )
+    self.assertEqual(
+        str(t._GetValue('Spirits,and,some')), 'Value Spirits,and,some ()'
+    )
+    self.assertEqual(
+        str(t._GetValue('Wine')), 'Value Filldown,Required Wine ((c|C)laret)'
+    )
 
     # Variable name too long.
-    buf = ('Value Filldown '
-           'nametoolong_nametoolong_nametoolo_nametoolong_nametoolong '
-           '(beer)\n\n')
-    f = StringIO(buf)
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      t._ParseFSMVariables, f)
+    buf = (
+        'Value Filldown '
+        'nametoolong_nametoolong_nametoolo_nametoolong_nametoolong '
+        '(beer)\n\n'
+    )
+    f = io.StringIO(buf)
+    self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMVariables, f)
 
   def testParseFSMState(self):
 
-    f = StringIO('Value Beer (.)\nValue Wine (\\w)\n\nStart\n')
+    f = io.StringIO('Value Beer (.)\nValue Wine (\\w)\n\nStart\n')
     t = textfsm.TextFSM(f)
 
     # Fails as we already have 'Start' state.
     buf = 'Start\n  ^.\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMState, f)
 
     # Remove start so we can test new Start state.
@@ -289,7 +311,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Single state.
     buf = '# Headline\nStart\n  ^.\n\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._ParseFSMState(f)
     self.assertEqual(str(t.states['Start'][0]), '  ^.')
     try:
@@ -299,7 +321,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Multiple states.
     buf = '# Headline\nStart\n  ^.\n  ^Hello World\n  ^Last-[Cc]ha$$nge\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t._line_num = 0
     t.states = {}
     t._ParseFSMState(f)
@@ -315,21 +337,23 @@ class UnitTestFSM(unittest.TestCase):
     t.states = {}
     # Malformed states.
     buf = 'St%art\n  ^.\n  ^Hello World\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMState, f)
 
     buf = 'Start\n^.\n  ^Hello World\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMState, f)
 
     buf = '  Start\n  ^.\n  ^Hello World\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMState, f)
 
     # Multiple variables and substitution (depends on _ParseFSMVariables).
-    buf = ('# Headline\nStart\n  ^.${Beer}${Wine}.\n'
-           '  ^Hello $Beer\n  ^Last-[Cc]ha$$nge\n')
-    f = StringIO(buf)
+    buf = (
+        '# Headline\nStart\n  ^.${Beer}${Wine}.\n'
+        '  ^Hello $Beer\n  ^Last-[Cc]ha$$nge\n'
+    )
+    f = io.StringIO(buf)
     t.states = {}
     t._ParseFSMState(f)
     self.assertEqual(str(t.states['Start'][0]), '  ^.${Beer}${Wine}.')
@@ -344,43 +368,52 @@ class UnitTestFSM(unittest.TestCase):
 
     # State name too long (>32 char).
     buf = 'rnametoolong_nametoolong_nametoolong_nametoolong_nametoolo\n  ^.\n\n'
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     self.assertRaises(textfsm.TextFSMTemplateError, t._ParseFSMState, f)
 
   def testInvalidStates(self):
 
     # 'Continue' should not accept a destination.
-    self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule,
-                      '^.* -> Continue Start')
+    self.assertRaises(
+        textfsm.TextFSMTemplateError,
+        textfsm.TextFSMRule,
+        '^.* -> Continue Start',
+    )
 
     # 'Error' accepts a text string but "next' state does not.
-    self.assertEqual(str(textfsm.TextFSMRule('  ^ -> Error "hi there"')),
-                     '  ^ -> Error "hi there"')
-    self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSMRule,
-                      '^.* -> Next "Hello World"')
+    self.assertEqual(
+        str(textfsm.TextFSMRule('  ^ -> Error "hi there"')),
+        '  ^ -> Error "hi there"',
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError,
+        textfsm.TextFSMRule,
+        '^.* -> Next "Hello World"',
+    )
 
   def testRuleStartsWithCarrot(self):
 
-    f = StringIO(
-        'Value Beer (.)\nValue Wine (\\w)\n\nStart\n  A Simple line')
+    f = io.StringIO(
+        'Value Beer (.)\nValue Wine (\\w)\n\nStart\n  A Simple line'
+    )
     self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, f)
 
   def testValidateFSM(self):
 
     # No Values.
-    f = StringIO('\nNotStart\n')
+    f = io.StringIO('\nNotStart\n')
     self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, f)
 
     # No states.
-    f = StringIO('Value unused (.)\n\n')
+    f = io.StringIO('Value unused (.)\n\n')
     self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, f)
 
     # No 'Start' state.
-    f = StringIO('Value unused (.)\n\nNotStart\n')
+    f = io.StringIO('Value unused (.)\n\nNotStart\n')
     self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, f)
 
     # Has 'Start' state with valid destination
-    f = StringIO('Value unused (.)\n\nStart\n')
+    f = io.StringIO('Value unused (.)\n\nStart\n')
     t = textfsm.TextFSM(f)
     t.states['Start'] = []
     t.states['Start'].append(textfsm.TextFSMRule('^.* -> Start'))
@@ -412,14 +445,14 @@ class UnitTestFSM(unittest.TestCase):
     # Trivial template
     buf = 'Value Beer (.*)\n\nStart\n  ^\\w\n'
     buf_result = buf
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t = textfsm.TextFSM(f)
     self.assertEqual(str(t), buf_result)
 
     # Slightly more complex, multple vars.
     buf = 'Value A (.*)\nValue B (.*)\n\nStart\n  ^\\w\n\nState1\n  ^.\n'
     buf_result = buf
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t = textfsm.TextFSM(f)
     self.assertEqual(str(t), buf_result)
 
@@ -427,7 +460,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Trivial FSM, no records produced.
     tplt = 'Value unused (.)\n\nStart\n  ^Trivial SFM\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'Non-matching text\nline1\nline 2\n'
     self.assertFalse(t.ParseText(data))
@@ -437,7 +470,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Simple FSM, One Variable no options.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next.Record\n\nEOF\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     # Matching one line.
     # Tests 'Next' & 'Record' actions.
@@ -452,10 +485,12 @@ class UnitTestFSM(unittest.TestCase):
     self.assertListEqual(result, [['Matching text'], ['And again']])
 
     # Two Variables and singular options.
-    tplt = ('Value Required boo (one)\nValue Filldown hoo (two)\n\n'
-            'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
-            'EOF\n')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value Required boo (one)\nValue Filldown hoo (two)\n\n'
+        'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
+        'EOF\n'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     # Matching two lines. Only one records returned due to 'Required' flag.
     # Tests 'Filldown' and 'Required' options.
@@ -463,7 +498,7 @@ class UnitTestFSM(unittest.TestCase):
     result = t.ParseText(data)
     self.assertListEqual(result, [['one', 'two']])
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     # Matching two lines. Two records returned due to 'Filldown' flag.
     data = 'two\none\none'
     t.Reset()
@@ -471,11 +506,13 @@ class UnitTestFSM(unittest.TestCase):
     self.assertListEqual(result, [['one', 'two'], ['one', 'two']])
 
     # Multiple Variables and options.
-    tplt = ('Value Required,Filldown boo (one)\n'
-            'Value Filldown,Required hoo (two)\n\n'
-            'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
-            'EOF\n')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value Required,Filldown boo (one)\n'
+        'Value Filldown,Required hoo (two)\n\n'
+        'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
+        'EOF\n'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'two\none\none'
     result = t.ParseText(data)
     self.assertListEqual(result, [['one', 'two'], ['one', 'two']])
@@ -484,7 +521,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Trivial FSM, no records produced.
     tplt = 'Value unused (.)\n\nStart\n  ^Trivial SFM\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'Non-matching text\nline1\nline 2\n'
     self.assertFalse(t.ParseText(data))
@@ -494,7 +531,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # Simple FSM, One Variable no options.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next.Record\n\nEOF\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     # Matching one line.
     # Tests 'Next' & 'Record' actions.
@@ -506,14 +543,17 @@ class UnitTestFSM(unittest.TestCase):
     t.Reset()
     data = 'Matching text\nAnd again'
     result = t.ParseTextToDicts(data)
-    self.assertListEqual(result,
-                     [{'boo': 'Matching text'}, {'boo': 'And again'}])
+    self.assertListEqual(
+        result, [{'boo': 'Matching text'}, {'boo': 'And again'}]
+    )
 
     # Two Variables and singular options.
-    tplt = ('Value Required boo (one)\nValue Filldown hoo (two)\n\n'
-            'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
-            'EOF\n')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value Required boo (one)\nValue Filldown hoo (two)\n\n'
+        'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
+        'EOF\n'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     # Matching two lines. Only one records returned due to 'Required' flag.
     # Tests 'Filldown' and 'Required' options.
@@ -521,30 +561,34 @@ class UnitTestFSM(unittest.TestCase):
     result = t.ParseTextToDicts(data)
     self.assertListEqual(result, [{'hoo': 'two', 'boo': 'one'}])
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     # Matching two lines. Two records returned due to 'Filldown' flag.
     data = 'two\none\none'
     t.Reset()
     result = t.ParseTextToDicts(data)
     self.assertListEqual(
-        result, [{'hoo': 'two', 'boo': 'one'}, {'hoo': 'two', 'boo': 'one'}])
+        result, [{'hoo': 'two', 'boo': 'one'}, {'hoo': 'two', 'boo': 'one'}]
+    )
 
     # Multiple Variables and options.
-    tplt = ('Value Required,Filldown boo (one)\n'
-            'Value Filldown,Required hoo (two)\n\n'
-            'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
-            'EOF\n')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value Required,Filldown boo (one)\n'
+        'Value Filldown,Required hoo (two)\n\n'
+        'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Record\n\n'
+        'EOF\n'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'two\none\none'
     result = t.ParseTextToDicts(data)
     self.assertListEqual(
-        result, [{'hoo': 'two', 'boo': 'one'}, {'hoo': 'two', 'boo': 'one'}])
+        result, [{'hoo': 'two', 'boo': 'one'}, {'hoo': 'two', 'boo': 'one'}]
+    )
 
   def testParseNullText(self):
 
     # Simple FSM, One Variable no options.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next.Record\n\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     # Null string
     data = ''
@@ -554,181 +598,210 @@ class UnitTestFSM(unittest.TestCase):
   def testReset(self):
 
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next.Record\n\nEOF\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'Matching text'
     result1 = t.ParseText(data)
     t.Reset()
     result2 = t.ParseText(data)
     self.assertListEqual(result1, result2)
 
-    tplt = ('Value boo (one)\nValue hoo (two)\n\n'
-            'Start\n  ^$boo -> State1\n\n'
-            'State1\n  ^$hoo -> Start\n\n'
-            'EOF')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value boo (one)\nValue hoo (two)\n\n'
+        'Start\n  ^$boo -> State1\n\n'
+        'State1\n  ^$hoo -> Start\n\n'
+        'EOF'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'one'
     t.ParseText(data)
     t.Reset()
     self.assertEqual(t._cur_state[0].match, '^$boo')
-    self.assertEqual(t._GetValue('boo').value, None)
-    self.assertEqual(t._GetValue('hoo').value, None)
+    self.assertIsNone(None, t._GetValue('boo').value)
+    self.assertIsNone(t._GetValue('hoo').value)
     self.assertEqual(t._result, [])
 
   def testClear(self):
 
     # Clear Filldown variable.
     # Tests 'Clear'.
-    tplt = ('Value Required boo (on.)\n'
-            'Value Filldown,Required hoo (tw.)\n\n'
-            'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Clear')
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Filldown,Required hoo (tw.)\n\n'
+        'Start\n  ^$boo -> Next.Record\n  ^$hoo -> Next.Clear'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\ntwo\nonE\ntwO'
     result = t.ParseText(data)
     self.assertListEqual(result, [['onE', 'two']])
 
     # Clearall, with Filldown variable.
     # Tests 'Clearall'.
-    tplt = ('Value Filldown boo (on.)\n'
-            'Value Filldown hoo (tw.)\n\n'
-            'Start\n  ^$boo -> Next.Clearall\n'
-            '  ^$hoo')
+    tplt = (
+        'Value Filldown boo (on.)\n'
+        'Value Filldown hoo (tw.)\n\n'
+        'Start\n  ^$boo -> Next.Clearall\n'
+        '  ^$hoo'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\ntwo'
     result = t.ParseText(data)
     self.assertListEqual(result, [['', 'two']])
 
   def testContinue(self):
 
-    tplt = ('Value Required boo (on.)\n'
-            'Value Filldown,Required hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Continue.Record')
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Filldown,Required hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Continue.Record'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\non0'
     result = t.ParseText(data)
     self.assertListEqual(result, [['one', 'one'], ['on0', 'on0']])
 
   def testError(self):
 
-    tplt = ('Value Required boo (on.)\n'
-            'Value Filldown,Required hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Error')
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Filldown,Required hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Error'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one'
     self.assertRaises(textfsm.TextFSMError, t.ParseText, data)
 
-    tplt = ('Value Required boo (on.)\n'
-            'Value Filldown,Required hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Error "Hello World"')
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Filldown,Required hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Error "Hello World"'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     self.assertRaises(textfsm.TextFSMError, t.ParseText, data)
 
   def testKey(self):
-    tplt = ('Value Required boo (on.)\n'
-            'Value Required,Key hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Record')
-
-    t = textfsm.TextFSM(StringIO(tplt))
-    self.assertTrue('Key' in t._GetValue('hoo').OptionNames())
-    self.assertTrue('Key' not in t._GetValue('boo').OptionNames())
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Required,Key hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Record'
+    )
+
+    t = textfsm.TextFSM(io.StringIO(tplt))
+    self.assertIn('Key', t._GetValue('hoo').OptionNames())
+    self.assertNotIn('Key', t._GetValue('boo').OptionNames())
 
   def testList(self):
 
-    tplt = ('Value List boo (on.)\n'
-            'Value hoo (tw.)\n\n'
-            'Start\n  ^$boo\n  ^$hoo -> Next.Record\n\n'
-            'EOF')
+    tplt = (
+        'Value List boo (on.)\n'
+        'Value hoo (tw.)\n\n'
+        'Start\n  ^$boo\n  ^$hoo -> Next.Record\n\n'
+        'EOF'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\ntwo\non0\ntw0'
     result = t.ParseText(data)
     self.assertListEqual(result, [[['one'], 'two'], [['on0'], 'tw0']])
 
-    tplt = ('Value List,Filldown boo (on.)\n'
-            'Value hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Next.Record\n\n'
-            'EOF')
+    tplt = (
+        'Value List,Filldown boo (on.)\n'
+        'Value hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Next.Record\n\n'
+        'EOF'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\non0\non1'
     result = t.ParseText(data)
-    self.assertEqual(result, ([[['one'], 'one'],
-                               [['one', 'on0'], 'on0'],
-                               [['one', 'on0', 'on1'], 'on1']]))
-
-    tplt = ('Value List,Required boo (on.)\n'
-            'Value hoo (tw.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Next.Record\n\n'
-            'EOF')
+    self.assertEqual(
+        result,
+        ([
+            [['one'], 'one'],
+            [['one', 'on0'], 'on0'],
+            [['one', 'on0', 'on1'], 'on1'],
+        ]),
+    )
+
+    tplt = (
+        'Value List,Required boo (on.)\n'
+        'Value hoo (tw.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Next.Record\n\n'
+        'EOF'
+    )
 
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'one\ntwo\ntw2'
     result = t.ParseText(data)
     self.assertListEqual(result, [[['one'], 'two']])
 
-
   def testNestedMatching(self):
-      """
-      Ensures that List-type values with nested regex capture groups are parsed
-      correctly as a list of dictionaries.
-
-      Additionaly, another value is used with the same group-name as one of the
-      nested groups to ensure that there are no conflicts when the same name is
-      used.
-      """
-      tplt = (
-          # A nested group is called "name"
-          r"Value List foo ((?P<name>\w+):\s+(?P<age>\d+)\s+(?P<state>\w{2})\s*)"
-          "\n"
-          # A regular value is called "name"
-          r"Value name (\w+)"
-          # "${name}" here refers to the Value called "name"
-          "\n\nStart\n"
-          r"  ^\s*${foo}"
-          "\n"
-          r"  ^\s*${name}"
-          "\n"
-          r"  ^\s*$$ -> Record"
-      )
-      t = textfsm.TextFSM(StringIO(tplt))
-      # Julia should be parsed as "name" separately
-      data = " Bob: 32 NC\n Alice: 27 NY\n Jeff: 45 CA\nJulia\n\n"
-      result = t.ParseText(data)
-      self.assertListEqual(
-          result, (
-              [[[
-                  {'name': 'Bob', 'age': '32', 'state': 'NC'},
-                  {'name': 'Alice', 'age': '27', 'state': 'NY'},
-                  {'name': 'Jeff', 'age': '45', 'state': 'CA'}
-              ], 'Julia']]
-          )
-      )
+    """List-type values with nested regex capture groups are parsed correctly.
 
-  def testNestedNameConflict(self):
-      tplt = (
-          # Two nested groups are called "name"
-          r"Value List foo ((?P<name>\w+)\s+(?P<name>\w+):\s+(?P<age>\d+)\s+(?P<state>\w{2})\s*)"
-          "\nStart\n"
-          r"^\s*${foo}"
-          "\n  ^"
-          r"\s*$$ -> Record"
-      )
-      self.assertRaises(textfsm.TextFSMTemplateError, textfsm.TextFSM, StringIO(tplt))
+    Additionaly, another value is used with the same group-name as one of the
+    nested groups to ensure that there are no conflicts when the same name is
+    used.
+    """
+
+    tplt = (
+        # A nested group is called "name"
+        r'Value List foo ((?P<name>\w+):\s+(?P<age>\d+)\s+(?P<state>\w{2})\s*)'
+        '\n'
+        # A regular value is called "name"
+        r'Value name (\w+)'
+        # "${name}" here refers to the Value called "name"
+        '\n\nStart\n'
+        r'  ^\s*${foo}'
+        '\n'
+        r'  ^\s*${name}'
+        '\n'
+        r'  ^\s*$$ -> Record'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
+    # Julia should be parsed as "name" separately
+    data = ' Bob: 32 NC\n Alice: 27 NY\n Jeff: 45 CA\nJulia\n\n'
+    result = t.ParseText(data)
+    self.assertListEqual(
+        result,
+        ([[
+            [
+                {'name': 'Bob', 'age': '32', 'state': 'NC'},
+                {'name': 'Alice', 'age': '27', 'state': 'NY'},
+                {'name': 'Jeff', 'age': '45', 'state': 'CA'},
+            ],
+            'Julia',
+        ]]),
+    )
 
+  def testNestedNameConflict(self):
+    tplt = (
+        # Two nested groups are called "name"
+        r'Value List foo'
+        r' ((?P<name>\w+)\s+(?P<name>\w+):\s+(?P<age>\d+)\s+(?P<state>\w{2})\s*)'
+        '\nStart\n'
+        r'^\s*${foo}'
+        '\n  ^'
+        r'\s*$$ -> Record'
+    )
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, textfsm.TextFSM, io.StringIO(tplt)
+    )
 
   def testGetValuesByAttrib(self):
 
-    tplt = ('Value Required boo (on.)\n'
-            'Value Required,List hoo (on.)\n\n'
-            'Start\n  ^$boo -> Continue\n  ^$hoo -> Record')
+    tplt = (
+        'Value Required boo (on.)\n'
+        'Value Required,List hoo (on.)\n\n'
+        'Start\n  ^$boo -> Continue\n  ^$hoo -> Record'
+    )
 
     # Explicit default.
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     self.assertEqual(t.GetValuesByAttrib('List'), ['hoo'])
     self.assertEqual(t.GetValuesByAttrib('Filldown'), [])
     result = t.GetValuesByAttrib('Required')
@@ -738,37 +811,41 @@ class UnitTestFSM(unittest.TestCase):
   def testStateChange(self):
 
     # Sinple state change, no actions
-    tplt = ('Value boo (one)\nValue hoo (two)\n\n'
-            'Start\n  ^$boo -> State1\n\nState1\n  ^$hoo -> Start\n\n'
-            'EOF')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value boo (one)\nValue hoo (two)\n\n'
+        'Start\n  ^$boo -> State1\n\nState1\n  ^$hoo -> Start\n\n'
+        'EOF'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'one'
     t.ParseText(data)
     self.assertEqual(t._cur_state[0].match, '^$hoo')
     self.assertEqual('one', t._GetValue('boo').value)
-    self.assertEqual(None, t._GetValue('hoo').value)
+    self.assertIsNone(t._GetValue('hoo').value)
     self.assertEqual(t._result, [])
 
     # State change with actions.
-    tplt = ('Value boo (one)\nValue hoo (two)\n\n'
-            'Start\n  ^$boo -> Next.Record State1\n\n'
-            'State1\n  ^$hoo -> Start\n\n'
-            'EOF')
-    t = textfsm.TextFSM(StringIO(tplt))
+    tplt = (
+        'Value boo (one)\nValue hoo (two)\n\n'
+        'Start\n  ^$boo -> Next.Record State1\n\n'
+        'State1\n  ^$hoo -> Start\n\n'
+        'EOF'
+    )
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'one'
     t.ParseText(data)
     self.assertEqual(t._cur_state[0].match, '^$hoo')
-    self.assertEqual(None, t._GetValue('boo').value)
-    self.assertEqual(None, t._GetValue('hoo').value)
+    self.assertIsNone(t._GetValue('boo').value)
+    self.assertIsNone(t._GetValue('hoo').value)
     self.assertEqual(t._result, [['one', '']])
 
   def testEOF(self):
 
     # Implicit EOF.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     data = 'Matching text'
     result = t.ParseText(data)
@@ -776,14 +853,14 @@ class UnitTestFSM(unittest.TestCase):
 
     # EOF explicitly suppressed in template.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next\n\nEOF\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     result = t.ParseText(data)
     self.assertListEqual(result, [])
 
     # Implicit EOF suppressed by argument.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     result = t.ParseText(data, eof=False)
     self.assertListEqual(result, [])
@@ -792,7 +869,7 @@ class UnitTestFSM(unittest.TestCase):
 
     # End State, EOF is skipped.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> End\n  ^$boo -> Record\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'Matching text A\nMatching text B'
 
     result = t.ParseText(data)
@@ -800,14 +877,14 @@ class UnitTestFSM(unittest.TestCase):
 
     # End State, with explicit Record.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Record End\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     result = t.ParseText(data)
     self.assertListEqual(result, [['Matching text A']])
 
     # EOF state transition is followed by implicit End State.
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> EOF\n  ^$boo -> Record\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
 
     result = t.ParseText(data)
     self.assertListEqual(result, [['Matching text A']])
@@ -815,14 +892,15 @@ class UnitTestFSM(unittest.TestCase):
   def testInvalidRegexp(self):
 
     tplt = 'Value boo (.$*)\n\nStart\n  ^$boo -> Next\n'
-    self.assertRaises(textfsm.TextFSMTemplateError,
-                      textfsm.TextFSM, StringIO(tplt))
+    self.assertRaises(
+        textfsm.TextFSMTemplateError, textfsm.TextFSM, io.StringIO(tplt)
+    )
 
   def testValidRegexp(self):
     """RegexObjects uncopyable in Python 2.6."""
 
     tplt = 'Value boo (fo*)\n\nStart\n  ^$boo -> Record\n'
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     data = 'f\nfo\nfoo\n'
     result = t.ParseText(data)
     self.assertListEqual(result, [['f'], ['fo'], ['foo']])
@@ -832,7 +910,7 @@ class UnitTestFSM(unittest.TestCase):
 
     tplt = 'Value boo (.*)\n\nStart\n  ^$boo -> Next Stop\n\nStop\n  ^abc\n'
     output_text = 'one\ntwo'
-    tmpl_file = StringIO(tplt)
+    tmpl_file = io.StringIO(tplt)
 
     t = textfsm.TextFSM(tmpl_file)
     t.ParseText(output_text)
@@ -856,10 +934,11 @@ Start
 2 A2 --
 3 -- B3
 """
-    t = textfsm.TextFSM(StringIO(tplt))
+    t = textfsm.TextFSM(io.StringIO(tplt))
     result = t.ParseText(data)
     self.assertListEqual(
-        result, [['1', 'A2', 'B1'], ['2', 'A2', 'B3'], ['3', '', 'B3']])
+        result, [['1', 'A2', 'B1'], ['2', 'A2', 'B3'], ['3', '', 'B3']]
+    )
 
 
 class UnitTestUnicode(unittest.TestCase):
@@ -918,7 +997,7 @@ State1
   ^$$ -> Next
   ^$$ -> End
 """
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t = textfsm.TextFSM(f)
     self.assertEqual(str(t), buf_result)
 
Index: textfsm-1.1.3/tests/texttable_test.py
===================================================================
--- textfsm-1.1.3.orig/tests/texttable_test.py
+++ textfsm-1.1.3/tests/texttable_test.py
@@ -16,14 +16,8 @@
 
 """Unittest for text table."""
 
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
-from builtins import range
+import io
 import unittest
-from io import StringIO
 from textfsm import terminal
 from textfsm import texttable
 
@@ -84,8 +78,8 @@ class UnitTestRow(unittest.TestCase):
     self.assertEqual(3, len(row))
 
     # Contains.
-    self.assertTrue('two' not in row)
-    self.assertTrue('Two' in row)
+    self.assertNotIn('two', row)
+    self.assertIn('Two', row)
 
     # Iteration.
     self.assertEqual(['one', 'Two', 'three'], list(row))
@@ -253,8 +247,8 @@ class UnitTestTextTable(unittest.TestCas
 
   def testContains(self):
     t = self.BasicTable()
-    self.assertTrue('a' in t)
-    self.assertFalse('x' in t)
+    self.assertIn('a', t)
+    self.assertNotIn('x', t)
 
   def testIteration(self):
     t = self.BasicTable()
@@ -271,6 +265,7 @@ class UnitTestTextTable(unittest.TestCas
 
     # Can we iterate repeatedly.
     index = 0
+    index2 = 0
     for r in t:
       index += 1
       self.assertEqual(r, t[index])
@@ -312,7 +307,7 @@ a,b, c, d  # Trim comment
 10, 11
 # More comments.
 """
-    f = StringIO(buf)
+    f = io.StringIO(buf)
     t = texttable.TextTable()
     self.assertEqual(2, t.CsvToTable(f))
     # pylint: disable=E1101
@@ -514,7 +509,7 @@ a,b, c, d  # Trim comment
         3, t._SmallestColSize('bbb ' + terminal.AnsiText('bb', ['red'])))
 
   def testFormattedTableColor(self):
-    # Test to sepcify the color defined in terminal.FG_COLOR_WORDS
+    # Test to specify the color defined in terminal.FG_COLOR_WORDS
     t = texttable.TextTable()
     t.header = ('LSP', 'Name')
     t.Append(('col1', 'col2'))
Index: textfsm-1.1.3/textfsm/clitable.py
===================================================================
--- textfsm-1.1.3.orig/textfsm/clitable.py
+++ textfsm-1.1.3/textfsm/clitable.py
@@ -23,20 +23,12 @@ output combinations and store the data i
 Is the glue between an automated command scraping program (such as RANCID) and
 the TextFSM output parser.
 """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
 
 import copy
 import os
 import re
 import threading
-from builtins import object    # pylint: disable=redefined-builtin
-from builtins import str       # pylint: disable=redefined-builtin
 import textfsm
-
-from textfsm import copyable_regex_object
 from textfsm import texttable
 
 
@@ -48,7 +40,7 @@ class IndexTableError(Error):
   """General INdexTable error."""
 
 
-class CliTableError(Error):
+class CliTableError(Error):  # pylint: disable=g-bad-exception-name
   """General CliTable error."""
 
 
@@ -139,7 +131,7 @@ class IndexTable(object):
         if precompile:
           row[col] = precompile(col, row[col])
         if row[col]:
-          row[col] = copyable_regex_object.CopyableRegexObject(row[col])
+          row[col] = re.compile(row[col])
 
   def GetRowMatch(self, attributes):
     """Returns the row number that matches the supplied attributes."""
@@ -148,8 +140,11 @@ class IndexTable(object):
         for key in attributes:
           # Silently skip attributes not present in the index file.
           # pylint: disable=E1103
-          if (key in row.header and row[key] and
-              not row[key].match(attributes[key])):
+          if (
+              key in row.header
+              and row[key]
+              and not row[key].match(attributes[key])
+          ):
             # This line does not match, so break and try next row.
             raise StopIteration()
         return row.row
@@ -184,11 +179,12 @@ class CliTable(texttable.TextTable):
 
     # pylint: disable=E0213
     def Wrapper(main_obj, *args, **kwargs):
-      main_obj._lock.acquire()                  # pylint: disable=W0212
+      main_obj._lock.acquire()  # pylint: disable=W0212
       try:
         return func(main_obj, *args, **kwargs)  # pylint: disable=E1102
       finally:
-        main_obj._lock.release()                # pylint: disable=W0212
+        main_obj._lock.release()  # pylint: disable=W0212
+
     return Wrapper
 
   @synchronised
@@ -227,7 +223,7 @@ class CliTable(texttable.TextTable):
       self.index = self.INDEX[fullpath]
 
     # Does the IndexTable have the right columns.
-    if 'Template' not in self.index.index.header:    # pylint: disable=E1103
+    if 'Template' not in self.index.index.header:  # pylint: disable=E1103
       raise CliTableError("Index file does not have 'Template' column.")
 
   def _TemplateNamesToFiles(self, template_str):
@@ -237,8 +233,7 @@ class CliTable(texttable.TextTable):
     template_files = []
     try:
       for tmplt in template_list:
-        template_files.append(
-            open(os.path.join(self.template_dir, tmplt), 'r'))
+        template_files.append(open(os.path.join(self.template_dir, tmplt), 'r'))
     except:
       for tmplt in template_files:
         tmplt.close()
@@ -269,8 +264,9 @@ class CliTable(texttable.TextTable):
       if row_idx:
         templates = self.index.index[row_idx]['Template']
       else:
-        raise CliTableError('No template found for attributes: "%s"' %
-                            attributes)
+        raise CliTableError(
+            'No template found for attributes: "%s"' % attributes
+        )
 
     template_files = self._TemplateNamesToFiles(templates)
 
@@ -282,8 +278,9 @@ class CliTable(texttable.TextTable):
 
       # Add additional columns from any additional tables.
       for tmplt in template_files[1:]:
-        self.extend(self._ParseCmdItem(self.raw, template_file=tmplt),
-                    set(self._keys))
+        self.extend(
+            self._ParseCmdItem(self.raw, template_file=tmplt), set(self._keys)
+        )
     finally:
       for f in template_files:
         f.close()
@@ -357,6 +354,7 @@ class CliTable(texttable.TextTable):
     if not key and self._keys:
       key = self.KeyValue
     super(CliTable, self).sort(cmp=cmp, key=key, reverse=reverse)
+
   # pylint: enable=W0622
 
   def AddKeys(self, key_list):
Index: textfsm-1.1.3/textfsm/parser.py
===================================================================
--- textfsm-1.1.3.orig/textfsm/parser.py
+++ textfsm-1.1.3/textfsm/parser.py
@@ -23,28 +23,19 @@ A simple template language is used to de
 parse a specific type of text input, returning a record of values
 for each input entity.
 """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
 
 import getopt
 import inspect
 import re
 import string
 import sys
-from builtins import object   # pylint: disable=redefined-builtin
-from builtins import str      # pylint: disable=redefined-builtin
-from builtins import zip      # pylint: disable=redefined-builtin
-import six
 
 
 class Error(Exception):
   """Base class for errors."""
 
 
-class Usage(Exception):
+class UsageError(Exception):
   """Error in command line execution."""
 
 
@@ -58,15 +49,15 @@ class TextFSMTemplateError(Error):
 
 # The below exceptions are internal state change triggers
 # and not used as Errors.
-class FSMAction(Exception):
+class FSMAction(Exception):  # pylint: disable=g-bad-exception-name
   """Base class for actions raised with the FSM."""
 
 
-class SkipRecord(FSMAction):
+class SkipRecord(FSMAction):  # pylint: disable=g-bad-exception-name
   """Indicate a record is to be skipped."""
 
 
-class SkipValue(FSMAction):
+class SkipValue(FSMAction):  # pylint: disable=g-bad-exception-name
   """Indicate a value is to be skipped."""
 
 
@@ -175,8 +166,8 @@ class TextFSMOptions(object):
     """Value constitutes part of the Key of the record."""
 
   class List(OptionBase):
-    r"""
-    Value takes the form of a list.
+    # pylint: disable=g-space-before-docstring-summary
+    r"""Value takes the form of a list.
 
     If the value regex contains nested match groups in the form (?P<name>regex),
     instead of adding a string to the list, we add a dictionary of the groups.
@@ -237,6 +228,7 @@ class TextFSMValue(object):
     fsm: A TextFSMBase(), the containing FSM.
     value: (str), the current value.
   """
+
   # The class which contains valid options.
 
   def __init__(self, fsm=None, max_name_len=48, options_class=None):
@@ -285,7 +277,6 @@ class TextFSMValue(object):
 
     Raises:
       TextFSMTemplateError: Value declaration contains an error.
-
     """
 
     value_line = value.split(' ')
@@ -310,15 +301,17 @@ class TextFSMValue(object):
 
     if len(self.name) > self.max_name_len:
       raise TextFSMTemplateError(
-          "Invalid Value name '%s' or name too long." % self.name)
+          "Invalid Value name '%s' or name too long." % self.name
+      )
 
-    if self.regex[0]!='(' or self.regex[-1]!=')' or self.regex[-2]=='\\':
+    if self.regex[0] != '(' or self.regex[-1] != ')' or self.regex[-2] == '\\':
       raise TextFSMTemplateError(
-          "Value '%s' must be contained within a '()' pair." % self.regex)
+          "Value '%s' must be contained within a '()' pair." % self.regex
+      )
     try:
       compiled_regex = re.compile(self.regex)
-    except re.error as e:
-      raise TextFSMTemplateError(str(e))
+    except re.error as exc:
+      raise TextFSMTemplateError(str(exc)) from exc
 
     self.template = re.sub(r'^\(', '(?P<%s>' % self.name, self.regex)
 
@@ -345,8 +338,8 @@ class TextFSMValue(object):
     # Create the option object
     try:
       option = self._options_cls.GetOption(name)(self)
-    except AttributeError:
-      raise TextFSMTemplateError('Unknown option "%s"' % name)
+    except AttributeError as exc:
+      raise TextFSMTemplateError('Unknown option "%s"' % name) from exc
 
     self.options.append(option)
 
@@ -361,7 +354,8 @@ class TextFSMValue(object):
       return 'Value %s %s %s' % (
           ','.join(self.OptionNames()),
           self.name,
-          self.regex)
+          self.regex,
+      )
     else:
       return 'Value %s %s' % (self.name, self.regex)
 
@@ -373,10 +367,10 @@ class CopyableRegexObject(object):
     self.pattern = pattern
     self.regex = re.compile(pattern)
 
-  def match(self, *args, **kwargs):
+  def match(self, *args, **kwargs):  # pylint: disable=invalid-name
     return self.regex.match(*args, **kwargs)
 
-  def sub(self, *args, **kwargs):
+  def sub(self, *args, **kwargs):  # pylint: disable=invalid-name
     return self.regex.sub(*args, **kwargs)
 
   def __copy__(self):
@@ -407,6 +401,7 @@ class TextFSMRule(object):
     regex_obj: Compiled regex for which the rule matches.
     line_num: Integer row number of Value.
   """
+
   # Implicit default is '(regexp) -> Next.NoRecord'
   MATCH_ACTION = re.compile(r'(?P<match>.*)(\s->(?P<action>.*))')
 
@@ -444,15 +439,16 @@ class TextFSMRule(object):
     self.match = ''
     self.regex = ''
     self.regex_obj = None
-    self.line_op = ''              # Equivalent to 'Next'.
-    self.record_op = ''            # Equivalent to 'NoRecord'.
-    self.new_state = ''            # Equivalent to current state.
+    self.line_op = ''  # Equivalent to 'Next'.
+    self.record_op = ''  # Equivalent to 'NoRecord'.
+    self.new_state = ''  # Equivalent to current state.
     self.line_num = line_num
 
     line = line.strip()
     if not line:
-      raise TextFSMTemplateError('Null data in FSMRule. Line: %s'
-                                 % self.line_num)
+      raise TextFSMTemplateError(
+          'Null data in FSMRule. Line: %s' % self.line_num
+      )
 
     # Is there '->' action present.
     match_action = self.MATCH_ACTION.match(line)
@@ -466,18 +462,20 @@ class TextFSMRule(object):
     if var_map:
       try:
         self.regex = string.Template(self.match).substitute(var_map)
-      except (ValueError, KeyError):
+      except (ValueError, KeyError) as exc:
         raise TextFSMTemplateError(
-            "Duplicate or invalid variable substitution: '%s'. Line: %s." %
-            (self.match, self.line_num))
+            "Duplicate or invalid variable substitution: '%s'. Line: %s."
+            % (self.match, self.line_num)
+        ) from exc
 
     try:
       # Work around a regression in Python 2.6 that makes RE Objects uncopyable.
       self.regex_obj = CopyableRegexObject(self.regex)
-    except re.error:
+    except re.error as exc:
       raise TextFSMTemplateError(
-          "Invalid regular expression: '%s'. Line: %s." %
-          (self.regex, self.line_num))
+          "Invalid regular expression: '%s'. Line: %s."
+          % (self.regex, self.line_num)
+      ) from exc
 
     # No '->' present, so done.
     if not match_action:
@@ -493,8 +491,9 @@ class TextFSMRule(object):
         action_re = self.ACTION3_RE.match(match_action.group('action'))
         if not action_re:
           # Last attempt, match an optional new state only.
-          raise TextFSMTemplateError("Badly formatted rule '%s'. Line: %s." %
-                                     (line, self.line_num))
+          raise TextFSMTemplateError(
+              "Badly formatted rule '%s'. Line: %s." % (line, self.line_num)
+          )
 
     # We have an Line operator.
     if 'ln_op' in action_re.groupdict() and action_re.group('ln_op'):
@@ -514,14 +513,16 @@ class TextFSMRule(object):
     if self.line_op == 'Continue' and self.new_state:
       raise TextFSMTemplateError(
           "Action '%s' with new state %s specified. Line: %s."
-          % (self.line_op, self.new_state, self.line_num))
+          % (self.line_op, self.new_state, self.line_num)
+      )
 
     # Check that an error message is present only with the 'Error' operator.
     if self.line_op != 'Error' and self.new_state:
       if not re.match(r'\w+', self.new_state):
         raise TextFSMTemplateError(
             'Alphanumeric characters only in state names. Line: %s.'
-            % (self.line_num))
+            % (self.line_num)
+        )
 
   def __str__(self):
     """Prints out the FSM Rule, mimic the input file."""
@@ -555,6 +556,7 @@ class TextFSM(object):
     header: Ordered list of values.
     state_list: Ordered list of valid states.
   """
+
   # Variable and State name length.
   MAX_NAME_LEN = 48
   comment_regex = re.compile(r'^\s*#')
@@ -709,7 +711,7 @@ class TextFSM(object):
       # Blank line signifies end of Value definitions.
       if not line:
         return
-      if not isinstance(line, six.string_types):
+      if not isinstance(line, str):
         line = line.decode('utf-8')
       # Skip commented lines.
       if self.comment_regex.match(line):
@@ -718,21 +720,28 @@ class TextFSM(object):
       if line.startswith('Value '):
         try:
           value = TextFSMValue(
-              fsm=self, max_name_len=self.MAX_NAME_LEN,
-              options_class=self._options_cls)
+              fsm=self,
+              max_name_len=self.MAX_NAME_LEN,
+              options_class=self._options_cls,
+          )
           value.Parse(line)
-        except TextFSMTemplateError as error:
-          raise TextFSMTemplateError('%s Line %s.' % (error, self._line_num))
+        except TextFSMTemplateError as exc:
+          raise TextFSMTemplateError(
+              '%s Line %s.' % (exc, self._line_num)
+          ) from exc
 
         if value.name in self.header:
           raise TextFSMTemplateError(
               "Duplicate declarations for Value '%s'. Line: %s."
-              % (value.name, self._line_num))
+              % (value.name, self._line_num)
+          )
 
         try:
           self._ValidateOptions(value)
-        except TextFSMTemplateError as error:
-          raise TextFSMTemplateError('%s Line %s.' % (error, self._line_num))
+        except TextFSMTemplateError as exc:
+          raise TextFSMTemplateError(
+              '%s Line %s.' % (exc, self._line_num)
+          ) from exc
 
         self.values.append(value)
         self.value_map[value.name] = value.template
@@ -742,7 +751,8 @@ class TextFSM(object):
       else:
         raise TextFSMTemplateError(
             'Expected blank line after last Value entry. Line: %s.'
-            % (self._line_num))
+            % (self._line_num)
+        )
 
   def _ValidateOptions(self, value):
     """Checks that combination of Options is valid."""
@@ -760,8 +770,8 @@ class TextFSM(object):
     not clash with reserved names and are unique.
 
     Args:
-      template: Valid template file after Value definitions
-      have already been read.
+      template: Valid template file after Value definitions have already been
+        read.
 
     Returns:
       Name of the state parsed from file. None otherwise.
@@ -778,22 +788,26 @@ class TextFSM(object):
     for line in template:
       self._line_num += 1
       line = line.rstrip()
-      if not isinstance(line, six.string_types):
+      if not isinstance(line, str):
         line = line.decode('utf-8')
       # First line is state definition
       if line and not self.comment_regex.match(line):
-         # Ensure statename has valid syntax and is not a reserved word.
-        if (not self.state_name_re.match(line) or
-            len(line) > self.MAX_NAME_LEN or
-            line in TextFSMRule.LINE_OP or
-            line in TextFSMRule.RECORD_OP):
-          raise TextFSMTemplateError("Invalid state name: '%s'. Line: %s"
-                                     % (line, self._line_num))
+        # Ensure statename has valid syntax and is not a reserved word.
+        if (
+            not self.state_name_re.match(line)
+            or len(line) > self.MAX_NAME_LEN
+            or line in TextFSMRule.LINE_OP
+            or line in TextFSMRule.RECORD_OP
+        ):
+          raise TextFSMTemplateError(
+              "Invalid state name: '%s'. Line: %s" % (line, self._line_num)
+          )
 
         state_name = line
         if state_name in self.states:
-          raise TextFSMTemplateError("Duplicate state name: '%s'. Line: %s"
-                                     % (line, self._line_num))
+          raise TextFSMTemplateError(
+              "Duplicate state name: '%s'. Line: %s" % (line, self._line_num)
+          )
         self.states[state_name] = []
         self.state_list.append(state_name)
         break
@@ -806,7 +820,7 @@ class TextFSM(object):
       # Finish rules processing on blank line.
       if not line:
         break
-      if not isinstance(line, six.string_types):
+      if not isinstance(line, str):
         line = line.decode('utf-8')
       if self.comment_regex.match(line):
         continue
@@ -814,11 +828,13 @@ class TextFSM(object):
       # A rule within a state, starts with 1 or 2 spaces, or a tab.
       if not line.startswith((' ^', '  ^', '\t^')):
         raise TextFSMTemplateError(
-            "Missing white space or carat ('^') before rule. Line: %s" %
-            self._line_num)
+            "Missing white space or carat ('^') before rule. Line: %s"
+            % self._line_num
+        )
 
       self.states[state_name].append(
-          TextFSMRule(line, self._line_num, self.value_map))
+          TextFSMRule(line, self._line_num, self.value_map)
+      )
 
     return state_name
 
@@ -864,8 +880,9 @@ class TextFSM(object):
 
         if rule.new_state not in self.states:
           raise TextFSMTemplateError(
-              "State '%s' not found, referenced in state '%s'" %
-              (rule.new_state, state))
+              "State '%s' not found, referenced in state '%s'"
+              % (rule.new_state, state)
+          )
 
     return True
 
@@ -877,7 +894,7 @@ class TextFSM(object):
     Args:
       text: (str), Text to parse with embedded newlines.
       eof: (boolean), Set to False if we are parsing only part of the file.
-            Suppresses triggering EOF state.
+        Suppresses triggering EOF state.
 
     Raises:
       TextFSMError: An error occurred within the FSM.
@@ -902,7 +919,7 @@ class TextFSM(object):
 
     return self._result
 
-  def ParseTextToDicts(self, *args, **kwargs):
+  def ParseTextToDicts(self, text, eof=True):
     """Calls ParseText and turns the result into list of dicts.
 
     List items are dicts of rows, dict key is column header and value is column
@@ -911,7 +928,7 @@ class TextFSM(object):
     Args:
       text: (str), Text to parse with embedded newlines.
       eof: (boolean), Set to False if we are parsing only part of the file.
-            Suppresses triggering EOF state.
+        Suppresses triggering EOF state.
 
     Raises:
       TextFSMError: An error occurred within the FSM.
@@ -920,7 +937,7 @@ class TextFSM(object):
       List of dicts.
     """
 
-    result_lists = self.ParseText(*args, **kwargs)
+    result_lists = self.ParseText(text, eof)
     result_dicts = []
 
     for row in result_lists:
@@ -972,9 +989,9 @@ class TextFSM(object):
       matched: (regexp.match) Named group for each matched value.
       value: (str) The matched value.
     """
-    _value = self._GetValue(value)
-    if _value is not None:
-      _value.AssignVar(matched.group(value))
+    self._value = self._GetValue(value)
+    if self._value is not None:
+      self._value.AssignVar(matched.group(value))
 
   def _Operations(self, rule, line):
     """Operators on the data record.
@@ -1017,11 +1034,15 @@ class TextFSM(object):
     # Lastly process line operators.
     if rule.line_op == 'Error':
       if rule.new_state:
-        raise TextFSMError('Error: %s. Rule Line: %s. Input Line: %s.'
-                           % (rule.new_state, rule.line_num, line))
-
-      raise TextFSMError('State Error raised. Rule Line: %s. Input Line: %s'
-                         % (rule.line_num, line))
+        raise TextFSMError(
+            'Error: %s. Rule Line: %s. Input Line: %s.'
+            % (rule.new_state, rule.line_num, line)
+        )
+
+      raise TextFSMError(
+          'State Error raised. Rule Line: %s. Input Line: %s'
+          % (rule.line_num, line)
+      )
 
     elif rule.line_op == 'Continue':
       # Continue with current line without returning to the start of the state.
@@ -1060,8 +1081,8 @@ def main(argv=None):
 
   try:
     opts, args = getopt.getopt(argv[1:], 'h', ['help'])
-  except getopt.error as msg:
-    raise Usage(msg)
+  except getopt.error as exc:
+    raise UsageError(exc) from exc
 
   for opt, _ in opts:
     if opt in ('-h', '--help'):
@@ -1070,10 +1091,11 @@ def main(argv=None):
       return 0
 
   if not args or len(args) > 4:
-    raise Usage('Invalid arguments.')
+    raise UsageError('Invalid arguments.')
 
   # If we have an argument, parse content of file and display as a template.
   # Template displayed will match input template, minus any comment lines.
+  result = ''
   with open(args[0], 'r') as template:
     fsm = TextFSM(template)
     print('FSM Template:\n%s\n' % fsm)
@@ -1108,7 +1130,7 @@ if __name__ == '__main__':
   help_msg = '%s [--help] template [input_file [output_file]]\n' % sys.argv[0]
   try:
     sys.exit(main())
-  except Usage as err:
+  except UsageError as err:
     print(err, file=sys.stderr)
     print('For help use --help', file=sys.stderr)
     sys.exit(2)
Index: textfsm-1.1.3/textfsm/terminal.py
===================================================================
--- textfsm-1.1.3.orig/textfsm/terminal.py
+++ textfsm-1.1.3/textfsm/terminal.py
@@ -16,26 +16,20 @@
 
 """Simple terminal related routines."""
 
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
-try:
-  # Import fails on Windows machines.
-  import fcntl
-  import termios
-  import tty
-except (ImportError, ModuleNotFoundError):
-  pass
 import getopt
 import os
 import re
 import struct
 import sys
 import time
-from builtins import object   # pylint: disable=redefined-builtin
-from builtins import str      # pylint: disable=redefined-builtin
+
+try:
+  # Import fails on Windows machines.
+  import fcntl  # pylint: disable=g-import-not-at-top
+  import termios  # pylint: disable=g-import-not-at-top
+  import tty  # pylint: disable=g-import-not-at-top
+except (ImportError, ModuleNotFoundError):
+  pass
 
 __version__ = '0.1.1'
 
@@ -67,34 +61,38 @@ SGR = {
     'bg_cyan': 46,
     'bg_white': 47,
     'bg_reset': 49,
-    }
+}
 
 # Provide a familar descriptive word for some ansi sequences.
-FG_COLOR_WORDS = {'black': ['black'],
-                  'dark_gray': ['bold', 'black'],
-                  'blue': ['blue'],
-                  'light_blue': ['bold', 'blue'],
-                  'green': ['green'],
-                  'light_green': ['bold', 'green'],
-                  'cyan': ['cyan'],
-                  'light_cyan': ['bold', 'cyan'],
-                  'red': ['red'],
-                  'light_red': ['bold', 'red'],
-                  'purple': ['magenta'],
-                  'light_purple': ['bold', 'magenta'],
-                  'brown': ['yellow'],
-                  'yellow': ['bold', 'yellow'],
-                  'light_gray': ['white'],
-                  'white': ['bold', 'white']}
-
-BG_COLOR_WORDS = {'black': ['bg_black'],
-                  'red': ['bg_red'],
-                  'green': ['bg_green'],
-                  'yellow': ['bg_yellow'],
-                  'dark_blue': ['bg_blue'],
-                  'purple': ['bg_magenta'],
-                  'light_blue': ['bg_cyan'],
-                  'grey': ['bg_white']}
+FG_COLOR_WORDS = {
+    'black': ['black'],
+    'dark_gray': ['bold', 'black'],
+    'blue': ['blue'],
+    'light_blue': ['bold', 'blue'],
+    'green': ['green'],
+    'light_green': ['bold', 'green'],
+    'cyan': ['cyan'],
+    'light_cyan': ['bold', 'cyan'],
+    'red': ['red'],
+    'light_red': ['bold', 'red'],
+    'purple': ['magenta'],
+    'light_purple': ['bold', 'magenta'],
+    'brown': ['yellow'],
+    'yellow': ['bold', 'yellow'],
+    'light_gray': ['white'],
+    'white': ['bold', 'white'],
+}
+
+BG_COLOR_WORDS = {
+    'black': ['bg_black'],
+    'red': ['bg_red'],
+    'green': ['bg_green'],
+    'yellow': ['bg_yellow'],
+    'dark_blue': ['bg_blue'],
+    'purple': ['bg_magenta'],
+    'light_blue': ['bg_cyan'],
+    'grey': ['bg_white'],
+}
 
 
 # Characters inserted at the start and end of ANSI strings
@@ -103,15 +101,14 @@ ANSI_START = '\001'
 ANSI_END = '\002'
 
 
-sgr_re = re.compile(r'(%s?\033\[\d+(?:;\d+)*m%s?)' % (
-    ANSI_START, ANSI_END))
+sgr_re = re.compile(r'(%s?\033\[\d+(?:;\d+)*m%s?)' % (ANSI_START, ANSI_END))
 
 
 class Error(Exception):
   """The base error class."""
 
 
-class Usage(Error):
+class UsageError(Error):
   """Command line format error."""
 
 
@@ -119,8 +116,8 @@ def _AnsiCmd(command_list):
   """Takes a list of SGR values and formats them as an ANSI escape sequence.
 
   Args:
-    command_list: List of strings, each string represents an SGR value.
-        e.g. 'fg_blue', 'bg_yellow'
+    command_list: List of strings, each string represents an SGR value. e.g.
+      'fg_blue', 'bg_yellow'
 
   Returns:
     The ANSI escape sequence.
@@ -138,7 +135,7 @@ def _AnsiCmd(command_list):
   # Convert to numerical strings.
   command_str = [str(SGR[x.lower()]) for x in command_list]
   # Wrap values in Ansi escape sequence (CSI prefix & SGR suffix).
-  return '\033[%sm' % (';'.join(command_str))
+  return '\033[%sm' % ';'.join(command_str)
 
 
 def AnsiText(text, command_list=None, reset=True):
@@ -146,8 +143,8 @@ def AnsiText(text, command_list=None, re
 
   Args:
     text: String to encase in sgr escape sequence.
-    command_list: List of strings, each string represents an sgr value.
-      e.g. 'fg_blue', 'bg_yellow'
+    command_list: List of strings, each string represents an sgr value. e.g.
+      'fg_blue', 'bg_yellow'
     reset: Boolean, if to add a reset sequence to the suffix of the text.
 
   Returns:
@@ -175,11 +172,11 @@ def TerminalSize():
   try:
     with open(os.ctermid()) as tty_instance:
       length_width = struct.unpack(
-          'hh', fcntl.ioctl(tty_instance.fileno(), termios.TIOCGWINSZ, '1234'))
+          'hh', fcntl.ioctl(tty_instance.fileno(), termios.TIOCGWINSZ, '1234')
+      )
   except (IOError, OSError, NameError):
     try:
-      length_width = (int(os.environ['LINES']),
-                      int(os.environ['COLUMNS']))
+      length_width = (int(os.environ['LINES']), int(os.environ['COLUMNS']))
     except (ValueError, KeyError):
       length_width = (24, 80)
   return length_width
@@ -201,27 +198,27 @@ def LineWrap(text, omit_sgr=False):
     token_list = sgr_re.split(text_line)
     text_line_list = []
     line_length = 0
-    for (index, token) in enumerate(token_list):
+    for index, token in enumerate(token_list):
       # Skip null tokens.
-      if token == '':
+      if not token:
         continue
 
       if sgr_re.match(token):
         # Add sgr escape sequences without splitting or counting length.
         text_line_list.append(token)
-        text_line = ''.join(token_list[index +1:])
+        text_line = ''.join(token_list[index + 1 :])
       else:
         if line_length + len(token) <= width:
           # Token fits in line and we count it towards overall length.
           text_line_list.append(token)
           line_length += len(token)
-          text_line = ''.join(token_list[index +1:])
+          text_line = ''.join(token_list[index + 1 :])
         else:
           # Line splits part way through this token.
           # So split the token, form a new line and carry the remainder.
-          text_line_list.append(token[:width - line_length])
-          text_line = token[width - line_length:]
-          text_line += ''.join(token_list[index +1:])
+          text_line_list.append(token[: width - line_length])
+          text_line = token[width - line_length :]
+          text_line += ''.join(token_list[index + 1 :])
           break
 
     return (''.join(text_line_list), text_line)
@@ -233,8 +230,9 @@ def LineWrap(text, omit_sgr=False):
   text_multiline = []
   for text_line in text.splitlines():
     # Is this a line that needs splitting?
-    while ((omit_sgr and (len(StripAnsiText(text_line)) > width)) or
-           (len(text_line) > width)):
+    while (omit_sgr and (len(StripAnsiText(text_line)) > width)) or (
+        len(text_line) > width
+    ):
       # If there are no sgr escape characters then do a straight split.
       if not omit_sgr:
         text_multiline.append(text_line[:width])
@@ -284,8 +282,8 @@ class Pager(object):
 
     Args:
       text: A string, the text that will be paged through.
-      delay: A boolean, if True will cause a slight delay
-        between line printing for more obvious scrolling.
+      delay: A boolean, if True will cause a slight delay between line printing
+        for more obvious scrolling.
     """
     self._text = text or ''
     self._delay = delay
@@ -356,7 +354,9 @@ class Pager(object):
     text = LineWrap(self._text).splitlines()
     while True:
       # Get a list of new lines to display.
-      self._newlines = text[self._displayed:self._displayed+self._lines_to_show]
+      self._newlines = text[
+          self._displayed : self._displayed + self._lines_to_show
+      ]
       for line in self._newlines:
         sys.stdout.write(line + '\n')
         if self._delay and self._lastscroll > 0:
@@ -366,19 +366,19 @@ class Pager(object):
       if self._currentpagelines >= self._lines_to_show:
         self._currentpagelines = 0
         wish = self._AskUser()
-        if wish == 'q':         # Quit pager.
+        if wish == 'q':  # Quit pager.
           return False
-        elif wish == 'g':       # Display till the end.
+        elif wish == 'g':  # Display till the end.
           self._Scroll(len(text) - self._displayed + 1)
-        elif wish == '\r':      #  Enter, down a line.
+        elif wish == '\r':  #  Enter, down a line.
           self._Scroll(1)
         elif wish == '\033[B':  # Down arrow, down a line.
           self._Scroll(1)
         elif wish == '\033[A':  # Up arrow, up a line.
           self._Scroll(-1)
-        elif wish == 'b':       # Up a page.
+        elif wish == 'b':  # Up a page.
           self._Scroll(0 - self._cli_lines)
-        else:                   # Next page.
+        else:  # Next page.
           self._Scroll()
       if self._displayed >= len(text):
         break
@@ -389,8 +389,8 @@ class Pager(object):
     """Set attributes to scroll the buffer correctly.
 
     Args:
-      lines: An int, number of lines to scroll. If None, scrolls
-        by the terminal length.
+      lines: An int, number of lines to scroll. If None, scrolls by the terminal
+        length.
     """
     if lines is None:
       lines = self._cli_lines
@@ -413,18 +413,19 @@ class Pager(object):
       A string, the character entered by the user.
     """
     if self._show_percent:
-      progress = int(self._displayed*100 / (len(self._text.splitlines())))
+      progress = int(self._displayed * 100 / (len(self._text.splitlines())))
       progress_text = ' (%d%%)' % progress
     else:
       progress_text = ''
     question = AnsiText(
-        'Enter: next line, Space: next page, '
-        'b: prev page, q: quit.%s' %
-        progress_text, ['green'])
+        'Enter: next line, Space: next page, b: prev page, q: quit.%s'
+        % progress_text,
+        ['green'],
+    )
     sys.stdout.write(question)
     sys.stdout.flush()
     ch = self._GetCh()
-    sys.stdout.write('\r%s\r' % (' '*len(question)))
+    sys.stdout.write('\r%s\r' % (' ' * len(question)))
     sys.stdout.flush()
     return ch
 
@@ -455,8 +456,8 @@ def main(argv=None):
 
   try:
     opts, args = getopt.getopt(argv[1:], 'dhs', ['nodelay', 'help', 'size'])
-  except getopt.error as msg:
-    raise Usage(msg)
+  except getopt.error as exc:
+    raise UsageError(exc) from exc
 
   # Print usage and return, regardless of presence of other args.
   for opt, _ in opts:
@@ -475,7 +476,7 @@ def main(argv=None):
     elif opt in ('-d', '--delay'):
       isdelay = True
     else:
-      raise Usage('Invalid arguments.')
+      raise UsageError('Invalid arguments.')
 
   # Page text supplied in either specified file or stdin.
 
@@ -491,7 +492,7 @@ if __name__ == '__main__':
   help_msg = '%s [--help] [--size] [--nodelay] [input_file]\n' % sys.argv[0]
   try:
     sys.exit(main())
-  except Usage as err:
+  except UsageError as err:
     print(err, file=sys.stderr)
     print('For help use --help', file=sys.stderr)
     sys.exit(2)
Index: textfsm-1.1.3/textfsm/texttable.py
===================================================================
--- textfsm-1.1.3.orig/textfsm/texttable.py
+++ textfsm-1.1.3/textfsm/texttable.py
@@ -22,22 +22,10 @@ Tables can be created from CSV input and
 formats such as CSV and variable sized and justified rows.
 """
 
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
 import copy
-from functools import cmp_to_key
+import functools
 import textwrap
 
-from builtins import next      # pylint: disable=redefined-builtin
-from builtins import object    # pylint: disable=redefined-builtin
-from builtins import range     # pylint: disable=redefined-builtin
-from builtins import str       # pylint: disable=redefined-builtin
-from builtins import zip       # pylint: disable=redefined-builtin
-import six
-
 from textfsm import terminal
 
 
@@ -56,8 +44,11 @@ class Row(dict):
   to make it behave like a regular dict() and list().
 
   Attributes:
+    color: Colour spec of this row.
+    header: List of row's headers.
     row: int, the row number in the container table. 0 is the header row.
     table: A TextTable(), the associated container table.
+    values: List of row's values.
   """
 
   def __init__(self, *args, **kwargs):
@@ -162,7 +153,7 @@ class Row(dict):
     except IndexError:
       return default_value
 
-  def index(self, column):
+  def index(self, column):  # pylint: disable=invalid-name
     """Fetches the column number (0 indexed).
 
     Args:
@@ -174,12 +165,12 @@ class Row(dict):
     Raises:
       ValueError: The specified column was not found.
     """
-    for i, key in enumerate(self._keys):
-      if key == column:
-        return i
-    raise ValueError('Column "%s" not found.' % column)
+    try:
+      return self._keys.index(column)
+    except ValueError as exc:
+      raise ValueError('Column "%s" not found.' % column) from exc
 
-  def iterkeys(self):
+  def iterkeys(self):  # pylint: disable=invalid-name
     return iter(self._keys)
 
   def items(self):
@@ -263,12 +254,13 @@ class Row(dict):
     elif isinstance(values, list) or isinstance(values, tuple):
       if len(values) != len(self._values):
         raise TypeError('Supplied list length != row length')
-      for (index, value) in enumerate(values):
+      for index, value in enumerate(values):
         self._values[index] = _ToStr(value)
 
     else:
-      raise TypeError('Supplied argument must be Row, dict or list, not %s',
-                      type(values))
+      raise TypeError(
+          'Supplied argument must be Row, dict or list, not %s' % type(values)
+      )
 
   def Insert(self, key, value, row_index):
     """Inserts new values at a specified offset.
@@ -317,8 +309,8 @@ class TextTable(object):
     """Initialises a new table.
 
     Args:
-      row_class: A class to use as the row object. This should be a
-        subclass of this module's Row() class.
+      row_class: A class to use as the row object. This should be a subclass of
+        this module's Row() class.
     """
     self.row_class = row_class
     self.separator = ', '
@@ -327,7 +319,7 @@ class TextTable(object):
   def Reset(self):
     self._row_index = 1
     self._table = [[]]
-    self._iterator = 0      # While loop row index
+    self._iterator = 0  # While loop row index
 
   def __repr__(self):
     return '%s(%r)' % (self.__class__.__name__, str(self))
@@ -337,7 +329,7 @@ class TextTable(object):
     return self.table
 
   def __incr__(self, incr=1):
-    self._SetRowIndex(self._row_index +incr)
+    self._SetRowIndex(self._row_index + incr)
 
   def __contains__(self, name):
     """Whether the given column header name exists."""
@@ -386,9 +378,9 @@ class TextTable(object):
     """Construct Textable from the rows of which the function returns true.
 
     Args:
-      function: A function applied to each row which returns a bool. If
-                function is None, all rows with empty column values are
-                removed.
+      function: A function applied to each row which returns a bool. If function
+        is None, all rows with empty column values are removed.
+
     Returns:
       A new TextTable()
 
@@ -403,7 +395,7 @@ class TextTable(object):
     # pylint: disable=protected-access
     new_table._table = [self.header]
     for row in self:
-      if function(row) is True:
+      if function(row):
         new_table.Append(row)
     return new_table
 
@@ -430,6 +422,7 @@ class TextTable(object):
     return new_table
 
   # pylint: disable=W0622
+  # pylint: disable=invalid-name
   def sort(self, cmp=None, key=None, reverse=False):
     """Sorts rows in the texttable.
 
@@ -455,7 +448,7 @@ class TextTable(object):
     new_table = self._table[1:]
 
     if cmp is not None:
-      key = cmp_to_key(cmp)
+      key = functools.cmp_to_key(cmp)
 
     new_table.sort(key=key, reverse=reverse)
 
@@ -465,17 +458,19 @@ class TextTable(object):
     # Re-write the 'row' attribute of each row
     for index, row in enumerate(self._table):
       row.row = index
+
   # pylint: enable=W0622
+  # pylint: enable=invalid-name
 
-  def extend(self, table, keys=None):
+  def extend(self, table, keys=None):  # pylint: disable=invalid-name
     """Extends all rows in the texttable.
 
     The rows are extended with the new columns from the table.
 
     Args:
       table: A texttable, the table to extend this table by.
-      keys: A set, the set of columns to use as the key. If None, the
-        row index is used.
+      keys: A set, the set of columns to use as the key. If None, the row index
+        is used.
 
     Raises:
       IndexError: If key is not a valid column name.
@@ -483,7 +478,7 @@ class TextTable(object):
     if keys:
       for k in keys:
         if k not in self._Header():
-          raise IndexError("Unknown key: '%s'", k)
+          raise IndexError("Unknown key: '%s'" % k)
 
     extend_with = []
     for column in table.header:
@@ -516,8 +511,8 @@ class TextTable(object):
     """Removes a row from the table.
 
     Args:
-      row: int, the row number to delete. Must be >= 1, as the header
-        cannot be removed.
+      row: int, the row number to delete. Must be >= 1, as the header cannot be
+        removed.
 
     Raises:
       TableError: Attempt to remove nonexistent or header row.
@@ -607,9 +602,7 @@ class TextTable(object):
     # Avoid the global lookup cost on each iteration.
     lstr = str
     for row in self._table:
-      result.append(
-          '%s\n' %
-          self.separator.join(lstr(v) for v in row))
+      result.append('%s\n' % self.separator.join(lstr(v) for v in row))
 
     return ''.join(result)
 
@@ -618,7 +611,7 @@ class TextTable(object):
     if not isinstance(table, TextTable):
       raise TypeError('Not an instance of TextTable.')
     self.Reset()
-    self._table = copy.deepcopy(table._table)   # pylint: disable=W0212
+    self._table = copy.deepcopy(table._table)  # pylint: disable=W0212
     # Point parent table of each row back ourselves.
     for row in self:
       row.table = self
@@ -666,15 +659,16 @@ class TextTable(object):
         result.extend(self._TextJustify(paragraph, col_size))
       return result
 
-    wrapper = textwrap.TextWrapper(width=col_size-2, break_long_words=False,
-                                   expand_tabs=False)
+    wrapper = textwrap.TextWrapper(
+        width=col_size - 2, break_long_words=False, expand_tabs=False
+    )
     try:
       text_list = wrapper.wrap(text)
-    except ValueError:
-      raise TableError('Field too small (minimum width: 3)')
+    except ValueError as exc:
+      raise TableError('Field too small (minimum width: 3)') from exc
 
     if not text_list:
-      return [' '*col_size]
+      return [' ' * col_size]
 
     for current_line in text_list:
       stripped_len = len(terminal.StripAnsiText(current_line))
@@ -687,16 +681,23 @@ class TextTable(object):
 
     return result
 
-  def FormattedTable(self, width=80, force_display=False, ml_delimiter=True,
-                     color=True, display_header=True, columns=None):
+  def FormattedTable(
+      self,
+      width=80,
+      force_display=False,
+      ml_delimiter=True,
+      color=True,
+      display_header=True,
+      columns=None,
+  ):
     """Returns whole table, with whitespace padding and row delimiters.
 
     Args:
       width: An int, the max width we want the table to fit in.
       force_display: A bool, if set to True will display table when the table
-          can't be made to fit to the width.
+        can't be made to fit to the width.
       ml_delimiter: A bool, if set to False will not display the multi-line
-          delimiter.
+        delimiter.
       color: A bool. If true, display any colours in row.colour.
       display_header: A bool. If true, display header.
       columns: A list of str, show only columns with these names.
@@ -780,8 +781,9 @@ class TextTable(object):
         for key in multi_word:
           # If we scale past the desired width for this particular column,
           # then give it its desired width and remove it from the wrapped list.
-          if (largest[key] <=
-              round((largest[key] / float(desired_width)) * spare_width)):
+          if largest[key] <= round(
+              (largest[key] / float(desired_width)) * spare_width
+          ):
             smallest[key] = largest[key]
             multi_word.remove(key)
             spare_width -= smallest[key]
@@ -789,8 +791,9 @@ class TextTable(object):
             done = False
           # If we scale below the minimum width for this particular column,
           # then leave it at its minimum and remove it from the wrapped list.
-          elif (smallest[key] >=
-                round((largest[key] / float(desired_width)) * spare_width)):
+          elif smallest[key] >= round(
+              (largest[key] / float(desired_width)) * spare_width
+          ):
             multi_word.remove(key)
             spare_width -= smallest[key]
             desired_width -= largest[key]
@@ -799,8 +802,9 @@ class TextTable(object):
       # Repeat the scaling algorithm with the final wrap list.
       # This time we assign the extra column space by increasing 'smallest'.
       for key in multi_word:
-        smallest[key] = int(round((largest[key] / float(desired_width))
-                                  * spare_width))
+        smallest[key] = int(
+            round((largest[key] / float(desired_width)) * spare_width)
+        )
 
     total_width = 0
     row_count = 0
@@ -822,7 +826,7 @@ class TextTable(object):
           header_list.append(result_dict[key][row_idx])
         except IndexError:
           # If no value than use whitespace of equal size.
-          header_list.append(' '*smallest[key])
+          header_list.append(' ' * smallest[key])
       header_list.append('\n')
 
     # Format and store the body lines
@@ -849,7 +853,7 @@ class TextTable(object):
         prev_muli_line = True
       # If current or prior line was multi-line then include delimiter.
       if not first_line and prev_muli_line and ml_delimiter:
-        body_list.append('-'*total_width + '\n')
+        body_list.append('-' * total_width + '\n')
         if row_count == 1:
           # Our current line was not wrapped, so clear flag.
           prev_muli_line = False
@@ -861,20 +865,20 @@ class TextTable(object):
             row_list.append(result_dict[key][row_idx])
           except IndexError:
             # If no value than use whitespace of equal size.
-            row_list.append(' '*smallest[key])
+            row_list.append(' ' * smallest[key])
         row_list.append('\n')
 
       if color and row.color is not None:
         body_list.append(
-            terminal.AnsiText(''.join(row_list)[:-1],
-                              command_list=row.color))
+            terminal.AnsiText(''.join(row_list)[:-1], command_list=row.color)
+        )
         body_list.append('\n')
       else:
         body_list.append(''.join(row_list))
 
       first_line = False
 
-    header = ''.join(header_list) + '='*total_width
+    header = ''.join(header_list) + '=' * total_width
     if color and self._Header().color is not None:
       header = terminal.AnsiText(header, command_list=self._Header().color)
     # Add double line delimiter between header and main body.
@@ -915,7 +919,7 @@ class TextTable(object):
 
     body = []
     for row in self:
-    # Some of the row values are pulled into the label, stored in label_prefix.
+      # Some row values are pulled into the label, stored in label_prefix.
       label_prefix = []
       value_list = []
       for key, value in row.items():
@@ -925,8 +929,9 @@ class TextTable(object):
         else:
           value_list.append('%s %s' % (key, value))
 
-      body.append(''.join(
-          ['%s.%s\n' % ('.'.join(label_prefix), v) for v in value_list]))
+      body.append(
+          ''.join(['%s.%s\n' % ('.'.join(label_prefix), v) for v in value_list])
+      )
 
     return '%s%s' % (label_str, ''.join(body))
 
@@ -964,7 +969,6 @@ class TextTable(object):
 
     Raises:
       TableError: Column name already exists.
-
     """
     if column in self.table:
       raise TableError('Column %r already in table.' % column)
@@ -1027,11 +1031,12 @@ class TextTable(object):
     self.Reset()
 
     header_row = self.row_class()
+    header_length = 0
     if header:
       line = buf.readline()
       header_str = ''
       while not header_str:
-        if not isinstance(line, six.string_types):
+        if not isinstance(line, str):
           line = line.decode('utf-8')
         # Remove comments.
         header_str = line.split('#')[0].strip()
@@ -1052,7 +1057,7 @@ class TextTable(object):
 
     # xreadlines would be better but not supported by StringIO for testing.
     for line in buf:
-      if not isinstance(line, six.string_types):
+      if not isinstance(line, str):
         line = line.decode('utf-8')
       # Support commented lines, provide '#' is first character of line.
       if line.startswith('#'):
@@ -1066,8 +1071,9 @@ class TextTable(object):
       if not header:
         header_row = self.row_class()
         header_length = len(lst)
-        header_row.values = dict(zip(range(header_length),
-                                     range(header_length)))
+        header_row.values = dict(
+            zip(range(header_length), range(header_length))
+        )
         self._table[0] = header_row
         header = True
         continue
@@ -1079,7 +1085,7 @@ class TextTable(object):
 
     return self.size
 
-  def index(self, name=None):
+  def index(self, name=None):  # pylint: disable=invalid-name
     """Returns index number of supplied column name.
 
     Args:
@@ -1093,5 +1099,5 @@ class TextTable(object):
     """
     try:
       return self.header.index(name)
-    except ValueError:
-      raise TableError('Unknown index name %s.' % name)
+    except ValueError as exc:
+      raise TableError('Unknown index name %s.' % name) from exc
openSUSE Build Service is sponsored by