Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions gcloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,89 @@

from pkg_resources import get_distribution

import six
import socket

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


__version__ = get_distribution('gcloud').version


HTTPConnection = six.moves.http_client.HTTPConnection


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

_curr_environ = None
_user_agent = None

def __init__(self, wrapped):
self._property_name = wrapped.__name__
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 = 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 __get__(self, inst, objtype=None):
if inst is None:
return 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)

setattr(inst, self._property_name, self._user_agent)
return self._user_agent
15 changes: 7 additions & 8 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,12 +29,6 @@ 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)
"""The user agent for gcloud-python requests."""

def __init__(self, credentials=None):
"""Constructor for Connection.

Expand All @@ -45,6 +39,11 @@ def __init__(self, credentials=None):
self._http = None
self._credentials = credentials

@gcloud._UserAgentReifyProperty # pragma: NO COVER

This comment was marked as spam.

This comment was marked as spam.

def user_agent(self):
"""The user agent for gcloud-python requests."""
# The decorator will handle the value.

@property
def credentials(self):
"""Getter for current credentials.
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': self.user_agent,
}
headers, content = self.http.request(
uri=self.build_api_url(dataset_id=dataset_id, method=method),
Expand Down
2 changes: 1 addition & 1 deletion gcloud/datastore/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def _verifyProtobufCall(self, called_with, URI, conn):
self.assertEqual(called_with['headers']['Content-Type'],
'application/x-protobuf')
self.assertEqual(called_with['headers']['User-Agent'],
conn.USER_AGENT)
conn.user_agent)

def test_w_deferred_from_backend_but_not_passed(self):
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
Expand Down
9 changes: 6 additions & 3 deletions gcloud/datastore/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ def _make_query_pb(self, kind):
return pb

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
conn = self._getTargetClass()(*args, **kw)
# Set the user agent so the user_agent is not lazily loaded.
setattr(conn, 'user_agent', 'gcloud-python/test')
return conn

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)
conn.user_agent)

def test_ctor_defaults(self):
conn = self._makeOne()
Expand Down Expand Up @@ -409,7 +412,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'], conn.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 @@ -300,7 +300,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': 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 @@ -169,7 +169,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'] = self.user_agent

return self.http.request(uri=url, method=method, headers=headers,
body=data)
Expand Down
2 changes: 1 addition & 1 deletion gcloud/storage/test_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,11 +904,11 @@ def _respond(self, **kw):
class _Connection(_Responder):

API_BASE_URL = 'http://example.com'
USER_AGENT = 'testing 1.2.3'
credentials = object()

def __init__(self, *responses):
super(_Connection, self).__init__(*responses)
self.user_agent = 'testing 1.2.3'
self._signed = []
self.http = _HTTP(*responses)

Expand Down
17 changes: 10 additions & 7 deletions gcloud/storage/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ def _getTargetClass(self):
return Connection

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
conn = self._getTargetClass()(*args, **kw)
# Set the user agent so the user_agent is not lazily loaded.
setattr(conn, 'user_agent', 'gcloud-python/test')
return conn

def test_ctor_defaults(self):
PROJECT = 'project'
Expand Down Expand Up @@ -194,7 +197,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': conn.user_agent,
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand All @@ -214,7 +217,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': conn.user_agent,
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand All @@ -234,7 +237,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': conn.user_agent,
}
self.assertEqual(http._called_with['headers'], expected_headers)

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

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

Expand Down Expand Up @@ -333,7 +336,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': conn.user_agent,
}
self.assertEqual(http._called_with['headers'], expected_headers)

Expand Down
Loading