[Autotest][PY3] Migrating client/common_lib (file_utils -> log)

Migrating client/common_lib from file_utils.py to log.py to python3.

BUG=chromium:990593
TEST= py_compile in py2 and py3. CQ. dummy_Pass. global_config_unittest

Change-Id: Ibeb840623d709a02094d42f8fcdfa3f3489069de
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2458770
Tested-by: Derek Beckett <dbeckett@chromium.org>
Commit-Queue: Derek Beckett <dbeckett@chromium.org>
Reviewed-by: Greg Edelston <gredelston@google.com>
diff --git a/client/common_lib/file_utils.py b/client/common_lib/file_utils.py
index b299f0b..7174eea 100644
--- a/client/common_lib/file_utils.py
+++ b/client/common_lib/file_utils.py
@@ -1,3 +1,4 @@
+# Lint as: python2, python3
 # Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -6,8 +7,8 @@
 import logging
 import os
 import shutil
+from six.moves import urllib
 import subprocess
-import urllib2
 
 from autotest_lib.client.common_lib import global_config
 
@@ -138,32 +139,32 @@
             proxies[name[:-6]] = value
 
     if proxies:
-        proxy_handler = urllib2.ProxyHandler(proxies)
-        opener = urllib2.build_opener(proxy_handler)
-        urllib2.install_opener(opener)
+        proxy_handler = urllib.request.ProxyHandler(proxies)
+        opener = urllib.request.build_opener(proxy_handler)
+        urllib.request.install_opener(opener)
 
     # Unlike urllib.urlopen urllib2.urlopen will immediately throw on error
     # If we could not find the file pointed by remote_path we will get an
     # exception, catch the exception to log useful information then re-raise
 
     try:
-        remote_file = urllib2.urlopen(remote_path)
+        remote_file = urllib.request.urlopen(remote_path)
 
         # Catch exceptions, extract exception properties and then re-raise
         # This helps us with debugging what went wrong quickly as we get to see
         # test_that output immediately
 
-    except urllib2.HTTPError as e:
+    except urllib.error.HTTPError as e:
         e.msg = (("""HTTPError raised while retrieving file %s\n.
                        Http Code = %s.\n. Reason = %s\n. Headers = %s.\n
                        Original Message = %s.\n""")
                  % (remote_path, e.code, e.reason, e.headers, e.msg))
         raise
 
-    except urllib2.URLError as e:
+    except urllib.error.URLError as e:
         e.msg = (("""URLError raised while retrieving file %s\n.
                         Reason = %s\n. Original Message = %s\n.""")
-                 % (remote_path, e.reason, e.message))
+                 % (remote_path, e.reason, str(e)))
         raise
 
     with open(local_path, 'wb') as local_file:
@@ -194,7 +195,7 @@
         logging.warning(stderr_data)
         return 0
 
-    return int(stdout_data.split('\t', 1)[0])
+    return int(stdout_data.split(b'\t', 1)[0])
 
 
 def recursive_path_permission(path):
diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
index 545045e..42eda8a 100644
--- a/client/common_lib/global_config.py
+++ b/client/common_lib/global_config.py
@@ -1,3 +1,4 @@
+# Lint as: python2, python3
 """A singleton class for accessing global config values
 
 provides access to global configuration file
@@ -13,16 +14,16 @@
 # When the code is running in a non-Moblab host, moblab_config.ini is ignored.
 # Config values in shadow config will override values in global config.
 
-__author__ = 'raphtee@google.com (Travis Miller)'
-
 import collections
-import ConfigParser
 import os
 import re
+import six.moves.configparser as ConfigParser
 import sys
 
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import lsbrelease_utils
+from autotest_lib.client.common_lib import seven
+
 
 class ConfigError(error.AutotestError):
     """Configuration error."""
@@ -160,7 +161,7 @@
         @param section: Section we want to turn into a config parser object.
         @return: ConfigParser() object containing all the contents of section.
         """
-        cfgparser = ConfigParser.ConfigParser()
+        cfgparser = seven.config_parser()
         cfgparser.add_section(section)
         for option, value in self.config.items(section):
             cfgparser.set(section, option, value)
@@ -292,7 +293,7 @@
 
     def parse_config_file(self):
         """Parse config files."""
-        self.config = ConfigParser.ConfigParser()
+        self.config = seven.config_parser()
         if self.config_file and os.path.exists(self.config_file):
             self.config.read(self.config_file)
         else:
@@ -302,7 +303,7 @@
         # overwrite the value in global config.
         if (lsbrelease_utils.is_moblab() and self.moblab_file and
             os.path.exists(self.moblab_file)):
-            moblab_config = ConfigParser.ConfigParser()
+            moblab_config = seven.config_parser()
             moblab_config.read(self.moblab_file)
             # now we merge moblab into global
             self.merge_configs(moblab_config)
@@ -311,7 +312,7 @@
         # this will overwrite anything that is found in the
         # other config
         if self.shadow_file and os.path.exists(self.shadow_file):
