util: Port git hooks to python3

This involves changing:
* git-commit
* git-pre-commit
* style verifiers

JIRA: https://gem5.atlassian.net/browse/GEM5-473

Change-Id: I7bd0b54469f942bf927c8be1fd94d12f67594d48
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/28588
Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/util/git-commit-msg.py b/util/git-commit-msg.py
index 23270bc..92e8100 100755
--- a/util/git-commit-msg.py
+++ b/util/git-commit-msg.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
 #
 # Copyright (c) 2019 Inria
 # All rights reserved
diff --git a/util/git-pre-commit.py b/util/git-pre-commit.py
index 1510949..b6d124a 100755
--- a/util/git-pre-commit.py
+++ b/util/git-pre-commit.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
 #
 # Copyright (c) 2016 ARM Limited
 # All rights reserved
@@ -35,6 +35,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from __future__ import print_function
+
 from tempfile import TemporaryFile
 import os
 import subprocess
@@ -66,7 +68,7 @@
 
 for status, fname in git.status(filter="MA", cached=True):
     if args.verbose:
-        print "Checking %s..." % fname
+        print("Checking {}...".format(fname))
     if check_ignores(fname):
         continue
     if status == "M":
@@ -77,7 +79,7 @@
     # Show they appropriate object and dump it to a file
     status = git.file_from_index(fname)
     f = TemporaryFile()
-    f.write(status)
+    f.write(status.encode())
 
     verifiers = [ v(ui, opts, base=repo_base) for v in all_verifiers ]
     for v in verifiers:
@@ -93,22 +95,25 @@
 
 if failing_files:
     if len(failing_files) > len(staged_mismatch):
-        print >> sys.stderr
-        print >> sys.stderr, "Style checker failed for the following files:"
+        print("\n", file=sys.stderr)
+        print("Style checker failed for the following files:", file=sys.stderr)
         for f in failing_files:
             if f not in staged_mismatch:
-                print >> sys.stderr, "\t%s" % f
-        print >> sys.stderr
-        print >> sys.stderr, \
-        "Please run the style checker manually to fix the offending files.\n" \
-        "To check your modifications, run: util/style.py -m"
+                print("\t{}".format(f), file=sys.stderr)
+        print("\n", file=sys.stderr)
+        print(
+            "Please run the style checker manually to fix "
+            "the offending files.\n"
+            "To check your modifications, run: util/style.py -m",
+            file=sys.stderr)
 
-    print >> sys.stderr
+    print("\n", file=sys.stderr)
     if staged_mismatch:
-        print >> sys.stderr, \
-        "It looks like you have forgotten to stage your fixes for commit in\n"\
-        "the following files: "
+        print(
+            "It looks like you have forgotten to stage your "
+            "fixes for commit in\n"
+            "the following files: ", file=sys.stderr)
         for f in staged_mismatch:
-            print >> sys.stderr, "\t%s" % f
-        print >> sys.stderr, "Please `git --add' them"
+            print("\t%s".format(f), file=sys.stderr)
+        print("Please `git --add' them", file=sys.stderr)
     sys.exit(1)
diff --git a/util/style/file_types.py b/util/style/file_types.py
index 4ffa7e0..2577492 100644
--- a/util/style/file_types.py
+++ b/util/style/file_types.py
@@ -77,7 +77,7 @@
     )
 
 # the list of all languages that we detect
-all_languages = frozenset(lang_types.itervalues())
+all_languages = frozenset(lang_types.values())
 all_languages |= frozenset(lang for start,lang in lang_prefixes)
 all_languages |= frozenset(lang for start,lang in hash_bang)
 
diff --git a/util/style/region.py b/util/style/region.py
index f4e12c5..ce00680 100644
--- a/util/style/region.py
+++ b/util/style/region.py
@@ -57,8 +57,8 @@
             args = tuple(arg)
 
         if len(args) != 2:
-            raise AttributeError, \
-                "Only one or two arguments allowed, %d provided" % (alen, )
+            raise(AttributeError, \
+                "Only one or two arguments allowed, %d provided" % (alen, ))
 
         return tuple.__new__(cls, args)
 
@@ -239,13 +239,13 @@
     n = Region(9,10)
 
     def test(left, right):
