Repository: googleapis/python-spanner-django

Issue:
dbapi: allow a configurable user-agent
Currently the user-agent says `spanner-django` given that this module was designed specifically for Django ORM as a backend but might be useful to ensure that anyone reusing spanner.dbapi can set their own application user-agent


Discussion:

Resolving patch:
diff --git a/spanner/dbapi/__init__.py b/spanner/dbapi/__init__.py
--- a/spanner/dbapi/__init__.py
+++ b/spanner/dbapi/__init__.py
@@ -31,7 +31,7 @@
 threadsafety = 0
 
 
-def connect(project=None, instance=None, database=None, credentials_uri=None):
+def connect(project=None, instance=None, database=None, credentials_uri=None, user_agent=None):
     """
     Connect to Cloud Spanner.
 
@@ -57,7 +57,7 @@ def connect(project=None, instance=None, database=None, credentials_uri=None):
 
     client_kwargs = {
         'project': project,
-        'client_info': google_client_info(),
+        'client_info': google_client_info(user_agent),
     }
     if credentials_uri:
         client = spanner.Client.from_service_account_json(credentials_uri, **client_kwargs)
@@ -78,7 +78,7 @@ def connect(project=None, instance=None, database=None, credentials_uri=None):
 __all__ = [
     'DatabaseError', 'DataError', 'Error', 'IntegrityError', 'InterfaceError',
     'InternalError', 'NotSupportedError', 'OperationalError', 'ProgrammingError',
-    'Warning', 'USER_AGENT', 'apilevel', 'connect', 'paramstyle', 'threadsafety',
+    'Warning', 'DEFAULT_USER_AGENT', 'apilevel', 'connect', 'paramstyle', 'threadsafety',
     'get_param_types',
     'Binary', 'Date', 'DateFromTicks', 'Time', 'TimeFromTicks', 'Timestamp',
     'TimestampFromTicks',
diff --git a/spanner/dbapi/version.py b/spanner/dbapi/version.py
--- a/spanner/dbapi/version.py
+++ b/spanner/dbapi/version.py
@@ -9,18 +9,18 @@
 from google.api_core.gapic_v1.client_info import ClientInfo
 
 VERSION = '0.0.1'
-USER_AGENT = 'spanner-django/' + VERSION
+DEFAULT_USER_AGENT = 'spanner-django/' + VERSION
 
 vers = sys.version_info
 
 
-def google_client_info():
+def google_client_info(user_agent=None):
     """
     Return a google.api_core.gapic_v1.client_info.ClientInfo
     containg the user_agent and python_version for this library
     """
 
     return ClientInfo(
-        user_agent=USER_AGENT,
+        user_agent=user_agent or DEFAULT_USER_AGENT,
         python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0),
     )
diff --git a/spanner/django/base.py b/spanner/django/base.py
--- a/spanner/django/base.py
+++ b/spanner/django/base.py
@@ -101,6 +101,7 @@ def get_connection_params(self):
             'project': self.settings_dict['PROJECT'],
             'instance': self.settings_dict['INSTANCE'],
             'database': self.settings_dict['NAME'],
+            'user_agent': 'spanner-django/0.0.1',
             **self.settings_dict['OPTIONS'],
         }
 


Test patch:
diff --git a/tests/spanner/dbapi/test_version.py b/tests/spanner/dbapi/test_version.py
--- a/tests/spanner/dbapi/test_version.py
+++ b/tests/spanner/dbapi/test_version.py
@@ -8,15 +8,24 @@
 from unittest import TestCase
 
 from google.api_core.gapic_v1.client_info import ClientInfo
-from spanner.dbapi.version import USER_AGENT, google_client_info
+from spanner.dbapi.version import DEFAULT_USER_AGENT, google_client_info
+
+vers = sys.version_info
 
 
 class VersionUtils(TestCase):
-    def test_google_client_info(self):
-        vers = sys.version_info
+    def test_google_client_info_default_useragent(self):
         got = google_client_info().to_grpc_metadata()
         want = ClientInfo(
-            user_agent=USER_AGENT,
+            user_agent=DEFAULT_USER_AGENT,
+            python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0),
+        ).to_grpc_metadata()
+        self.assertEqual(got, want)
+
+    def test_google_client_info_custom_useragent(self):
+        got = google_client_info('custom-user-agent').to_grpc_metadata()
+        want = ClientInfo(
+            user_agent='custom-user-agent',
             python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0),
         ).to_grpc_metadata()
         self.assertEqual(got, want)