From f24256a7a6db62cdeb972425d2836bab19612cd7 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 21 Feb 2017 09:23:32 -0800 Subject: [PATCH 1/5] Add 'gccl' metrics header to Spanner client library. --- spanner/google/cloud/spanner/__init__.py | 4 +++ spanner/google/cloud/spanner/client.py | 11 ++++-- spanner/google/cloud/spanner/database.py | 4 ++- spanner/unit_tests/test_client.py | 43 ++++++++++++++++++++++-- spanner/unit_tests/test_database.py | 6 +++- 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/spanner/google/cloud/spanner/__init__.py b/spanner/google/cloud/spanner/__init__.py index 6c5f790366b9..25e8ec04c238 100644 --- a/spanner/google/cloud/spanner/__init__.py +++ b/spanner/google/cloud/spanner/__init__.py @@ -15,6 +15,10 @@ """Cloud Spanner API package.""" +import pkg_resources +__version__ = pkg_resources.get_distribution('google-cloud-spanner').version + + from google.cloud.spanner.client import Client from google.cloud.spanner.keyset import KeyRange diff --git a/spanner/google/cloud/spanner/client.py b/spanner/google/cloud/spanner/client.py index 95e5bac86f5a..61fa05a0f961 100644 --- a/spanner/google/cloud/spanner/client.py +++ b/spanner/google/cloud/spanner/client.py @@ -38,6 +38,7 @@ from google.cloud.client import _ClientProjectMixin from google.cloud.credentials import get_credentials from google.cloud.iterator import GAXIterator +from google.cloud.spanner import __version__ from google.cloud.spanner._helpers import _options_with_prefix from google.cloud.spanner.instance import DEFAULT_NODE_COUNT from google.cloud.spanner.instance import Instance @@ -152,14 +153,20 @@ def project_name(self): def instance_admin_api(self): """Helper for session-related API calls.""" if self._instance_admin_api is None: - self._instance_admin_api = InstanceAdminClient() + self._instance_admin_api = InstanceAdminClient( + lib_name='gccl', + lib_version=__version__, + ) return self._instance_admin_api @property def database_admin_api(self): """Helper for session-related API calls.""" if self._database_admin_api is None: - self._database_admin_api = DatabaseAdminClient() + self._database_admin_api = DatabaseAdminClient( + lib_name='gccl', + lib_version=__version__, + ) return self._database_admin_api def copy(self): diff --git a/spanner/google/cloud/spanner/database.py b/spanner/google/cloud/spanner/database.py index 16864b4b0c78..b447e6088449 100644 --- a/spanner/google/cloud/spanner/database.py +++ b/spanner/google/cloud/spanner/database.py @@ -30,6 +30,7 @@ from google.cloud.exceptions import Conflict from google.cloud.exceptions import NotFound from google.cloud.operation import register_type +from google.cloud.spanner import __version__ from google.cloud.spanner._helpers import _options_with_prefix from google.cloud.spanner.batch import Batch from google.cloud.spanner.session import Session @@ -179,7 +180,8 @@ def ddl_statements(self): def spanner_api(self): """Helper for session-related API calls.""" if self._spanner_api is None: - self._spanner_api = SpannerClient() + self._spanner_api = SpannerClient(lib_name='gccl', + lib_version=__version__) return self._spanner_api def __eq__(self, other): diff --git a/spanner/unit_tests/test_client.py b/spanner/unit_tests/test_client.py index 9383471f7fdb..6f9fce2abf3e 100644 --- a/spanner/unit_tests/test_client.py +++ b/spanner/unit_tests/test_client.py @@ -106,6 +106,43 @@ def test_constructor_credentials_wo_create_scoped(self): expected_scopes = None self._constructor_test_helper(expected_scopes, creds) + def test_admin_api_lib_name(self): + """Establish that the lib_name and lib_version are passed to + the database and instance API objects in the GAPIC. + """ + from google.cloud.spanner import __version__ + from google.cloud.spanner import client as MUT + from google.cloud.gapic.spanner_admin_database import v1 as db + from google.cloud.gapic.spanner_admin_instance import v1 as inst + + # Get the actual admin client classes. + DatabaseAdminClient = db.database_admin_client.DatabaseAdminClient + InstanceAdminClient = inst.instance_admin_client.InstanceAdminClient + + # Test that the DatabaseAdminClient is called with the gccl library + # name and version. + with mock.patch.object(DatabaseAdminClient, '__init__') as mock_dac: + mock_dac.return_value = None + client = self._make_one() + self.assertIsInstance(client.database_admin_api, + DatabaseAdminClient) + mock_dac.assert_called_once() + self.assertEqual(mock_dac.mock_calls[0][2]['lib_name'], 'gccl') + self.assertEqual(mock_dac.mock_calls[0][2]['lib_version'], + __version__) + + # Test that the InstanceAdminClient is called with the gccl library + # name and version. + with mock.patch.object(InstanceAdminClient, '__init__') as mock_iac: + mock_iac.return_value = None + client = self._make_one() + self.assertIsInstance(client.instance_admin_api, + InstanceAdminClient) + mock_iac.assert_called_once() + self.assertEqual(mock_iac.mock_calls[0][2]['lib_name'], 'gccl') + self.assertEqual(mock_iac.mock_calls[0][2]['lib_version'], + __version__) + def test_instance_admin_api(self): from google.cloud._testing import _Monkey from google.cloud.spanner import client as MUT @@ -114,7 +151,8 @@ def test_instance_admin_api(self): client = self._make_one(project=self.PROJECT, credentials=creds) class _Client(object): - pass + def __init__(self, *args, **kwargs): + pass with _Monkey(MUT, InstanceAdminClient=_Client): api = client.instance_admin_api @@ -131,7 +169,8 @@ def test_database_admin_api(self): client = self._make_one(project=self.PROJECT, credentials=creds) class _Client(object): - pass + def __init__(self, *args, **kwargs): + pass with _Monkey(MUT, DatabaseAdminClient=_Client): api = client.database_admin_api diff --git a/spanner/unit_tests/test_database.py b/spanner/unit_tests/test_database.py index 9d36e6635509..bb04af090b7b 100644 --- a/spanner/unit_tests/test_database.py +++ b/spanner/unit_tests/test_database.py @@ -17,6 +17,8 @@ import mock +from google.cloud.spanner import __version__ + from google.cloud._testing import _GAXBaseAPI @@ -188,7 +190,9 @@ def test_spanner_api_property(self): _client = object() _clients = [_client] - def _mock_spanner_client(): + def _mock_spanner_client(*args, **kwargs): + self.assertEqual(kwargs['lib_name'], 'gccl') + self.assertEqual(kwargs['lib_version'], __version__) return _clients.pop(0) with _Monkey(MUT, SpannerClient=_mock_spanner_client): From 92ac89409455457622a671588e75e8777fbb8fd2 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 21 Feb 2017 09:27:54 -0800 Subject: [PATCH 2/5] Bump version to 0.23.2. --- spanner/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spanner/setup.py b/spanner/setup.py index e864c977f415..2cac3d85a5e8 100644 --- a/spanner/setup.py +++ b/spanner/setup.py @@ -59,7 +59,7 @@ setup( name='google-cloud-spanner', - version='0.23.1', + version='0.23.2', description='Python Client for Cloud Spanner', long_description=README, namespace_packages=[ From 87b2c556ce532a5b9e6fa7ce25f4ba706f9c8f1a Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 21 Feb 2017 09:47:08 -0800 Subject: [PATCH 3/5] Address feedback and CI issues. --- spanner/google/cloud/spanner/database.py | 4 ++-- spanner/unit_tests/test_client.py | 19 ++++++++++++++----- spanner/unit_tests/test_database.py | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/spanner/google/cloud/spanner/database.py b/spanner/google/cloud/spanner/database.py index b447e6088449..fef8594cae4b 100644 --- a/spanner/google/cloud/spanner/database.py +++ b/spanner/google/cloud/spanner/database.py @@ -180,8 +180,8 @@ def ddl_statements(self): def spanner_api(self): """Helper for session-related API calls.""" if self._spanner_api is None: - self._spanner_api = SpannerClient(lib_name='gccl', - lib_version=__version__) + self._spanner_api = SpannerClient( + lib_name='gccl', lib_version=__version__) return self._spanner_api def __eq__(self, other): diff --git a/spanner/unit_tests/test_client.py b/spanner/unit_tests/test_client.py index 6f9fce2abf3e..32529dc652b3 100644 --- a/spanner/unit_tests/test_client.py +++ b/spanner/unit_tests/test_client.py @@ -111,7 +111,6 @@ def test_admin_api_lib_name(self): the database and instance API objects in the GAPIC. """ from google.cloud.spanner import __version__ - from google.cloud.spanner import client as MUT from google.cloud.gapic.spanner_admin_database import v1 as db from google.cloud.gapic.spanner_admin_instance import v1 as inst @@ -123,7 +122,10 @@ def test_admin_api_lib_name(self): # name and version. with mock.patch.object(DatabaseAdminClient, '__init__') as mock_dac: mock_dac.return_value = None - client = self._make_one() + client = self._make_one( + credentials=_make_credentials(), + project='foo', + ) self.assertIsInstance(client.database_admin_api, DatabaseAdminClient) mock_dac.assert_called_once() @@ -135,7 +137,10 @@ def test_admin_api_lib_name(self): # name and version. with mock.patch.object(InstanceAdminClient, '__init__') as mock_iac: mock_iac.return_value = None - client = self._make_one() + client = self._make_one( + credentials=_make_credentials(), + project='foo', + ) self.assertIsInstance(client.instance_admin_api, InstanceAdminClient) mock_iac.assert_called_once() @@ -152,7 +157,8 @@ def test_instance_admin_api(self): class _Client(object): def __init__(self, *args, **kwargs): - pass + self.args = args + self.kwargs = kwargs with _Monkey(MUT, InstanceAdminClient=_Client): api = client.instance_admin_api @@ -160,6 +166,7 @@ def __init__(self, *args, **kwargs): self.assertTrue(isinstance(api, _Client)) again = client.instance_admin_api self.assertTrue(again is api) + self.assertEqual(api.kwargs['lib_name'], 'gccl') def test_database_admin_api(self): from google.cloud._testing import _Monkey @@ -170,7 +177,8 @@ def test_database_admin_api(self): class _Client(object): def __init__(self, *args, **kwargs): - pass + self.args = args + self.kwargs = kwargs with _Monkey(MUT, DatabaseAdminClient=_Client): api = client.database_admin_api @@ -178,6 +186,7 @@ def __init__(self, *args, **kwargs): self.assertTrue(isinstance(api, _Client)) again = client.database_admin_api self.assertTrue(again is api) + self.assertEqual(api.kwargs['lib_name'], 'gccl') def test_copy(self): credentials = _Credentials('value') diff --git a/spanner/unit_tests/test_database.py b/spanner/unit_tests/test_database.py index bb04af090b7b..8804e71bf667 100644 --- a/spanner/unit_tests/test_database.py +++ b/spanner/unit_tests/test_database.py @@ -191,6 +191,7 @@ def test_spanner_api_property(self): _clients = [_client] def _mock_spanner_client(*args, **kwargs): + self.assertIsInstance(args, (list, tuple)) self.assertEqual(kwargs['lib_name'], 'gccl') self.assertEqual(kwargs['lib_version'], __version__) return _clients.pop(0) From 4839c1229c13a2f8da39885cca3128877fbe33b8 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Wed, 22 Feb 2017 07:31:33 -0800 Subject: [PATCH 4/5] *args is always a tuple. --- spanner/unit_tests/test_database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spanner/unit_tests/test_database.py b/spanner/unit_tests/test_database.py index 8804e71bf667..a7a2a174116c 100644 --- a/spanner/unit_tests/test_database.py +++ b/spanner/unit_tests/test_database.py @@ -191,7 +191,7 @@ def test_spanner_api_property(self): _clients = [_client] def _mock_spanner_client(*args, **kwargs): - self.assertIsInstance(args, (list, tuple)) + self.assertIsInstance(args, tuple) self.assertEqual(kwargs['lib_name'], 'gccl') self.assertEqual(kwargs['lib_version'], __version__) return _clients.pop(0) From 5039a6e193dd51cba192dbe2c5e0ce51275b5066 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Wed, 22 Feb 2017 07:35:07 -0800 Subject: [PATCH 5/5] Remove test docstring. --- spanner/unit_tests/test_client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/spanner/unit_tests/test_client.py b/spanner/unit_tests/test_client.py index 32529dc652b3..0b0bafd4a967 100644 --- a/spanner/unit_tests/test_client.py +++ b/spanner/unit_tests/test_client.py @@ -107,9 +107,6 @@ def test_constructor_credentials_wo_create_scoped(self): self._constructor_test_helper(expected_scopes, creds) def test_admin_api_lib_name(self): - """Establish that the lib_name and lib_version are passed to - the database and instance API objects in the GAPIC. - """ from google.cloud.spanner import __version__ from google.cloud.gapic.spanner_admin_database import v1 as db from google.cloud.gapic.spanner_admin_instance import v1 as inst