Skip to content

Commit

Permalink
Adding lazy loading __user_agent__ to main package.
Browse files Browse the repository at this point in the history
Fixes #566.
  • Loading branch information
dhermes committed Jan 29, 2015
1 parent d858ff0 commit a53d2c8
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 21 deletions.
83 changes: 83 additions & 0 deletions gcloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,87 @@

from pkg_resources import get_distribution

import httplib
import socket

try:
from google import appengine
except ImportError:
appengine = None


__version__ = get_distribution('gcloud').version


class _LazyUserAgent(object):
"""Helper for extra environment information."""

_curr_environ = None
_user_agent = None

def __init__(self):
self._curr_environ = self.environ_at_init()

@staticmethod
def environ_at_init():
"""Checks environment variables during instance initialization.
Intended to infer as much as possible from the environment without
running code.
:rtype: string or ``NoneType``
:returns: Either ``'-GAE'`` if on App Engine else ``None``
"""
if appengine is not None:
return '-GAE'

def environ_post_init(self):
"""Checks environment variables after instance initialization.
This is meant for checks which can't be performed instantaneously.
:rtype: string
:returns: Either ``'-GCE'`` if on Compute Engine else an empty string.
"""
gce_environ = self.check_compute_engine()
if gce_environ is not None:
return gce_environ

return ''

@staticmethod
def check_compute_engine():
"""Checks if the current environment is Compute Engine.
:rtype: string or ``NoneType``
:returns: The string ``'-GCE'`` if on Compute Engine else ``None``.
"""
host = '169.254.169.254'
uri_path = '/computeMetadata/v1/project/project-id'
headers = {'Metadata-Flavor': 'Google'}
connection = httplib.HTTPConnection(host, timeout=0.1)
try:
connection.request('GET', uri_path, headers=headers)
response = connection.getresponse()
if response.status == 200:
return '-GCE'
except socket.error: # Expect timeout or host is down
pass
finally:
connection.close()

def __str__(self):
if self._curr_environ is None:
self._curr_environ = self.environ_post_init()

if self._user_agent is None:
self._user_agent = "gcloud-python/{0}{1}".format(
__version__, self._curr_environ)

return self._user_agent

def __repr__(self):
return '<_LazyUserAgent: %r>' % (self.__str__(),)


__user_agent__ = _LazyUserAgent()
9 changes: 3 additions & 6 deletions gcloud/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

""" Shared implementation of connections to API servers."""

from pkg_resources import get_distribution

import httplib2

import gcloud


