Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-google-api-python-client for
openSUSE:Factory checked in at 2021-07-07 18:30:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-api-python-client (Old)
and /work/SRC/openSUSE:Factory/.python-google-api-python-client.new.2625
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-api-python-client"
Wed Jul 7 18:30:25 2021 rev:17 rq:904372 version:1.12.8
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-google-api-python-client/python-google-api-python-client.changes
2020-10-15 13:49:23.453274035 +0200
+++
/work/SRC/openSUSE:Factory/.python-google-api-python-client.new.2625/python-google-api-python-client.changes
2021-07-07 18:31:33.910479864 +0200
@@ -1,0 +2,23 @@
+Mon Jul 5 19:41:14 UTC 2021 - Martin Wilck <[email protected]>
+
+- Update to version 1.12.8
+ * add httplib2 authorization to thread_safety (#1005), closes #808
+- from version 1.12.7
+ * Update Webmasters API sample (#1092)
+- from version 1.12.6
+ * Dcoumentation fixes
+- from version 1.12.5
+ * don't raise when downloading zero byte files (#1074)
+- from version 1.12.4
+ * don't set content-range on empty uploads (#1070)
+- from version 1.12.3
+ * deps: update setup.py to install httplib2>=0.15.0 (#1050)
+- from version 1.12.2
+ * add method to close httplib2 connections (#1038), closes #618
+- from version 1.12.1
+ * deps: require six>=1.13.0 (#1030)
+- from version 1.12.0
+ * convert print statement to function (#988), closes #987
+ * remove http from batch execute docs (#1003), closes #1002
+
+-------------------------------------------------------------------
Old:
----
google-api-python-client-1.11.0.tar.gz
New:
----
google-api-python-client-1.12.8.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-google-api-python-client.spec ++++++
--- /var/tmp/diff_new_pack.IMBE6k/_old 2021-07-07 18:31:34.282476945 +0200
+++ /var/tmp/diff_new_pack.IMBE6k/_new 2021-07-07 18:31:34.286476913 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-google-api-python-client
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-google-api-python-client
-Version: 1.11.0
+Version: 1.12.8
Release: 0
Summary: Google APIs Python Client
License: Apache-2.0
@@ -27,24 +27,24 @@
Source:
https://files.pythonhosted.org/packages/source/g/google-api-python-client/google-api-python-client-%{version}.tar.gz
# https://github.com/googleapis/google-api-python-client/pull/929
Patch0: python-google-api-python-client-no-unittest2.patch
-BuildRequires: %{python_module google-api-core >= 1.18.0}
+BuildRequires: %{python_module google-api-core >= 1.21.0}
BuildRequires: %{python_module google-auth >= 1.16.0}
BuildRequires: %{python_module google-auth-httplib2 >= 0.0.3}
-BuildRequires: %{python_module httplib2 >= 0.9.2}
+BuildRequires: %{python_module httplib2 >= 0.15.0}
BuildRequires: %{python_module mock}
BuildRequires: %{python_module oauth2client}
BuildRequires: %{python_module parameterized}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
-BuildRequires: %{python_module six >= 1.6.1}
+BuildRequires: %{python_module six >= 1.13.0}
BuildRequires: %{python_module uritemplate >= 3.0.0}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-google-api-core >= 1.18.0
+Requires: python-google-api-core >= 1.21.0
Requires: python-google-auth >= 1.16.0
Requires: python-google-auth-httplib2 >= 0.0.3
-Requires: python-httplib2 >= 0.9.2
-Requires: python-six >= 1.6.1
+Requires: python-httplib2 >= 0.15.0
+Requires: python-six >= 1.13.0
Requires: python-uritemplate >= 3.0.0
# Package renamed in SLE 12, do not remove Provides, Obsolete directives
# until after SLE 12 EOL
@@ -70,7 +70,8 @@
%check
# DiscoveryFromDocument::test_api_endpoint_override_from_client_options and
# DiscoveryFromDocument::test_api_endpoint_override_from_client_options_dict
fail with "server unavailable"
-%pytest -k "not (test_api_endpoint_override_from_client_options and Document)"
+# DiscoveryErrors::test_credentials_and_credentials_file_mutually_exclusive
fails with "socket.gaierror: [Errno -3] Temporary failure in name resolution"
+%pytest -k "not (test_api_endpoint_override_from_client_options and Document)
and not test_credentials_and_credentials_file_mutually_exclusive"
%files %{python_files}
%doc README.md
++++++ google-api-python-client-1.11.0.tar.gz ->
google-api-python-client-1.12.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.11.0/PKG-INFO
new/google-api-python-client-1.12.8/PKG-INFO
--- old/google-api-python-client-1.11.0/PKG-INFO 2020-08-27
23:35:31.385575800 +0200
+++ new/google-api-python-client-1.12.8/PKG-INFO 2020-11-18
18:36:23.823939000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-api-python-client
-Version: 1.11.0
+Version: 1.12.8
Summary: Google API Client Library for Python
Home-page: https://github.com/googleapis/google-api-python-client/
Author: Google LLC
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/google_api_python_client.egg-info/PKG-INFO
new/google-api-python-client-1.12.8/google_api_python_client.egg-info/PKG-INFO
---
old/google-api-python-client-1.11.0/google_api_python_client.egg-info/PKG-INFO
2020-08-27 23:35:31.000000000 +0200
+++
new/google-api-python-client-1.12.8/google_api_python_client.egg-info/PKG-INFO
2020-11-18 18:36:23.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-api-python-client
-Version: 1.11.0
+Version: 1.12.8
Summary: Google API Client Library for Python
Home-page: https://github.com/googleapis/google-api-python-client/
Author: Google LLC
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/google_api_python_client.egg-info/requires.txt
new/google-api-python-client-1.12.8/google_api_python_client.egg-info/requires.txt
---
old/google-api-python-client-1.11.0/google_api_python_client.egg-info/requires.txt
2020-08-27 23:35:31.000000000 +0200
+++
new/google-api-python-client-1.12.8/google_api_python_client.egg-info/requires.txt
2020-11-18 18:36:23.000000000 +0100
@@ -1,6 +1,6 @@
-httplib2<1dev,>=0.9.2
+httplib2<1dev,>=0.15.0
google-auth>=1.16.0
google-auth-httplib2>=0.0.3
-google-api-core<2dev,>=1.18.0
-six<2dev,>=1.6.1
+google-api-core<2dev,>=1.21.0
+six<2dev,>=1.13.0
uritemplate<4dev,>=3.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/googleapiclient/_auth.py
new/google-api-python-client-1.12.8/googleapiclient/_auth.py
--- old/google-api-python-client-1.11.0/googleapiclient/_auth.py
2020-08-27 23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/googleapiclient/_auth.py
2020-11-18 18:33:45.000000000 +0100
@@ -38,12 +38,27 @@
HAS_OAUTH2CLIENT = False
-def default_credentials():
+def credentials_from_file(filename, scopes=None, quota_project_id=None):
+ """Returns credentials loaded from a file."""
+ if HAS_GOOGLE_AUTH:
+ credentials, _ = google.auth.load_credentials_from_file(filename,
scopes=scopes, quota_project_id=quota_project_id)
+ return credentials
+ else:
+ raise EnvironmentError(
+ "client_options.credentials_file is only supported in google-auth.")
+
+
+def default_credentials(scopes=None, quota_project_id=None):
"""Returns Application Default Credentials."""
if HAS_GOOGLE_AUTH:
- credentials, _ = google.auth.default()
+ credentials, _ = google.auth.default(scopes=scopes,
quota_project_id=quota_project_id)
return credentials
elif HAS_OAUTH2CLIENT:
+ if scopes is not None or quota_project_id is not None:
+ raise EnvironmentError(
+ "client_options.scopes and client_options.quota_project_id are
not supported in oauth2client."
+ "Please install google-auth."
+ )
return oauth2client.client.GoogleCredentials.get_application_default()
else:
raise EnvironmentError(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/googleapiclient/discovery.py
new/google-api-python-client-1.12.8/googleapiclient/discovery.py
--- old/google-api-python-client-1.11.0/googleapiclient/discovery.py
2020-08-27 23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/googleapiclient/discovery.py
2020-11-18 18:33:45.000000000 +0100
@@ -30,6 +30,7 @@
# Standard library imports
import copy
from collections import OrderedDict
+
try:
from email.generator import BytesGenerator
except ImportError:
@@ -260,16 +261,21 @@
else:
discovery_http = http
- for discovery_url in \
- _discovery_service_uri_options(discoveryServiceUrl, version):
+ service = None
+
+ for discovery_url in _discovery_service_uri_options(discoveryServiceUrl,
version):
requested_url = uritemplate.expand(discovery_url, params)
try:
content = _retrieve_discovery_doc(
- requested_url, discovery_http, cache_discovery, cache,
- developerKey, num_retries=num_retries
+ requested_url,
+ discovery_http,
+ cache_discovery,
+ cache,
+ developerKey,
+ num_retries=num_retries,
)
- return build_from_document(
+ service = build_from_document(
content,
base=discovery_url,
http=http,
@@ -281,13 +287,22 @@
adc_cert_path=adc_cert_path,
adc_key_path=adc_key_path,
)
+ break # exit if a service was created
except HttpError as e:
if e.resp.status == http_client.NOT_FOUND:
continue
else:
raise e
- raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName,
version))
+ # If discovery_http was created by this function, we are done with it
+ # and can safely close it
+ if http is None:
+ discovery_http.close()
+
+ if service is None:
+ raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName,
version))
+ else:
+ return service
def _discovery_service_uri_options(discoveryServiceUrl, version):
@@ -308,13 +323,15 @@
# V1 Discovery won't work if the requested version is None
if discoveryServiceUrl == V1_DISCOVERY_URI and version is None:
logger.warning(
- "Discovery V1 does not support empty versions. Defaulting to
V2...")
+ "Discovery V1 does not support empty versions. Defaulting to V2..."
+ )
urls.pop(0)
return list(OrderedDict.fromkeys(urls))
-def _retrieve_discovery_doc(url, http, cache_discovery,
- cache=None, developerKey=None, num_retries=1):
+def _retrieve_discovery_doc(
+ url, http, cache_discovery, cache=None, developerKey=None, num_retries=1
+):
"""Retrieves the discovery_doc from cache or the internet.
Args:
@@ -444,8 +461,20 @@
setting up mutual TLS channel.
"""
- if http is not None and credentials is not None:
- raise ValueError("Arguments http and credentials are mutually
exclusive.")
+ if client_options is None:
+ client_options = google.api_core.client_options.ClientOptions()
+ if isinstance(client_options, six.moves.collections_abc.Mapping):
+ client_options =
google.api_core.client_options.from_dict(client_options)
+
+ if http is not None:
+ # if http is passed, the user cannot provide credentials
+ banned_options = [
+ (credentials, "credentials"),
+ (client_options.credentials_file,
"client_options.credentials_file"),
+ ]
+ for option, name in banned_options:
+ if option is not None:
+ raise ValueError("Arguments http and {} are mutually
exclusive".format(name))
if isinstance(service, six.string_types):
service = json.loads(service)
@@ -463,11 +492,8 @@
# If an API Endpoint is provided on client options, use that as the base
URL
base = urljoin(service["rootUrl"], service["servicePath"])
- if client_options:
- if isinstance(client_options, six.moves.collections_abc.Mapping):
- client_options =
google.api_core.client_options.from_dict(client_options)
- if client_options.api_endpoint:
- base = client_options.api_endpoint
+ if client_options.api_endpoint:
+ base = client_options.api_endpoint
schema = Schemas(service)
@@ -483,13 +509,30 @@
# If so, then the we need to setup authentication if no developerKey is
# specified.
if scopes and not developerKey:
+ # Make sure the user didn't pass multiple credentials
+ if client_options.credentials_file and credentials:
+ raise google.api_core.exceptions.DuplicateCredentialArgs(
+ "client_options.credentials_file and credentials are
mutually exclusive."
+ )
+ # Check for credentials file via client options
+ if client_options.credentials_file:
+ credentials = _auth.credentials_from_file(
+ client_options.credentials_file,
+ scopes=client_options.scopes,
+ quota_project_id=client_options.quota_project_id,
+ )
# If the user didn't pass in credentials, attempt to acquire
application
# default credentials.
if credentials is None:
- credentials = _auth.default_credentials()
+ credentials = _auth.default_credentials(
+ scopes=client_options.scopes,
+ quota_project_id=client_options.quota_project_id,
+ )
# The credentials need to be scoped.
- credentials = _auth.with_scopes(credentials, scopes)
+ # If the user provided scopes via client_options don't override
them
+ if not client_options.scopes:
+ credentials = _auth.with_scopes(credentials, scopes)
# If credentials are provided, create an authorized http instance;
# otherwise, skip authentication.
@@ -519,7 +562,9 @@
and client_options.client_encrypted_cert_source
):
client_cert_to_use =
client_options.client_encrypted_cert_source
- elif adc_cert_path and adc_key_path and
mtls.has_default_client_cert_source():
+ elif (
+ adc_cert_path and adc_key_path and
mtls.has_default_client_cert_source()
+ ):
client_cert_to_use = mtls.default_client_encrypted_cert_source(
adc_cert_path, adc_key_path
)
@@ -1275,6 +1320,20 @@
self._dynamic_attrs = []
self._set_service_methods()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc, exc_tb):
+ self.close()
+
+ def close(self):
+ """Close httplib2 connections."""
+ # httplib2 leaves sockets open by default.
+ # Cleanup using the `close` method.
+ # https://github.com/httplib2/httplib2/issues/148
+ self._http.http.close()
+
def _set_service_methods(self):
self._add_basic_methods(self._resourceDesc, self._rootDesc,
self._schema)
self._add_nested_resources(self._resourceDesc, self._rootDesc,
self._schema)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/googleapiclient/errors.py
new/google-api-python-client-1.12.8/googleapiclient/errors.py
--- old/google-api-python-client-1.11.0/googleapiclient/errors.py
2020-08-27 23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/googleapiclient/errors.py
2020-11-18 18:33:45.000000000 +0100
@@ -51,10 +51,9 @@
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 "detail" in data["error"]:
- self.error_details = data["error"]["detail"]
+ error_detail_keyword = next((kw for kw in ["detail",
"details", "message"] if kw in data["error"]), "")
+ if error_detail_keyword:
+ self.error_details = data["error"][error_detail_keyword]
elif isinstance(data, list) and len(data) > 0:
first_error = data[0]
reason = first_error["error"]["message"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/googleapiclient/http.py
new/google-api-python-client-1.12.8/googleapiclient/http.py
--- old/google-api-python-client-1.11.0/googleapiclient/http.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/googleapiclient/http.py 2020-11-18
18:33:45.000000000 +0100
@@ -745,8 +745,16 @@
if self._total_size is None or self._progress == self._total_size:
self._done = True
return MediaDownloadProgress(self._progress, self._total_size),
self._done
- else:
- raise HttpError(resp, content, uri=self._uri)
+ elif resp.status == 416:
+ # 416 is Range Not Satisfiable
+ # This typically occurs with a zero byte file
+ content_range = resp["content-range"]
+ length = content_range.rsplit("/", 1)[1]
+ self._total_size = int(length)
+ if self._total_size == 0:
+ self._done = True
+ return MediaDownloadProgress(self._progress,
self._total_size), self._done
+ raise HttpError(resp, content, uri=self._uri)
class _StreamSlice(object):
@@ -1026,13 +1034,17 @@
chunk_end = self.resumable_progress + len(data) - 1
headers = {
- "Content-Range": "bytes %d-%d/%s"
- % (self.resumable_progress, chunk_end, size),
# Must set the content-length header here because httplib can't
# calculate the size when working with _StreamSlice.
"Content-Length": str(chunk_end - self.resumable_progress + 1),
}
+ # An empty file results in chunk_end = -1 and size = 0
+ # sending "bytes 0--1/0" results in an invalid request
+ # Only add header "Content-Range" if chunk_end != -1
+ if chunk_end != -1:
+ headers["Content-Range"] = "bytes %d-%d/%s" %
(self.resumable_progress, chunk_end, size)
+
for retry_num in range(num_retries + 1):
if retry_num > 0:
self._sleep(self._rand() * 2 ** retry_num)
@@ -1720,6 +1732,8 @@
self.headers = headers
return httplib2.Response(self.response_headers), self.data
+ def close(self):
+ return None
class HttpMockSequence(object):
"""Mock of httplib2.Http
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.11.0/setup.py
new/google-api-python-client-1.12.8/setup.py
--- old/google-api-python-client-1.11.0/setup.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/setup.py 2020-11-18
18:33:45.000000000 +0100
@@ -35,14 +35,11 @@
packages = ["apiclient", "googleapiclient", "googleapiclient/discovery_cache"]
install_requires = [
- # NOTE: Apache Beam tests depend on this library and cannot
- # currently upgrade their httplib2 version.
- # Please see
https://github.com/googleapis/google-api-python-client/pull/841
- "httplib2>=0.9.2,<1dev",
+ "httplib2>=0.15.0,<1dev",
"google-auth>=1.16.0",
"google-auth-httplib2>=0.0.3",
- "google-api-core>=1.18.0,<2dev",
- "six>=1.6.1,<2dev",
+ "google-api-core>=1.21.0,<2dev",
+ "six>=1.13.0,<2dev",
"uritemplate>=3.0.0,<4dev",
]
@@ -52,7 +49,7 @@
with io.open(readme_filename, encoding="utf-8") as readme_file:
readme = readme_file.read()
-version = "1.11.0"
+version = "1.12.8"
setup(
name="google-api-python-client",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.11.0/tests/test__auth.py
new/google-api-python-client-1.12.8/tests/test__auth.py
--- old/google-api-python-client-1.11.0/tests/test__auth.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/tests/test__auth.py 2020-11-18
18:33:45.000000000 +0100
@@ -40,6 +40,35 @@
self.assertEqual(credentials, mock.sentinel.credentials)
+ def test_credentials_from_file(self):
+ with mock.patch(
+ "google.auth.load_credentials_from_file", autospec=True
+ ) as default:
+ default.return_value = (mock.sentinel.credentials,
mock.sentinel.project)
+
+ credentials = _auth.credentials_from_file("credentials.json")
+
+ self.assertEqual(credentials, mock.sentinel.credentials)
+ default.assert_called_once_with(
+ "credentials.json", scopes=None, quota_project_id=None
+ )
+
+ def test_default_credentials_with_scopes(self):
+ with mock.patch("google.auth.default", autospec=True) as default:
+ default.return_value = (mock.sentinel.credentials,
mock.sentinel.project)
+ credentials = _auth.default_credentials(scopes=["1", "2"])
+
+ default.assert_called_once_with(scopes=["1", "2"],
quota_project_id=None)
+ self.assertEqual(credentials, mock.sentinel.credentials)
+
+ def test_default_credentials_with_quota_project(self):
+ with mock.patch("google.auth.default", autospec=True) as default:
+ default.return_value = (mock.sentinel.credentials,
mock.sentinel.project)
+ credentials =
_auth.default_credentials(quota_project_id="my-project")
+
+ default.assert_called_once_with(scopes=None,
quota_project_id="my-project")
+ self.assertEqual(credentials, mock.sentinel.credentials)
+
def test_with_scopes_non_scoped(self):
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
@@ -95,6 +124,16 @@
self.assertEqual(credentials, mock.sentinel.credentials)
+ def test_credentials_from_file(self):
+ with self.assertRaises(EnvironmentError):
+ credentials = _auth.credentials_from_file("credentials.json")
+
+ def test_default_credentials_with_scopes_and_quota_project(self):
+ with self.assertRaises(EnvironmentError):
+ credentials = _auth.default_credentials(
+ scopes=["1", "2"], quota_project_id="my-project"
+ )
+
def test_with_scopes_non_scoped(self):
credentials = mock.Mock(spec=oauth2client.client.Credentials)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-api-python-client-1.11.0/tests/test_discovery.py
new/google-api-python-client-1.12.8/tests/test_discovery.py
--- old/google-api-python-client-1.11.0/tests/test_discovery.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/tests/test_discovery.py 2020-11-18
18:33:45.000000000 +0100
@@ -47,6 +47,8 @@
from google.auth.transport import mtls
from google.auth.exceptions import MutualTLSChannelError
import google_auth_httplib2
+import google.api_core.exceptions
+
from googleapiclient.discovery import _fix_up_media_upload
from googleapiclient.discovery import _fix_up_method_description
from googleapiclient.discovery import _fix_up_parameters
@@ -118,23 +120,21 @@
assertUrisEqual(testcase, expanded_requested_uri, actual)
-def validate_discovery_requests(testcase, http_mock, service_name,
- version, discovery):
+def validate_discovery_requests(testcase, http_mock, service_name, version,
discovery):
"""Validates that there have > 0 calls to Http Discovery
and that LAST discovery URI used was the one that was expected
for a given service and version."""
testcase.assertTrue(len(http_mock.request_sequence) > 0)
if len(http_mock.request_sequence) > 0:
actual_uri = http_mock.request_sequence[-1][0]
- assert_discovery_uri(testcase,
- actual_uri, service_name, version, discovery)
+ assert_discovery_uri(testcase, actual_uri, service_name, version,
discovery)
def datafile(filename):
return os.path.join(DATA_DIR, filename)
-def read_datafile(filename, mode='r'):
+def read_datafile(filename, mode="r"):
with open(datafile(filename), mode=mode) as f:
return f.read()
@@ -437,6 +437,13 @@
self.assertEqual(parameters.enum_params, {})
+class Discovery(unittest.TestCase):
+ def test_discovery_http_is_closed(self):
+ http = HttpMock(datafile("malformed.json"), {"status": "200"})
+ service = build("plus", "v1", credentials=mock.sentinel.credentials)
+ http.close.assert_called_once()
+
+
class DiscoveryErrors(unittest.TestCase):
def test_tests_should_be_run_with_strict_positional_enforcement(self):
try:
@@ -468,6 +475,29 @@
with self.assertRaises(ValueError):
build("plus", "v1", http=http,
credentials=mock.sentinel.credentials)
+ def test_credentials_file_and_http_mutually_exclusive(self):
+ http = HttpMock(datafile("plus.json"), {"status": "200"})
+ with self.assertRaises(ValueError):
+ build(
+ "plus",
+ "v1",
+ http=http,
+ client_options=google.api_core.client_options.ClientOptions(
+ credentials_file="credentials.json"
+ ),
+ )
+
+ def test_credentials_and_credentials_file_mutually_exclusive(self):
+ with
self.assertRaises(google.api_core.exceptions.DuplicateCredentialArgs):
+ build(
+ "plus",
+ "v1",
+ credentials=mock.sentinel.credentials,
+ client_options=google.api_core.client_options.ClientOptions(
+ credentials_file="credentials.json"
+ ),
+ )
+
class DiscoveryFromDocument(unittest.TestCase):
MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
@@ -549,6 +579,25 @@
# application default credentials were used.
self.assertNotIsInstance(plus._http,
google_auth_httplib2.AuthorizedHttp)
+ def test_building_with_context_manager(self):
+ discovery = read_datafile("plus.json")
+ with mock.patch("httplib2.Http") as http:
+ with build_from_document(discovery,
base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus:
+ self.assertIsNotNone(plus)
+ self.assertTrue(hasattr(plus, "activities"))
+ plus._http.http.close.assert_called_once()
+
+ def test_resource_close(self):
+ discovery = read_datafile("plus.json")
+ with mock.patch("httplib2.Http") as http:
+ plus = build_from_document(
+ discovery,
+ base="https://www.googleapis.com/",
+ credentials=self.MOCK_CREDENTIALS,
+ )
+ plus.close()
+ plus._http.http.close.assert_called_once()
+
def test_api_endpoint_override_from_client_options(self):
discovery = read_datafile("plus.json")
api_endpoint = "https://foo.googleapis.com/"
@@ -566,10 +615,8 @@
discovery = read_datafile("plus.json")
api_endpoint = "https://foo.googleapis.com/"
mapping_object = defaultdict(str)
- mapping_object['api_endpoint'] = api_endpoint
- plus = build_from_document(
- discovery, client_options=mapping_object
- )
+ mapping_object["api_endpoint"] = api_endpoint
+ plus = build_from_document(discovery, client_options=mapping_object)
self.assertEqual(plus._baseUrl, api_endpoint)
@@ -584,6 +631,44 @@
self.assertEqual(plus._baseUrl, api_endpoint)
+ def test_scopes_from_client_options(self):
+ discovery = read_datafile("plus.json")
+
+ with mock.patch("googleapiclient._auth.default_credentials") as
default:
+ plus = build_from_document(
+ discovery, client_options={"scopes": ["1", "2"]},
+ )
+
+ default.assert_called_once_with(scopes=["1", "2"],
quota_project_id=None)
+
+ def test_quota_project_from_client_options(self):
+ discovery = read_datafile("plus.json")
+
+ with mock.patch("googleapiclient._auth.default_credentials") as
default:
+ plus = build_from_document(
+ discovery,
+ client_options=google.api_core.client_options.ClientOptions(
+ quota_project_id="my-project"
+ ),
+ )
+
+ default.assert_called_once_with(scopes=None,
quota_project_id="my-project")
+
+ def test_credentials_file_from_client_options(self):
+ discovery = read_datafile("plus.json")
+
+ with mock.patch("googleapiclient._auth.credentials_from_file") as
default:
+ plus = build_from_document(
+ discovery,
+ client_options=google.api_core.client_options.ClientOptions(
+ credentials_file="credentials.json"
+ ),
+ )
+
+ default.assert_called_once_with(
+ "credentials.json", scopes=None, quota_project_id=None
+ )
+
REGULAR_ENDPOINT = "https://www.googleapis.com/plus/v1/"
MTLS_ENDPOINT = "https://www.mtls.googleapis.com/plus/v1/"
@@ -912,33 +997,24 @@
self.assertEqual(zoo._baseUrl, api_endpoint)
def test_discovery_with_empty_version_uses_v2(self):
- http = HttpMockSequence(
- [
- ({"status": "200"}, read_datafile("zoo.json", "rb")),
- ]
- )
+ http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json",
"rb")),])
build("zoo", version=None, http=http, cache_discovery=False)
validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI)
def test_discovery_with_empty_version_preserves_custom_uri(self):
- http = HttpMockSequence(
- [
- ({"status": "200"}, read_datafile("zoo.json", "rb")),
- ]
- )
+ http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json",
"rb")),])
custom_discovery_uri = "https://foo.bar/$discovery"
build(
- "zoo", version=None, http=http,
- cache_discovery=False, discoveryServiceUrl=custom_discovery_uri)
- validate_discovery_requests(
- self, http, "zoo", None, custom_discovery_uri)
+ "zoo",
+ version=None,
+ http=http,
+ cache_discovery=False,
+ discoveryServiceUrl=custom_discovery_uri,
+ )
+ validate_discovery_requests(self, http, "zoo", None,
custom_discovery_uri)
def test_discovery_with_valid_version_uses_v1(self):
- http = HttpMockSequence(
- [
- ({"status": "200"}, read_datafile("zoo.json", "rb")),
- ]
- )
+ http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json",
"rb")),])
build("zoo", version="v123", http=http, cache_discovery=False)
validate_discovery_requests(self, http, "zoo", "v123",
V1_DISCOVERY_URI)
@@ -1255,7 +1331,7 @@
def test_batch_request_from_default(self):
self.http = HttpMock(datafile("plus.json"), {"status": "200"})
# plus does not define a batchPath
- plus = build("plus", "v1", http=self.http)
+ plus = build("plus", "v1", http=self.http, cache_discovery=False)
batch_request = plus.new_batch_http_request()
self.assertEqual(batch_request._batch_uri,
"https://www.googleapis.com/batch")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.11.0/tests/test_errors.py
new/google-api-python-client-1.12.8/tests/test_errors.py
--- old/google-api-python-client-1.11.0/tests/test_errors.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/tests/test_errors.py 2020-11-18
18:33:45.000000000 +0100
@@ -47,6 +47,24 @@
}
"""
+JSON_ERROR_CONTENT_NO_DETAIL = b"""
+{
+ "error": {
+ "errors": [
+ {
+ "domain": "global",
+ "reason": "required",
+ "message": "country is required",
+ "locationType": "parameter",
+ "location": "country"
+ }
+ ],
+ "code": 400,
+ "message": "country is required"
+ }
+}
+"""
+
def fake_response(data, headers, reason="Ok"):
response = httplib2.Response(headers)
@@ -112,3 +130,14 @@
resp, content = fake_response(b"}NOT OK", {"status": "400"},
reason=None)
error = HttpError(resp, content)
self.assertEqual(str(error), '<HttpError 400 "">')
+
+ def test_error_detail_for_missing_message_in_error(self):
+ """Test handling of data with missing 'details' or 'detail' element."""
+ resp, content = fake_response(
+ JSON_ERROR_CONTENT_NO_DETAIL,
+ {"status": "400", "content-type": "application/json"},
+ reason="Failed",
+ )
+ error = HttpError(resp, content)
+ self.assertEqual(str(error), '<HttpError 400 when requesting None
returned "country is required". Details: "country is required">')
+ self.assertEqual(error.error_details, 'country is required')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-api-python-client-1.11.0/tests/test_http.py
new/google-api-python-client-1.12.8/tests/test_http.py
--- old/google-api-python-client-1.11.0/tests/test_http.py 2020-08-27
23:33:51.000000000 +0200
+++ new/google-api-python-client-1.12.8/tests/test_http.py 2020-11-18
18:33:45.000000000 +0100
@@ -443,6 +443,33 @@
request._sleep.assert_not_called()
+ def test_media_io_base_empty_file(self):
+ fd = BytesIO()
+ upload = MediaIoBaseUpload(
+ fd=fd, mimetype="image/png", chunksize=500, resumable=True
+ )
+
+ http = HttpMockSequence(
+ [
+ ({"status": "200", "location":
"https://www.googleapis.com/someapi/v1/upload?foo=bar"}, "{}"),
+ ({"status": "200", "location":
"https://www.googleapis.com/someapi/v1/upload?foo=bar"}, "{}")
+ ]
+ )
+
+ model = JsonModel()
+ uri = u"https://www.googleapis.com/someapi/v1/upload/?foo=bar"
+ method = u"POST"
+ request = HttpRequest(
+ http, model.response, uri, method=method, headers={},
resumable=upload
+ )
+
+ request.execute()
+
+ # Check that "Content-Range" header is not set in the PUT request
+ self.assertTrue("Content-Range" not in http.request_sequence[-1][-1])
+ self.assertEqual("0", http.request_sequence[-1][-1]["Content-Length"])
+
+
class TestMediaIoBaseDownload(unittest.TestCase):
def setUp(self):
http = HttpMock(datafile("zoo.json"), {"status": "200"})
@@ -645,6 +672,26 @@
self.assertEqual(0, download._total_size)
self.assertEqual(0, status.progress())
+ def test_media_io_base_download_empty_file_416_response(self):
+ self.request.http = HttpMockSequence(
+ [({"status": "416", "content-range": "0-0/0"}, b"")]
+ )
+
+ download = MediaIoBaseDownload(fd=self.fd, request=self.request,
chunksize=3)
+
+ self.assertEqual(self.fd, download._fd)
+ self.assertEqual(0, download._progress)
+ self.assertEqual(None, download._total_size)
+ self.assertEqual(False, download._done)
+ self.assertEqual(self.request.uri, download._uri)
+
+ status, done = download.next_chunk()
+
+ self.assertEqual(True, done)
+ self.assertEqual(0, download._progress)
+ self.assertEqual(0, download._total_size)
+ self.assertEqual(0, status.progress())
+
def test_media_io_base_download_unknown_media_size(self):
self.request.http = HttpMockSequence([({"status": "200"}, b"123")])
@@ -1520,7 +1567,8 @@
expected = (
"<HttpError 403 when requesting "
"https://www.googleapis.com/someapi/v1/collection/?foo=bar
returned "
- '"Access Not Configured">'
+ '"Access Not Configured". '
+ 'Details: "Access Not Configured">'
)
self.assertEqual(expected, str(callbacks.exceptions["2"]))
@@ -1651,7 +1699,7 @@
socket.setdefaulttimeout(0)
http = build_http()
self.assertEqual(http.timeout, 0)
-
+
def test_build_http_default_308_is_excluded_as_redirect(self):
http = build_http()
self.assertTrue(308 not in http.redirect_codes)