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)

Reply via email to