From 6fd373bd3201915771326d864c5e7f46398132ec Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 24 Jun 2021 13:17:35 -0400 Subject: [PATCH 1/2] fix: pass kwargs through in 'from_service_account_json' Also, pin 'googleapis-common-protos' for Python 2.7 tests. Closes #108. --- google/cloud/client.py | 2 +- testing/constraints-2.7.txt | 1 + tests/unit/test_client.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/google/cloud/client.py b/google/cloud/client.py index 9e09450..1254d74 100644 --- a/google/cloud/client.py +++ b/google/cloud/client.py @@ -106,7 +106,7 @@ def from_service_account_json(cls, json_credentials_path, *args, **kwargs): with io.open(json_credentials_path, "r", encoding="utf-8") as json_fi: credentials_info = json.load(json_fi) - return cls.from_service_account_info(credentials_info) + return cls.from_service_account_info(credentials_info, **kwargs) class Client(_ClientFactoryMixin): diff --git a/testing/constraints-2.7.txt b/testing/constraints-2.7.txt index 8679811..3b6cd2c 100644 --- a/testing/constraints-2.7.txt +++ b/testing/constraints-2.7.txt @@ -1,2 +1,3 @@ google-api-core==1.21.0 +googleapis-common-protos<1.53.0 six==1.12.0 diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 1b933a2..fa38eba 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -456,13 +456,13 @@ def _from_service_account_json_helper(self, project=None): klass = self._get_target_class() - info = {"dummy": "value", "valid": "json"} + default_project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": default_project} if project is None: expected_project = "eye-d-of-project" else: expected_project = project - info["project_id"] = expected_project # Mock both the file opening and the credentials constructor. json_fi = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) file_open_patch = mock.patch("io.open", return_value=json_fi) From 6b0b42cd8559f401f3ff4d0be5c71662e8f34fa6 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 24 Jun 2021 18:28:06 -0400 Subject: [PATCH 2/2] fix: ensure posargs can be passed to 'from_service_account_*' --- google/cloud/client.py | 2 +- tests/unit/test_client.py | 69 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/google/cloud/client.py b/google/cloud/client.py index 1254d74..c2e7cff 100644 --- a/google/cloud/client.py +++ b/google/cloud/client.py @@ -106,7 +106,7 @@ def from_service_account_json(cls, json_credentials_path, *args, **kwargs): with io.open(json_credentials_path, "r", encoding="utf-8") as json_fi: credentials_info = json.load(json_fi) - return cls.from_service_account_info(credentials_info, **kwargs) + return cls.from_service_account_info(credentials_info, *args, **kwargs) class Client(_ClientFactoryMixin): diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index fa38eba..c7753a8 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -420,17 +420,16 @@ def test_constructor_explicit_unicode(self): def _from_service_account_info_helper(self, project=None): klass = self._get_target_class() - info = {"dummy": "value", "valid": "json"} + default_project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": default_project} kwargs = {} if project is None: - expected_project = "eye-d-of-project" + expected_project = default_project else: expected_project = project kwargs["project"] = project - info["project_id"] = expected_project - constructor_patch = mock.patch( "google.oauth2.service_account.Credentials.from_service_account_info", return_value=_make_credentials(), @@ -451,6 +450,33 @@ def test_from_service_account_info(self): def test_from_service_account_info_with_project(self): self._from_service_account_info_helper(project="prah-jekt") + def test_from_service_account_info_with_posarg(self): + class Derived(self._get_target_class()): + def __init__(self, required, **kwargs): + super(Derived, self).__init__(**kwargs) + self.required = required + + project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": project} + + # Mock both the file opening and the credentials constructor. + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with constructor_patch as constructor: + client_obj = Derived.from_service_account_info(info, "REQUIRED") + + self.assertIsInstance(client_obj, Derived) + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + self.assertEqual(client_obj.project, project) + self.assertEqual(client_obj.required, "REQUIRED") + + # Check that mocks were called as expected. + constructor.assert_called_once_with(info) + def _from_service_account_json_helper(self, project=None): from google.cloud import _helpers @@ -492,3 +518,38 @@ def test_from_service_account_json(self): def test_from_service_account_json_project_set(self): self._from_service_account_json_helper(project="prah-jekt") + + def test_from_service_account_json_with_posarg(self): + from google.cloud import _helpers + + class Derived(self._get_target_class()): + def __init__(self, required, **kwargs): + super(Derived, self).__init__(**kwargs) + self.required = required + + project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": project} + + # Mock both the file opening and the credentials constructor. + json_fi = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) + file_open_patch = mock.patch("io.open", return_value=json_fi) + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with file_open_patch as file_open: + with constructor_patch as constructor: + client_obj = Derived.from_service_account_json( + mock.sentinel.filename, "REQUIRED" + ) + + self.assertIsInstance(client_obj, Derived) + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + self.assertEqual(client_obj.project, project) + self.assertEqual(client_obj.required, "REQUIRED") + + # Check that mocks were called as expected. + file_open.assert_called_once_with(mock.sentinel.filename, "r", encoding="utf-8") + constructor.assert_called_once_with(info)