- On January 1, 2020 this library will no longer support Python 2 on the latest released version.
- Previously released library versions will continue to be available. For more information please
+ As of January 1, 2020 this library no longer supports Python 2 on the latest released version.
+ Library versions released prior to that date will continue to be available. For more information please
visit
Python 2 support on Google Cloud.
{% block body %} {% endblock %}
diff --git a/synth.metadata b/synth.metadata
index b301f5f..77b4f46 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,14 +4,14 @@
"git": {
"name": ".",
"remote": "https://github.com/googleapis/python-cloud-core.git",
- "sha": "2f13d19babd66fea6185684a456573fdf8c1b784"
+ "sha": "5076ce7330e594fa0e1223e92eef61629508be71"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "cd522c3b4dde821766d95c80ae5aeb43d7a41170"
+ "sha": "303271797a360f8a439203413f13a160f2f5b3b4"
}
}
]
From f727aba432d4726cce72da5f74e8be6adb945a80 Mon Sep 17 00:00:00 2001
From: Tres Seaver
Date: Tue, 4 Aug 2020 15:31:03 -0400
Subject: [PATCH 7/9] feat: add support for Python 3.8 (#17)
Closes #16
---
noxfile.py | 2 +-
setup.py | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/noxfile.py b/noxfile.py
index d6ad371..9856032 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -47,7 +47,7 @@ def default(session):
)
-@nox.session(python=["2.7", "3.5", "3.6", "3.7"])
+@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"])
def unit(session):
"""Default unit test session."""
default(session)
diff --git a/setup.py b/setup.py
index beef8c8..e6987bd 100644
--- a/setup.py
+++ b/setup.py
@@ -71,7 +71,8 @@
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
- 'Programming Language :: Python :: 3.7',
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
"Operating System :: OS Independent",
"Topic :: Internet",
],
@@ -80,7 +81,7 @@
namespace_packages=namespaces,
install_requires=dependencies,
extras_require=extras,
- python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
+ python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
include_package_data=True,
zip_safe=False,
)
From a1e11e1f81a2a7e17b87fe4321eb497f7f3ccdc5 Mon Sep 17 00:00:00 2001
From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com>
Date: Tue, 4 Aug 2020 12:42:23 -0700
Subject: [PATCH 8/9] feat: add quota_project, credentials file, and scopes
options (#15)
Co-authored-by: Tres Seaver
---
google/cloud/client.py | 43 +++++++++++++----
tests/unit/test_client.py | 99 ++++++++++++++++++++++++++-------------
2 files changed, 99 insertions(+), 43 deletions(-)
diff --git a/google/cloud/client.py b/google/cloud/client.py
index f204ba8..6b9117f 100644
--- a/google/cloud/client.py
+++ b/google/cloud/client.py
@@ -20,6 +20,8 @@
import six
+import google.api_core.client_options
+import google.api_core.exceptions
import google.auth
import google.auth.credentials
import google.auth.transport.requests
@@ -102,6 +104,8 @@ class Client(_ClientFactoryMixin):
(Optional) The OAuth2 Credentials to use for this client. If not
passed (and if no ``_http`` object is passed), falls back to the
default inferred from the environment.
+ client_options (google.api_core.client_options.ClientOptions):
+ (Optional) Custom options for the client.
_http (requests.Session):
(Optional) HTTP object to make requests. Can be any object that
defines ``request()`` with the same interface as
@@ -123,16 +127,35 @@ class Client(_ClientFactoryMixin):
Needs to be set by subclasses.
"""
- def __init__(self, credentials=None, _http=None):
- if credentials is not None and not isinstance(
- credentials, google.auth.credentials.Credentials
- ):
+ def __init__(self, credentials=None, _http=None, client_options=None):
+ if isinstance(client_options, dict):
+ client_options = google.api_core.client_options.from_dict(client_options)
+ if client_options is None:
+ client_options = google.api_core.client_options.ClientOptions()
+
+ if credentials and client_options.credentials_file:
+ raise google.api_core.exceptions.DuplicateCredentialArgs(
+ "'credentials' and 'client_options.credentials_file' are mutually exclusive.")
+
+ if credentials and not isinstance(credentials, google.auth.credentials.Credentials):
raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP)
- if credentials is None and _http is None:
- credentials, _ = google.auth.default()
+
+ scopes = client_options.scopes or self.SCOPE
+
+ # if no http is provided, credentials must exist
+ if not _http and credentials is None:
+ if client_options.credentials_file:
+ credentials, _ = google.auth.load_credentials_from_file(
+ client_options.credentials_file, scopes=scopes)
+ else:
+ credentials, _ = google.auth.default(scopes=scopes)
+
self._credentials = google.auth.credentials.with_scopes_if_required(
- credentials, self.SCOPE
- )
+ credentials, scopes=scopes)
+
+ if client_options.quota_project_id:
+ self._credentials = self._credentials.with_quota_project(client_options.quota_project_id)
+
self._http_internal = _http
def __getstate__(self):
@@ -222,6 +245,6 @@ class ClientWithProject(Client, _ClientProjectMixin):
_SET_PROJECT = True # Used by from_service_account_json()
- def __init__(self, project=None, credentials=None, _http=None):
+ def __init__(self, project=None, credentials=None, client_options=None, _http=None):
_ClientProjectMixin.__init__(self, project=project)
- Client.__init__(self, credentials=credentials, _http=_http)
+ Client.__init__(self, credentials=credentials, client_options=client_options, _http=_http)
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index 7aec69f..59e2ea0 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -50,14 +50,14 @@ def _make_one(self, *args, **kw):
def test_unpickleable(self):
import pickle
- CREDENTIALS = _make_credentials()
+ credentials = _make_credentials()
HTTP = object()
- client_obj = self._make_one(credentials=CREDENTIALS, _http=HTTP)
+ client_obj = self._make_one(credentials=credentials, _http=HTTP)
with self.assertRaises(pickle.PicklingError):
pickle.dumps(client_obj)
- def test_constructor_defaults(self):
+ def test_ctor_defaults(self):
credentials = _make_credentials()
patch = mock.patch("google.auth.default", return_value=(credentials, None))
@@ -66,9 +66,9 @@ def test_constructor_defaults(self):
self.assertIs(client_obj._credentials, credentials)
self.assertIsNone(client_obj._http_internal)
- default.assert_called_once_with()
+ default.assert_called_once_with(scopes=None)
- def test_constructor_explicit(self):
+ def test_ctor_explicit(self):
credentials = _make_credentials()
http = mock.sentinel.http
client_obj = self._make_one(credentials=credentials, _http=http)
@@ -76,12 +76,71 @@ def test_constructor_explicit(self):
self.assertIs(client_obj._credentials, credentials)
self.assertIs(client_obj._http_internal, http)
- def test_constructor_bad_credentials(self):
+ def test_ctor_client_options_w_conflicting_creds(self):
+ from google.api_core.exceptions import DuplicateCredentialArgs
+
+ credentials = _make_credentials()
+ client_options = {'credentials_file': '/path/to/creds.json'}
+ with self.assertRaises(DuplicateCredentialArgs):
+ self._make_one(credentials=credentials, client_options=client_options)
+
+ def test_ctor_bad_credentials(self):
credentials = mock.sentinel.credentials
with self.assertRaises(ValueError):
self._make_one(credentials=credentials)
+ def test_ctor_client_options_w_creds_file_scopes(self):
+ credentials = _make_credentials()
+ credentials_file = '/path/to/creds.json'
+ scopes = ['SCOPE1', 'SCOPE2']
+ client_options = {'credentials_file': credentials_file, 'scopes': scopes}
+
+ patch = mock.patch("google.auth.load_credentials_from_file", return_value=(credentials, None))
+ with patch as load_credentials_from_file:
+ client_obj = self._make_one(client_options=client_options)
+
+ self.assertIs(client_obj._credentials, credentials)
+ self.assertIsNone(client_obj._http_internal)
+ load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+
+ def test_ctor_client_options_w_quota_project(self):
+ credentials = _make_credentials()
+ quota_project_id = 'quota-project-123'
+ client_options = {'quota_project_id': quota_project_id}
+
+ client_obj = self._make_one(credentials=credentials, client_options=client_options)
+
+ self.assertIs(client_obj._credentials, credentials.with_quota_project.return_value)
+ credentials.with_quota_project.assert_called_once_with(quota_project_id)
+
+ def test_ctor__http_property_existing(self):
+ credentials = _make_credentials()
+ http = object()
+ client = self._make_one(credentials=credentials, _http=http)
+ self.assertIs(client._http_internal, http)
+ self.assertIs(client._http, http)
+
+ def test_ctor__http_property_new(self):
+ from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT
+
+ credentials = _make_credentials()
+ client = self._make_one(credentials=credentials)
+ self.assertIsNone(client._http_internal)
+
+ authorized_session_patch = mock.patch(
+ "google.auth.transport.requests.AuthorizedSession",
+ return_value=mock.sentinel.http,
+ )
+ with authorized_session_patch as AuthorizedSession:
+ self.assertIs(client._http, mock.sentinel.http)
+ # Check the mock.
+ AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT)
+ # Make sure the cached value is used on subsequent access.
+ self.assertIs(client._http_internal, mock.sentinel.http)
+ self.assertIs(client._http, mock.sentinel.http)
+ self.assertEqual(AuthorizedSession.call_count, 1)
+
def test_from_service_account_json(self):
from google.cloud import _helpers
@@ -114,32 +173,6 @@ def test_from_service_account_json_bad_args(self):
mock.sentinel.filename, credentials=mock.sentinel.credentials
)
- def test__http_property_existing(self):
- credentials = _make_credentials()
- http = object()
- client = self._make_one(credentials=credentials, _http=http)
- self.assertIs(client._http_internal, http)
- self.assertIs(client._http, http)
-
- def test__http_property_new(self):
- from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT
- credentials = _make_credentials()
- client = self._make_one(credentials=credentials)
- self.assertIsNone(client._http_internal)
-
- authorized_session_patch = mock.patch(
- "google.auth.transport.requests.AuthorizedSession",
- return_value=mock.sentinel.http,
- )
- with authorized_session_patch as AuthorizedSession:
- self.assertIs(client._http, mock.sentinel.http)
- # Check the mock.
- AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT)
- # Make sure the cached value is used on subsequent access.
- self.assertIs(client._http_internal, mock.sentinel.http)
- self.assertIs(client._http, mock.sentinel.http)
- self.assertEqual(AuthorizedSession.call_count, 1)
-
class TestClientWithProject(unittest.TestCase):
@staticmethod
@@ -167,7 +200,7 @@ def test_constructor_defaults(self):
self.assertEqual(client_obj.project, project)
self.assertIs(client_obj._credentials, credentials)
self.assertIsNone(client_obj._http_internal)
- default.assert_called_once_with()
+ default.assert_called_once_with(scopes=None)
_determine_default_project.assert_called_once_with(None)
def test_constructor_missing_project(self):
From 0969ab4eb38129710c89e0579dd6f3cf3d3fa122 Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Tue, 4 Aug 2020 12:49:29 -0700
Subject: [PATCH 9/9] chore: release 1.4.0 (#18)
* chore: updated CHANGELOG.md [ci skip]
* chore: updated setup.cfg [ci skip]
* chore: updated setup.py
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
---
CHANGELOG.md | 8 ++++++++
setup.py | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b3d91c..2af08a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@
[1]: https://pypi.org/project/google-cloud-core/#history
+## [1.4.0](https://www.github.com/googleapis/python-cloud-core/compare/v1.3.0...v1.4.0) (2020-08-04)
+
+
+### Features
+
+* add quota_project, credentials file, and scopes options ([#15](https://www.github.com/googleapis/python-cloud-core/issues/15)) ([a1e11e1](https://www.github.com/googleapis/python-cloud-core/commit/a1e11e1f81a2a7e17b87fe4321eb497f7f3ccdc5))
+* add support for Python 3.8 ([#17](https://www.github.com/googleapis/python-cloud-core/issues/17)) ([f727aba](https://www.github.com/googleapis/python-cloud-core/commit/f727aba432d4726cce72da5f74e8be6adb945a80)), closes [#16](https://www.github.com/googleapis/python-cloud-core/issues/16)
+
## 1.3.0
01-31-2020 13:30 PST
diff --git a/setup.py b/setup.py
index e6987bd..eea8633 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@
name = "google-cloud-core"
description = "Google Cloud API client core library"
-version = "1.3.0"
+version = "1.4.0"
# Should be one of:
# 'Development Status :: 3 - Alpha'
# 'Development Status :: 4 - Beta'