Hello community,
here is the log from the commit of package python-google-api-python-client for
openSUSE:Factory checked in at 2017-10-09 19:40:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-api-python-client (Old)
and /work/SRC/openSUSE:Factory/.python-google-api-python-client.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-api-python-client"
Mon Oct 9 19:40:54 2017 rev:4 rq:530446 version:1.6.4
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-google-api-python-client/python-google-api-python-client.changes
2017-07-19 12:21:43.802254936 +0200
+++
/work/SRC/openSUSE:Factory/.python-google-api-python-client.new/python-google-api-python-client.changes
2017-10-09 19:40:58.774680524 +0200
@@ -1,0 +2,22 @@
+Sat Sep 30 16:36:20 UTC 2017 - [email protected]
+
+- update to version 1.6.4:
+ * Warn when google-auth credentials are used but
+ google-auth-httplib2 isn't available. (#443)
+
+- changes from version 1.6.3:
+ * Add notification of maintenance mode to README. (#410)
+ * Fix generation of methods with abnormal page token
+ conventions. (#338)
+ * Raise ValueError is credentials and developerKey are both
+ specified. (#358)
+ * Re-generate documentation. (#364, #373, #401)
+ * Fix method signature documentation for multiline required
+ parameters. (#374)
+ * Fix ZeroDivisionError in MediaDownloadProgress.progress. (#377)
+ * Fix dead link to WebTest in README. (#378)
+ * Fix details missing in googleapiclient.errors.HttpError. (#412)
+ * Don't treat httplib2.Credentials as oauth credentials. (#425)
+ * Various fixes to the Django sample. (#413)
+
+-------------------------------------------------------------------
Old:
----
google-api-python-client-1.6.2.tar.gz
New:
----
google-api-python-client-1.6.4.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-google-api-python-client.spec ++++++
--- /var/tmp/diff_new_pack.FMrlBQ/_old 2017-10-09 19:40:59.386653627 +0200
+++ /var/tmp/diff_new_pack.FMrlBQ/_new 2017-10-09 19:40:59.390653451 +0200
@@ -15,12 +15,13 @@
# Please submit bugfixes or comments via http://bugs.opensuse.org/
#
+
# Test scripts not included in source archive
%bcond_with test
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-google-api-python-client
-Version: 1.6.2
+Version: 1.6.4
Release: 0
Summary: Google APIs Python Client
License: Apache-2.0
@@ -28,14 +29,14 @@
Url: https://github.com/google/google-api-python-client
Source:
https://files.pythonhosted.org/packages/source/g/google-api-python-client/google-api-python-client-%{version}.tar.gz
Patch0: hidePythonDeps.patch
-BuildRequires: fdupes
-BuildRequires: python-rpm-macros
BuildRequires: %{python_module devel}
-BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module httplib2 >= 0.9.2}
BuildRequires: %{python_module oauth2client >= 1.5.0}
+BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module six >= 1.6.1}
BuildRequires: %{python_module uritemplate >= 3.0.0}
+BuildRequires: fdupes
+BuildRequires: python-rpm-macros
%if %{with test}
# Test requirements
BuildRequires: %{python_module nose}
++++++ google-api-python-client-1.6.2.tar.gz ->
google-api-python-client-1.6.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.6.2/CHANGELOG
new/google-api-python-client-1.6.4/CHANGELOG
--- old/google-api-python-client-1.6.2/CHANGELOG 2017-02-08
19:21:11.000000000 +0100
+++ new/google-api-python-client-1.6.4/CHANGELOG 2017-09-20
17:54:26.000000000 +0200
@@ -1,3 +1,26 @@
+v1.6.4
+ Version 1.6.4
+
+ Bugfix release
+
+ - Warn when google-auth credentials are used but google-auth-httplib2 isn't
available. (#443)
+
+v1.6.3
+ Version 1.6.3
+
+ Bugfix release
+
+ - Add notification of maintenance mode to README. (#410)
+ - Fix generation of methods with abnormal page token conventions. (#338)
+ - Raise ValueError is credentials and developerKey are both specified. (#358)
+ - Re-generate documentation. (#364, #373, #401)
+ - Fix method signature documentation for multiline required parameters.
(#374)
+ - Fix ZeroDivisionError in MediaDownloadProgress.progress. (#377)
+ - Fix dead link to WebTest in README. (#378)
+ - Fix details missing in googleapiclient.errors.HttpError. (#412)
+ - Don't treat httplib2.Credentials as oauth credentials. (#425)
+ - Various fixes to the Django sample. (#413)
+
v1.6.2
Version 1.6.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.6.2/PKG-INFO
new/google-api-python-client-1.6.4/PKG-INFO
--- old/google-api-python-client-1.6.2/PKG-INFO 2017-02-08 19:23:28.000000000
+0100
+++ new/google-api-python-client-1.6.4/PKG-INFO 2017-09-20 17:57:53.000000000
+0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: google-api-python-client
-Version: 1.6.2
+Version: 1.6.4
Summary: Google API Client Library for Python
Home-page: http://github.com/google/google-api-python-client/
Author: Google Inc.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/google_api_python_client.egg-info/PKG-INFO
new/google-api-python-client-1.6.4/google_api_python_client.egg-info/PKG-INFO
---
old/google-api-python-client-1.6.2/google_api_python_client.egg-info/PKG-INFO
2017-02-08 19:23:28.000000000 +0100
+++
new/google-api-python-client-1.6.4/google_api_python_client.egg-info/PKG-INFO
2017-09-20 17:57:53.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: google-api-python-client
-Version: 1.6.2
+Version: 1.6.4
Summary: Google API Client Library for Python
Home-page: http://github.com/google/google-api-python-client/
Author: Google Inc.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/google_api_python_client.egg-info/requires.txt
new/google-api-python-client-1.6.4/google_api_python_client.egg-info/requires.txt
---
old/google-api-python-client-1.6.2/google_api_python_client.egg-info/requires.txt
2017-02-08 19:23:28.000000000 +0100
+++
new/google-api-python-client-1.6.4/google_api_python_client.egg-info/requires.txt
2017-09-20 17:57:53.000000000 +0200
@@ -1,4 +1,4 @@
-httplib2>=0.9.2,<1dev
-oauth2client>=1.5.0,<5.0.0dev
-six>=1.6.1,<2dev
-uritemplate>=3.0.0,<4dev
+httplib2<1dev,>=0.9.2
+oauth2client<5.0.0dev,>=1.5.0
+six<2dev,>=1.6.1
+uritemplate<4dev,>=3.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/__init__.py
new/google-api-python-client-1.6.4/googleapiclient/__init__.py
--- old/google-api-python-client-1.6.2/googleapiclient/__init__.py
2017-02-08 19:21:11.000000000 +0100
+++ new/google-api-python-client-1.6.4/googleapiclient/__init__.py
2017-09-20 17:54:26.000000000 +0200
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-__version__ = "1.6.2"
+__version__ = "1.6.4"
# Set default logging handler to avoid "No handler found" warnings.
import logging
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/_auth.py
new/google-api-python-client-1.6.4/googleapiclient/_auth.py
--- old/google-api-python-client-1.6.2/googleapiclient/_auth.py 2017-01-30
19:50:41.000000000 +0100
+++ new/google-api-python-client-1.6.4/googleapiclient/_auth.py 2017-09-20
03:41:21.000000000 +0200
@@ -14,23 +14,27 @@
"""Helpers for authentication using oauth2client or google-auth."""
+import httplib2
+
try:
import google.auth
import google.auth.credentials
- import google_auth_httplib2
HAS_GOOGLE_AUTH = True
except ImportError: # pragma: NO COVER
HAS_GOOGLE_AUTH = False
try:
+ import google_auth_httplib2
+except ImportError: # pragma: NO COVER
+ google_auth_httplib2 = None
+
+try:
import oauth2client
import oauth2client.client
HAS_OAUTH2CLIENT = True
except ImportError: # pragma: NO COVER
HAS_OAUTH2CLIENT = False
-from googleapiclient.http import build_http
-
def default_credentials():
"""Returns Application Default Credentials."""
@@ -84,9 +88,56 @@
Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
authorized http client.
"""
+ from googleapiclient.http import build_http
+
if HAS_GOOGLE_AUTH and isinstance(
credentials, google.auth.credentials.Credentials):
+ if google_auth_httplib2 is None:
+ raise ValueError(
+ 'Credentials from google.auth specified, but '
+ 'google-api-python-client is unable to use these credentials '
+ 'unless google-auth-httplib2 is installed. Please install '
+ 'google-auth-httplib2.')
return google_auth_httplib2.AuthorizedHttp(credentials,
http=build_http())
else:
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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/discovery.py
new/google-api-python-client-1.6.4/googleapiclient/discovery.py
--- old/google-api-python-client-1.6.2/googleapiclient/discovery.py
2017-02-08 19:14:31.000000000 +0100
+++ new/google-api-python-client-1.6.4/googleapiclient/discovery.py
2017-03-30 21:50:34.000000000 +0200
@@ -117,6 +117,7 @@
'type': 'string',
'required': False,
}
+_PAGE_TOKEN_NAMES = ('pageToken', 'nextPageToken')
# Parameters accepted by the stack, but not visible via discovery.
# TODO(dhermes): Remove 'userip' in 'v2'.
@@ -333,6 +334,10 @@
if http is not None and credentials is not None:
raise ValueError('Arguments http and credentials are mutually exclusive.')
+ if developerKey is not None and credentials is not None:
+ raise ValueError(
+ 'Arguments developerKey and credentials are mutually exclusive.')
+
if isinstance(service, six.string_types):
service = json.loads(service)
@@ -724,7 +729,11 @@
for name in parameters.required_params:
if name not in kwargs:
- raise TypeError('Missing required parameter "%s"' % name)
+ # temporary workaround for non-paging methods incorrectly requiring
+ # page token parameter (cf. drive.changes.watch vs. drive.changes.list)
+ if name not in _PAGE_TOKEN_NAMES or _findPageTokenName(
+ _methodProperties(methodDesc, schema, 'response')):
+ raise TypeError('Missing required parameter "%s"' % name)
for name, regex in six.iteritems(parameters.pattern_params):
if name in kwargs:
@@ -927,13 +936,20 @@
return (methodName, method)
-def createNextMethod(methodName):
+def createNextMethod(methodName,
+ pageTokenName='pageToken',
+ nextPageTokenName='nextPageToken',
+ isPageTokenParameter=True):
"""Creates any _next methods for attaching to a Resource.
The _next methods allow for easy iteration through list() responses.
Args:
methodName: string, name of the method to use.
+ pageTokenName: string, name of request page token field.
+ nextPageTokenName: string, name of response page token field.
+ isPageTokenParameter: Boolean, True if request page token is a query
+ parameter, False if request page token is a field of the request body.
"""
methodName = fix_method_name(methodName)
@@ -951,24 +967,24 @@
# Retrieve nextPageToken from previous_response
# Use as pageToken in previous_request to create new request.
- if 'nextPageToken' not in previous_response or not
previous_response['nextPageToken']:
+ nextPageToken = previous_response.get(nextPageTokenName, None)
+ if not nextPageToken:
return None
request = copy.copy(previous_request)
- pageToken = previous_response['nextPageToken']
- 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] = urlencode(newq)
- uri = urlunparse(parsed)
-
- request.uri = uri
-
- logger.info('URL being requested: %s %s' % (methodName,uri))
+ if isPageTokenParameter:
+ # Replace pageToken value in URI
+ request.uri = _add_query_parameter(
+ request.uri, pageTokenName, nextPageToken)
+ logger.info('Next page request URL: %s %s' % (methodName, request.uri))
+ else:
+ # Replace pageToken value in request body
+ model = self._model
+ body = model.deserialize(request.body)
+ body[pageTokenName] = nextPageToken
+ request.body = model.serialize(body)
+ logger.info('Next page request body: %s %s' % (methodName, body))
return request
@@ -1116,19 +1132,59 @@
method.__get__(self, self.__class__))
def _add_next_methods(self, resourceDesc, schema):
- # Add _next() methods
- # Look for response bodies in schema that contain nextPageToken, and
methods
- # that take a pageToken parameter.
- if 'methods' in resourceDesc:
- for methodName, methodDesc in six.iteritems(resourceDesc['methods']):
- if 'response' in methodDesc:
- responseSchema = methodDesc['response']
- if '$ref' in responseSchema:
- responseSchema = schema.get(responseSchema['$ref'])
- hasNextPageToken = 'nextPageToken' in
responseSchema.get('properties',
- {})
- hasPageToken = 'pageToken' in methodDesc.get('parameters', {})
- if hasNextPageToken and hasPageToken:
- fixedMethodName, method = createNextMethod(methodName + '_next')
- self._set_dynamic_attr(fixedMethodName,
- method.__get__(self, self.__class__))
+ # Add _next() methods if and only if one of the names 'pageToken' or
+ # 'nextPageToken' occurs among the fields of both the method's response
+ # type either the method's request (query parameters) or request body.
+ if 'methods' not in resourceDesc:
+ return
+ for methodName, methodDesc in six.iteritems(resourceDesc['methods']):
+ nextPageTokenName = _findPageTokenName(
+ _methodProperties(methodDesc, schema, 'response'))
+ if not nextPageTokenName:
+ continue
+ isPageTokenParameter = True
+ pageTokenName = _findPageTokenName(methodDesc.get('parameters', {}))
+ if not pageTokenName:
+ isPageTokenParameter = False
+ pageTokenName = _findPageTokenName(
+ _methodProperties(methodDesc, schema, 'request'))
+ if not pageTokenName:
+ continue
+ fixedMethodName, method = createNextMethod(
+ methodName + '_next', pageTokenName, nextPageTokenName,
+ isPageTokenParameter)
+ self._set_dynamic_attr(fixedMethodName,
+ method.__get__(self, self.__class__))
+
+
+def _findPageTokenName(fields):
+ """Search field names for one like a page token.
+
+ Args:
+ fields: container of string, names of fields.
+
+ Returns:
+ First name that is either 'pageToken' or 'nextPageToken' if one exists,
+ otherwise None.
+ """
+ return next((tokenName for tokenName in _PAGE_TOKEN_NAMES
+ if tokenName in fields), None)
+
+def _methodProperties(methodDesc, schema, name):
+ """Get properties of a field in a method description.
+
+ Args:
+ methodDesc: object, fragment of deserialized discovery document that
+ describes the method.
+ schema: object, mapping of schema names to schema descriptions.
+ name: string, name of top-level field in method description.
+
+ Returns:
+ Object representing fragment of deserialized discovery document
+ corresponding to 'properties' field of object corresponding to named field
+ in method description, if it exists, otherwise empty dict.
+ """
+ desc = methodDesc.get(name, {})
+ if '$ref' in desc:
+ desc = schema.get(desc['$ref'], {})
+ return desc.get('properties', {})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/errors.py
new/google-api-python-client-1.6.4/googleapiclient/errors.py
--- old/google-api-python-client-1.6.2/googleapiclient/errors.py
2016-12-20 21:10:49.000000000 +0100
+++ new/google-api-python-client-1.6.4/googleapiclient/errors.py
2017-08-02 21:36:39.000000000 +0200
@@ -46,6 +46,7 @@
raise TypeError("HTTP content should be bytes")
self.content = content
self.uri = uri
+ self.error_details = ''
def _get_reason(self):
"""Calculate the reason for the error from the response content."""
@@ -54,9 +55,13 @@
data = json.loads(self.content.decode('utf-8'))
if isinstance(data, dict):
reason = data['error']['message']
+ if 'details' in data['error']:
+ self.error_details = data['error']['details']
elif isinstance(data, list) and len(data) > 0:
first_error = data[0]
reason = first_error['error']['message']
+ if 'details' in first_error['error']:
+ self.error_details = first_error['error']['details']
except (ValueError, KeyError, TypeError):
pass
if reason is None:
@@ -64,7 +69,11 @@
return reason
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">' % (
self.resp.status, self.uri, self._get_reason().strip())
else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/http.py
new/google-api-python-client-1.6.4/googleapiclient/http.py
--- old/google-api-python-client-1.6.2/googleapiclient/http.py 2017-01-30
19:50:41.000000000 +0100
+++ new/google-api-python-client-1.6.4/googleapiclient/http.py 2017-08-03
17:55:12.000000000 +0200
@@ -62,6 +62,7 @@
except ImportError:
from oauth2client import _helpers as util
+from googleapiclient import _auth
from googleapiclient import mimeparse
from googleapiclient.errors import BatchError
from googleapiclient.errors import HttpError
@@ -203,7 +204,7 @@
the percentage complete as a float, returning 0.0 if the total size of
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)
else:
return 0.0
@@ -229,7 +230,7 @@
the percentage complete as a float, returning 0.0 if the total size of
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)
else:
return 0.0
@@ -817,6 +818,7 @@
if 'content-length' not in self.headers:
self.headers['content-length'] = str(self.body_size)
# If the request URI is too long then turn it into a POST request.
+ # Assume that a GET request never contains a request body.
if len(self.uri) > MAX_URI_LENGTH and self.method == 'GET':
self.method = 'POST'
self.headers['x-http-method-override'] = 'GET'
@@ -1125,21 +1127,25 @@
# If there is no http per the request then refresh the http passed in
# via execute()
creds = None
- if request.http is not None and hasattr(request.http.request,
- 'credentials'):
- creds = request.http.request.credentials
- elif http is not None and hasattr(http.request, 'credentials'):
- creds = http.request.credentials
+ request_credentials = False
+
+ if request.http is not None:
+ creds = _auth.get_credentials_from_http(request.http)
+ 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 id(creds) not in self._refreshed_credentials:
- creds.refresh(http)
+ _auth.refresh_credentials(creds)
self._refreshed_credentials[id(creds)] = 1
# Only apply the credentials if we are using the http object passed in,
# otherwise apply() will get called during _serialize_request().
- if request.http is None or not hasattr(request.http.request,
- 'credentials'):
- creds.apply(request.headers)
+ if request.http is None or not request_credentials:
+ _auth.apply_credentials(creds, request.headers)
+
def _id_to_header(self, id_):
"""Convert an id to a Content-ID header value.
@@ -1199,9 +1205,10 @@
msg = MIMENonMultipart(major, minor)
headers = request.headers.copy()
- if request.http is not None and hasattr(request.http.request,
- 'credentials'):
- request.http.request.credentials.apply(headers)
+ if request.http is not None:
+ credentials = _auth.get_credentials_from_http(request.http)
+ if credentials is not None:
+ _auth.apply_credentials(credentials, headers)
# MIMENonMultipart adds its own Content-Type header.
if 'content-type' in headers:
@@ -1408,11 +1415,11 @@
# Special case for OAuth2Credentials-style objects which have not yet been
# refreshed with an initial access_token.
- if getattr(http.request, 'credentials', None) is not None:
- creds = http.request.credentials
- if not getattr(creds, 'access_token', None):
+ creds = _auth.get_credentials_from_http(http)
+ if creds is not None:
+ if not _auth.is_valid(creds):
LOGGER.info('Attempting refresh to obtain initial access_token')
- creds.refresh(http)
+ _auth.refresh_credentials(creds)
self._execute(http, self._order, self._requests)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.6.2/googleapiclient/schema.py
new/google-api-python-client-1.6.4/googleapiclient/schema.py
--- old/google-api-python-client-1.6.2/googleapiclient/schema.py
2016-08-15 22:35:23.000000000 +0200
+++ new/google-api-python-client-1.6.4/googleapiclient/schema.py
2017-03-30 21:50:34.000000000 +0200
@@ -161,13 +161,14 @@
# Return with trailing comma and newline removed.
return self._prettyPrintSchema(schema, dent=1)[:-2]
- def get(self, name):
+ def get(self, name, default=None):
"""Get deserialized JSON schema from the schema name.
Args:
name: string, Schema name.
+ default: object, return value if name not found.
"""
- return self.schemas[name]
+ return self.schemas.get(name, default)
class _SchemaToStruct(object):