Merge pull request #62 from methane/modernize

Run modernize over the codebase (work toward Python 3).
diff --git a/googleapiclient/channel.py b/googleapiclient/channel.py
index 265273e..702186b 100644
--- a/googleapiclient/channel.py
+++ b/googleapiclient/channel.py
@@ -55,12 +55,14 @@
 
   service.channels().stop(channel.body())
 """
+from __future__ import absolute_import
 
 import datetime
 import uuid
 
 from googleapiclient import errors
 from oauth2client import util
+import six
 
 
 # The unix time epoch starts at midnight 1970.
@@ -88,7 +90,7 @@
 
 def _upper_header_keys(headers):
   new_headers = {}
-  for k, v in headers.iteritems():
+  for k, v in six.iteritems(headers):
     new_headers[k.upper()] = v
   return new_headers
 
@@ -218,7 +220,7 @@
     Args:
       resp: dict, The response from a watch() method.
     """
-    for json_name, param_name in CHANNEL_PARAMS.iteritems():
+    for json_name, param_name in six.iteritems(CHANNEL_PARAMS):
       value = resp.get(json_name)
       if value is not None:
         setattr(self, param_name, value)
diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py
index cbb10db..cdbf140 100644
--- a/googleapiclient/discovery.py
+++ b/googleapiclient/discovery.py
@@ -17,6 +17,8 @@
 A client library for Google's discovery based APIs.
 """
 from __future__ import absolute_import
+import six
+from six.moves import zip
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 __all__ = [
@@ -254,7 +256,7 @@
   # future is no longer used.
   future = {}
 
-  if isinstance(service, basestring):
+  if isinstance(service, six.string_types):
     service = json.loads(service)
   base = urlparse.urljoin(service['rootUrl'], service['servicePath'])
   schema = Schemas(service)
@@ -272,7 +274,7 @@
         credentials.create_scoped_required()):
       scopes = service.get('auth', {}).get('oauth2', {}).get('scopes', {})
       if scopes:
-        credentials = credentials.create_scoped(scopes.keys())
+        credentials = credentials.create_scoped(list(scopes.keys()))
       else:
         # No need to authorize the http object
         # if the service does not require authentication.
@@ -386,7 +388,7 @@
   parameters = method_desc.setdefault('parameters', {})
 
   # Add in the parameters common to all methods.
-  for name, description in root_desc.get('parameters', {}).iteritems():
+  for name, description in six.iteritems(root_desc.get('parameters', {})):
     parameters[name] = description
 
   # Add in undocumented query parameters.
@@ -569,7 +571,7 @@
           comes from the dictionary of methods stored in the 'methods' key in
           the deserialized discovery document.
     """
-    for arg, desc in method_desc.get('parameters', {}).iteritems():
+    for arg, desc in six.iteritems(method_desc.get('parameters', {})):
       param = key2param(arg)
       self.argmap[param] = arg
 
@@ -617,12 +619,12 @@
   def method(self, **kwargs):
     # Don't bother with doc string, it will be over-written by createMethod.
 
-    for name in kwargs.iterkeys():
+    for name in six.iterkeys(kwargs):
       if name not in parameters.argmap:
         raise TypeError('Got an unexpected keyword argument "%s"' % name)
 
     # Remove args that have a value of None.
-    keys = kwargs.keys()
+    keys = list(kwargs.keys())
     for name in keys:
       if kwargs[name] is None:
         del kwargs[name]
@@ -631,9 +633,9 @@
       if name not in kwargs:
         raise TypeError('Missing required parameter "%s"' % name)
 
-    for name, regex in parameters.pattern_params.iteritems():
+    for name, regex in six.iteritems(parameters.pattern_params):
       if name in kwargs:
-        if isinstance(kwargs[name], basestring):
+        if isinstance(kwargs[name], six.string_types):
           pvalues = [kwargs[name]]
         else:
           pvalues = kwargs[name]
