diff --git a/bigquery/google/cloud/bigquery/_http.py b/bigquery/google/cloud/bigquery/_http.py index fd5bb3cb8b23..8e2c218c1cc9 100644 --- a/bigquery/google/cloud/bigquery/_http.py +++ b/bigquery/google/cloud/bigquery/_http.py @@ -18,7 +18,11 @@ class Connection(_http.JSONConnection): - """A connection to Google BigQuery via the JSON REST API.""" + """A connection to Google BigQuery via the JSON REST API. + + :type client: :class:`~google.cloud.bigquery.client.Client` + :param client: The client that owns the current connection. + """ API_BASE_URL = 'https://www.googleapis.com' """The base of the API call URL.""" @@ -28,7 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/bigquery/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/bigquery', - 'https://www.googleapis.com/auth/cloud-platform') - """The scopes required for authenticating as a BigQuery consumer.""" diff --git a/bigquery/google/cloud/bigquery/client.py b/bigquery/google/cloud/bigquery/client.py index a00a1a28abaa..a85cb10eede6 100644 --- a/bigquery/google/cloud/bigquery/client.py +++ b/bigquery/google/cloud/bigquery/client.py @@ -72,11 +72,14 @@ class Client(ClientWithProject): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/bigquery', + 'https://www.googleapis.com/auth/cloud-platform') + """The scopes required for authenticating as a BigQuery consumer.""" + def __init__(self, project=None, credentials=None, http=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def list_projects(self, max_results=None, page_token=None): """List projects for the project associated with this client. diff --git a/bigquery/unit_tests/test__http.py b/bigquery/unit_tests/test__http.py index 6beaa3a8cc4d..4fd8de8017fc 100644 --- a/bigquery/unit_tests/test__http.py +++ b/bigquery/unit_tests/test__http.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, 'bigquery', @@ -40,7 +40,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) diff --git a/core/google/cloud/_http.py b/core/google/cloud/_http.py index c68958e356a6..e5d6cd81b239 100644 --- a/core/google/cloud/_http.py +++ b/core/google/cloud/_http.py @@ -19,10 +19,6 @@ import six from six.moves.urllib.parse import urlencode -import google.auth.credentials -import google_auth_httplib2 -import httplib2 - from google.cloud.exceptions import make_exception @@ -37,50 +33,14 @@ class Connection(object): """A generic connection to Google Cloud Platform. - Subclasses should understand only the basic types in method arguments, - however they should be capable of returning advanced types. - - If no value is passed in for ``http``, a :class:`httplib2.Http` object - will be created and authorized with the ``credentials``. If not, the - ``credentials`` and ``http`` need not be related. - - Subclasses may seek to use the private key from ``credentials`` to sign - data. - - A custom (non-``httplib2``) HTTP object must have a ``request`` method - which accepts the following arguments: - - * ``uri`` - * ``method`` - * ``body`` - * ``headers`` - - In addition, ``redirections`` and ``connection_type`` may be used. - - Without the use of ``credentials.authorize(http)``, a custom ``http`` - object will also need to be able to add a bearer token to API - requests and handle token refresh on 401 errors. - - :type credentials: :class:`google.auth.credentials.Credentials` or - :class:`NoneType` - :param credentials: The credentials to use for this connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: An optional HTTP object to make requests. + :type client: :class:`~google.cloud.client.Client` + :param client: The client that owns the current connection. """ USER_AGENT = DEFAULT_USER_AGENT - SCOPE = None - """The scopes required for authenticating with a service. - - Needs to be set by subclasses. - """ - - def __init__(self, credentials=None, http=None): - self._http = http - self._credentials = google.auth.credentials.with_scopes_if_required( - credentials, self.SCOPE) + def __init__(self, client): + self._client = client @property def credentials(self): @@ -90,7 +50,7 @@ def credentials(self): :class:`NoneType` :returns: The credentials object associated with this connection. """ - return self._credentials + return self._client._credentials @property def http(self): @@ -99,13 +59,7 @@ def http(self): :rtype: :class:`httplib2.Http` :returns: A Http object used to transport data. """ - if self._http is None: - if self._credentials: - self._http = google_auth_httplib2.AuthorizedHttp( - self._credentials) - else: - self._http = httplib2.Http() - return self._http + return self._client._http class JSONConnection(Connection): diff --git a/core/google/cloud/client.py b/core/google/cloud/client.py index 01dec6498b9e..7fa603b77527 100644 --- a/core/google/cloud/client.py +++ b/core/google/cloud/client.py @@ -16,6 +16,7 @@ import google.auth.credentials from google.oauth2 import service_account +import google_auth_httplib2 import six from google.cloud._helpers import _determine_default_project @@ -74,6 +75,26 @@ class Client(_ClientFactoryMixin): Stores ``credentials`` and ``http`` object so that subclasses can pass them along to a connection class. + If no value is passed in for ``http``, a :class:`httplib2.Http` object + will be created and authorized with the ``credentials``. If not, the + ``credentials`` and ``http`` need not be related. + + Callers and subclasses may seek to use the private key from + ``credentials`` to sign data. + + A custom (non-``httplib2``) HTTP object must have a ``request`` method + which accepts the following arguments: + + * ``uri`` + * ``method`` + * ``body`` + * ``headers`` + + In addition, ``redirections`` and ``connection_type`` may be used. + + A custom ``http`` object will also need to be able to add a bearer token + to API requests and handle token refresh on 401 errors. + :type credentials: :class:`~google.auth.credentials.Credentials` :param credentials: (Optional) The OAuth2 Credentials to use for this client. If not passed (and if no ``http`` object is @@ -88,6 +109,12 @@ class Client(_ClientFactoryMixin): ``credentials`` for the current object. """ + SCOPE = None + """The scopes required for authenticating with a service. + + Needs to be set by subclasses. + """ + def __init__(self, credentials=None, http=None): if (credentials is not None and not isinstance( @@ -95,8 +122,21 @@ def __init__(self, credentials=None, http=None): raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP) if credentials is None and http is None: credentials = get_credentials() - self._credentials = credentials - self._http = http + self._credentials = google.auth.credentials.with_scopes_if_required( + credentials, self.SCOPE) + self._http_internal = http + + @property + def _http(self): + """Getter for object used for HTTP transport. + + :rtype: :class:`~httplib2.Http` + :returns: An HTTP object. + """ + if self._http_internal is None: + self._http_internal = google_auth_httplib2.AuthorizedHttp( + self._credentials) + return self._http_internal class _ClientProjectMixin(object): diff --git a/core/unit_tests/test__http.py b/core/unit_tests/test__http.py index 23a198d1f68b..28e44045f976 100644 --- a/core/unit_tests/test__http.py +++ b/core/unit_tests/test__http.py @@ -28,61 +28,27 @@ def _get_target_class(): def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) - def test_ctor_defaults(self): - conn = self._make_one() - self.assertIsNone(conn.credentials) + def test_constructor(self): + client = object() + conn = self._make_one(client) + self.assertIs(conn._client, client) - def test_ctor_explicit(self): - import google.auth.credentials + def test_credentials_property(self): + client = mock.Mock(spec=['_credentials']) + conn = self._make_one(client) + self.assertIs(conn.credentials, client._credentials) - credentials = mock.Mock(spec=google.auth.credentials.Scoped) - - conn = self._make_one(credentials) - - credentials.with_scopes.assert_called_once_with(conn.SCOPE) - self.assertIs(conn.credentials, credentials.with_scopes.return_value) - self.assertIsNone(conn._http) - - def test_ctor_explicit_http(self): - http = object() - conn = self._make_one(http=http) - self.assertIsNone(conn.credentials) - self.assertIs(conn.http, http) - - def test_ctor_credentials_wo_create_scoped(self): - credentials = object() - conn = self._make_one(credentials) - self.assertIs(conn.credentials, credentials) - self.assertIsNone(conn._http) - - def test_http_w_existing(self): - conn = self._make_one() - conn._http = http = object() - self.assertIs(conn.http, http) - - def test_http_wo_creds(self): - import httplib2 - - conn = self._make_one() - self.assertIsInstance(conn.http, httplib2.Http) - - def test_http_w_creds(self): - import google.auth.credentials - import google_auth_httplib2 - - credentials = mock.Mock(spec=google.auth.credentials.Credentials) - - conn = self._make_one(credentials) - - self.assertIsInstance(conn.http, google_auth_httplib2.AuthorizedHttp) - self.assertIs(conn.http.credentials, credentials) + def test_http_property(self): + client = mock.Mock(spec=['_http']) + conn = self._make_one(client) + self.assertIs(conn.http, client._http) def test_user_agent_format(self): from pkg_resources import get_distribution expected_ua = 'gcloud-python/{0}'.format( get_distribution('google-cloud-core').version) - conn = self._make_one() + conn = self._make_one(object()) self.assertEqual(conn.USER_AGENT, expected_ua) @@ -97,7 +63,7 @@ def _get_target_class(): def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) - def _makeMockOne(self, *args, **kw): + def _make_mock_one(self, *args, **kw): class MockConnection(self._get_target_class()): API_URL_TEMPLATE = '{api_base_url}/mock/{api_version}{path}' API_BASE_URL = 'http://mock' @@ -110,38 +76,14 @@ def test_class_defaults(self): self.assertIsNone(klass.API_BASE_URL) self.assertIsNone(klass.API_VERSION) - def test_ctor_defaults(self): - conn = self._make_one() - self.assertIsNone(conn.credentials) - - def test_ctor_explicit(self): - conn = self._make_one(mock.sentinel.credentials) - self.assertIs(conn.credentials, mock.sentinel.credentials) - - def test_http_w_existing(self): - conn = self._make_one() - conn._http = http = object() - self.assertIs(conn.http, http) - - def test_http_wo_creds(self): - import httplib2 - - conn = self._make_one() - self.assertIsInstance(conn.http, httplib2.Http) - - def test_http_w_creds(self): - import google.auth.credentials - import google_auth_httplib2 - - credentials = mock.Mock(spec=google.auth.credentials.Credentials) - - conn = self._make_one(credentials) - - self.assertIsInstance(conn.http, google_auth_httplib2.AuthorizedHttp) - self.assertIs(conn.http.credentials, credentials) + def test_constructor(self): + client = object() + conn = self._make_one(client) + self.assertIs(conn._client, client) def test_build_api_url_no_extra_query_params(self): - conn = self._makeMockOne() + client = object() + conn = self._make_mock_one(client) # Intended to emulate self.mock_template URI = '/'.join([ conn.API_BASE_URL, @@ -155,7 +97,8 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._makeMockOne() + client = object() + conn = self._make_mock_one(client) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) @@ -172,12 +115,13 @@ def test_build_api_url_w_extra_query_params(self): self.assertEqual(parms['bar'], 'baz') def test__make_request_no_data_no_content_type_no_headers(self): - conn = self._make_one() - URI = 'http://example.com/test' - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'text/plain'}, b'', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) + URI = 'http://example.com/test' headers, content = conn._make_request('GET', URI) self.assertEqual(headers['status'], '200') self.assertEqual(headers['content-type'], 'text/plain') @@ -193,12 +137,13 @@ def test__make_request_no_data_no_content_type_no_headers(self): self.assertEqual(http._called_with['headers'], expected_headers) def test__make_request_w_data_no_extra_headers(self): - conn = self._make_one() - URI = 'http://example.com/test' - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'text/plain'}, b'', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) + URI = 'http://example.com/test' conn._make_request('GET', URI, {}, 'application/json') self.assertEqual(http._called_with['method'], 'GET') self.assertEqual(http._called_with['uri'], URI) @@ -212,12 +157,13 @@ def test__make_request_w_data_no_extra_headers(self): self.assertEqual(http._called_with['headers'], expected_headers) def test__make_request_w_extra_headers(self): - conn = self._make_one() - URI = 'http://example.com/test' - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'text/plain'}, b'', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) + URI = 'http://example.com/test' conn._make_request('GET', URI, headers={'X-Foo': 'foo'}) self.assertEqual(http._called_with['method'], 'GET') self.assertEqual(http._called_with['uri'], URI) @@ -231,18 +177,19 @@ def test__make_request_w_extra_headers(self): self.assertEqual(http._called_with['headers'], expected_headers) def test_api_request_defaults(self): + http = _Http( + {'status': '200', 'content-type': 'application/json'}, + b'{}', + ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) PATH = '/path/required' - conn = self._makeMockOne() # Intended to emulate self.mock_template URI = '/'.join([ conn.API_BASE_URL, 'mock', '%s%s' % (conn.API_VERSION, PATH), ]) - http = conn._http = _Http( - {'status': '200', 'content-type': 'application/json'}, - b'{}', - ) self.assertEqual(conn.api_request('GET', PATH), {}) self.assertEqual(http._called_with['method'], 'GET') self.assertEqual(http._called_with['uri'], URI) @@ -255,20 +202,22 @@ def test_api_request_defaults(self): self.assertEqual(http._called_with['headers'], expected_headers) def test_api_request_w_non_json_response(self): - conn = self._makeMockOne() - conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'text/plain'}, b'CONTENT', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertRaises(TypeError, conn.api_request, 'GET', '/') def test_api_request_wo_json_expected(self): - conn = self._makeMockOne() - conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'text/plain'}, b'CONTENT', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertEqual(conn.api_request('GET', '/', expect_json=False), b'CONTENT') @@ -276,11 +225,12 @@ def test_api_request_w_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._makeMockOne() - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'application/json'}, b'{}', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertEqual(conn.api_request('GET', '/', {'foo': 'bar'}), {}) self.assertEqual(http._called_with['method'], 'GET') uri = http._called_with['uri'] @@ -307,11 +257,12 @@ def test_api_request_w_query_params(self): def test_api_request_w_headers(self): from six.moves.urllib.parse import urlsplit - conn = self._makeMockOne() - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'application/json'}, b'{}', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertEqual( conn.api_request('GET', '/', headers={'X-Foo': 'bar'}), {}) self.assertEqual(http._called_with['method'], 'GET') @@ -341,7 +292,12 @@ def test_api_request_w_data(self): DATA = {'foo': 'bar'} DATAJ = json.dumps(DATA) - conn = self._makeMockOne() + http = _Http( + {'status': '200', 'content-type': 'application/json'}, + b'{}', + ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) # Intended to emulate self.mock_template URI = '/'.join([ conn.API_BASE_URL, @@ -349,10 +305,6 @@ def test_api_request_w_data(self): conn.API_VERSION, '', ]) - http = conn._http = _Http( - {'status': '200', 'content-type': 'application/json'}, - b'{}', - ) self.assertEqual(conn.api_request('POST', '/', data=DATA), {}) self.assertEqual(http._called_with['method'], 'POST') self.assertEqual(http._called_with['uri'], URI) @@ -368,29 +320,33 @@ def test_api_request_w_data(self): def test_api_request_w_404(self): from google.cloud.exceptions import NotFound - conn = self._makeMockOne() - conn._http = _Http( + http = _Http( {'status': '404', 'content-type': 'text/plain'}, b'{}' ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertRaises(NotFound, conn.api_request, 'GET', '/') def test_api_request_w_500(self): from google.cloud.exceptions import InternalServerError - conn = self._makeMockOne() - conn._http = _Http( + http = _Http( {'status': '500', 'content-type': 'text/plain'}, b'{}', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) self.assertRaises(InternalServerError, conn.api_request, 'GET', '/') def test_api_request_non_binary_response(self): - conn = self._makeMockOne() - http = conn._http = _Http( + http = _Http( {'status': '200', 'content-type': 'application/json'}, u'{}', ) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_mock_one(client) + result = conn.api_request('GET', '/') # Intended to emulate self.mock_template URI = '/'.join([ diff --git a/core/unit_tests/test_client.py b/core/unit_tests/test_client.py index e3e58f238d85..d76e3d776bfe 100644 --- a/core/unit_tests/test_client.py +++ b/core/unit_tests/test_client.py @@ -62,7 +62,7 @@ def mock_get_credentials(): client_obj = self._make_one() self.assertIs(client_obj._credentials, CREDENTIALS) - self.assertIsNone(client_obj._http) + self.assertIsNone(client_obj._http_internal) self.assertEqual(FUNC_CALLS, ['get_credentials']) def test_ctor_explicit(self): @@ -71,7 +71,7 @@ def test_ctor_explicit(self): client_obj = self._make_one(credentials=CREDENTIALS, http=HTTP) self.assertIs(client_obj._credentials, CREDENTIALS) - self.assertIs(client_obj._http, HTTP) + self.assertIs(client_obj._http_internal, HTTP) def test_ctor_bad_credentials(self): CREDENTIALS = object() @@ -93,7 +93,7 @@ def test_from_service_account_json(self): self.assertIs( client_obj._credentials, constructor.return_value) - self.assertIsNone(client_obj._http) + self.assertIsNone(client_obj._http_internal) constructor.assert_called_once_with(mock.sentinel.filename) def test_from_service_account_json_bad_args(self): @@ -103,6 +103,30 @@ def test_from_service_account_json_bad_args(self): KLASS.from_service_account_json( 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): + credentials = _make_credentials() + client = self._make_one(credentials=credentials) + self.assertIsNone(client._http_internal) + + patch = mock.patch('google_auth_httplib2.AuthorizedHttp', + return_value=mock.sentinel.http) + with patch as mocked: + self.assertIs(client._http, mock.sentinel.http) + # Check the mock. + mocked.assert_called_once_with(credentials) + self.assertEqual(mocked.call_count, 1) + # 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(mocked.call_count, 1) + class TestClientWithProject(unittest.TestCase): @@ -137,7 +161,7 @@ def mock_get_credentials(): self.assertEqual(client_obj.project, PROJECT) self.assertIs(client_obj._credentials, CREDENTIALS) - self.assertIsNone(client_obj._http) + self.assertIsNone(client_obj._http_internal) self.assertEqual( FUNC_CALLS, [(None, '_determine_default_project'), 'get_credentials']) @@ -178,7 +202,7 @@ def _explicit_ctor_helper(self, project): else: self.assertEqual(client_obj.project, project) self.assertIs(client_obj._credentials, CREDENTIALS) - self.assertIs(client_obj._http, HTTP) + self.assertIs(client_obj._http_internal, HTTP) def test_ctor_explicit_bytes(self): PROJECT = b'PROJECT' diff --git a/datastore/google/cloud/datastore/_http.py b/datastore/google/cloud/datastore/_http.py index a6bae476dff8..4c01f60c84d2 100644 --- a/datastore/google/cloud/datastore/_http.py +++ b/datastore/google/cloud/datastore/_http.py @@ -397,11 +397,8 @@ class Connection(connection_module.Connection): in method arguments, however it should be capable of returning advanced types. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: The OAuth2 Credentials to use for this connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: An optional HTTP object to make requests. + :type client: :class:`~google.cloud.datastore.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://' + DATASTORE_API_HOST @@ -414,11 +411,8 @@ class Connection(connection_module.Connection): '/{project}:{method}') """A template for the URL of a particular API call.""" - SCOPE = ('https://www.googleapis.com/auth/datastore',) - """The scopes required for authenticating as a Cloud Datastore consumer.""" - - def __init__(self, credentials=None, http=None): - super(Connection, self).__init__(credentials=credentials, http=http) + def __init__(self, client): + super(Connection, self).__init__(client) try: self.host = os.environ[GCD_HOST] self.api_base_url = 'http://' + self.host diff --git a/datastore/google/cloud/datastore/client.py b/datastore/google/cloud/datastore/client.py index 42b0c6497f88..b76a8cce7fc1 100644 --- a/datastore/google/cloud/datastore/client.py +++ b/datastore/google/cloud/datastore/client.py @@ -18,8 +18,7 @@ from google.cloud._helpers import _LocalStack from google.cloud._helpers import ( _determine_default_project as _base_default_project) -from google.cloud.client import _ClientProjectMixin -from google.cloud.client import Client as _BaseClient +from google.cloud.client import ClientWithProject from google.cloud.datastore._http import Connection from google.cloud.datastore import helpers from google.cloud.datastore.batch import Batch @@ -143,7 +142,7 @@ def _extended_lookup(connection, project, key_pbs, return results -class Client(_BaseClient, _ClientProjectMixin): +class Client(ClientWithProject): """Convenience wrapper for invoking APIs/factories w/ a project. .. doctest:: @@ -171,13 +170,14 @@ class Client(_BaseClient, _ClientProjectMixin): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/datastore',) + """The scopes required for authenticating as a Cloud Datastore consumer.""" + def __init__(self, project=None, namespace=None, credentials=None, http=None): - _ClientProjectMixin.__init__(self, project=project) - _BaseClient.__init__(self, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) - + super(Client, self).__init__( + project=project, credentials=credentials, http=http) + self._connection = Connection(self) self.namespace = namespace self._batch_stack = _LocalStack() diff --git a/datastore/unit_tests/test__http.py b/datastore/unit_tests/test__http.py index d779aacedf92..ced9b65a4cd0 100644 --- a/datastore/unit_tests/test__http.py +++ b/datastore/unit_tests/test__http.py @@ -380,10 +380,10 @@ def _make_query_pb(self, kind): pb.kind.add().name = kind return pb - def _make_one(self, credentials=None, http=None, use_grpc=False): + def _make_one(self, client, use_grpc=False): with mock.patch('google.cloud.datastore._http._USE_GRPC', new=use_grpc): - return self._get_target_class()(credentials=credentials, http=http) + return self._get_target_class()(client) def _verifyProtobufCall(self, called_with, URI, conn): self.assertEqual(called_with['uri'], URI) @@ -395,7 +395,7 @@ def _verifyProtobufCall(self, called_with, URI, conn): def test_default_url(self): klass = self._get_target_class() - conn = self._make_one() + conn = self._make_one(object()) self.assertEqual(conn.api_base_url, klass.API_BASE_URL) def test_custom_url_from_env(self): @@ -406,17 +406,19 @@ def test_custom_url_from_env(self): fake_environ = {GCD_HOST: HOST} with mock.patch('os.environ', new=fake_environ): - conn = self._make_one() + conn = self._make_one(object()) self.assertNotEqual(conn.api_base_url, API_BASE_URL) self.assertEqual(conn.api_base_url, 'http://' + HOST) - def test_ctor_defaults(self): - conn = self._make_one() - self.assertIsNone(conn.credentials) + def test_constructor(self): + client = object() + conn = self._make_one(client) + self.assertIs(conn._client, client) - def test_ctor_without_grpc(self): + def test_constructor_without_grpc(self): connections = [] + client = object() return_val = object() def mock_api(connection): @@ -427,14 +429,15 @@ def mock_api(connection): 'google.cloud.datastore._http._DatastoreAPIOverHttp', new=mock_api) with patch: - conn = self._make_one(use_grpc=False) + conn = self._make_one(client, use_grpc=False) - self.assertIsNone(conn.credentials) + self.assertIs(conn._client, client) self.assertIs(conn._datastore_api, return_val) self.assertEqual(connections, [conn]) - def test_ctor_with_grpc(self): + def test_constructor_with_grpc(self): api_args = [] + client = object() return_val = object() def mock_api(connection, secure): @@ -445,43 +448,16 @@ def mock_api(connection, secure): 'google.cloud.datastore._http._DatastoreAPIOverGRPC', new=mock_api) with patch: - conn = self._make_one(use_grpc=True) + conn = self._make_one(client, use_grpc=True) - self.assertIsNone(conn.credentials) + self.assertIs(conn._client, client) self.assertIs(conn._datastore_api, return_val) self.assertEqual(api_args, [(conn, True)]) - def test_ctor_explicit(self): - class Creds(object): - pass - - creds = Creds() - conn = self._make_one(creds) - self.assertIs(conn.credentials, creds) - - def test_http_w_existing(self): - conn = self._make_one() - conn._http = http = object() - self.assertIs(conn.http, http) - - def test_http_wo_creds(self): - import httplib2 - - conn = self._make_one() - self.assertIsInstance(conn.http, httplib2.Http) - - def test_http_w_creds(self): - class Creds(object): - pass - - creds = Creds() - conn = self._make_one(creds) - self.assertIs(conn.http.credentials, creds) - def test_build_api_url_w_default_base_version(self): PROJECT = 'PROJECT' METHOD = 'METHOD' - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, @@ -495,7 +471,7 @@ def test_build_api_url_w_explicit_base_version(self): VER = '3.1415926' PROJECT = 'PROJECT' METHOD = 'METHOD' - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ BASE, VER, @@ -511,14 +487,15 @@ def test_lookup_single_key_empty_response(self): PROJECT = 'PROJECT' key_pb = self._make_key_pb(PROJECT) rsp_pb = datastore_pb2.LookupResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) found, missing, deferred = conn.lookup(PROJECT, [key_pb]) self.assertEqual(len(found), 0) self.assertEqual(len(missing), 0) @@ -538,14 +515,15 @@ def test_lookup_single_key_empty_response_w_eventual(self): PROJECT = 'PROJECT' key_pb = self._make_key_pb(PROJECT) rsp_pb = datastore_pb2.LookupResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) found, missing, deferred = conn.lookup(PROJECT, [key_pb], eventual=True) self.assertEqual(len(found), 0) @@ -567,7 +545,7 @@ def test_lookup_single_key_empty_response_w_eventual_and_transaction(self): PROJECT = 'PROJECT' TRANSACTION = b'TRANSACTION' key_pb = self._make_key_pb(PROJECT) - conn = self._make_one() + conn = self._make_one(object()) self.assertRaises(ValueError, conn.lookup, PROJECT, key_pb, eventual=True, transaction_id=TRANSACTION) @@ -578,14 +556,15 @@ def test_lookup_single_key_empty_response_w_transaction(self): TRANSACTION = b'TRANSACTION' key_pb = self._make_key_pb(PROJECT) rsp_pb = datastore_pb2.LookupResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) found, missing, deferred = conn.lookup(PROJECT, [key_pb], transaction_id=TRANSACTION) self.assertEqual(len(found), 0) @@ -611,14 +590,15 @@ def test_lookup_single_key_nonempty_response(self): entity = entity_pb2.Entity() entity.key.CopyFrom(key_pb) rsp_pb.found.add(entity=entity) - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) (found,), missing, deferred = conn.lookup(PROJECT, [key_pb]) self.assertEqual(len(missing), 0) self.assertEqual(len(deferred), 0) @@ -640,14 +620,15 @@ def test_lookup_multiple_keys_empty_response(self): key_pb1 = self._make_key_pb(PROJECT) key_pb2 = self._make_key_pb(PROJECT, id_=2345) rsp_pb = datastore_pb2.LookupResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) found, missing, deferred = conn.lookup(PROJECT, [key_pb1, key_pb2]) self.assertEqual(len(found), 0) self.assertEqual(len(missing), 0) @@ -673,14 +654,15 @@ def test_lookup_multiple_keys_w_missing(self): er_1.entity.key.CopyFrom(key_pb1) er_2 = rsp_pb.missing.add() er_2.entity.key.CopyFrom(key_pb2) - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) result, missing, deferred = conn.lookup(PROJECT, [key_pb1, key_pb2]) self.assertEqual(result, []) self.assertEqual(len(deferred), 0) @@ -705,14 +687,15 @@ def test_lookup_multiple_keys_w_deferred(self): rsp_pb = datastore_pb2.LookupResponse() rsp_pb.deferred.add().CopyFrom(key_pb1) rsp_pb.deferred.add().CopyFrom(key_pb2) - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':lookup', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) result, missing, deferred = conn.lookup(PROJECT, [key_pb1, key_pb2]) self.assertEqual(result, []) self.assertEqual(len(missing), 0) @@ -745,14 +728,15 @@ def test_run_query_w_eventual_no_transaction(self): no_more = query_pb2.QueryResultBatch.NO_MORE_RESULTS rsp_pb.batch.more_results = no_more rsp_pb.batch.entity_result_type = query_pb2.EntityResult.FULL - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':runQuery', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) pbs, end, more, skipped = conn.run_query(PROJECT, q_pb, eventual=True) self.assertEqual(pbs, []) @@ -784,14 +768,15 @@ def test_run_query_wo_eventual_w_transaction(self): no_more = query_pb2.QueryResultBatch.NO_MORE_RESULTS rsp_pb.batch.more_results = no_more rsp_pb.batch.entity_result_type = query_pb2.EntityResult.FULL - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':runQuery', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) pbs, end, more, skipped = conn.run_query( PROJECT, q_pb, transaction_id=TRANSACTION) self.assertEqual(pbs, []) @@ -824,7 +809,7 @@ def test_run_query_w_eventual_and_transaction(self): no_more = query_pb2.QueryResultBatch.NO_MORE_RESULTS rsp_pb.batch.more_results = no_more rsp_pb.batch.entity_result_type = query_pb2.EntityResult.FULL - conn = self._make_one() + conn = self._make_one(object()) self.assertRaises(ValueError, conn.run_query, PROJECT, q_pb, eventual=True, transaction_id=TRANSACTION) @@ -841,14 +826,15 @@ def test_run_query_wo_namespace_empty_result(self): no_more = query_pb2.QueryResultBatch.NO_MORE_RESULTS rsp_pb.batch.more_results = no_more rsp_pb.batch.entity_result_type = query_pb2.EntityResult.FULL - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':runQuery', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) pbs, end, more, skipped = conn.run_query(PROJECT, q_pb) self.assertEqual(pbs, []) self.assertEqual(end, CURSOR) @@ -874,14 +860,15 @@ def test_run_query_w_namespace_nonempty_result(self): rsp_pb.batch.entity_results.add(entity=entity) rsp_pb.batch.entity_result_type = 1 # FULL rsp_pb.batch.more_results = 3 # NO_MORE_RESULTS - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':runQuery', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) pbs = conn.run_query(PROJECT, q_pb, 'NS')[0] self.assertEqual(len(pbs), 1) cw = http._called_with @@ -899,14 +886,15 @@ def test_begin_transaction(self): TRANSACTION = b'TRANSACTION' rsp_pb = datastore_pb2.BeginTransactionResponse() rsp_pb.transaction = TRANSACTION - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':beginTransaction', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) self.assertEqual(conn.begin_transaction(PROJECT), TRANSACTION) cw = http._called_with self._verifyProtobufCall(cw, URI, conn) @@ -927,14 +915,15 @@ def test_commit_wo_transaction(self): insert.key.CopyFrom(key_pb) value_pb = _new_value_pb(insert, 'foo') value_pb.string_value = u'Foo' - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':commit', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) # Set up mock for parsing the response. expected_result = object() @@ -974,14 +963,15 @@ def test_commit_w_transaction(self): insert.key.CopyFrom(key_pb) value_pb = _new_value_pb(insert, 'foo') value_pb.string_value = u'Foo' - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':commit', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) # Set up mock for parsing the response. expected_result = object() @@ -1015,14 +1005,15 @@ def test_rollback_ok(self): TRANSACTION = b'xact' rsp_pb = datastore_pb2.RollbackResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':rollback', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) self.assertIsNone(conn.rollback(PROJECT, TRANSACTION)) cw = http._called_with self._verifyProtobufCall(cw, URI, conn) @@ -1036,14 +1027,15 @@ def test_allocate_ids_empty(self): PROJECT = 'PROJECT' rsp_pb = datastore_pb2.AllocateIdsResponse() - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':allocateIds', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) self.assertEqual(conn.allocate_ids(PROJECT, []), []) cw = http._called_with self._verifyProtobufCall(cw, URI, conn) @@ -1067,14 +1059,15 @@ def test_allocate_ids_non_empty(self): rsp_pb = datastore_pb2.AllocateIdsResponse() rsp_pb.keys.add().CopyFrom(after_key_pbs[0]) rsp_pb.keys.add().CopyFrom(after_key_pbs[1]) - conn = self._make_one() + http = Http({'status': '200'}, rsp_pb.SerializeToString()) + client = mock.Mock(_http=http, spec=['_http']) + conn = self._make_one(client) URI = '/'.join([ conn.api_base_url, conn.API_VERSION, 'projects', PROJECT + ':allocateIds', ]) - http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString()) self.assertEqual(conn.allocate_ids(PROJECT, before_key_pbs), after_key_pbs) cw = http._called_with diff --git a/datastore/unit_tests/test_client.py b/datastore/unit_tests/test_client.py index 8481b8a759e3..32236ecf2c49 100644 --- a/datastore/unit_tests/test_client.py +++ b/datastore/unit_tests/test_client.py @@ -168,16 +168,17 @@ def fallback_mock(project): new=fallback_mock) patch2 = mock.patch( 'google.cloud.client.get_credentials', - new=lambda: creds) + return_value=creds) with patch1: with patch2: client = klass() + self.assertEqual(client.project, OTHER) self.assertIsNone(client.namespace) self.assertIsInstance(client._connection, _MockConnection) - self.assertIs(client._connection.credentials, creds) - self.assertIsNone(client._connection.http) + self.assertIs(client._credentials, creds) + self.assertIsNone(client._http_internal) self.assertIsNone(client.current_batch) self.assertIsNone(client.current_transaction) self.assertEqual(default_called, [None]) @@ -194,8 +195,8 @@ def test_ctor_w_explicit_inputs(self): self.assertEqual(client.project, OTHER) self.assertEqual(client.namespace, NAMESPACE) self.assertIsInstance(client._connection, _MockConnection) - self.assertIs(client._connection.credentials, creds) - self.assertIs(client._connection.http, http) + self.assertIs(client._credentials, creds) + self.assertIs(client._http_internal, http) self.assertIsNone(client.current_batch) self.assertEqual(list(client._batch_stack), []) diff --git a/dns/google/cloud/dns/__init__.py b/dns/google/cloud/dns/__init__.py index da7c0abf9e8d..9d456e16257c 100644 --- a/dns/google/cloud/dns/__init__.py +++ b/dns/google/cloud/dns/__init__.py @@ -32,4 +32,4 @@ from google.cloud.dns.resource_record_set import ResourceRecordSet -SCOPE = Connection.SCOPE +SCOPE = Client.SCOPE diff --git a/dns/google/cloud/dns/client.py b/dns/google/cloud/dns/client.py index 3d33aa4711b2..22f72dfb32b4 100644 --- a/dns/google/cloud/dns/client.py +++ b/dns/google/cloud/dns/client.py @@ -43,11 +43,13 @@ class Client(ClientWithProject): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/ndev.clouddns.readwrite',) + """The scopes required for authenticating as a Cloud DNS consumer.""" + def __init__(self, project=None, credentials=None, http=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def quotas(self): """Return DNS quotas for the project associated with this client. diff --git a/dns/google/cloud/dns/connection.py b/dns/google/cloud/dns/connection.py index 1b2283f3403b..cf0ef347a8d4 100644 --- a/dns/google/cloud/dns/connection.py +++ b/dns/google/cloud/dns/connection.py @@ -18,7 +18,11 @@ class Connection(_http.JSONConnection): - """A connection to Google Cloud DNS via the JSON REST API.""" + """A connection to Google Cloud DNS via the JSON REST API. + + :type client: :class:`~google.cloud.dns.client.Client` + :param client: The client that owns the current connection. + """ API_BASE_URL = 'https://www.googleapis.com' """The base of the API call URL.""" @@ -28,6 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/dns/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/ndev.clouddns.readwrite',) - """The scopes required for authenticating as a Cloud DNS consumer.""" diff --git a/dns/unit_tests/test_connection.py b/dns/unit_tests/test_connection.py index 2878bacf2ce6..9453483e4a46 100644 --- a/dns/unit_tests/test_connection.py +++ b/dns/unit_tests/test_connection.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, 'dns', @@ -40,7 +40,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) diff --git a/language/google/cloud/language/client.py b/language/google/cloud/language/client.py index bad3ac7be918..d788c9b3d62d 100644 --- a/language/google/cloud/language/client.py +++ b/language/google/cloud/language/client.py @@ -37,11 +37,13 @@ class Client(client_module.Client): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) + """The scopes required for authenticating as an API consumer.""" + def __init__(self, credentials=None, http=None): super(Client, self).__init__( credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def document_from_text(self, content, **kwargs): """Create a plain text document bound to this client. diff --git a/language/google/cloud/language/connection.py b/language/google/cloud/language/connection.py index 196824b9a40e..f419c49bd941 100644 --- a/language/google/cloud/language/connection.py +++ b/language/google/cloud/language/connection.py @@ -18,7 +18,11 @@ class Connection(_http.JSONConnection): - """A connection to Google Cloud Natural Language JSON REST API.""" + """A connection to Google Cloud Natural Language JSON REST API. + + :type client: :class:`~google.cloud.language.client.Client` + :param client: The client that owns the current connection. + """ API_BASE_URL = 'https://language.googleapis.com' """The base of the API call URL.""" @@ -28,6 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}/documents:{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) - """The scopes required for authenticating as an API consumer.""" diff --git a/language/unit_tests/test_connection.py b/language/unit_tests/test_connection.py index 4b5694a437ff..f595cc6130aa 100644 --- a/language/unit_tests/test_connection.py +++ b/language/unit_tests/test_connection.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url(self): - conn = self._make_one() + conn = self._make_one(object()) uri = '/'.join([ conn.API_BASE_URL, conn.API_VERSION, diff --git a/logging/google/cloud/logging/_http.py b/logging/google/cloud/logging/_http.py index 8056689235db..e666daae2d3c 100644 --- a/logging/google/cloud/logging/_http.py +++ b/logging/google/cloud/logging/_http.py @@ -26,16 +26,8 @@ class Connection(_http.JSONConnection): """A connection to Google Stackdriver Logging via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. - - :type api_base_url: str - :param api_base_url: The base of the API call URL. Defaults to the value - :attr:`Connection.API_BASE_URL`. + :type client: :class:`~google.cloud.logging.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://logging.googleapis.com' @@ -47,12 +39,6 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - SCOPE = ('https://www.googleapis.com/auth/logging.read', - 'https://www.googleapis.com/auth/logging.write', - 'https://www.googleapis.com/auth/logging.admin', - 'https://www.googleapis.com/auth/cloud-platform') - """The scopes required for authenticating as a Logging consumer.""" - class _LoggingAPI(object): """Helper mapping logging-related APIs. diff --git a/logging/google/cloud/logging/client.py b/logging/google/cloud/logging/client.py index 2a29c0d03b49..130db45c2855 100644 --- a/logging/google/cloud/logging/client.py +++ b/logging/google/cloud/logging/client.py @@ -91,12 +91,17 @@ class Client(ClientWithProject): _sinks_api = None _metrics_api = None + SCOPE = ('https://www.googleapis.com/auth/logging.read', + 'https://www.googleapis.com/auth/logging.write', + 'https://www.googleapis.com/auth/logging.admin', + 'https://www.googleapis.com/auth/cloud-platform') + """The scopes required for authenticating as a Logging consumer.""" + def __init__(self, project=None, credentials=None, http=None, use_gax=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) if use_gax is None: self._use_gax = _USE_GAX else: diff --git a/logging/unit_tests/test__http.py b/logging/unit_tests/test__http.py index 953291dad1e9..b3536d2bf7b3 100644 --- a/logging/unit_tests/test__http.py +++ b/logging/unit_tests/test__http.py @@ -38,9 +38,9 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_default_url(self): - creds = _make_credentials() - conn = self._make_one(creds) - self.assertEqual(conn.credentials, creds) + client = object() + conn = self._make_one(client) + self.assertIs(conn._client, client) class Test_LoggingAPI(unittest.TestCase): diff --git a/monitoring/google/cloud/monitoring/__init__.py b/monitoring/google/cloud/monitoring/__init__.py index 982824f4c01a..8a350cbc3622 100644 --- a/monitoring/google/cloud/monitoring/__init__.py +++ b/monitoring/google/cloud/monitoring/__init__.py @@ -44,4 +44,4 @@ ) -SCOPE = Connection.SCOPE +SCOPE = Client.SCOPE diff --git a/monitoring/google/cloud/monitoring/client.py b/monitoring/google/cloud/monitoring/client.py index aec2144aa73e..099f16105cec 100644 --- a/monitoring/google/cloud/monitoring/client.py +++ b/monitoring/google/cloud/monitoring/client.py @@ -68,11 +68,15 @@ class Client(ClientWithProject): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/monitoring.read', + 'https://www.googleapis.com/auth/monitoring', + 'https://www.googleapis.com/auth/cloud-platform') + """The scopes required for authenticating as a Monitoring consumer.""" + def __init__(self, project=None, credentials=None, http=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def query(self, metric_type=Query.DEFAULT_METRIC_TYPE, diff --git a/monitoring/google/cloud/monitoring/connection.py b/monitoring/google/cloud/monitoring/connection.py index 671f00b4e96c..9d6b7c4a48e5 100644 --- a/monitoring/google/cloud/monitoring/connection.py +++ b/monitoring/google/cloud/monitoring/connection.py @@ -20,16 +20,8 @@ class Connection(_http.JSONConnection): """A connection to Google Stackdriver Monitoring via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()`` - :param http: (Optional) HTTP object to make requests. - - :type api_base_url: str - :param api_base_url: The base of the API call URL. Defaults to the value - :attr:`Connection.API_BASE_URL`. + :type client: :class:`~google.cloud.monitoring.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://monitoring.googleapis.com' @@ -40,8 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/monitoring.read', - 'https://www.googleapis.com/auth/monitoring', - 'https://www.googleapis.com/auth/cloud-platform') - """The scopes required for authenticating as a Monitoring consumer.""" diff --git a/monitoring/unit_tests/test_connection.py b/monitoring/unit_tests/test_connection.py index bd9852e23768..5d56acb40908 100644 --- a/monitoring/unit_tests/test_connection.py +++ b/monitoring/unit_tests/test_connection.py @@ -27,6 +27,6 @@ def _make_one(self, *args, **kwargs): return self._get_target_class()(*args, **kwargs) def test_constructor(self): - credentials = object() - connection = self._make_one(credentials) - self.assertEqual(connection.credentials, credentials) + client = object() + connection = self._make_one(client) + self.assertIs(connection._client, client) diff --git a/pubsub/google/cloud/pubsub/_http.py b/pubsub/google/cloud/pubsub/_http.py index 583413e313b6..bd438f72948d 100644 --- a/pubsub/google/cloud/pubsub/_http.py +++ b/pubsub/google/cloud/pubsub/_http.py @@ -34,12 +34,8 @@ class Connection(_http.JSONConnection): """A connection to Google Cloud Pub/Sub via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. + :type client: :class:`~google.cloud.pubsub.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://' + PUBSUB_API_HOST @@ -51,12 +47,8 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - SCOPE = ('https://www.googleapis.com/auth/pubsub', - 'https://www.googleapis.com/auth/cloud-platform') - """The scopes required for authenticating as a Cloud Pub/Sub consumer.""" - - def __init__(self, credentials=None, http=None): - super(Connection, self).__init__(credentials=credentials, http=http) + def __init__(self, client): + super(Connection, self).__init__(client) emulator_host = os.getenv(PUBSUB_EMULATOR) if emulator_host is None: self.host = self.__class__.API_BASE_URL diff --git a/pubsub/google/cloud/pubsub/client.py b/pubsub/google/cloud/pubsub/client.py index 60c4a510d1fa..f92182d7ef4e 100644 --- a/pubsub/google/cloud/pubsub/client.py +++ b/pubsub/google/cloud/pubsub/client.py @@ -75,12 +75,15 @@ class Client(ClientWithProject): _subscriber_api = None _iam_policy_api = None + SCOPE = ('https://www.googleapis.com/auth/pubsub', + 'https://www.googleapis.com/auth/cloud-platform') + """The scopes required for authenticating as a Cloud Pub/Sub consumer.""" + def __init__(self, project=None, credentials=None, http=None, use_gax=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) if use_gax is None: self._use_gax = _USE_GAX else: @@ -96,7 +99,7 @@ def publisher_api(self): host=self._connection.host) else: generated = make_gax_publisher_api( - credentials=self._connection._credentials) + credentials=self._credentials) self._publisher_api = GAXPublisherAPI(generated, self) else: self._publisher_api = JSONPublisherAPI(self) @@ -112,7 +115,7 @@ def subscriber_api(self): host=self._connection.host) else: generated = make_gax_subscriber_api( - credentials=self._connection._credentials) + credentials=self._credentials) self._subscriber_api = GAXSubscriberAPI(generated, self) else: self._subscriber_api = JSONSubscriberAPI(self) diff --git a/pubsub/unit_tests/test__http.py b/pubsub/unit_tests/test__http.py index 72847782c4f0..69130536608e 100644 --- a/pubsub/unit_tests/test__http.py +++ b/pubsub/unit_tests/test__http.py @@ -46,7 +46,7 @@ def _get_target_class(): return Connection def test_default_url(self): - conn = self._make_one() + conn = self._make_one(object()) klass = self._get_target_class() self.assertEqual(conn.api_base_url, klass.API_BASE_URL) @@ -57,14 +57,14 @@ def test_custom_url_from_env(self): fake_environ = {PUBSUB_EMULATOR: HOST} with mock.patch('os.environ', new=fake_environ): - conn = self._make_one() + conn = self._make_one(object()) klass = self._get_target_class() self.assertNotEqual(conn.api_base_url, klass.API_BASE_URL) self.assertEqual(conn.api_base_url, 'http://' + HOST) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, conn.API_VERSION, @@ -76,7 +76,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) @@ -88,7 +88,7 @@ def test_build_api_url_w_extra_query_params(self): def test_build_api_url_w_base_url_override(self): base_url1 = 'api-base-url1' base_url2 = 'api-base-url2' - conn = self._make_one() + conn = self._make_one(object()) conn.api_base_url = base_url1 URI = '/'.join([ base_url2, diff --git a/resource_manager/google/cloud/resource_manager/__init__.py b/resource_manager/google/cloud/resource_manager/__init__.py index 1686b92f734a..9b172f4f3223 100644 --- a/resource_manager/google/cloud/resource_manager/__init__.py +++ b/resource_manager/google/cloud/resource_manager/__init__.py @@ -20,4 +20,4 @@ from google.cloud.resource_manager.project import Project -SCOPE = Connection.SCOPE +SCOPE = Client.SCOPE diff --git a/resource_manager/google/cloud/resource_manager/client.py b/resource_manager/google/cloud/resource_manager/client.py index a9f78d6a0cb4..e1258ea1af70 100644 --- a/resource_manager/google/cloud/resource_manager/client.py +++ b/resource_manager/google/cloud/resource_manager/client.py @@ -47,11 +47,13 @@ class Client(BaseClient): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) + """The scopes required for authenticating as a Resouce Manager consumer.""" + def __init__(self, credentials=None, http=None): super(Client, self).__init__( credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def new_project(self, project_id, name=None, labels=None): """Create a project bound to the current client. diff --git a/resource_manager/google/cloud/resource_manager/connection.py b/resource_manager/google/cloud/resource_manager/connection.py index ee3bbfcc3e9a..803be52d4f9b 100644 --- a/resource_manager/google/cloud/resource_manager/connection.py +++ b/resource_manager/google/cloud/resource_manager/connection.py @@ -21,12 +21,8 @@ class Connection(_http.JSONConnection): """A connection to Google Cloud Resource Manager via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. + :type client: :class:`~google.cloud.resource_manager.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://cloudresourcemanager.googleapis.com' @@ -37,6 +33,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) - """The scopes required for authenticating as a Resouce Manager consumer.""" diff --git a/resource_manager/unit_tests/test_client.py b/resource_manager/unit_tests/test_client.py index 95c7c90cff49..4425e76c5f9f 100644 --- a/resource_manager/unit_tests/test_client.py +++ b/resource_manager/unit_tests/test_client.py @@ -41,8 +41,8 @@ def test_constructor(self): credentials = _make_credentials() client = self._make_one(credentials=credentials, http=http) self.assertIsInstance(client._connection, Connection) - self.assertEqual(client._connection._credentials, credentials) - self.assertEqual(client._connection._http, http) + self.assertIs(client._credentials, credentials) + self.assertIs(client._http_internal, http) def test_new_project_factory(self): from google.cloud.resource_manager.project import Project diff --git a/resource_manager/unit_tests/test_connection.py b/resource_manager/unit_tests/test_connection.py index 5f967b262ebf..59747cd0541b 100644 --- a/resource_manager/unit_tests/test_connection.py +++ b/resource_manager/unit_tests/test_connection.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, conn.API_VERSION, @@ -39,7 +39,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) diff --git a/runtimeconfig/google/cloud/runtimeconfig/client.py b/runtimeconfig/google/cloud/runtimeconfig/client.py index 0dc95e655dd8..1f40b62895a5 100644 --- a/runtimeconfig/google/cloud/runtimeconfig/client.py +++ b/runtimeconfig/google/cloud/runtimeconfig/client.py @@ -42,11 +42,13 @@ class Client(ClientWithProject): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/cloudruntimeconfig',) + """The scopes required for authenticating as a RuntimeConfig consumer.""" + def __init__(self, project=None, credentials=None, http=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def config(self, config_name): """Factory constructor for config object. diff --git a/runtimeconfig/google/cloud/runtimeconfig/connection.py b/runtimeconfig/google/cloud/runtimeconfig/connection.py index 5074158c5f47..a3caf3ff7ed0 100644 --- a/runtimeconfig/google/cloud/runtimeconfig/connection.py +++ b/runtimeconfig/google/cloud/runtimeconfig/connection.py @@ -22,16 +22,8 @@ class Connection(_http.JSONConnection): """A connection to Google Cloud RuntimeConfig via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. - - :type api_base_url: str - :param api_base_url: The base of the API call URL. Defaults to the value - :attr:`Connection.API_BASE_URL`. + :type client: :class:`~google.cloud.runtimeconfig.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://runtimeconfig.googleapis.com' @@ -42,6 +34,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloudruntimeconfig',) - """The scopes required for authenticating as a RuntimeConfig consumer.""" diff --git a/runtimeconfig/unit_tests/test_connection.py b/runtimeconfig/unit_tests/test_connection.py index e8fe17a439fe..c89465fc0603 100644 --- a/runtimeconfig/unit_tests/test_connection.py +++ b/runtimeconfig/unit_tests/test_connection.py @@ -27,6 +27,6 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_default_url(self): - creds = object() - conn = self._make_one(creds) - self.assertEqual(conn.credentials, creds) + client = object() + conn = self._make_one(client) + self.assertIs(conn._client, client) diff --git a/speech/google/cloud/speech/client.py b/speech/google/cloud/speech/client.py index 0bf96c68e100..abb15be6aace 100644 --- a/speech/google/cloud/speech/client.py +++ b/speech/google/cloud/speech/client.py @@ -55,14 +55,14 @@ class Client(BaseClient): variable """ + SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) + """The scopes required for authenticating as an API consumer.""" + _speech_api = None def __init__(self, credentials=None, http=None, use_gax=None): super(Client, self).__init__(credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, - http=self._http, - ) + self._connection = Connection(self) # Save on the actual client class whether we use GAX or not. if use_gax is None: diff --git a/speech/google/cloud/speech/connection.py b/speech/google/cloud/speech/connection.py index c4661c01e9d5..0067cc0aae7d 100644 --- a/speech/google/cloud/speech/connection.py +++ b/speech/google/cloud/speech/connection.py @@ -18,7 +18,11 @@ class Connection(_http.JSONConnection): - """A connection to Google Cloud Speech JSON REST API.""" + """A connection to Google Cloud Speech JSON REST API. + + :type client: :class:`~google.cloud.speech.client.Client` + :param client: The client that owns the current connection. + """ API_BASE_URL = 'https://speech.googleapis.com' """The base of the API call URL.""" @@ -28,6 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}/{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) - """The scopes required for authenticating as an API consumer.""" diff --git a/speech/unit_tests/test_connection.py b/speech/unit_tests/test_connection.py index 1d2b40489c8b..f7b2d618b831 100644 --- a/speech/unit_tests/test_connection.py +++ b/speech/unit_tests/test_connection.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url(self): - conn = self._make_one() + conn = self._make_one(object()) method = 'speech:syncrecognize' uri = '/'.join([ conn.API_BASE_URL, diff --git a/storage/google/cloud/storage/_http.py b/storage/google/cloud/storage/_http.py index 9deaf4fd37ca..5137082f955d 100644 --- a/storage/google/cloud/storage/_http.py +++ b/storage/google/cloud/storage/_http.py @@ -20,12 +20,8 @@ class Connection(_http.JSONConnection): """A connection to Google Cloud Storage via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. + :type client: :class:`~google.cloud.storage.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = _http.API_BASE_URL @@ -36,8 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/storage/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/devstorage.full_control', - 'https://www.googleapis.com/auth/devstorage.read_only', - 'https://www.googleapis.com/auth/devstorage.read_write') - """The scopes required for authenticating as a Cloud Storage consumer.""" diff --git a/storage/google/cloud/storage/batch.py b/storage/google/cloud/storage/batch.py index a68d9cdc3ea6..146c52a227bc 100644 --- a/storage/google/cloud/storage/batch.py +++ b/storage/google/cloud/storage/batch.py @@ -132,8 +132,7 @@ class Batch(Connection): _MAX_BATCH_SIZE = 1000 def __init__(self, client): - super(Batch, self).__init__() - self._client = client + super(Batch, self).__init__(client) self._requests = [] self._target_objects = [] diff --git a/storage/google/cloud/storage/client.py b/storage/google/cloud/storage/client.py index 624954e6e7f0..6af7ac650dd3 100644 --- a/storage/google/cloud/storage/client.py +++ b/storage/google/cloud/storage/client.py @@ -46,12 +46,16 @@ class Client(ClientWithProject): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/devstorage.full_control', + 'https://www.googleapis.com/auth/devstorage.read_only', + 'https://www.googleapis.com/auth/devstorage.read_write') + """The scopes required for authenticating as a Cloud Storage consumer.""" + def __init__(self, project=None, credentials=None, http=None): self._base_connection = None super(Client, self).__init__(project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) self._batch_stack = _LocalStack() @property diff --git a/storage/unit_tests/test__http.py b/storage/unit_tests/test__http.py index 3bd1f0031923..ca9bde20cbd3 100644 --- a/storage/unit_tests/test__http.py +++ b/storage/unit_tests/test__http.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, 'storage', @@ -40,7 +40,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) uri = conn.build_api_url('/foo', {'bar': 'baz'}) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) diff --git a/storage/unit_tests/test_batch.py b/storage/unit_tests/test_batch.py index 8557a1a48a46..41861f3c18d7 100644 --- a/storage/unit_tests/test_batch.py +++ b/storage/unit_tests/test_batch.py @@ -399,7 +399,7 @@ def test_as_context_mgr_wo_error(self): project = 'PROJECT' credentials = _make_credentials() client = Client(project=project, credentials=credentials) - client._base_connection._http = http + client._http_internal = http self.assertEqual(list(client._batch_stack), []) diff --git a/storage/unit_tests/test_client.py b/storage/unit_tests/test_client.py index 8ee437fc0ec8..9696d4e5fa51 100644 --- a/storage/unit_tests/test_client.py +++ b/storage/unit_tests/test_client.py @@ -140,7 +140,7 @@ def test_get_bucket_miss(self): 'b', 'nonesuch?projection=noAcl', ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '404', 'content-type': 'application/json'}, b'{}', ) @@ -163,7 +163,7 @@ def test_get_bucket_hit(self): 'b', '%s?projection=noAcl' % (BLOB_NAME,), ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, '{{"name": "{0}"}}'.format(BLOB_NAME).encode('utf-8'), ) @@ -187,7 +187,7 @@ def test_lookup_bucket_miss(self): 'b', 'nonesuch?projection=noAcl', ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '404', 'content-type': 'application/json'}, b'{}', ) @@ -211,7 +211,7 @@ def test_lookup_bucket_hit(self): 'b', '%s?projection=noAcl' % (BLOB_NAME,), ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, '{{"name": "{0}"}}'.format(BLOB_NAME).encode('utf-8'), ) @@ -236,7 +236,7 @@ def test_create_bucket_conflict(self): client._connection.API_VERSION, 'b?project=%s' % (PROJECT,), ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '409', 'content-type': 'application/json'}, '{"error": {"message": "Conflict"}}', ) @@ -259,7 +259,7 @@ def test_create_bucket_success(self): client._connection.API_VERSION, 'b?project=%s' % (PROJECT,), ]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, '{{"name": "{0}"}}'.format(BLOB_NAME).encode('utf-8'), ) @@ -282,7 +282,7 @@ def test_list_buckets_empty(self): 'project': [PROJECT], 'projection': ['noAcl'], } - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, b'{}', ) @@ -319,7 +319,7 @@ def test_list_buckets_non_empty(self): client._connection.API_VERSION, ]) URI = '/'.join([BASE_URI, 'b?%s' % (query_params,)]) - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, '{{"items": [{{"name": "{0}"}}]}}'.format(BUCKET_NAME) .encode('utf-8'), @@ -354,7 +354,7 @@ def test_list_buckets_all_arguments(self): 'fields': [FIELDS], } - http = client._connection._http = _Http( + http = client._http_internal = _Http( {'status': '200', 'content-type': 'application/json'}, '{"items": []}', ) diff --git a/translate/google/cloud/translate/client.py b/translate/google/cloud/translate/client.py index c8c915118cab..12e64c403f5e 100644 --- a/translate/google/cloud/translate/client.py +++ b/translate/google/cloud/translate/client.py @@ -54,12 +54,14 @@ class Client(BaseClient): ``credentials`` for the current object. """ + SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) + """The scopes required for authenticating.""" + def __init__(self, target_language=ENGLISH_ISO_639, credentials=None, http=None): self.target_language = target_language super(Client, self).__init__(credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) def get_languages(self, target_language=None): """Get list of supported languages for translation. diff --git a/translate/google/cloud/translate/connection.py b/translate/google/cloud/translate/connection.py index 518e7e424a93..25be55217e32 100644 --- a/translate/google/cloud/translate/connection.py +++ b/translate/google/cloud/translate/connection.py @@ -18,7 +18,11 @@ class Connection(_http.JSONConnection): - """A connection to Google Cloud Translation API via the JSON REST API.""" + """A connection to Google Cloud Translation API via the JSON REST API. + + :type client: :class:`~google.cloud.translate.client.Client` + :param client: The client that owns the current connection. + """ API_BASE_URL = 'https://translation.googleapis.com' """The base of the API call URL.""" @@ -28,6 +32,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/language/translate/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) - """The scopes required for authenticating.""" diff --git a/translate/unit_tests/test_connection.py b/translate/unit_tests/test_connection.py index 73efadad26a3..e7c9e58ea6f3 100644 --- a/translate/unit_tests/test_connection.py +++ b/translate/unit_tests/test_connection.py @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_build_api_url_no_extra_query_params(self): - conn = self._make_one() + conn = self._make_one(object()) URI = '/'.join([ conn.API_BASE_URL, 'language', @@ -41,7 +41,7 @@ def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import urlsplit - conn = self._make_one() + conn = self._make_one(object()) query_params = [('q', 'val1'), ('q', 'val2')] uri = conn.build_api_url('/foo', query_params=query_params) scheme, netloc, path, qs, _ = urlsplit(uri) diff --git a/vision/google/cloud/vision/client.py b/vision/google/cloud/vision/client.py index 4d0a5771300c..518522963197 100644 --- a/vision/google/cloud/vision/client.py +++ b/vision/google/cloud/vision/client.py @@ -55,14 +55,17 @@ class Client(ClientWithProject): falls back to the ``GOOGLE_CLOUD_DISABLE_GRPC`` environment variable """ + + SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) + """The scopes required for authenticating as a Cloud Vision consumer.""" + _vision_api_internal = None def __init__(self, project=None, credentials=None, http=None, use_gax=None): super(Client, self).__init__( project=project, credentials=credentials, http=http) - self._connection = Connection( - credentials=self._credentials, http=self._http) + self._connection = Connection(self) if use_gax is None: self._use_gax = _USE_GAX else: diff --git a/vision/google/cloud/vision/connection.py b/vision/google/cloud/vision/connection.py index 0cf38b7b404f..7983c93eceea 100644 --- a/vision/google/cloud/vision/connection.py +++ b/vision/google/cloud/vision/connection.py @@ -22,16 +22,8 @@ class Connection(_http.JSONConnection): """A connection to Google Cloud Vision via the JSON REST API. - :type credentials: :class:`oauth2client.client.OAuth2Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - connection. - - :type http: :class:`httplib2.Http` or class that defines ``request()``. - :param http: (Optional) HTTP object to make requests. - - :type api_base_url: str - :param api_base_url: The base of the API call URL. Defaults to the value - :attr:`Connection.API_BASE_URL`. + :type client: :class:`~google.cloud.vision.client.Client` + :param client: The client that owns the current connection. """ API_BASE_URL = 'https://vision.googleapis.com' @@ -42,6 +34,3 @@ class Connection(_http.JSONConnection): API_URL_TEMPLATE = '{api_base_url}/{api_version}{path}' """A template for the URL of a particular API call.""" - - SCOPE = ('https://www.googleapis.com/auth/cloud-platform',) - """The scopes required for authenticating as a Cloud Vision consumer.""" diff --git a/vision/unit_tests/test_connection.py b/vision/unit_tests/test_connection.py index f00e81f3de90..042784336c59 100644 --- a/vision/unit_tests/test_connection.py +++ b/vision/unit_tests/test_connection.py @@ -26,6 +26,6 @@ def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) def test_default_url(self): - creds = object() - conn = self._make_one(creds) - self.assertEqual(conn.credentials, creds) + client = object() + conn = self._make_one(client) + self.assertEqual(conn._client, client)