-        print "%s == %s: %s" % (left, right, left == right)
-        print "%s != %s: %s" % (left, right, left != right)
-        print "%s <  %s: %s" % (left, right, left <  right)
-        print "%s <= %s: %s" % (left, right, left <= right)
-        print "%s >  %s: %s" % (left, right, left >  right)
-        print "%s >= %s: %s" % (left, right, left >= right)
-        print
+        print("%s == %s: %s" % (left, right, left == right))
+        print("%s != %s: %s" % (left, right, left != right))
+        print("%s <  %s: %s" % (left, right, left <  right))
+        print("%s <= %s: %s" % (left, right, left <= right))
+        print("%s >  %s: %s" % (left, right, left >  right))
+        print("%s >= %s: %s" % (left, right, left >= right))
+        print("\n")
 
     test(neg_inf, neg_inf)
     test(neg_inf, pos_inf)
@@ -268,14 +268,14 @@
     test(-11111, pos_inf)
     test(11111, pos_inf)
 
-    print x
-    print y
-    print x & y
-    print z
+    print(x)
+    print(y)
+    print(x & y)
+    print(z)
 
-    print 4 in x
-    print 4 in z
-    print 5 not in x
-    print 6 not in z
-    print z in y
-    print n in y, n not in y
+    print(4 in x)
+    print(4 in z)
+    print(5 not in x)
+    print(6 not in z)
+    print(z in y)
+    print(n in y, n not in y)
diff --git a/util/style/repo.py b/util/style/repo.py
index acf59ce..a0eee98 100644
--- a/util/style/repo.py
+++ b/util/style/repo.py
@@ -39,8 +39,8 @@
 import os
 import subprocess
 
-from region import *
-from style import modified_regions
+from .region import *
+from .style import modified_regions
 
 class AbstractRepo(object):
     __metaclass__ = ABCMeta
@@ -118,7 +118,8 @@
     def repo_base(self):
         if self._repo_base is None:
             self._repo_base = subprocess.check_output(
-                [ self.git, "rev-parse", "--show-toplevel" ]).rstrip("\n")
+                [ self.git, "rev-parse", "--show-toplevel" ]) \
+                .decode().rstrip("\n")
 
         return self._repo_base
 
@@ -159,7 +160,7 @@
         try:
             self._head_revision = subprocess.check_output(
                 [ self.git, "rev-parse", "--verify", "HEAD" ],
-                stderr=subprocess.PIPE).rstrip("\n")
+                stderr=subprocess.PIPE).decode().rstrip("\n")
         except subprocess.CalledProcessError:
             # Assume that the repo is empty and use the semi-magic
             # empty tree revision if git rev-parse returned an error.
@@ -185,7 +186,7 @@
         if filter:
             cmd += [ "--diff-filter=%s" % filter ]
         cmd += [ self.head_revision(), "--" ] + files
-        status = subprocess.check_output(cmd).rstrip("\n")
+        status = subprocess.check_output(cmd).decode().rstrip("\n")
 
         if status:
             return [ f.split("\t") for f in status.split("\n") ]
@@ -194,11 +195,12 @@
 
     def file_from_index(self, name):
         return subprocess.check_output(
-            [ self.git, "show", ":%s" % (name, ) ])
+            [ self.git, "show", ":%s" % (name, ) ]).decode()
 
     def file_from_head(self, name):
         return subprocess.check_output(
-            [ self.git, "show", "%s:%s" % (self.head_revision(), name) ])
+            [ self.git, "show", "%s:%s" % (self.head_revision(), name) ]) \
+            .decode()
 
 class MercurialRepo(AbstractRepo):
     def __init__(self):
@@ -208,7 +210,7 @@
     def repo_base(self):
         if self._repo_base is None:
             self._repo_base = subprocess.check_output(
-                [ self.hg, "root" ]).rstrip("\n")
+                [ self.hg, "root" ]).decode().rstrip("\n")
 
         return self._repo_base
 
@@ -233,14 +235,16 @@
         return modified_regions(old, new, context=context)
 
     def status(self, filter=None):
-        files = subprocess.check_output([ self.hg, "status" ]).rstrip("\n")
+        files = subprocess.check_output([ self.hg, "status" ]) \
+            .decode().rstrip("\n")
         if files:
             return [ f.split(" ") for f in files.split("\n") ]
         else:
             return []
 
     def file_from_tip(self, name):