@@ -643,13 +645,13 @@
                 'Parameter "%s" value "%s" does not match the pattern "%s"' %
                 (name, pvalue, regex))
 
-    for name, enums in parameters.enum_params.iteritems():
+    for name, enums in six.iteritems(parameters.enum_params):
       if name in kwargs:
         # We need to handle the case of a repeated enum
         # name differently, since we want to handle both
         # arg='value' and arg=['value1', 'value2']
         if (name in parameters.repeated_params and
-            not isinstance(kwargs[name], basestring)):
+            not isinstance(kwargs[name], six.string_types)):
           values = kwargs[name]
         else:
           values = [kwargs[name]]
@@ -661,7 +663,7 @@
 
     actual_query_params = {}
     actual_path_params = {}
-    for key, value in kwargs.iteritems():
+    for key, value in six.iteritems(kwargs):
       to_type = parameters.param_types.get(key, 'string')
       # For repeated parameters we cast each member of the list.
       if key in parameters.repeated_params and type(value) == type([]):
@@ -696,7 +698,7 @@
 
     if media_filename:
       # Ensure we end up with a valid MediaUpload object.
-      if isinstance(media_filename, basestring):
+      if isinstance(media_filename, six.string_types):
         (media_mime_type, encoding) = mimetypes.guess_type(media_filename)
         if media_mime_type is None:
           raise UnknownFileType(media_filename)
@@ -775,10 +777,10 @@
     docs.append('Args:\n')
 
   # Skip undocumented params and params common to all methods.
-  skip_parameters = rootDesc.get('parameters', {}).keys()
+  skip_parameters = list(rootDesc.get('parameters', {}).keys())
   skip_parameters.extend(STACK_QUERY_PARAMETERS)
 
-  all_args = parameters.argmap.keys()
+  all_args = list(parameters.argmap.keys())
   args_ordered = [key2param(s) for s in methodDesc.get('parameterOrder', [])]
 
   # Move body to the front of the line.
@@ -950,7 +952,7 @@
   def _add_basic_methods(self, resourceDesc, rootDesc, schema):
     # Add basic methods to Resource
     if 'methods' in resourceDesc:
-      for methodName, methodDesc in resourceDesc['methods'].iteritems():
+      for methodName, methodDesc in six.iteritems(resourceDesc['methods']):
         fixedMethodName, method = createMethod(
             methodName, methodDesc, rootDesc, schema)
         self._set_dynamic_attr(fixedMethodName,
@@ -989,7 +991,7 @@
 
         return (methodName, methodResource)
 
-      for methodName, methodDesc in resourceDesc['resources'].iteritems():
+      for methodName, methodDesc in six.iteritems(resourceDesc['resources']):
         fixedMethodName, method = createResourceMethod(methodName, methodDesc)
         self._set_dynamic_attr(fixedMethodName,
                                method.__get__(self, self.__class__))
@@ -999,7 +1001,7 @@
     # Look for response bodies in schema that contain nextPageToken, and methods
     # that take a pageToken parameter.
     if 'methods' in resourceDesc:
-      for methodName, methodDesc in resourceDesc['methods'].iteritems():
+      for methodName, methodDesc in six.iteritems(resourceDesc['methods']):
         if 'response' in methodDesc:
           responseSchema = methodDesc['response']
           if '$ref' in responseSchema:
diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py
index e31ffe4..6656bd1 100644
--- a/googleapiclient/errors.py
+++ b/googleapiclient/errors.py
@@ -17,6 +17,7 @@
 All exceptions defined by the library
 should be defined in this file.
 """
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
diff --git a/googleapiclient/http.py b/googleapiclient/http.py
index 0edf884..f0e9133 100644
--- a/googleapiclient/http.py
+++ b/googleapiclient/http.py
@@ -19,6 +19,8 @@
 actuall HTTP request.
 """
 from __future__ import absolute_import
+import six
+from six.moves import range
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -539,7 +541,7 @@
         }
     http = self._request.http
 
