diff --git a/apiclient/__init__.py b/apiclient/__init__.py index 5efb142e01e..d75e7a1311f 100644 --- a/apiclient/__init__.py +++ b/apiclient/__init__.py @@ -1,5 +1,7 @@ """Retain apiclient as an alias for googleapiclient.""" +from six import iteritems + import googleapiclient try: @@ -36,5 +38,5 @@ } import sys -for module_name, module in _SUBMODULES.iteritems(): +for module_name, module in iteritems(_SUBMODULES): sys.modules['apiclient.%s' % module_name] = module diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index cdbf1400582..f3e5690abc2 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py @@ -28,9 +28,11 @@ 'key2param', ] +from six import StringIO +from six.moves.urllib.parse import urlencode, urlparse, urljoin, \ + urlunparse, parse_qsl # Standard library imports -import StringIO import copy from email.generator import Generator from email.mime.multipart import MIMEMultipart @@ -41,20 +43,13 @@ import mimetypes import os import re -import urllib -import urlparse - -try: - from urlparse import parse_qsl -except ImportError: - from cgi import parse_qsl # Third-party imports import httplib2 -from . import mimeparse import uritemplate # Local imports +from googleapiclient import mimeparse from googleapiclient.errors import HttpError from googleapiclient.errors import InvalidJsonError from googleapiclient.errors import MediaUploadSizeError @@ -206,6 +201,11 @@ def build(serviceName, if resp.status >= 400: raise HttpError(resp, content, uri=requested_url) + try: + content = content.decode('utf-8') + except AttributeError: + pass + try: service = json.loads(content) except ValueError as e: @@ -258,7 +258,7 @@ def build_from_document( if isinstance(service, six.string_types): service = json.loads(service) - base = urlparse.urljoin(service['rootUrl'], service['servicePath']) + base = urljoin(service['rootUrl'], service['servicePath']) schema = Schemas(service) if credentials: @@ -505,7 +505,7 @@ def _urljoin(base, url): # exception here is the case of media uploads, where url will be an # absolute url. if url.startswith('http://') or url.startswith('https://'): - return urlparse.urljoin(base, url) + return urljoin(base, url) new_base = base if base.endswith('/') else base + '/' new_url = url[1:] if url.startswith('/') else url return new_base + new_url @@ -712,7 +712,7 @@ def method(self, **kwargs): raise TypeError('media_filename must be str or MediaUpload.') # Check the maxSize - if maxSize > 0 and media_upload.size() > maxSize: + if media_upload.size() is not None and media_upload.size() > maxSize > 0: raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads @@ -752,7 +752,7 @@ def method(self, **kwargs): msgRoot.attach(msg) # encode the body: note that we can't use `as_string`, because # it plays games with `From ` lines. - fp = StringIO.StringIO() + fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(msgRoot, unixfrom=False) body = fp.getvalue() @@ -859,14 +859,14 @@ def methodNext(self, previous_request, previous_response): request = copy.copy(previous_request) pageToken = previous_response['nextPageToken'] - parsed = list(urlparse.urlparse(request.uri)) + parsed = list(urlparse(request.uri)) q = parse_qsl(parsed[4]) # Find and remove old 'pageToken' value from URI newq = [(key, value) for (key, value) in q if key != 'pageToken'] newq.append(('pageToken', pageToken)) - parsed[4] = urllib.urlencode(newq) - uri = urlparse.urlunparse(parsed) + parsed[4] = urlencode(newq) + uri = urlunparse(parsed) request.uri = uri diff --git a/googleapiclient/http.py b/googleapiclient/http.py index f0e91338a28..d09483c55ce 100644 --- a/googleapiclient/http.py +++ b/googleapiclient/http.py @@ -24,34 +24,35 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)' -import StringIO +from six import BytesIO, StringIO +from six.moves.urllib.parse import urlparse, urlunparse, quote, unquote + import base64 import copy import gzip import httplib2 import json import logging -from . import mimeparse import mimetypes import os import random import sys import time -import urllib -import urlparse import uuid from email.generator import Generator from email.mime.multipart import MIMEMultipart from email.mime.nonmultipart import MIMENonMultipart from email.parser import FeedParser -from .errors import BatchError -from .errors import HttpError -from .errors import InvalidChunkSizeError -from .errors import ResumableUploadError -from .errors import UnexpectedBodyError -from .errors import UnexpectedMethodError -from .model import JsonModel + +from googleapiclient import mimeparse +from googleapiclient.errors import BatchError +from googleapiclient.errors import HttpError +from googleapiclient.errors import InvalidChunkSizeError +from googleapiclient.errors import ResumableUploadError +from googleapiclient.errors import UnexpectedBodyError +from googleapiclient.errors import UnexpectedMethodError +from googleapiclient.model import JsonModel from oauth2client import util @@ -262,7 +263,7 @@ class MediaIoBaseUpload(MediaUpload): Note that the Python file object is compatible with io.Base and can be used with this class also. - fh = io.BytesIO('...Some data to upload...') + fh = BytesIO('...Some data to upload...') media = MediaIoBaseUpload(fh, mimetype='image/png', chunksize=1024*1024, resumable=True) farm.animals().insert( @@ -468,7 +469,7 @@ def __init__(self, body, mimetype='application/octet-stream', resumable: bool, True if this is a resumable upload. False means upload in a single request. """ - fd = StringIO.StringIO(body) + fd = BytesIO(body) super(MediaInMemoryUpload, self).__init__(fd, mimetype, chunksize=chunksize, resumable=resumable) @@ -702,8 +703,8 @@ def execute(self, http=None, num_retries=0): self.method = 'POST' self.headers['x-http-method-override'] = 'GET' self.headers['content-type'] = 'application/x-www-form-urlencoded' - parsed = urlparse.urlparse(self.uri) - self.uri = urlparse.urlunparse( + parsed = urlparse(self.uri) + self.uri = urlunparse( (parsed.scheme, parsed.netloc, parsed.path, parsed.params, None, None) ) @@ -1051,7 +1052,7 @@ def _id_to_header(self, id_): if self._base_id is None: self._base_id = uuid.uuid4() - return '<%s+%s>' % (self._base_id, urllib.quote(id_)) + return '<%s+%s>' % (self._base_id, quote(id_)) def _header_to_id(self, header): """Convert a Content-ID header value to an id. @@ -1074,7 +1075,7 @@ def _header_to_id(self, header): raise BatchError("Invalid value for Content-ID: %s" % header) base, id_ = header[1:-1].rsplit('+', 1) - return urllib.unquote(id_) + return unquote(id_) def _serialize_request(self, request): """Convert an HttpRequest object into a string. @@ -1086,9 +1087,9 @@ def _serialize_request(self, request): The request as a string in application/http format. """ # Construct status line - parsed = urlparse.urlparse(request.uri) - request_line = urlparse.urlunparse( - (None, None, parsed.path, parsed.params, parsed.query, None) + parsed = urlparse(request.uri) + request_line = urlunparse( + ('', '', parsed.path, parsed.params, parsed.query, '') ) status_line = request.method + ' ' + request_line + ' HTTP/1.1\n' major, minor = request.headers.get('content-type', 'application/json').split('/') @@ -1113,7 +1114,7 @@ def _serialize_request(self, request): msg['content-length'] = str(len(request.body)) # Serialize the mime message. - fp = StringIO.StringIO() + fp = StringIO() # maxheaderlen=0 means don't line wrap headers. g = Generator(fp, maxheaderlen=0) g.flatten(msg, unixfrom=False) @@ -1123,7 +1124,7 @@ def _serialize_request(self, request): if request.body is None: body = body[:-2] - return status_line.encode('utf-8') + body + return status_line + body def _deserialize_response(self, payload): """Convert string into httplib2 response and content. @@ -1236,7 +1237,7 @@ def _execute(self, http, order, requests): # encode the body: note that we can't use `as_string`, because # it plays games with `From ` lines. - fp = StringIO.StringIO() + fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(message, unixfrom=False) body = fp.getvalue() diff --git a/googleapiclient/model.py b/googleapiclient/model.py index 9be5a5905f7..e8afb63d6c5 100644 --- a/googleapiclient/model.py +++ b/googleapiclient/model.py @@ -26,10 +26,11 @@ import json import logging -import urllib + +from six.moves.urllib.parse import urlencode from googleapiclient import __version__ -from .errors import HttpError +from googleapiclient.errors import HttpError dump_request_response = False @@ -170,7 +171,7 @@ def _build_query(self, params): if isinstance(value, six.text_type) and callable(value.encode): value = value.encode('utf-8') astuples.append((key, value)) - return '?' + urllib.urlencode(astuples) + return '?' + urlencode(astuples) def _log_response(self, resp, content): """Logs debugging information about the response if requested.""" @@ -257,7 +258,10 @@ def serialize(self, body_value): return json.dumps(body_value) def deserialize(self, content): - content = content.decode('utf-8') + try: + content = content.decode('utf-8') + except AttributeError: + pass body = json.loads(content) if self._data_wrapper and isinstance(body, dict) and 'data' in body: body = body['data'] diff --git a/googleapiclient/sample_tools.py b/googleapiclient/sample_tools.py index 3e56c0a5d07..2b4e7b45dbe 100644 --- a/googleapiclient/sample_tools.py +++ b/googleapiclient/sample_tools.py @@ -95,9 +95,9 @@ def init(argv, name, version, doc, filename, scope=None, parents=[], discovery_f service = discovery.build(name, version, http=http) else: # Construct a service object using a local discovery document file. - with open(discovery_filename) as discovery_file: - service = discovery.build_from_document( - discovery_file.read(), - base='https://www.googleapis.com/', - http=http) + with open(discovery_filename) as discovery_file: + service = discovery.build_from_document( + discovery_file.read(), + base='https://www.googleapis.com/', + http=http) return (service, flags) diff --git a/tests/test_discovery.py b/tests/test_discovery.py index ccc69c2b05c..e2677b0a117 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -25,6 +25,9 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)' +from six import BytesIO, StringIO +from six.moves.urllib.parse import urlparse, parse_qs + import copy import datetime import httplib2 @@ -34,15 +37,6 @@ import pickle import sys import unittest2 as unittest -import urlparse -import StringIO - - -try: - from urlparse import parse_qs -except ImportError: - from cgi import parse_qs - from googleapiclient.discovery import _fix_up_media_upload from googleapiclient.discovery import _fix_up_method_description @@ -82,8 +76,8 @@ def assertUrisEqual(testcase, expected, actual): """Test that URIs are the same, up to reordering of query parameters.""" - expected = urlparse.urlparse(expected) - actual = urlparse.urlparse(actual) + expected = urlparse(expected) + actual = urlparse(actual) testcase.assertEqual(expected.scheme, actual.scheme) testcase.assertEqual(expected.netloc, actual.netloc) testcase.assertEqual(expected.path, actual.path) @@ -442,7 +436,7 @@ def test_method_error_checking(self): self.assertTrue('unexpected' in str(e)) def _check_query_types(self, request): - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['q'], ['foo']) self.assertEqual(q['i'], ['1']) @@ -479,7 +473,7 @@ def test_optional_stack_query_parameters(self): zoo = build('zoo', 'v1', http=http) request = zoo.query(trace='html', fields='description') - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['trace'], ['html']) self.assertEqual(q['fields'], ['description']) @@ -489,7 +483,7 @@ def test_string_params_value_of_none_get_dropped(self): zoo = build('zoo', 'v1', http=http) request = zoo.query(trace=None, fields='description') - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertFalse('trace' in q) @@ -498,7 +492,7 @@ def test_model_added_query_parameters(self): zoo = build('zoo', 'v1', http=http) request = zoo.animals().get(name='Lion') - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['alt'], ['json']) self.assertEqual(request.headers['accept'], 'application/json') @@ -508,7 +502,7 @@ def test_fallback_to_raw_model(self): zoo = build('zoo', 'v1', http=http) request = zoo.animals().getmedia(name='Lion') - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertTrue('alt' not in q) self.assertEqual(request.headers['accept'], '*/*') @@ -560,7 +554,7 @@ def test_full_featured(self): self.assertTrue(getattr(zoo, 'animals')) request = zoo.animals().list(name='bat', projection="full") - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['name'], ['bat']) self.assertEqual(q['projection'], ['full']) @@ -570,16 +564,17 @@ def test_nested_resources(self): zoo = build('zoo', 'v1', http=self.http) self.assertTrue(getattr(zoo, 'animals')) request = zoo.my().favorites().list(max_results="5") - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['max-results'], ['5']) + @unittest.skipIf(six.PY3, 'print is not a reserved name in Python 3') def test_methods_with_reserved_names(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) self.assertTrue(getattr(zoo, 'animals')) request = zoo.global_().print_().assert_(max_results="5") - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) self.assertEqual(parsed[2], '/zoo/v1/global/print/assert') def test_top_level_functions(self): @@ -587,7 +582,7 @@ def test_top_level_functions(self): zoo = build('zoo', 'v1', http=self.http) self.assertTrue(getattr(zoo, 'query')) request = zoo.query(q="foo") - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['q'], ['foo']) @@ -602,7 +597,7 @@ def test_simple_media_upload_no_max_size_provided(self): zoo = build('zoo', 'v1', http=self.http) request = zoo.animals().crossbreed(media_body=datafile('small.png')) self.assertEquals('image/png', request.headers['content-type']) - self.assertEquals('PNG', request.body[1:4]) + self.assertEquals(b'PNG', request.body[1:4]) def test_simple_media_raise_correct_exceptions(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) @@ -626,7 +621,7 @@ def test_simple_media_good_upload(self): request = zoo.animals().insert(media_body=datafile('small.png')) self.assertEquals('image/png', request.headers['content-type']) - self.assertEquals('PNG', request.body[1:4]) + self.assertEquals(b'PNG', request.body[1:4]) assertUrisEqual(self, 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json', request.uri) @@ -857,62 +852,48 @@ def test_media_io_base_stream_unlimited_chunksize_resume(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) - try: - import io - - # Set up a seekable stream and try to upload in single chunk. - fd = io.BytesIO('01234"56789"') - media_upload = MediaIoBaseUpload( - fd=fd, mimetype='text/plain', chunksize=-1, resumable=True) - - request = zoo.animals().insert(media_body=media_upload, body=None) - - # The single chunk fails, restart at the right point. - http = HttpMockSequence([ - ({'status': '200', - 'location': 'http://upload.example.com'}, ''), - ({'status': '308', - 'location': 'http://upload.example.com/2', - 'range': '0-4'}, ''), - ({'status': '200'}, 'echo_request_body'), - ]) + # Set up a seekable stream and try to upload in single chunk. + fd = BytesIO(b'01234"56789"') + media_upload = MediaIoBaseUpload( + fd=fd, mimetype='text/plain', chunksize=-1, resumable=True) - body = request.execute(http=http) - self.assertEqual('56789', body) + request = zoo.animals().insert(media_body=media_upload, body=None) - except ImportError: - pass + # The single chunk fails, restart at the right point. + http = HttpMockSequence([ + ({'status': '200', + 'location': 'http://upload.example.com'}, ''), + ({'status': '308', + 'location': 'http://upload.example.com/2', + 'range': '0-4'}, ''), + ({'status': '200'}, 'echo_request_body'), + ]) + body = request.execute(http=http) + self.assertEqual('56789', body) def test_media_io_base_stream_chunksize_resume(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) - try: - import io - - # Set up a seekable stream and try to upload in chunks. - fd = io.BytesIO('0123456789') - media_upload = MediaIoBaseUpload( - fd=fd, mimetype='text/plain', chunksize=5, resumable=True) - - request = zoo.animals().insert(media_body=media_upload, body=None) + # Set up a seekable stream and try to upload in chunks. + fd = BytesIO(b'0123456789') + media_upload = MediaIoBaseUpload( + fd=fd, mimetype='text/plain', chunksize=5, resumable=True) - # The single chunk fails, pull the content sent out of the exception. - http = HttpMockSequence([ - ({'status': '200', - 'location': 'http://upload.example.com'}, ''), - ({'status': '400'}, 'echo_request_body'), - ]) - - try: - body = request.execute(http=http) - except HttpError as e: - self.assertEqual('01234', e.content) + request = zoo.animals().insert(media_body=media_upload, body=None) - except ImportError: - pass + # The single chunk fails, pull the content sent out of the exception. + http = HttpMockSequence([ + ({'status': '200', + 'location': 'http://upload.example.com'}, ''), + ({'status': '400'}, 'echo_request_body'), + ]) + try: + body = request.execute(http=http) + except HttpError as e: + self.assertEqual(b'01234', e.content) def test_resumable_media_handle_uploads_of_unknown_size(self): http = HttpMockSequence([ @@ -1021,7 +1002,7 @@ def test_resumable_media_handle_uploads_of_unknown_size_eof(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=self.http) - fd = StringIO.StringIO('data goes here') + fd = BytesIO(b'data goes here') # Create an upload that doesn't know the full size of the media. upload = MediaIoBaseUpload( @@ -1045,7 +1026,7 @@ def test_resumable_media_handle_resume_of_upload_of_unknown_size(self): zoo = build('zoo', 'v1', http=self.http) # Create an upload that doesn't know the full size of the media. - fd = StringIO.StringIO('data goes here') + fd = BytesIO(b'data goes here') upload = MediaIoBaseUpload( fd=fd, mimetype='image/png', chunksize=500, resumable=True) @@ -1179,7 +1160,7 @@ def test_next_successful_with_next_page_token(self): request = tasks.tasklists().list() next_request = tasks.tasklists().list_next( request, {'nextPageToken': '123abc'}) - parsed = list(urlparse.urlparse(next_request.uri)) + parsed = list(urlparse(next_request.uri)) q = parse_qs(parsed[4]) self.assertEqual(q['pageToken'][0], '123abc') @@ -1196,7 +1177,7 @@ def test_get_media(self): zoo = build('zoo', 'v1', http=http) request = zoo.animals().get_media(name='Lion') - parsed = urlparse.urlparse(request.uri) + parsed = urlparse(request.uri) q = parse_qs(parsed[4]) self.assertEqual(q['alt'], ['media']) self.assertEqual(request.headers['accept'], '*/*') diff --git a/tests/test_http.py b/tests/test_http.py index a3ff0aca5a4..b47b9dc4fa3 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -23,14 +23,17 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)' +from six import PY3 +from six import BytesIO, StringIO +from io import FileIO +from six.moves.urllib.parse import urlencode + # Do not remove the httplib2 import import httplib2 import logging import os import unittest2 as unittest -import urllib import random -import StringIO import time from googleapiclient.discovery import build @@ -135,7 +138,7 @@ def test_media_file_upload_to_from_json(self): self.assertEqual(190, upload.size()) self.assertEqual(True, upload.resumable()) self.assertEqual(500, upload.chunksize()) - self.assertEqual('PNG', upload.getbytes(1, 3)) + self.assertEqual(b'PNG', upload.getbytes(1, 3)) json = upload.to_json() new_upload = MediaUpload.new_from_json(json) @@ -144,7 +147,7 @@ def test_media_file_upload_to_from_json(self): self.assertEqual(190, new_upload.size()) self.assertEqual(True, new_upload.resumable()) self.assertEqual(500, new_upload.chunksize()) - self.assertEqual('PNG', new_upload.getbytes(1, 3)) + self.assertEqual(b'PNG', new_upload.getbytes(1, 3)) def test_media_file_upload_raises_on_invalid_chunksize(self): self.assertRaises(InvalidChunkSizeError, MediaFileUpload, @@ -152,12 +155,12 @@ def test_media_file_upload_raises_on_invalid_chunksize(self): resumable=True) def test_media_inmemory_upload(self): - media = MediaInMemoryUpload('abcdef', mimetype='text/plain', chunksize=10, + media = MediaInMemoryUpload(b'abcdef', mimetype='text/plain', chunksize=10, resumable=True) self.assertEqual('text/plain', media.mimetype()) self.assertEqual(10, media.chunksize()) self.assertTrue(media.resumable()) - self.assertEqual('bc', media.getbytes(1, 2)) + self.assertEqual(b'bc', media.getbytes(1, 2)) self.assertEqual(6, media.size()) def test_http_request_to_from_json(self): @@ -196,33 +199,28 @@ def _postproc(*kwargs): class TestMediaIoBaseUpload(unittest.TestCase): def test_media_io_base_upload_from_file_io(self): - try: - import io - - fd = io.FileIO(datafile('small.png'), 'r') - upload = MediaIoBaseUpload( - fd=fd, mimetype='image/png', chunksize=500, resumable=True) - self.assertEqual('image/png', upload.mimetype()) - self.assertEqual(190, upload.size()) - self.assertEqual(True, upload.resumable()) - self.assertEqual(500, upload.chunksize()) - self.assertEqual('PNG', upload.getbytes(1, 3)) - except ImportError: - pass + fd = FileIO(datafile('small.png'), 'r') + upload = MediaIoBaseUpload( + fd=fd, mimetype='image/png', chunksize=500, resumable=True) + self.assertEqual('image/png', upload.mimetype()) + self.assertEqual(190, upload.size()) + self.assertEqual(True, upload.resumable()) + self.assertEqual(500, upload.chunksize()) + self.assertEqual(b'PNG', upload.getbytes(1, 3)) def test_media_io_base_upload_from_file_object(self): - f = open(datafile('small.png'), 'r') + f = open(datafile('small.png'), 'rb') upload = MediaIoBaseUpload( fd=f, mimetype='image/png', chunksize=500, resumable=True) self.assertEqual('image/png', upload.mimetype()) self.assertEqual(190, upload.size()) self.assertEqual(True, upload.resumable()) self.assertEqual(500, upload.chunksize()) - self.assertEqual('PNG', upload.getbytes(1, 3)) + self.assertEqual(b'PNG', upload.getbytes(1, 3)) f.close() def test_media_io_base_upload_serializable(self): - f = open(datafile('small.png'), 'r') + f = open(datafile('small.png'), 'rb') upload = MediaIoBaseUpload(fd=f, mimetype='image/png') try: @@ -231,9 +229,10 @@ def test_media_io_base_upload_serializable(self): except NotImplementedError: pass + @unittest.skipIf(PY3, 'Strings and Bytes are different types') def test_media_io_base_upload_from_string_io(self): - f = open(datafile('small.png'), 'r') - fd = StringIO.StringIO(f.read()) + f = open(datafile('small.png'), 'rb') + fd = StringIO(f.read()) f.close() upload = MediaIoBaseUpload( @@ -242,56 +241,36 @@ def test_media_io_base_upload_from_string_io(self): self.assertEqual(190, upload.size()) self.assertEqual(True, upload.resumable()) self.assertEqual(500, upload.chunksize()) - self.assertEqual('PNG', upload.getbytes(1, 3)) + self.assertEqual(b'PNG', upload.getbytes(1, 3)) f.close() def test_media_io_base_upload_from_bytes(self): - try: - import io - - f = open(datafile('small.png'), 'r') - fd = io.BytesIO(f.read()) - upload = MediaIoBaseUpload( - fd=fd, mimetype='image/png', chunksize=500, resumable=True) - self.assertEqual('image/png', upload.mimetype()) - self.assertEqual(190, upload.size()) - self.assertEqual(True, upload.resumable()) - self.assertEqual(500, upload.chunksize()) - self.assertEqual('PNG', upload.getbytes(1, 3)) - except ImportError: - pass + f = open(datafile('small.png'), 'rb') + fd = BytesIO(f.read()) + upload = MediaIoBaseUpload( + fd=fd, mimetype='image/png', chunksize=500, resumable=True) + self.assertEqual('image/png', upload.mimetype()) + self.assertEqual(190, upload.size()) + self.assertEqual(True, upload.resumable()) + self.assertEqual(500, upload.chunksize()) + self.assertEqual(b'PNG', upload.getbytes(1, 3)) def test_media_io_base_upload_raises_on_invalid_chunksize(self): - try: - import io - - f = open(datafile('small.png'), 'r') - fd = io.BytesIO(f.read()) - self.assertRaises(InvalidChunkSizeError, MediaIoBaseUpload, - fd, 'image/png', chunksize=-2, resumable=True) - except ImportError: - pass + f = open(datafile('small.png'), 'rb') + fd = BytesIO(f.read()) + self.assertRaises(InvalidChunkSizeError, MediaIoBaseUpload, + fd, 'image/png', chunksize=-2, resumable=True) def test_media_io_base_upload_streamable(self): - try: - import io - - fd = io.BytesIO('stuff') - upload = MediaIoBaseUpload( - fd=fd, mimetype='image/png', chunksize=500, resumable=True) - self.assertEqual(True, upload.has_stream()) - self.assertEqual(fd, upload.stream()) - except ImportError: - pass + fd = BytesIO(b'stuff') + upload = MediaIoBaseUpload( + fd=fd, mimetype='image/png', chunksize=500, resumable=True) + self.assertEqual(True, upload.has_stream()) + self.assertEqual(fd, upload.stream()) def test_media_io_base_next_chunk_retries(self): - try: - import io - except ImportError: - return - - f = open(datafile('small.png'), 'r') - fd = io.BytesIO(f.read()) + f = open(datafile('small.png'), 'rb') + fd = BytesIO(f.read()) upload = MediaIoBaseUpload( fd=fd, mimetype='image/png', chunksize=500, resumable=True) @@ -333,14 +312,14 @@ def setUp(self): http = HttpMock(datafile('zoo.json'), {'status': '200'}) zoo = build('zoo', 'v1', http=http) self.request = zoo.animals().get_media(name='Lion') - self.fd = StringIO.StringIO() + self.fd = BytesIO() def test_media_io_base_download(self): self.request.http = HttpMockSequence([ ({'status': '200', - 'content-range': '0-2/5'}, '123'), + 'content-range': '0-2/5'}, b'123'), ({'status': '200', - 'content-range': '3-4/5'}, '45'), + 'content-range': '3-4/5'}, b'45'), ]) self.assertEqual(True, self.request.http.follow_redirects) @@ -356,7 +335,7 @@ def test_media_io_base_download(self): status, done = download.next_chunk() - self.assertEqual(self.fd.getvalue(), '123') + self.assertEqual(self.fd.getvalue(), b'123') self.assertEqual(False, done) self.assertEqual(3, download._progress) self.assertEqual(5, download._total_size) @@ -364,7 +343,7 @@ def test_media_io_base_download(self): status, done = download.next_chunk() - self.assertEqual(self.fd.getvalue(), '12345') + self.assertEqual(self.fd.getvalue(), b'12345') self.assertEqual(True, done) self.assertEqual(5, download._progress) self.assertEqual(5, download._total_size) @@ -372,9 +351,9 @@ def test_media_io_base_download(self): def test_media_io_base_download_handle_redirects(self): self.request.http = HttpMockSequence([ ({'status': '200', - 'content-location': 'https://secure.example.net/lion'}, ''), + 'content-location': 'https://secure.example.net/lion'}, b''), ({'status': '200', - 'content-range': '0-2/5'}, 'abc'), + 'content-range': '0-2/5'}, b'abc'), ]) download = MediaIoBaseDownload( @@ -401,12 +380,12 @@ def test_media_io_base_download_handle_4xx(self): # Even after raising an exception we can pick up where we left off. self.request.http = HttpMockSequence([ ({'status': '200', - 'content-range': '0-2/5'}, '123'), + 'content-range': '0-2/5'}, b'123'), ]) status, done = download.next_chunk() - self.assertEqual(self.fd.getvalue(), '123') + self.assertEqual(self.fd.getvalue(), b'123') def test_media_io_base_download_retries_5xx(self): self.request.http = HttpMockSequence([ @@ -414,12 +393,12 @@ def test_media_io_base_download_retries_5xx(self): ({'status': '500'}, ''), ({'status': '500'}, ''), ({'status': '200', - 'content-range': '0-2/5'}, '123'), + 'content-range': '0-2/5'}, b'123'), ({'status': '503'}, ''), ({'status': '503'}, ''), ({'status': '503'}, ''), ({'status': '200', - 'content-range': '3-4/5'}, '45'), + 'content-range': '3-4/5'}, b'45'), ]) download = MediaIoBaseDownload( @@ -442,7 +421,7 @@ def test_media_io_base_download_retries_5xx(self): # Check for exponential backoff using the rand function above. self.assertEqual([20, 40, 80], sleeptimes) - self.assertEqual(self.fd.getvalue(), '123') + self.assertEqual(self.fd.getvalue(), b'123') self.assertEqual(False, done) self.assertEqual(3, download._progress) self.assertEqual(5, download._total_size) @@ -456,7 +435,7 @@ def test_media_io_base_download_retries_5xx(self): # Check for exponential backoff using the rand function above. self.assertEqual([20, 40, 80], sleeptimes) - self.assertEqual(self.fd.getvalue(), '12345') + self.assertEqual(self.fd.getvalue(), b'12345') self.assertEqual(True, done) self.assertEqual(5, download._progress) self.assertEqual(5, download._total_size) @@ -693,7 +672,7 @@ def test_serialize_request(self): None, 'https://www.googleapis.com/someapi/v1/collection/?foo=bar', method='POST', - body='{}', + body=u'{}', headers={'content-type': 'application/json'}, methodId=None, resumable=None) @@ -702,7 +681,7 @@ def test_serialize_request(self): def test_serialize_request_media_body(self): batch = BatchHttpRequest() - f = open(datafile('small.png')) + f = open(datafile('small.png'), 'rb') body = f.read() f.close() @@ -725,7 +704,7 @@ def test_serialize_request_no_body(self): None, 'https://www.googleapis.com/someapi/v1/collection/?foo=bar', method='POST', - body='', + body=b'', headers={'content-type': 'application/json'}, methodId=None, resumable=None) @@ -940,7 +919,7 @@ def _postproc(resp, content): req = HttpRequest( http, _postproc, - 'http://example.com?' + urllib.urlencode(query), + 'http://example.com?' + urlencode(query), method='GET', body=None, headers={}, @@ -963,21 +942,21 @@ class TestStreamSlice(unittest.TestCase): """Test _StreamSlice.""" def setUp(self): - self.stream = StringIO.StringIO('0123456789') + self.stream = BytesIO(b'0123456789') def test_read(self): s = _StreamSlice(self.stream, 0, 4) - self.assertEqual('', s.read(0)) - self.assertEqual('0', s.read(1)) - self.assertEqual('123', s.read()) + self.assertEqual(b'', s.read(0)) + self.assertEqual(b'0', s.read(1)) + self.assertEqual(b'123', s.read()) def test_read_too_much(self): s = _StreamSlice(self.stream, 1, 4) - self.assertEqual('1234', s.read(6)) + self.assertEqual(b'1234', s.read(6)) def test_read_all(self): s = _StreamSlice(self.stream, 2, 1) - self.assertEqual('2', s.read(-1)) + self.assertEqual(b'2', s.read(-1)) class TestResponseCallback(unittest.TestCase): diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 462038afd6b..b1986523f16 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -34,11 +34,7 @@ from googleapiclient.errors import HttpError from googleapiclient.model import JsonModel -# Python 2.5 requires different modules -try: - from urlparse import parse_qs -except ImportError: - from cgi import parse_qs +from six.moves.urllib.parse import parse_qs class Model(unittest.TestCase): @@ -125,7 +121,12 @@ def test_json_build_query(self): query_dict = parse_qs(query[1:]) self.assertEqual(query_dict['foo'], ['1']) - self.assertEqual(query_dict['bar'], [u'\N{COMET}'.encode('utf-8')]) + if six.PY3: + # Python 3, no need to encode + self.assertEqual(query_dict['bar'], [u'\N{COMET}']) + else: + # Python 2, encode string + self.assertEqual(query_dict['bar'], [u'\N{COMET}'.encode('utf-8')]) self.assertEqual(query_dict['baz'], ['fe', 'fi', 'fo', 'fum']) self.assertTrue('qux' not in query_dict) self.assertEqual(body, '{}') diff --git a/tests/test_protobuf_model.py b/tests/test_protobuf_model.py index b47812fa543..465d12089c1 100644 --- a/tests/test_protobuf_model.py +++ b/tests/test_protobuf_model.py @@ -29,11 +29,7 @@ from googleapiclient.errors import HttpError from googleapiclient.model import ProtocolBufferModel -# Python 2.5 requires different modules -try: - from urlparse import parse_qs -except ImportError: - from cgi import parse_qs +from six.moves.urllib.parse import parse_qs class MockProtocolBuffer(object): diff --git a/tests/test_schema.py b/tests/test_schema.py index 8d07fedddf9..c1216a57edc 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -20,7 +20,6 @@ import json import os import unittest2 as unittest -import StringIO from googleapiclient.schema import Schemas