googleapiclient 1.6.3

This commit is contained in:
Jay Lee
2017-09-13 20:50:08 -04:00
parent c6b1a163af
commit d33eb3b455
4 changed files with 78 additions and 22 deletions

View File

@ -12,7 +12,7 @@
# 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.6.2" __version__ = "1.6.3"
# Set default logging handler to avoid "No handler found" warnings. # Set default logging handler to avoid "No handler found" warnings.
import logging import logging

View File

@ -14,6 +14,8 @@
"""Helpers for authentication using oauth2client or google-auth.""" """Helpers for authentication using oauth2client or google-auth."""
import httplib2
try: try:
import google.auth import google.auth
import google.auth.credentials import google.auth.credentials
@ -29,8 +31,6 @@ try:
except ImportError: # pragma: NO COVER except ImportError: # pragma: NO COVER
HAS_OAUTH2CLIENT = False HAS_OAUTH2CLIENT = False
from googleapiclient.http import build_http
def default_credentials(): def default_credentials():
"""Returns Application Default Credentials.""" """Returns Application Default Credentials."""
@ -84,9 +84,50 @@ def authorized_http(credentials):
Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
authorized http client. authorized http client.
""" """
from googleapiclient.http import build_http
if HAS_GOOGLE_AUTH and isinstance( if HAS_GOOGLE_AUTH and isinstance(
credentials, google.auth.credentials.Credentials): credentials, google.auth.credentials.Credentials):
return google_auth_httplib2.AuthorizedHttp(credentials, return google_auth_httplib2.AuthorizedHttp(credentials,
http=build_http()) http=build_http())
else: else:
return credentials.authorize(build_http()) return credentials.authorize(build_http())
def refresh_credentials(credentials):
# Refresh must use a new http instance, as the one associated with the
# credentials could be a AuthorizedHttp or an oauth2client-decorated
# Http instance which would cause a weird recursive loop of refreshing
# and likely tear a hole in spacetime.
refresh_http = httplib2.Http()
if HAS_GOOGLE_AUTH and isinstance(
credentials, google.auth.credentials.Credentials):
request = google_auth_httplib2.Request(refresh_http)
return credentials.refresh(request)
else:
return credentials.refresh(refresh_http)
def apply_credentials(credentials, headers):
# oauth2client and google-auth have the same interface for this.
return credentials.apply(headers)
def is_valid(credentials):
if HAS_GOOGLE_AUTH and isinstance(
credentials, google.auth.credentials.Credentials):
return credentials.valid
else:
return not credentials.access_token_expired
def get_credentials_from_http(http):
if http is None:
return None
elif hasattr(http.request, 'credentials'):
return http.request.credentials
elif (hasattr(http, 'credentials')
and not isinstance(http.credentials, httplib2.Credentials)):
return http.credentials
else:
return None

View File

@ -46,6 +46,7 @@ class HttpError(Error):
raise TypeError("HTTP content should be bytes") raise TypeError("HTTP content should be bytes")
self.content = content self.content = content
self.uri = uri self.uri = uri
self.error_details = ''
def _get_reason(self): def _get_reason(self):
"""Calculate the reason for the error from the response content.""" """Calculate the reason for the error from the response content."""
@ -54,9 +55,13 @@ class HttpError(Error):
data = json.loads(self.content.decode('utf-8')) data = json.loads(self.content.decode('utf-8'))
if isinstance(data, dict): if isinstance(data, dict):
reason = data['error']['message'] reason = data['error']['message']
if 'details' in data['error']:
self.error_details = data['error']['details']
elif isinstance(data, list) and len(data) > 0: elif isinstance(data, list) and len(data) > 0:
first_error = data[0] first_error = data[0]
reason = first_error['error']['message'] reason = first_error['error']['message']
if 'details' in first_error['error']:
self.error_details = first_error['error']['details']
except (ValueError, KeyError, TypeError): except (ValueError, KeyError, TypeError):
pass pass
if reason is None: if reason is None:
@ -64,7 +69,11 @@ class HttpError(Error):
return reason return reason
def __repr__(self): def __repr__(self):
if self.uri: reason = self._get_reason()
if self.error_details:
return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % \
(self.resp.status, self.uri, reason.strip(), self.error_details)
elif self.uri:
return '<HttpError %s when requesting %s returned "%s">' % ( return '<HttpError %s when requesting %s returned "%s">' % (
self.resp.status, self.uri, self._get_reason().strip()) self.resp.status, self.uri, self._get_reason().strip())
else: else:

