Repository: googleapis/google-api-python-client

Issue:
Ensure client is reporting Python version
We should ensure that the version of Python being used is properly reported in the user agent for all requests.  Specifically we want to measure how much Python 2.7 vs 3.x usage we have.  This very well may already exist, but figured we should check to be sure :)


Discussion:

Resolving patch:
diff --git a/googleapiclient/model.py b/googleapiclient/model.py
--- a/googleapiclient/model.py
+++ b/googleapiclient/model.py
@@ -26,12 +26,14 @@
 
 import json
 import logging
+import platform
 
 from six.moves.urllib.parse import urlencode
 
 from googleapiclient import __version__
 from googleapiclient.errors import HttpError
 
+_PY_VERSION = platform.python_version()
 
 LOGGER = logging.getLogger(__name__)
 
@@ -144,7 +146,12 @@ def request(self, headers, path_params, query_params, body_value):
       headers['user-agent'] += ' '
     else:
       headers['user-agent'] = ''
-    headers['user-agent'] += 'google-api-python-client/%s (gzip)' % __version__
+    headers['user-agent'] += '(gzip)'
+    if 'x-goog-api-client' in headers:
+      headers['x-goog-api-client'] += ' '
+    else:
+      headers['x-goog-api-client'] = ''
+    headers['x-goog-api-client'] += 'gdcl/%s gl-python/%s' % (__version__, _PY_VERSION)
 
     if body_value is not None:
       headers['content-type'] = self.content_type


Test patch:
diff --git a/tests/test_http.py b/tests/test_http.py
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -485,26 +485,22 @@ def test_media_io_base_download_custom_request_headers(self):
     download = MediaIoBaseDownload(
         fd=self.fd, request=self.request, chunksize=3)
 
-    self.assertEqual(download._headers, {'Cache-Control':'no-store'})
+    self.assertEqual(download._headers.get('Cache-Control'), 'no-store')
 
     status, done = download.next_chunk()
 
-    result = self.fd.getvalue().decode('utf-8')
+    result = json.loads(self.fd.getvalue().decode('utf-8'))
 
-    # we abuse the internals of the object we're testing, pay no attention
-    # to the actual bytes= values here; we are just asserting that the
-    # header we added to the original request is sent up to the server
-    # on each call to next_chunk
+    # assert that that the header we added to the original request is
+    # sent up to the server on each call to next_chunk
 
-    self.assertEqual(json.loads(result),
-                     {"Cache-Control": "no-store", "range": "bytes=0-3"})
+    self.assertEqual(result.get("Cache-Control"), "no-store")
 
     download._fd = self.fd = BytesIO()
     status, done = download.next_chunk()
 
-    result = self.fd.getvalue().decode('utf-8')
-    self.assertEqual(json.loads(result),
-                     {"Cache-Control": "no-store", "range": "bytes=51-54"})
+    result = json.loads(self.fd.getvalue().decode('utf-8'))
+    self.assertEqual(result.get("Cache-Control"), "no-store")
 
   def test_media_io_base_download_handle_redirects(self):
     self.request.http = HttpMockSequence([
diff --git a/tests/test_json_model.py b/tests/test_json_model.py
--- a/tests/test_json_model.py
+++ b/tests/test_json_model.py
@@ -26,6 +26,7 @@
 import copy
 import json
 import os
+import platform
 import unittest2 as unittest
 import httplib2
 import googleapiclient.model
@@ -143,8 +144,22 @@ def test_user_agent(self):
         headers, path_params, query_params, body)
 
     self.assertEqual(headers['user-agent'],
-        'my-test-app/1.23.4 google-api-python-client/' + __version__ +
-        ' (gzip)')
+        'my-test-app/1.23.4 (gzip)')
+
+  def test_x_goog_api_client(self):
+    model = JsonModel(data_wrapper=False)
+
+    # test header composition for cloud clients that wrap discovery
+    headers = {'x-goog-api-client': 'gccl/1.23.4'}
+    path_params = {}
+    query_params = {}
+    body = {}
+
+    headers, unused_params, unused_query, body = model.request(
+        headers, path_params, query_params, body)
+
+    self.assertEqual(headers['x-goog-api-client'],
+        'gccl/1.23.4' + ' gdcl/' + __version__ + ' gl-python/' + platform.python_version())
 
   def test_bad_response(self):
     model = JsonModel(data_wrapper=False)