-            shadow_config = ConfigParser.ConfigParser()
+            shadow_config = seven.config_parser()
             shadow_config.read(self.shadow_file)
             # now we merge shadow into global
             self.merge_configs(shadow_config)
diff --git a/client/common_lib/global_config_unittest.py b/client/common_lib/global_config_unittest.py
index c8279e9..a8491d4 100755
--- a/client/common_lib/global_config_unittest.py
+++ b/client/common_lib/global_config_unittest.py
@@ -99,14 +99,14 @@
     def test_float(self):
         """Test converting float value."""
         val = self.conf.get_config_value("SECTION_A", "value_1", float)
-        self.assertEquals(type(val), types.FloatType)
+        self.assertEquals(type(val), float)
         self.assertEquals(val, 6.0)
 
 
     def test_int(self):
         """Test converting int value."""
         val = self.conf.get_config_value("SECTION_B", "value_1", int)
-        self.assertEquals(type(val), types.IntType)
+        self.assertEquals(type(val), int)
         self.assertTrue(val < 0)
         val = self.conf.get_config_value("SECTION_B", "value_3", int)
         self.assertEquals(val, 0)
@@ -117,7 +117,7 @@
     def test_string(self):
         """Test converting string value."""
         val = self.conf.get_config_value("SECTION_A", "value_2")
-        self.assertEquals(type(val),types.StringType)
+        self.assertEquals(type(val),bytes)
         self.assertEquals(val, "hello")
 
 
diff --git a/client/common_lib/gtest_parser.py b/client/common_lib/gtest_parser.py
index 2599b46..d310678 100644
--- a/client/common_lib/gtest_parser.py
+++ b/client/common_lib/gtest_parser.py
@@ -1,3 +1,4 @@
+# Lint as: python2, python3
 # Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -151,7 +152,7 @@
 
     def SuppressionHashes(self):
         """Returns list of suppression hashes found in the log."""
-        return self._suppressions.keys()
+        return list(self._suppressions.keys())
 
     def Suppression(self, suppression_hash):
         """Returns a list containing the suppression for a given hash.
diff --git a/client/common_lib/i2c_node.py b/client/common_lib/i2c_node.py
index 99d86dc..882825a 100644
--- a/client/common_lib/i2c_node.py
+++ b/client/common_lib/i2c_node.py
@@ -1,3 +1,4 @@
+# Lint as: python2, python3
 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -64,7 +65,7 @@
         logging.info('Attempt to load shared library %s', self.load_lib)
         try:
             return ctypes.cdll.LoadLibrary(self.load_lib)
-        except OSError, e:
+        except OSError as e:
             raise I2cError('Error loading C library %s: %s' %
                             (self.load_lib, e))
         logging.info('Successfully loaded shared library %s', self.load_lib)
diff --git a/client/common_lib/kernel_versions.py b/client/common_lib/kernel_versions.py
index 1b796c1..605fb9b 100644
--- a/client/common_lib/kernel_versions.py
+++ b/client/common_lib/kernel_versions.py
@@ -1,10 +1,16 @@
+# Lint as: python2, python3
 #
 # kernel_versions.py -- linux kernel version comparisons
 #
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
 __author__ = """Copyright Andy Whitcroft 2007"""
 
 import sys,re
 
+from six.moves import range
+
 #
 # Sort key for ordering versions chronologically.  The key ordering
 # problem is between that introduced by -rcN.  These come _before_
diff --git a/client/common_lib/log.py b/client/common_lib/log.py
index f79f7de..3505105 100644
--- a/client/common_lib/log.py
+++ b/client/common_lib/log.py
@@ -1,5 +1,9 @@
+# Lint as: python2, python3
 # pylint: disable=missing-docstring
 
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
 import sys, re, traceback
 
 # these statuses are ordered such that a status earlier in the list will
@@ -24,7 +28,7 @@
             try:
                 fn(*args, **dargs)
             except Exception:
-                print >> sys.stderr, msg
+                print(msg, file=sys.stderr)
                 traceback.print_exc(file=sys.stderr)
         return decorated_func
     return decorator
diff --git a/client/common_lib/seven.py b/client/common_lib/seven.py
index 729ca02..f766ed3 100644
--- a/client/common_lib/seven.py
+++ b/client/common_lib/seven.py
@@ -9,8 +9,8 @@
 """
 
 import six
+import six.moves.configparser
 import socket
-
 if six.PY3:
     import builtins
     SOCKET_ERRORS = (builtins.ConnectionError, socket.timeout, socket.gaierror,
@@ -45,3 +45,14 @@
                 dont_inherit=1,
         )
     return six.exec_(code_obj, globals_, locals_)
+
+
+def config_parser():
+    """config_parser returns a non-strict config parser.
+
+    Unfortunately, in six configparser is not same between 2/3. For our .ini's
+    we do not want it to be strict (ie, error upon duplicates).
+    """
+    if six.PY3:
+        return six.moves.configparser.ConfigParser(strict=False)
+    return six.moves.configparser.ConfigParser()