class Connection(object):
"""A generic connection to Google Cloud Platform.
Expand All @@ -29,10 +29,7 @@ class Connection(object):
API_BASE_URL = 'https://www.googleapis.com'
"""The base of the API call URL."""

_EMPTY = object()
"""A pointer to represent an empty value for default arguments."""

USER_AGENT = "gcloud-python/{0}".format(get_distribution('gcloud').version)
USER_AGENT = gcloud.__user_agent__
"""The user agent for gcloud-python requests."""

def __init__(self, credentials=None):
Expand Down
2 changes: 1 addition & 1 deletion gcloud/datastore/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _request(self, dataset_id, method, data):
headers = {
'Content-Type': 'application/x-protobuf',
'Content-Length': str(len(data)),
'User-Agent': self.USER_AGENT,
'User-Agent': str(self.USER_AGENT),
}
headers, content = self.http.request(
uri=self.build_api_url(dataset_id=dataset_id, method=method),
Expand Down
8 changes: 6 additions & 2 deletions gcloud/datastore/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ def _make_query_pb(self, kind):
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def _user_agent(self):
import gcloud
return "gcloud-python/{0}".format(gcloud.__version__)

def _verifyProtobufCall(self, called_with, URI, conn):
self.assertEqual(called_with['uri'], URI)
self.assertEqual(called_with['method'], 'POST')
self.assertEqual(called_with['headers']['Content-Type'],
'application/x-protobuf')
self.assertEqual(called_with['headers']['User-Agent'],
conn.USER_AGENT)
self._user_agent())

def test_ctor_defaults(self):
conn = self._makeOne()
Expand Down Expand Up @@ -404,7 +408,7 @@ def test_lookup_multiple_keys_w_deferred(self):
self.assertEqual(cw['method'], 'POST')
self.assertEqual(cw['headers']['Content-Type'],
'application/x-protobuf')
self.assertEqual(cw['headers']['User-Agent'], conn.USER_AGENT)
self.assertEqual(cw['headers']['User-Agent'], self._user_agent())
rq_class = datastore_pb.LookupRequest
request = rq_class()
request.ParseFromString(cw['body'])
Expand Down
2 changes: 1 addition & 1 deletion gcloud/storage/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def upload_from_file(self, file_obj, rewind=False, size=None,
headers = {
'Accept': 'application/json',
'Accept-Encoding': 'gzip, deflate',
'User-Agent': conn.USER_AGENT,
'User-Agent': str(conn.USER_AGENT),
}

upload = transfer.Upload(file_obj,
Expand Down
2 changes: 1 addition & 1 deletion gcloud/storage/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def make_request(self, method, url, data=None, content_type=None,
if content_type:
headers['Content-Type'] = content_type

headers['User-Agent'] = self.USER_AGENT
headers['User-Agent'] = str(self.USER_AGENT)

return self.http.request(uri=url, method=method, headers=headers,
body=data)
Expand Down
16 changes: 10 additions & 6 deletions gcloud/storage/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ def test_build_api_url_w_upload(self):
])
self.assertEqual(conn.build_api_url('/foo', upload=True), URI)

def _user_agent(self):
import gcloud
return "gcloud-python/{0}".format(gcloud.__version__)

def test_make_request_no_data_no_content_type_no_headers(self):
PROJECT = 'project'
conn = self._makeOne(PROJECT)
Expand All @@ -194,7 +198,7 @@ def test_make_request_no_data_no_content_type_no_headers(self):
expected_headers = {
'Accept-Encoding': 'gzip',
'Content-Length': 0,
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand All @@ -214,7 +218,7 @@ def test_make_request_w_data_no_extra_headers(self):
'Accept-Encoding': 'gzip',
'Content-Length': 0,
'Content-Type': 'application/json',
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand All @@ -234,7 +238,7 @@ def test_make_request_w_extra_headers(self):
'Accept-Encoding': 'gzip',
'Content-Length': 0,
'X-Foo': 'foo',
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand All @@ -258,7 +262,7 @@ def test_api_request_defaults(self):
expected_headers = {
'Accept-Encoding': 'gzip',
'Content-Length': 0,
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand Down Expand Up @@ -305,7 +309,7 @@ def test_api_request_w_query_params(self):
expected_headers = {
'Accept-Encoding': 'gzip',
'Content-Length': 0,
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand Down Expand Up @@ -333,7 +337,7 @@ def test_api_request_w_data(self):
'Accept-Encoding': 'gzip',
'Content-Length': len(DATAJ),
'Content-Type': 'application/json',
'User-Agent': conn.USER_AGENT,
'User-Agent': self._user_agent(),
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand Down
9 changes: 5 additions & 4 deletions gcloud/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ def authorize(self, http):
self.assertTrue(isinstance(creds._called_with, Http))

def test_user_agent_format(self):
from pkg_resources import get_distribution
expected_ua = 'gcloud-python/{0}'.format(
get_distribution('gcloud').version)
import gcloud

expected_ua = 'gcloud-python/{0}'.format(gcloud.__version__)
conn = self._makeOne()
self.assertEqual(conn.USER_AGENT, expected_ua)
self.assertTrue(isinstance(conn.USER_AGENT, gcloud._LazyUserAgent))
self.assertEqual(str(conn.USER_AGENT), expected_ua)

0 comments on commit a53d2c8

Please sign in to comment.