-        return subprocess.check_output([ self.hg, "cat", name ])
+        return subprocess.check_output([ self.hg, "cat", name ]) \
+            .decode()
 
 def detect_repo(path="."):
     """Auto-detect the revision control system used for a source code
diff --git a/util/style/sort_includes.py b/util/style/sort_includes.py
index f2f929c..ab0bb5f 100644
--- a/util/style/sort_includes.py
+++ b/util/style/sort_includes.py
@@ -42,7 +42,7 @@
 import re
 import sys
 
-from file_types import *
+from .file_types import *
 
 cpp_c_headers = {
     'assert.h' : 'cassert',
@@ -314,6 +314,6 @@
         for filename,language in find_files(base, languages=opts.languages,
                 file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore):
             if opts.dry_run:
-                print "%s: %s" % (filename, language)
+                print("{}: {}".format(filename, language))
             else:
                 update_file(filename, filename, language, SortIncludes())
diff --git a/util/style/style.py b/util/style/style.py
index 68c77a7..c505daf 100644
--- a/util/style/style.py
+++ b/util/style/style.py
@@ -44,7 +44,7 @@
 import re
 import sys
 
-from region import *
+from .region import *
 
 tabsize = 8
 lead = re.compile(r'^([ \t]+)')
diff --git a/util/style/verifiers.py b/util/style/verifiers.py
index c2217d5..00cf070 100644
--- a/util/style/verifiers.py
+++ b/util/style/verifiers.py
@@ -47,10 +47,12 @@
 import re
 import sys
 
-import style
-import sort_includes
-from region import *
-from file_types import lang_type
+from six import add_metaclass
+
+from . import style
+from . import sort_includes
+from .region import *
+from .file_types import lang_type
 
 
 def safefix(fix_func):
@@ -100,6 +102,7 @@
     return regions
 
 
+@add_metaclass(ABCMeta)
 class Verifier(object):
     """Base class for style verifiers
 
@@ -117,7 +120,6 @@
 
     """
 
-    __metaclass__ = ABCMeta
 
     def __init__(self, ui, opts, base=None):
         self.ui = ui
@@ -144,9 +146,9 @@
 
     def open(self, filename, mode):
         try:
-            f = file(filename, mode)
-        except OSError, msg:
-            print 'could not open file %s: %s' % (filename, msg)
+            f = open(filename, mode)
+        except OSError as msg:
+            print('could not open file {}: {}'.format(filename, msg))
             return None
 
         return f
@@ -222,11 +224,12 @@
         """
         pass
 
+@add_metaclass(ABCMeta)
 class LineVerifier(Verifier):
     def check(self, filename, regions=all_regions, fobj=None, silent=False):
         close = False
         if fobj is None:
-            fobj = self.open(filename, 'r')
+            fobj = self.open(filename, 'rb')
             close = True
 
         lang = lang_type(filename)
@@ -236,13 +239,13 @@
         for num,line in enumerate(fobj):
             if num not in regions:
                 continue
-            line = line.rstrip('\n')
-            if not self.check_line(line, language=lang):
+            s_line = line.decode().rstrip('\n')
+            if not self.check_line(s_line, language=lang):
                 if not silent:
                     self.ui.write("invalid %s in %s:%d\n" % \
                                   (self.test_name, filename, num + 1))
                     if self.ui.verbose:
-                        self.ui.write(">>%s<<\n" % line[:-1])
+                        self.ui.write(">>%s<<\n" % s_line[:-1])
                 errors += 1
         if close:
             fobj.close()
@@ -348,7 +351,7 @@
             close = True
         norm_fname = self.normalize_filename(filename)
 
-        old = [ l.rstrip('\n') for l in fobj.xreadlines() ]
+        old = [ l.decode().rstrip('\n') for l in fobj ]
         if close:
             fobj.close()
 
@@ -428,14 +431,14 @@
     test_name = 'control character'
     opt_name = 'ascii'
 
-    valid = ('\n', '\t')
-    invalid = "".join([chr(i) for i in range(0, 0x20) if chr(i) not in valid])
+    invalid = "".join([chr(i) for i in range(0, 0x20) \
+        if chr(i) not in ('\n', '\t')])
 
     def check_line(self, line, **kwargs):
         return self.fix_line(line) == line
 
     def fix_line(self, line, **kwargs):
-        return line.translate(None, ControlCharacters.invalid)
+        return ''.join(c for c in line if c not in ControlCharacters.invalid)
 
 class BoolCompare(LineVerifier):
     languages = set(('C', 'C++', 'python'))