View File

@ -62,6 +62,7 @@ try:
except ImportError: except ImportError:
from oauth2client import _helpers as util from oauth2client import _helpers as util
from googleapiclient import _auth
from googleapiclient import mimeparse from googleapiclient import mimeparse
from googleapiclient.errors import BatchError from googleapiclient.errors import BatchError
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
@ -203,7 +204,7 @@ class MediaUploadProgress(object):
the percentage complete as a float, returning 0.0 if the total size of the percentage complete as a float, returning 0.0 if the total size of
the upload is unknown. the upload is unknown.
""" """
if self.total_size is not None: if self.total_size is not None and self.total_size != 0:
return float(self.resumable_progress) / float(self.total_size) return float(self.resumable_progress) / float(self.total_size)
else: else:
return 0.0 return 0.0
@ -229,7 +230,7 @@ class MediaDownloadProgress(object):
the percentage complete as a float, returning 0.0 if the total size of the percentage complete as a float, returning 0.0 if the total size of
the download is unknown. the download is unknown.
""" """
if self.total_size is not None: if self.total_size is not None and self.total_size != 0:
return float(self.resumable_progress) / float(self.total_size) return float(self.resumable_progress) / float(self.total_size)
else: else:
return 0.0 return 0.0
@ -1126,21 +1127,25 @@ class BatchHttpRequest(object):
# If there is no http per the request then refresh the http passed in # If there is no http per the request then refresh the http passed in
# via execute() # via execute()
creds = None creds = None
if request.http is not None and hasattr(request.http.request, request_credentials = False
'credentials'):
creds = request.http.request.credentials if request.http is not None:
elif http is not None and hasattr(http.request, 'credentials'): creds = _auth.get_credentials_from_http(request.http)
creds = http.request.credentials request_credentials = True
if creds is None and http is not None:
creds = _auth.get_credentials_from_http(http)
if creds is not None: if creds is not None:
if id(creds) not in self._refreshed_credentials: if id(creds) not in self._refreshed_credentials:
creds.refresh(http) _auth.refresh_credentials(creds)
self._refreshed_credentials[id(creds)] = 1 self._refreshed_credentials[id(creds)] = 1
# Only apply the credentials if we are using the http object passed in, # Only apply the credentials if we are using the http object passed in,
# otherwise apply() will get called during _serialize_request(). # otherwise apply() will get called during _serialize_request().
if request.http is None or not hasattr(request.http.request, if request.http is None or not request_credentials:
'credentials'): _auth.apply_credentials(creds, request.headers)
creds.apply(request.headers)
def _id_to_header(self, id_): def _id_to_header(self, id_):
"""Convert an id to a Content-ID header value. """Convert an id to a Content-ID header value.
@ -1200,9 +1205,10 @@ class BatchHttpRequest(object):
msg = MIMENonMultipart(major, minor) msg = MIMENonMultipart(major, minor)
headers = request.headers.copy() headers = request.headers.copy()
if request.http is not None and hasattr(request.http.request, if request.http is not None:
'credentials'): credentials = _auth.get_credentials_from_http(request.http)
request.http.request.credentials.apply(headers) if credentials is not None:
_auth.apply_credentials(credentials, headers)
# MIMENonMultipart adds its own Content-Type header. # MIMENonMultipart adds its own Content-Type header.
if 'content-type' in headers: if 'content-type' in headers:
@ -1409,11 +1415,11 @@ class BatchHttpRequest(object):
# Special case for OAuth2Credentials-style objects which have not yet been # Special case for OAuth2Credentials-style objects which have not yet been
# refreshed with an initial access_token. # refreshed with an initial access_token.
if getattr(http.request, 'credentials', None) is not None: creds = _auth.get_credentials_from_http(http)
creds = http.request.credentials if creds is not None:
if not getattr(creds, 'access_token', None): if not _auth.is_valid(creds):
LOGGER.info('Attempting refresh to obtain initial access_token') LOGGER.info('Attempting refresh to obtain initial access_token')
creds.refresh(http) _auth.refresh_credentials(creds)
self._execute(http, self._order, self._requests) self._execute(http, self._order, self._requests)