Upgrade googleapiclient to 1.5.0

This commit is contained in:
Jay Lee
2016-03-16 13:05:55 -04:00
parent d8a78d96ae
commit 190c4f212d
4 changed files with 70 additions and 44 deletions

View File

@@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
__version__ = "1.4.2" __version__ = "1.5.0"

View File

@@ -28,14 +28,17 @@ __all__ = [
'key2param', 'key2param',
] ]
from six import StringIO from six import BytesIO
from six.moves import http_client from six.moves import http_client
from six.moves.urllib.parse import urlencode, urlparse, urljoin, \ from six.moves.urllib.parse import urlencode, urlparse, urljoin, \
urlunparse, parse_qsl urlunparse, parse_qsl
# Standard library imports # Standard library imports
import copy import copy
from email.generator import Generator try:
from email.generator import BytesGenerator
except ImportError:
from email.generator import Generator as BytesGenerator
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart from email.mime.nonmultipart import MIMENonMultipart
import json import json
@@ -102,6 +105,10 @@ STACK_QUERY_PARAMETER_DEFAULT_VALUE = {'type': 'string', 'location': 'query'}
# Library-specific reserved words beyond Python keywords. # Library-specific reserved words beyond Python keywords.
RESERVED_WORDS = frozenset(['body']) RESERVED_WORDS = frozenset(['body'])
# patch _write_lines to avoid munging '\r' into '\n'
# ( https://bugs.python.org/issue18886 https://bugs.python.org/issue19003 )
class _BytesGenerator(BytesGenerator):
_write_lines = BytesGenerator.write
def fix_method_name(name): def fix_method_name(name):
"""Fix method names to avoid reserved word conflicts. """Fix method names to avoid reserved word conflicts.
@@ -797,8 +804,8 @@ def createMethod(methodName, methodDesc, rootDesc, schema):
msgRoot.attach(msg) msgRoot.attach(msg)
# encode the body: note that we can't use `as_string`, because # encode the body: note that we can't use `as_string`, because
# it plays games with `From ` lines. # it plays games with `From ` lines.
fp = StringIO() fp = BytesIO()
g = Generator(fp, mangle_from_=False) g = _BytesGenerator(fp, mangle_from_=False)
g.flatten(msgRoot, unixfrom=False) g.flatten(msgRoot, unixfrom=False)
body = fp.getvalue() body = fp.getvalue()

View File

@@ -29,7 +29,7 @@ import os
import tempfile import tempfile
import threading import threading
from oauth2client.locked_file import LockedFile from oauth2client.contrib.locked_file import LockedFile
from . import base from . import base
from ..discovery_cache import DISCOVERY_DOC_MAX_AGE from ..discovery_cache import DISCOVERY_DOC_MAX_AGE

View File