-    for retry_num in xrange(num_retries + 1):
+    for retry_num in range(num_retries + 1):
       if retry_num > 0:
         self._sleep(self._rand() * 2**retry_num)
         logging.warning(
@@ -709,7 +711,7 @@
       self.headers['content-length'] = str(len(self.body))
 
     # Handle retries for server-side errors.
-    for retry_num in xrange(num_retries + 1):
+    for retry_num in range(num_retries + 1):
       if retry_num > 0:
         self._sleep(self._rand() * 2**retry_num)
         logging.warning('Retry #%d for request: %s %s, following status: %d'
@@ -792,7 +794,7 @@
         start_headers['X-Upload-Content-Length'] = size
       start_headers['content-length'] = str(self.body_size)
 
-      for retry_num in xrange(num_retries + 1):
+      for retry_num in range(num_retries + 1):
         if retry_num > 0:
           self._sleep(self._rand() * 2**retry_num)
           logging.warning(
@@ -857,7 +859,7 @@
         'Content-Length': str(chunk_end - self.resumable_progress + 1)
         }
 
-    for retry_num in xrange(num_retries + 1):
+    for retry_num in range(num_retries + 1):
       if retry_num > 0:
         self._sleep(self._rand() * 2**retry_num)
         logging.warning(
@@ -1101,7 +1103,7 @@
     if 'content-type' in headers:
       del headers['content-type']
 
-    for key, value in headers.iteritems():
+    for key, value in six.iteritems(headers):
       msg[key] = value
     msg['Host'] = parsed.netloc
     msg.set_unixfrom(None)
@@ -1457,7 +1459,7 @@
     if headers is None:
       headers = {'status': '200 OK'}
     if filename:
-      f = file(filename, 'r')
+      f = open(filename, 'r')
       self.data = f.read()
       f.close()
     else:
diff --git a/googleapiclient/mimeparse.py b/googleapiclient/mimeparse.py
index 68d3a3c..bc9ad09 100644
--- a/googleapiclient/mimeparse.py
+++ b/googleapiclient/mimeparse.py
@@ -21,7 +21,9 @@
  - best_match():        Choose the mime-type with the highest quality ('q')
                           from a list of candidates.
 """
+from __future__ import absolute_import
 from functools import reduce
+import six
 
 __version__ = '0.1.3'
 __author__ = 'Joe Gregorio'
@@ -99,7 +101,7 @@
                          target_subtype == '*')
         if type_match and subtype_match:
             param_matches = reduce(lambda x, y: x + y, [1 for (key, value) in \
-                    target_params.iteritems() if key != 'q' and \
+                    six.iteritems(target_params) if key != 'q' and \
                     key in params and value == params[key]], 0)
             fitness = (type == target_type) and 100 or 0
             fitness += (subtype == target_subtype) and 10 or 0
diff --git a/googleapiclient/model.py b/googleapiclient/model.py
index e55b4fa..9be5a59 100644
--- a/googleapiclient/model.py
+++ b/googleapiclient/model.py
@@ -20,6 +20,7 @@
 object representation.
 """
 from __future__ import absolute_import
+import six
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -105,11 +106,11 @@
     if dump_request_response:
       logging.info('--request-start--')
       logging.info('-headers-start-')
-      for h, v in headers.iteritems():
+      for h, v in six.iteritems(headers):
         logging.info('%s: %s', h, v)
       logging.info('-headers-end-')
       logging.info('-path-parameters-start-')
-      for h, v in path_params.iteritems():
+      for h, v in six.iteritems(path_params):
         logging.info('%s: %s', h, v)
       logging.info('-path-parameters-end-')
       logging.info('body: %s', body)
@@ -160,13 +161,13 @@
     if self.alt_param is not None:
       params.update({'alt': self.alt_param})
     astuples = []
-    for key, value in params.iteritems():
+    for key, value in six.iteritems(params):
       if type(value) == type([]):
         for x in value:
           x = x.encode('utf-8')
           astuples.append((key, x))
       else:
-        if isinstance(value, unicode) and callable(value.encode):
+        if isinstance(value, six.text_type) and callable(value.encode):
           value = value.encode('utf-8')
         astuples.append((key, value))
     return '?' + urllib.urlencode(astuples)
@@ -175,7 +176,7 @@
     """Logs debugging information about the response if requested."""
     if dump_request_response:
       logging.info('--response-start--')
-      for h, v in resp.iteritems():
+      for h, v in six.iteritems(resp):
         logging.info('%s: %s', h, v)
       if content:
         logging.info(content)
@@ -360,7 +361,7 @@
       body=makepatch(original, item)).execute()
   """
   patch = {}
-  for key, original_value in original.iteritems():
+  for key, original_value in six.iteritems(original):
     modified_value = modified.get(key, None)
     if modified_value is None:
       # Use None to signal that the element is deleted
diff --git a/googleapiclient/sample_tools.py b/googleapiclient/sample_tools.py
index 69f698e..3e56c0a 100644
--- a/googleapiclient/sample_tools.py
+++ b/googleapiclient/sample_tools.py
@@ -16,6 +16,7 @@
 
 Consolidates a lot of code commonly repeated in sample applications.
 """
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 __all__ = ['init']
diff --git a/googleapiclient/schema.py b/googleapiclient/schema.py
index af41317..ecb3f8b 100644
--- a/googleapiclient/schema.py
+++ b/googleapiclient/schema.py
@@ -56,6 +56,8 @@
 
 The constructor takes a discovery document in which to look up named schema.
 """
+from __future__ import absolute_import
+import six
 
 # TODO(jcgregorio) support format, enum, minimum, maximum
 
@@ -249,7 +251,7 @@
       self.emitEnd('{', schema.get('description', ''))
       self.indent()
       if 'properties' in schema:
-        for pname, pschema in schema.get('properties', {}).iteritems():
+        for pname, pschema in six.iteritems(schema.get('properties', {})):
           self.emitBegin('"%s": ' % pname)
           self._to_str_impl(pschema)
       elif 'additionalProperties' in schema:
diff --git a/setup.py b/setup.py
index 40dbc0f..eb5f5f6 100644
--- a/setup.py
+++ b/setup.py
@@ -60,6 +60,7 @@
 install_requires = [
     'httplib2>=0.8',
     'oauth2client>=1.3',
+    'six>=1.6.1',
     'uritemplate>=0.6',
 ]
 
diff --git a/tests/__init__.py b/tests/__init__.py
index 7913e6f..c4022b7 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -11,6 +11,7 @@
 # limitations under the License.
 
 """Test Package set up."""
+from __future__ import absolute_import
 
 __author__ = 'afshar@google.com (Ali Afshar)'
 
diff --git a/tests/test_channel.py b/tests/test_channel.py
index 0e348a5..2651fcd 100644
--- a/tests/test_channel.py
+++ b/tests/test_channel.py
@@ -1,4 +1,5 @@
 """Notification channels tests."""
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index c16f470..b8cc959 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -20,6 +20,8 @@
 
 Unit tests for objects created from discovery documents.
 """
+from __future__ import absolute_import
+import six
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -89,9 +91,9 @@
   testcase.assertEqual(expected.fragment, actual.fragment)
   expected_query = parse_qs(expected.query)
   actual_query = parse_qs(actual.query)
-  for name in expected_query.keys():
+  for name in list(expected_query.keys()):
     testcase.assertEqual(expected_query[name], actual_query[name])
-  for name in actual_query.keys():
+  for name in list(actual_query.keys()):
     testcase.assertEqual(expected_query[name], actual_query[name])
 
 
@@ -133,7 +135,7 @@
       self.assertEqual(STACK_QUERY_PARAMETER_DEFAULT_VALUE,
                        parameters[param_name])
 
-    for param_name, value in root_desc.get('parameters', {}).iteritems():
+    for param_name, value in six.iteritems(root_desc.get('parameters', {})):
       self.assertEqual(value, parameters[param_name])
 
     return parameters
@@ -300,7 +302,7 @@
                    'o': 'object',
                    'q': 'string',
                    'rr': 'string'}
-    keys = param_types.keys()
+    keys = list(param_types.keys())
     self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
     self.assertEqual(parameters.required_params, [])
     self.assertEqual(sorted(parameters.repeated_params), ['er', 'rr'])
@@ -318,7 +320,7 @@
     parameters = ResourceMethodParameters(method_desc)
 
     param_types = {'name': 'string'}
-    keys = param_types.keys()
+    keys = list(param_types.keys())
     self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
     self.assertEqual(parameters.required_params, ['name'])
     self.assertEqual(parameters.repeated_params, [])
diff --git a/tests/test_errors.py b/tests/test_errors.py
index efb4afd..0cef892 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -16,6 +16,7 @@
 
 """Tests for errors handling
 """
+from __future__ import absolute_import
 
 __author__ = 'afshar@google.com (Ali Afshar)'
 
diff --git a/tests/test_http.py b/tests/test_http.py
index 8989409..c0ec5f9 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -18,6 +18,8 @@
 
 Unit tests for the googleapiclient.http.
 """
+from __future__ import absolute_import
+from six.moves import range
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -624,7 +626,7 @@
     request.execute(num_retries=num_retries)
 
     self.assertEqual(num_retries, len(sleeptimes))
-    for retry_num in xrange(num_retries):
+    for retry_num in range(num_retries):
       self.assertEqual(10 * 2**(retry_num + 1), sleeptimes[retry_num])
 
   def test_no_retry_fails_fast(self):
diff --git a/tests/test_json_model.py b/tests/test_json_model.py
index af9841a..fec7191 100644
--- a/tests/test_json_model.py
+++ b/tests/test_json_model.py
@@ -18,6 +18,8 @@
 
 Unit tests for the JSON model.
 """
+from __future__ import absolute_import
+import six
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -214,7 +216,7 @@
       def __init__(self, items):
         super(MockResponse, self).__init__()
         self.status = items['status']
-        for key, value in items.iteritems():
+        for key, value in six.iteritems(items):
           self[key] = value
     old_logging = googleapiclient.model.logging
     googleapiclient.model.logging = MockLogging()
diff --git a/tests/test_mocks.py b/tests/test_mocks.py
index e3c550f..34cc8a9 100644
--- a/tests/test_mocks.py
+++ b/tests/test_mocks.py
@@ -18,6 +18,7 @@
 
 Unit tests for the Mocks.
 """
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
diff --git a/tests/test_model.py b/tests/test_model.py
index 9f29db9..1d48194 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -19,6 +19,7 @@
 
 Unit tests for model utility methods.
 """
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
diff --git a/tests/test_protobuf_model.py b/tests/test_protobuf_model.py
index fe3abc7..b765943 100644
--- a/tests/test_protobuf_model.py
+++ b/tests/test_protobuf_model.py
@@ -18,6 +18,7 @@
 
 Unit tests for the Protocol Buffer model.
 """
+from __future__ import absolute_import
 
 __author__ = 'mmcdonald@google.com (Matt McDonald)'
 
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 476575c..eb41adf 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 """Unit tests for googleapiclient.schema."""
+from __future__ import absolute_import
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
@@ -48,7 +49,7 @@
 
 class SchemasTest(unittest.TestCase):
   def setUp(self):
-    f = file(datafile('zoo.json'))
+    f = open(datafile('zoo.json'))
     discovery = f.read()
     f.close()
     discovery = json.loads(discovery)