From 30684734106f345312f249c9ac86f700dd79c1e1 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Thu, 25 Jun 2015 18:16:52 -0700 Subject: [PATCH 1/3] Adding base client class in core. This is pre-work in #952, but came up in #933 as well. --- gcloud/client.py | 104 +++++++++++++++++++++++++++++++++++ gcloud/connection.py | 20 +++++-- gcloud/pubsub/client.py | 57 +------------------ gcloud/pubsub/test_client.py | 49 ----------------- gcloud/test_client.py | 83 ++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 108 deletions(-) create mode 100644 gcloud/client.py create mode 100644 gcloud/test_client.py diff --git a/gcloud/client.py b/gcloud/client.py new file mode 100644 index 000000000000..eefa74a352b8 --- /dev/null +++ b/gcloud/client.py @@ -0,0 +1,104 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""gcloud client base class for interacting with API.""" + + +from gcloud.credentials import get_for_service_account_json +from gcloud.credentials import get_for_service_account_p12 + + +class Client(object): + """Client to bundle configuration needed for API requests. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 Credentials to use for the connection + owned by this client. If not passed (and if no ``http`` + object is passed), falls back to the default inferred + from the environment. + + :type http: :class:`httplib2.Http` or class that defines ``request()``. + :param http: An optional HTTP object to make requests. If not passed, an + ``http`` object is created that is bound to the + ``credentials`` for the current object. + """ + + def __init__(self, credentials=None, http=None): + self.credentials = credentials + self.http = http + + @classmethod + def from_service_account_json(cls, json_credentials_path, *args, **kwargs): + """Factory to retrieve JSON credentials while creating client. + + :type json_credentials_path: string + :param json_credentials_path: The path to a private key file (this file + was given to you when you created the + service account). This file must contain + a JSON object with a private key and + other credentials information (downloaded + from the Google APIs console). + + :type args: tuple + :param args: Remaining positional arguments to pass to constructor. + + :type kwargs: dictionary + :param kwargs: Remaining keyword arguments to pass to constructor. + + :rtype: :class:`gcloud.pubsub.client.Client` + :returns: The client created with the retrieved JSON credentials. + :raises: class:`TypeError` if there is a conflict with the kwargs + and the credentials created by the factory. + """ + if 'credentials' in kwargs: + raise TypeError('credentials must not be in keyword arguments') + credentials = get_for_service_account_json(json_credentials_path) + kwargs['credentials'] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_p12(cls, client_email, private_key_path, + *args, **kwargs): + """Factory to retrieve P12 credentials while creating client. + + .. note:: + Unless you have an explicit reason to use a PKCS12 key for your + service account, we recommend using a JSON key. + + :type client_email: string + :param client_email: The e-mail attached to the service account. + + :type private_key_path: string + :param private_key_path: The path to a private key file (this file was + given to you when you created the service + account). This file must be in P12 format. + + :type args: tuple + :param args: Remaining positional arguments to pass to constructor. + + :type kwargs: dictionary + :param kwargs: Remaining keyword arguments to pass to constructor. + + :rtype: :class:`gcloud.client.Client` + :returns: The client created with the retrieved P12 credentials. + :raises: class:`TypeError` if there is a conflict with the kwargs + and the credentials created by the factory. + """ + if 'credentials' in kwargs: + raise TypeError('credentials must not be in keyword arguments') + credentials = get_for_service_account_p12(client_email, + private_key_path) + kwargs['credentials'] = credentials + return cls(*args, **kwargs) diff --git a/gcloud/connection.py b/gcloud/connection.py index bc6505d45519..d61df0458b26 100644 --- a/gcloud/connection.py +++ b/gcloud/connection.py @@ -135,10 +135,12 @@ def from_service_account_json(cls, json_credentials_path, *args, **kwargs): :rtype: :class:`gcloud.connection.Connection` :returns: The connection created with the retrieved JSON credentials. + :raises: class:`TypeError` if there is a conflict with the kwargs + and the credentials created by the factory. """ - credentials = get_for_service_account_json(json_credentials_path) if 'credentials' in kwargs: raise TypeError('credentials must not be in keyword arguments') + credentials = get_for_service_account_json(json_credentials_path) kwargs['credentials'] = credentials return cls(*args, **kwargs) @@ -167,11 +169,13 @@ def from_service_account_p12(cls, client_email, private_key_path, :rtype: :class:`gcloud.connection.Connection` :returns: The connection created with the retrieved P12 credentials. + :raises: class:`TypeError` if there is a conflict with the kwargs + and the credentials created by the factory. """ - credentials = get_for_service_account_p12(client_email, - private_key_path) if 'credentials' in kwargs: raise TypeError('credentials must not be in keyword arguments') + credentials = get_for_service_account_p12(client_email, + private_key_path) kwargs['credentials'] = credentials return cls(*args, **kwargs) @@ -179,13 +183,21 @@ def from_service_account_p12(cls, client_email, private_key_path, def from_environment(cls, *args, **kwargs): """Factory to retrieve implicit credentials while creating connection. + :type args: tuple + :param args: Remaining positional arguments to pass to constructor. + + :type kwargs: dictionary + :param kwargs: Remaining keyword arguments to pass to constructor. + :rtype: :class:`gcloud.connection.Connection` :returns: The connection created with the retrieved implicit credentials. + :raises: class:`TypeError` if there is a conflict with the kwargs + and the credentials created by the factory. """ - credentials = get_credentials() if 'credentials' in kwargs: raise TypeError('credentials must not be in keyword arguments') + credentials = get_credentials() kwargs['credentials'] = credentials return cls(*args, **kwargs) diff --git a/gcloud/pubsub/client.py b/gcloud/pubsub/client.py index cca81da176c6..8e934e1d42c6 100644 --- a/gcloud/pubsub/client.py +++ b/gcloud/pubsub/client.py @@ -16,15 +16,14 @@ from gcloud._helpers import _get_production_project +from gcloud.client import Client as BaseClient from gcloud.credentials import get_credentials -from gcloud.credentials import get_for_service_account_json -from gcloud.credentials import get_for_service_account_p12 from gcloud.pubsub.connection import Connection from gcloud.pubsub.subscription import Subscription from gcloud.pubsub.topic import Topic -class Client(object): +class Client(BaseClient): """Client to bundle configuration needed for API requests. :type project: string @@ -59,58 +58,6 @@ def __init__(self, project=None, credentials=None, http=None): credentials = get_credentials() self.connection = Connection(credentials=credentials, http=http) - @classmethod - def from_service_account_json(cls, json_credentials_path, project=None): - """Factory to retrieve JSON credentials while creating client. - - :type json_credentials_path: string - :param json_credentials_path: The path to a private key file (this file - was given to you when you created the - service account). This file must contain - a JSON object with a private key and - other credentials information (downloaded - from the Google APIs console). - - :type project: string - :param project: the project which the client acts on behalf of. Will be - passed when creating a topic. If not passed, falls - back to the default inferred from the environment. - - :rtype: :class:`gcloud.pubsub.client.Client` - :returns: The client created with the retrieved JSON credentials. - """ - credentials = get_for_service_account_json(json_credentials_path) - return cls(project=project, credentials=credentials) - - @classmethod - def from_service_account_p12(cls, client_email, private_key_path, - project=None): - """Factory to retrieve P12 credentials while creating client. - - .. note:: - Unless you have an explicit reason to use a PKCS12 key for your - service account, we recommend using a JSON key. - - :type client_email: string - :param client_email: The e-mail attached to the service account. - - :type private_key_path: string - :param private_key_path: The path to a private key file (this file was - given to you when you created the service - account). This file must be in P12 format. - - :type project: string - :param project: the project which the client acts on behalf of. Will be - passed when creating a topic. If not passed, falls - back to the default inferred from the environment. - - :rtype: :class:`gcloud.pubsub.client.Client` - :returns: The client created with the retrieved P12 credentials. - """ - credentials = get_for_service_account_p12(client_email, - private_key_path) - return cls(project=project, credentials=credentials) - def list_topics(self, page_size=None, page_token=None): """List topics for the project associated with this client. diff --git a/gcloud/pubsub/test_client.py b/gcloud/pubsub/test_client.py index f0f1d30a81be..d3d0cf45e71d 100644 --- a/gcloud/pubsub/test_client.py +++ b/gcloud/pubsub/test_client.py @@ -82,55 +82,6 @@ def test_ctor_explicit(self): self.assertTrue(client_obj.connection._credentials is CREDS) self.assertEqual(CREDS._scopes, SCOPE) - def test_from_service_account_json(self): - from gcloud._testing import _Monkey - from gcloud.pubsub import client - from gcloud.pubsub.connection import Connection - - PROJECT = 'PROJECT' - KLASS = self._getTargetClass() - CREDS = _Credentials() - _CALLED = [] - - def mock_creds(arg1): - _CALLED.append((arg1,)) - return CREDS - - BOGUS_ARG = object() - with _Monkey(client, get_for_service_account_json=mock_creds): - client_obj = KLASS.from_service_account_json( - BOGUS_ARG, project=PROJECT) - - self.assertEqual(client_obj.project, PROJECT) - self.assertTrue(isinstance(client_obj.connection, Connection)) - self.assertTrue(client_obj.connection._credentials is CREDS) - self.assertEqual(_CALLED, [(BOGUS_ARG,)]) - - def test_from_service_account_p12(self): - from gcloud._testing import _Monkey - from gcloud.pubsub import client - from gcloud.pubsub.connection import Connection - - PROJECT = 'PROJECT' - KLASS = self._getTargetClass() - CREDS = _Credentials() - _CALLED = [] - - def mock_creds(arg1, arg2): - _CALLED.append((arg1, arg2)) - return CREDS - - BOGUS_ARG1 = object() - BOGUS_ARG2 = object() - with _Monkey(client, get_for_service_account_p12=mock_creds): - client_obj = KLASS.from_service_account_p12( - BOGUS_ARG1, BOGUS_ARG2, project=PROJECT) - - self.assertEqual(client_obj.project, PROJECT) - self.assertTrue(isinstance(client_obj.connection, Connection)) - self.assertTrue(client_obj.connection._credentials is CREDS) - self.assertEqual(_CALLED, [(BOGUS_ARG1, BOGUS_ARG2)]) - def test_list_topics_no_paging(self): from gcloud.pubsub.topic import Topic PROJECT = 'PROJECT' diff --git a/gcloud/test_client.py b/gcloud/test_client.py new file mode 100644 index 000000000000..34fbe0614ce8 --- /dev/null +++ b/gcloud/test_client.py @@ -0,0 +1,83 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest2 + + +class TestClient(unittest2.TestCase): + + def _getTargetClass(self): + from gcloud.client import Client + return Client + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_ctor_explict(self): + CREDENTIALS = object() + HTTP = object() + CLIENT_OBJ = self._makeOne(credentials=CREDENTIALS, http=HTTP) + self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) + self.assertTrue(CLIENT_OBJ.http is HTTP) + + def test_from_service_account_json(self): + from gcloud._testing import _Monkey + from gcloud import client + + KLASS = self._getTargetClass() + CREDENTIALS = object() + _CALLED = [] + + def mock_creds(arg1): + _CALLED.append((arg1,)) + return CREDENTIALS + + BOGUS_ARG = object() + with _Monkey(client, get_for_service_account_json=mock_creds): + CLIENT_OBJ = KLASS.from_service_account_json(BOGUS_ARG) + + self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) + self.assertEqual(_CALLED, [(BOGUS_ARG,)]) + + def test_from_service_account_json_fail(self): + KLASS = self._getTargetClass() + CREDENTIALS = object() + self.assertRaises(TypeError, KLASS.from_service_account_json, None, + credentials=CREDENTIALS) + + def test_from_service_account_p12(self): + from gcloud._testing import _Monkey + from gcloud import client + + KLASS = self._getTargetClass() + CREDENTIALS = object() + _CALLED = [] + + def mock_creds(arg1, arg2): + _CALLED.append((arg1, arg2)) + return CREDENTIALS + + BOGUS_ARG1 = object() + BOGUS_ARG2 = object() + with _Monkey(client, get_for_service_account_p12=mock_creds): + CLIENT_OBJ = KLASS.from_service_account_p12(BOGUS_ARG1, BOGUS_ARG2) + + self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) + self.assertEqual(_CALLED, [(BOGUS_ARG1, BOGUS_ARG2)]) + + def test_from_service_account_p12_fail(self): + KLASS = self._getTargetClass() + CREDENTIALS = object() + self.assertRaises(TypeError, KLASS.from_service_account_p12, None, + None, credentials=CREDENTIALS) From 76eaeced52e252607a7af66f562fd8ae5f013a65 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Thu, 25 Jun 2015 18:39:24 -0700 Subject: [PATCH 2/3] Factoring out pubsub client constructor into core. --- gcloud/client.py | 49 ++++++++++++++++++++++ gcloud/pubsub/client.py | 22 ++-------- gcloud/pubsub/test_client.py | 58 -------------------------- gcloud/test_client.py | 80 ++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 76 deletions(-) diff --git a/gcloud/client.py b/gcloud/client.py index eefa74a352b8..8c528ac3a736 100644 --- a/gcloud/client.py +++ b/gcloud/client.py @@ -15,6 +15,9 @@ """gcloud client base class for interacting with API.""" +from gcloud._helpers import _get_production_project +from gcloud.connection import Connection +from gcloud.credentials import get_credentials from gcloud.credentials import get_for_service_account_json from gcloud.credentials import get_for_service_account_p12 @@ -102,3 +105,49 @@ def from_service_account_p12(cls, client_email, private_key_path, private_key_path) kwargs['credentials'] = credentials return cls(*args, **kwargs) + + +class JSONClient(Client): + """Client to for Google JSON-based API. + + Assumes such APIs use the `project` and the client needs to store this + value. + + Also assumes that the associated ``_connection_class`` only accepts + ``http`` and ``credentials`` in it's constructor. + + :type project: string + :param project: the project which the client acts on behalf of. If not + passed falls back to the default inferred from the + environment. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 Credentials to use for the connection + owned by this client. If not passed (and if no ``http`` + object is passed), falls back to the default inferred + from the environment. + + :type http: :class:`httplib2.Http` or class that defines ``request()``. + :param http: An optional HTTP object to make requests. If not passed, an + ``http`` object is created that is bound to the + ``credentials`` for the current object. + + :raises: :class:`ValueError` if the project is neither passed in nor + set in the environment. + """ + + _connection_class = Connection + + def __init__(self, project=None, credentials=None, http=None): + if project is None: + project = _get_production_project() + if project is None: + raise ValueError('Project was not passed and could not be ' + 'determined from the environment.') + self.project = project + + if credentials is None and http is None: + credentials = get_credentials() + self.connection = self._connection_class( + credentials=credentials, http=http) diff --git a/gcloud/pubsub/client.py b/gcloud/pubsub/client.py index 8e934e1d42c6..1163f873d817 100644 --- a/gcloud/pubsub/client.py +++ b/gcloud/pubsub/client.py @@ -15,15 +15,13 @@ """gcloud pubsub client for interacting with API.""" -from gcloud._helpers import _get_production_project -from gcloud.client import Client as BaseClient -from gcloud.credentials import get_credentials +from gcloud.client import JSONClient from gcloud.pubsub.connection import Connection from gcloud.pubsub.subscription import Subscription from gcloud.pubsub.topic import Topic -class Client(BaseClient): +class Client(JSONClient): """Client to bundle configuration needed for API requests. :type project: string @@ -42,21 +40,9 @@ class Client(BaseClient): :param http: An optional HTTP object to make requests. If not passed, an ``http`` object is created that is bound to the ``credentials`` for the current object. - - :raises: :class:`ValueError` if the project is neither passed in nor - set in the environment. """ - def __init__(self, project=None, credentials=None, http=None): - if project is None: - project = _get_production_project() - if project is None: - raise ValueError('Project was not passed and could not be ' - 'determined from the environment.') - self.project = project - - if credentials is None and http is None: - credentials = get_credentials() - self.connection = Connection(credentials=credentials, http=http) + + _connection_class = Connection def list_topics(self, page_size=None, page_token=None): """List topics for the project associated with this client. diff --git a/gcloud/pubsub/test_client.py b/gcloud/pubsub/test_client.py index d3d0cf45e71d..a13e1ec909db 100644 --- a/gcloud/pubsub/test_client.py +++ b/gcloud/pubsub/test_client.py @@ -24,64 +24,6 @@ def _getTargetClass(self): def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) - def test_ctor_defaults(self): - from gcloud._testing import _Monkey - from gcloud.pubsub import SCOPE - from gcloud.pubsub import client - from gcloud.pubsub.connection import Connection - - PROJECT = 'PROJECT' - CREDS = _Credentials() - FUNC_CALLS = [] - - def mock_get_proj(): - FUNC_CALLS.append('_get_production_project') - return PROJECT - - def mock_get_credentials(): - FUNC_CALLS.append('get_credentials') - return CREDS - - with _Monkey(client, get_credentials=mock_get_credentials, - _get_production_project=mock_get_proj): - client_obj = self._makeOne() - - self.assertEqual(client_obj.project, PROJECT) - self.assertTrue(isinstance(client_obj.connection, Connection)) - self.assertTrue(client_obj.connection._credentials is CREDS) - self.assertEqual(client_obj.connection._credentials._scopes, SCOPE) - self.assertEqual(FUNC_CALLS, - ['_get_production_project', 'get_credentials']) - - def test_ctor_missing_project(self): - from gcloud._testing import _Monkey - from gcloud.pubsub import client - - FUNC_CALLS = [] - - def mock_get_proj(): - FUNC_CALLS.append('_get_production_project') - return None - - with _Monkey(client, _get_production_project=mock_get_proj): - self.assertRaises(ValueError, self._makeOne) - - self.assertEqual(FUNC_CALLS, ['_get_production_project']) - - def test_ctor_explicit(self): - from gcloud.pubsub import SCOPE - from gcloud.pubsub.connection import Connection - - PROJECT = 'PROJECT' - CREDS = _Credentials() - - client_obj = self._makeOne(project=PROJECT, credentials=CREDS) - - self.assertEqual(client_obj.project, PROJECT) - self.assertTrue(isinstance(client_obj.connection, Connection)) - self.assertTrue(client_obj.connection._credentials is CREDS) - self.assertEqual(CREDS._scopes, SCOPE) - def test_list_topics_no_paging(self): from gcloud.pubsub.topic import Topic PROJECT = 'PROJECT' diff --git a/gcloud/test_client.py b/gcloud/test_client.py index 34fbe0614ce8..e12234f0a2c3 100644 --- a/gcloud/test_client.py +++ b/gcloud/test_client.py @@ -81,3 +81,83 @@ def test_from_service_account_p12_fail(self): CREDENTIALS = object() self.assertRaises(TypeError, KLASS.from_service_account_p12, None, None, credentials=CREDENTIALS) + + +class TestJSONClient(unittest2.TestCase): + + def setUp(self): + KLASS = self._getTargetClass() + self.original_cnxn_class = KLASS._connection_class + KLASS._connection_class = _MockConnection + + def tearDown(self): + KLASS = self._getTargetClass() + KLASS._connection_class = self.original_cnxn_class + + def _getTargetClass(self): + from gcloud.client import JSONClient + return JSONClient + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_ctor_defaults(self): + from gcloud._testing import _Monkey + from gcloud import client + + PROJECT = object() + CREDENTIALS = object() + FUNC_CALLS = [] + + def mock_get_proj(): + FUNC_CALLS.append('_get_production_project') + return PROJECT + + def mock_get_credentials(): + FUNC_CALLS.append('get_credentials') + return CREDENTIALS + + with _Monkey(client, get_credentials=mock_get_credentials, + _get_production_project=mock_get_proj): + client_obj = self._makeOne() + + self.assertTrue(client_obj.project is PROJECT) + self.assertTrue(isinstance(client_obj.connection, _MockConnection)) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) + self.assertEqual(FUNC_CALLS, + ['_get_production_project', 'get_credentials']) + + def test_ctor_missing_project(self): + from gcloud._testing import _Monkey + from gcloud import client + + FUNC_CALLS = [] + + def mock_get_proj(): + FUNC_CALLS.append('_get_production_project') + return None + + with _Monkey(client, _get_production_project=mock_get_proj): + self.assertRaises(ValueError, self._makeOne) + + self.assertEqual(FUNC_CALLS, ['_get_production_project']) + + def test_ctor_explicit(self): + PROJECT = object() + CREDENTIALS = object() + HTTP = object() + + client_obj = self._makeOne(project=PROJECT, credentials=CREDENTIALS, + http=HTTP) + + self.assertTrue(client_obj.project is PROJECT) + self.assertTrue(isinstance(client_obj.connection, _MockConnection)) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) + self.assertTrue(client_obj.connection.http is HTTP) + + +class _MockConnection(object): + + def __init__(self, credentials=None, http=None): + self.credentials = credentials + self.http = http From a65c4abf03beb681a7f3e2dcf18e99a321aeae0c Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Fri, 26 Jun 2015 10:55:08 -0700 Subject: [PATCH 3/3] Moving default cnxn from JSONClient to Client. This behavior is needed to the dataset client as well, so we put it on the base class. The JSON client class also checks for a project. --- gcloud/client.py | 21 ++++++++++---------- gcloud/test_client.py | 45 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/gcloud/client.py b/gcloud/client.py index 8c528ac3a736..b67ec28e726c 100644 --- a/gcloud/client.py +++ b/gcloud/client.py @@ -25,6 +25,9 @@ class Client(object): """Client to bundle configuration needed for API requests. + Assumes that the associated ``_connection_class`` only accepts + ``http`` and ``credentials`` in its constructor. + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or :class:`NoneType` :param credentials: The OAuth2 Credentials to use for the connection @@ -38,9 +41,13 @@ class Client(object): ``credentials`` for the current object. """ + _connection_class = Connection + def __init__(self, credentials=None, http=None): - self.credentials = credentials - self.http = http + if credentials is None and http is None: + credentials = get_credentials() + self.connection = self._connection_class( + credentials=credentials, http=http) @classmethod def from_service_account_json(cls, json_credentials_path, *args, **kwargs): @@ -113,9 +120,6 @@ class JSONClient(Client): Assumes such APIs use the `project` and the client needs to store this value. - Also assumes that the associated ``_connection_class`` only accepts - ``http`` and ``credentials`` in it's constructor. - :type project: string :param project: the project which the client acts on behalf of. If not passed falls back to the default inferred from the @@ -137,8 +141,6 @@ class JSONClient(Client): set in the environment. """ - _connection_class = Connection - def __init__(self, project=None, credentials=None, http=None): if project is None: project = _get_production_project() @@ -147,7 +149,4 @@ def __init__(self, project=None, credentials=None, http=None): 'determined from the environment.') self.project = project - if credentials is None and http is None: - credentials = get_credentials() - self.connection = self._connection_class( - credentials=credentials, http=http) + super(JSONClient, self).__init__(credentials=credentials, http=http) diff --git a/gcloud/test_client.py b/gcloud/test_client.py index e12234f0a2c3..77214ccbff33 100644 --- a/gcloud/test_client.py +++ b/gcloud/test_client.py @@ -17,6 +17,15 @@ class TestClient(unittest2.TestCase): + def setUp(self): + KLASS = self._getTargetClass() + self.original_cnxn_class = KLASS._connection_class + KLASS._connection_class = _MockConnection + + def tearDown(self): + KLASS = self._getTargetClass() + KLASS._connection_class = self.original_cnxn_class + def _getTargetClass(self): from gcloud.client import Client return Client @@ -24,12 +33,32 @@ def _getTargetClass(self): def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) - def test_ctor_explict(self): + def test_ctor_defaults(self): + from gcloud._testing import _Monkey + from gcloud import client + + CREDENTIALS = object() + FUNC_CALLS = [] + + def mock_get_credentials(): + FUNC_CALLS.append('get_credentials') + return CREDENTIALS + + with _Monkey(client, get_credentials=mock_get_credentials): + client_obj = self._makeOne() + + self.assertTrue(isinstance(client_obj.connection, _MockConnection)) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) + self.assertEqual(FUNC_CALLS, ['get_credentials']) + + def test_ctor_explicit(self): CREDENTIALS = object() HTTP = object() - CLIENT_OBJ = self._makeOne(credentials=CREDENTIALS, http=HTTP) - self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) - self.assertTrue(CLIENT_OBJ.http is HTTP) + client_obj = self._makeOne(credentials=CREDENTIALS, http=HTTP) + + self.assertTrue(isinstance(client_obj.connection, _MockConnection)) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) + self.assertTrue(client_obj.connection.http is HTTP) def test_from_service_account_json(self): from gcloud._testing import _Monkey @@ -45,9 +74,9 @@ def mock_creds(arg1): BOGUS_ARG = object() with _Monkey(client, get_for_service_account_json=mock_creds): - CLIENT_OBJ = KLASS.from_service_account_json(BOGUS_ARG) + client_obj = KLASS.from_service_account_json(BOGUS_ARG) - self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) self.assertEqual(_CALLED, [(BOGUS_ARG,)]) def test_from_service_account_json_fail(self): @@ -71,9 +100,9 @@ def mock_creds(arg1, arg2): BOGUS_ARG1 = object() BOGUS_ARG2 = object() with _Monkey(client, get_for_service_account_p12=mock_creds): - CLIENT_OBJ = KLASS.from_service_account_p12(BOGUS_ARG1, BOGUS_ARG2) + client_obj = KLASS.from_service_account_p12(BOGUS_ARG1, BOGUS_ARG2) - self.assertTrue(CLIENT_OBJ.credentials is CREDENTIALS) + self.assertTrue(client_obj.connection.credentials is CREDENTIALS) self.assertEqual(_CALLED, [(BOGUS_ARG1, BOGUS_ARG2)]) def test_from_service_account_p12_fail(self):