@@ -36,6 +36,7 @@ import logging
import mimetypes import mimetypes
import os import os
import random import random
import ssl
import sys import sys
import time import time
import uuid import uuid
@@ -61,6 +62,46 @@ DEFAULT_CHUNK_SIZE = 512*1024
MAX_URI_LENGTH = 2048 MAX_URI_LENGTH = 2048
def _retry_request(http, num_retries, req_type, sleep, rand, uri, method, *args,
**kwargs):
"""Retries an HTTP request multiple times while handling errors.
If after all retries the request still fails, last error is either returned as
return value (for HTTP 5xx errors) or thrown (for ssl.SSLError).
Args:
http: Http object to be used to execute request.
num_retries: Maximum number of retries.
req_type: Type of the request (used for logging retries).
sleep, rand: Functions to sleep for random time between retries.
uri: URI to be requested.
method: HTTP method to be used.
args, kwargs: Additional arguments passed to http.request.
Returns:
resp, content - Response from the http request (may be HTTP 5xx).
"""
resp = None
for retry_num in range(num_retries + 1):
if retry_num > 0:
sleep(rand() * 2**retry_num)
logging.warning(
'Retry #%d for %s: %s %s%s' % (retry_num, req_type, method, uri,
', following status: %d' % resp.status if resp else ''))
try:
resp, content = http.request(uri, method, *args, **kwargs)
except ssl.SSLError:
if retry_num == num_retries:
raise
else:
continue
if resp.status < 500:
break
return resp, content
class MediaUploadProgress(object): class MediaUploadProgress(object):
"""Status of a resumable upload.""" """Status of a resumable upload."""
@@ -425,7 +466,11 @@ class MediaFileUpload(MediaIoBaseUpload):
self._filename = filename self._filename = filename
fd = open(self._filename, 'rb') fd = open(self._filename, 'rb')
if mimetype is None: if mimetype is None:
(mimetype, encoding) = mimetypes.guess_type(filename) # No mimetype provided, make a guess.
mimetype, _ = mimetypes.guess_type(filename)
if mimetype is None:
# Guess failed, use octet-stream.
mimetype = 'application/octet-stream'
super(MediaFileUpload, self).__init__(fd, mimetype, chunksize=chunksize, super(MediaFileUpload, self).__init__(fd, mimetype, chunksize=chunksize,
resumable=resumable) resumable=resumable)
@@ -542,16 +587,9 @@ class MediaIoBaseDownload(object):
} }
http = self._request.http http = self._request.http
for retry_num in range(num_retries + 1): resp, content = _retry_request(
if retry_num > 0: http, num_retries, 'media download', self._sleep, self._rand, self._uri,
self._sleep(self._rand() * 2**retry_num) 'GET', headers=headers)
logging.warning(
'Retry #%d for media download: GET %s, following status: %d'
% (retry_num, self._uri, resp.status))
resp, content = http.request(self._uri, headers=headers)
if resp.status < 500:
break
if resp.status in [200, 206]: if resp.status in [200, 206]:
if 'content-location' in resp and resp['content-location'] != self._uri: if 'content-location' in resp and resp['content-location'] != self._uri:
@@ -650,7 +688,7 @@ class HttpRequest(object):
# Pull the multipart boundary out of the content-type header. # Pull the multipart boundary out of the content-type header.
major, minor, params = mimeparse.parse_mime_type( major, minor, params = mimeparse.parse_mime_type(
headers.get('content-type', 'application/json')) self.headers.get('content-type', 'application/json'))
# The size of the non-media part of the request. # The size of the non-media part of the request.
self.body_size = len(self.body or '') self.body_size = len(self.body or '')
@@ -712,16 +750,9 @@ class HttpRequest(object):
self.headers['content-length'] = str(len(self.body)) self.headers['content-length'] = str(len(self.body))
# Handle retries for server-side errors. # Handle retries for server-side errors.
for retry_num in range(num_retries + 1): resp, content = _retry_request(
if retry_num > 0: http, num_retries, 'request', self._sleep, self._rand, str(self.uri),
self._sleep(self._rand() * 2**retry_num) method=str(self.method), body=self.body, headers=self.headers)
logging.warning('Retry #%d for request: %s %s, following status: %d'
% (retry_num, self.method, self.uri, resp.status))
resp, content = http.request(str(self.uri), method=str(self.method),
body=self.body, headers=self.headers)
if resp.status < 500:
break
for callback in self.response_callbacks: for callback in self.response_callbacks:
callback(resp) callback(resp)
@@ -795,18 +826,9 @@ class HttpRequest(object):
start_headers['X-Upload-Content-Length'] = size start_headers['X-Upload-Content-Length'] = size
start_headers['content-length'] = str(self.body_size) start_headers['content-length'] = str(self.body_size)
for retry_num in range(num_retries + 1): resp, content = _retry_request(
if retry_num > 0: http, num_retries, 'resumable URI request', self._sleep, self._rand,
self._sleep(self._rand() * 2**retry_num) self.uri, method=self.method, body=self.body, headers=start_headers)
logging.warning(
'Retry #%d for resumable URI request: %s %s, following status: %d'
% (retry_num, self.method, self.uri, resp.status))
resp, content = http.request(self.uri, method=self.method,
body=self.body,
headers=start_headers)
if resp.status < 500:
break
if resp.status == 200 and 'location' in resp: if resp.status == 200 and 'location' in resp:
self.resumable_uri = resp['location'] self.resumable_uri = resp['location']
@@ -827,10 +849,7 @@ class HttpRequest(object):
# The upload was complete. # The upload was complete.
return (status, body) return (status, body)
# The httplib.request method can take streams for the body parameter, but if self.resumable.has_stream():
# only in Python 2.6 or later. If a stream is available under those
# conditions then use it as the body argument.
if self.resumable.has_stream() and sys.version_info[1] >= 6:
data = self.resumable.stream() data = self.resumable.stream()
if self.resumable.chunksize() == -1: if self.resumable.chunksize() == -1:
data.seek(self.resumable_progress) data.seek(self.resumable_progress)