Skip to content

Commit cc79bc2

Browse files
committed
Adding lazy loading __user_agent__ to main package.
Fixes #566.
1 parent d858ff0 commit cc79bc2

File tree

8 files changed

+133
-44
lines changed

8 files changed

+133
-44
lines changed

gcloud/__init__.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,87 @@
1616

1717
from pkg_resources import get_distribution
1818

19+
import httplib
20+
import socket
21+
22+
try:
23+
from google import appengine
24+
except ImportError:
25+
appengine = None
26+
27+
1928
__version__ = get_distribution('gcloud').version
29+
30+
31+
class _LazyUserAgent(object):
32+
"""Helper for extra environment information."""
33+
34+
_curr_environ = None
35+
_user_agent = None
36+
37+
def __init__(self):
38+
self._curr_environ = self.environ_at_init()
39+
40+
@staticmethod
41+
def environ_at_init():
42+
"""Checks environment variables during instance initialization.
43+
44+
Intended to infer as much as possible from the environment without
45+
running code.
46+
47+
:rtype: string or ``NoneType``
48+
:returns: Either ``'-GAE'`` if on App Engine else ``None``
49+
"""
50+
if appengine is not None:
51+
return '-GAE'
52+
53+
def environ_post_init(self):
54+
"""Checks environment variables after instance initialization.
55+
56+
This is meant for checks which can't be performed instantaneously.
57+
58+
:rtype: string
59+
:returns: Either ``'-GCE'`` if on Compute Engine else an empty string.
60+
"""
61+
gce_environ = self.check_compute_engine()
62+
if gce_environ is not None:
63+
return gce_environ
64+
65+
return ''
66+
67+
@staticmethod
68+
def check_compute_engine():
69+
"""Checks if the current environment is Compute Engine.
70+
71+
:rtype: string or ``NoneType``
72+
:returns: The string ``'-GCE'`` if on Compute Engine else ``None``.
73+
"""
74+
host = '169.254.169.254'
75+
uri_path = '/computeMetadata/v1/project/project-id'
76+
headers = {'Metadata-Flavor': 'Google'}
77+
connection = httplib.HTTPConnection(host, timeout=0.1)
78+
try:
79+
connection.request('GET', uri_path, headers=headers)
80+
response = connection.getresponse()
81+
if response.status == 200:
82+
return '-GCE'
83+
except socket.error: # Expect timeout or host is down
84+
pass
85+
finally:
86+
connection.close()
87+
88+
def __str__(self):
89+
if self._curr_environ is None:
90+
self._curr_environ = self.environ_post_init()
91+
92+
if self._user_agent is None:
93+
self._user_agent = "gcloud-python/{0}{1}".format(
94+
__version__, self._curr_environ)
95+
96+
return self._user_agent
97+
98+
def __repr__(self):
99+
return '<_LazyUserAgent: %r>' % (self.__str__(),)
100+
101+
102+
__user_agent__ = _LazyUserAgent()

gcloud/connection.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414

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

17-
from pkg_resources import get_distribution
18-
1917
import httplib2
2018

19+
import gcloud
20+
2121

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

32-
_EMPTY = object()
33-
"""A pointer to represent an empty value for default arguments."""
34-
35-
USER_AGENT = "gcloud-python/{0}".format(get_distribution('gcloud').version)
32+
USER_AGENT = gcloud.__user_agent__
3633
"""The user agent for gcloud-python requests."""
3734

3835
def __init__(self, credentials=None):

gcloud/datastore/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def _request(self, dataset_id, method, data):
5959
headers = {
6060
'Content-Type': 'application/x-protobuf',
6161
'Content-Length': str(len(data)),
62-
'User-Agent': self.USER_AGENT,
62+
'User-Agent': str(self.USER_AGENT),
6363
}
6464
headers, content = self.http.request(
6565
uri=self.build_api_url(dataset_id=dataset_id, method=method),

gcloud/datastore/test_connection.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,17 @@ def _make_query_pb(self, kind):
3838
def _makeOne(self, *args, **kw):
3939
return self._getTargetClass()(*args, **kw)
4040

41-
def _verifyProtobufCall(self, called_with, URI, conn):
41+
def _user_agent(self):
42+
import gcloud
43+
return "gcloud-python/{0}".format(gcloud.__version__)
44+
45+
def _verifyProtobufCall(self, called_with, URI):
4246
self.assertEqual(called_with['uri'], URI)
4347
self.assertEqual(called_with['method'], 'POST')
4448
self.assertEqual(called_with['headers']['Content-Type'],
4549
'application/x-protobuf')
4650
self.assertEqual(called_with['headers']['User-Agent'],
47-
conn.USER_AGENT)
51+
self._user_agent())
4852

4953
def test_ctor_defaults(self):
5054
conn = self._makeOne()
@@ -96,7 +100,7 @@ def test__request_w_200(self):
96100
])
97101
http = conn._http = Http({'status': '200'}, 'CONTENT')
98102
self.assertEqual(conn._request(DATASET_ID, METHOD, DATA), 'CONTENT')
99-
self._verifyProtobufCall(http._called_with, URI, conn)
103+
self._verifyProtobufCall(http._called_with, URI)
100104
self.assertEqual(http._called_with['body'], DATA)
101105

102106
def test__request_not_200(self):
@@ -144,7 +148,7 @@ def FromString(cls, pb):
144148
response = conn._rpc(DATASET_ID, METHOD, ReqPB(), RspPB)
145149
self.assertTrue(isinstance(response, RspPB))
146150
self.assertEqual(response._pb, 'CONTENT')
147-
self._verifyProtobufCall(http._called_with, URI, conn)
151+
self._verifyProtobufCall(http._called_with, URI)
148152
self.assertEqual(http._called_with['body'], REQPB)
149153

150154
def test_build_api_url_w_default_base_version(self):
@@ -196,7 +200,7 @@ def test_lookup_single_key_empty_response(self):
196200
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
197201
self.assertEqual(conn.lookup(DATASET_ID, key_pb), None)
198202
cw = http._called_with
199-
self._verifyProtobufCall(cw, URI, conn)
203+
self._verifyProtobufCall(cw, URI)
200204
rq_class = datastore_pb.LookupRequest
201205
request = rq_class()
202206
request.ParseFromString(cw['body'])
@@ -222,7 +226,7 @@ def test_lookup_single_key_empty_response_w_eventual(self):
222226
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
223227
self.assertEqual(conn.lookup(DATASET_ID, key_pb, eventual=True), None)
224228
cw = http._called_with
225-
self._verifyProtobufCall(cw, URI, conn)
229+
self._verifyProtobufCall(cw, URI)
226230
rq_class = datastore_pb.LookupRequest
227231
request = rq_class()
228232
request.ParseFromString(cw['body'])
@@ -261,7 +265,7 @@ def test_lookup_single_key_empty_response_w_transaction(self):
261265
found = conn.lookup(DATASET_ID, key_pb, transaction_id=TRANSACTION)
262266
self.assertEqual(found, None)
263267
cw = http._called_with
264-
self._verifyProtobufCall(cw, URI, conn)
268+
self._verifyProtobufCall(cw, URI)
265269
rq_class = datastore_pb.LookupRequest
266270
request = rq_class()
267271
request.ParseFromString(cw['body'])
@@ -293,7 +297,7 @@ def test_lookup_single_key_nonempty_response(self):
293297
self.assertEqual(found.key.path_element[0].kind, 'Kind')
294298
self.assertEqual(found.key.path_element[0].id, 1234)
295299
cw = http._called_with
296-
self._verifyProtobufCall(cw, URI, conn)
300+
self._verifyProtobufCall(cw, URI)
297301
rq_class = datastore_pb.LookupRequest
298302
request = rq_class()
299303
request.ParseFromString(cw['body'])
@@ -320,7 +324,7 @@ def test_lookup_multiple_keys_empty_response(self):
320324
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
321325
self.assertEqual(conn.lookup(DATASET_ID, [key_pb1, key_pb2]), [])
322326
cw = http._called_with
323-
self._verifyProtobufCall(cw, URI, conn)
327+
self._verifyProtobufCall(cw, URI)
324328
rq_class = datastore_pb.LookupRequest
325329
request = rq_class()
326330
request.ParseFromString(cw['body'])
@@ -356,7 +360,7 @@ def test_lookup_multiple_keys_w_missing(self):
356360
self.assertEqual([missed.key for missed in missing],
357361
[key_pb1, key_pb2])
358362
cw = http._called_with
359-
self._verifyProtobufCall(cw, URI, conn)
363+
self._verifyProtobufCall(cw, URI)
360364
rq_class = datastore_pb.LookupRequest
361365
request = rq_class()
362366
request.ParseFromString(cw['body'])
@@ -399,12 +403,12 @@ def test_lookup_multiple_keys_w_deferred(self):
399403
self.assertEqual(result, [])
400404
self.assertEqual([def_key for def_key in deferred], [key_pb1, key_pb2])
401405
cw = http._called_with
402-
self._verifyProtobufCall(cw, URI, conn)
406+
self._verifyProtobufCall(cw, URI)
403407
self.assertEqual(cw['uri'], URI)
404408
self.assertEqual(cw['method'], 'POST')
405409
self.assertEqual(cw['headers']['Content-Type'],
406410
'application/x-protobuf')
407-
self.assertEqual(cw['headers']['User-Agent'], conn.USER_AGENT)
411+
self.assertEqual(cw['headers']['User-Agent'], self._user_agent())
408412
rq_class = datastore_pb.LookupRequest
409413
request = rq_class()
410414
request.ParseFromString(cw['body'])
@@ -462,14 +466,14 @@ def test_lookup_multiple_keys_w_deferred_from_backend_but_not_passed(self):
462466
request = rq_class()
463467
self.assertEqual(len(cw), 2)
464468

465-
self._verifyProtobufCall(cw[0], URI, conn)
469+
self._verifyProtobufCall(cw[0], URI)
466470
request.ParseFromString(cw[0]['body'])
467471
keys = list(request.key)
468472
self.assertEqual(len(keys), 2)
469473
_compare_key_pb_after_request(self, key_pb1, keys[0])
470474
_compare_key_pb_after_request(self, key_pb2, keys[1])
471475

472-
self._verifyProtobufCall(cw[1], URI, conn)
476+
self._verifyProtobufCall(cw[1], URI)
473477
request.ParseFromString(cw[1]['body'])
474478
keys = list(request.key)
475479
self.assertEqual(len(keys), 1)
@@ -504,7 +508,7 @@ def test_run_query_w_eventual_no_transaction(self):
504508
self.assertTrue(more)
505509
self.assertEqual(skipped, 0)
506510
cw = http._called_with
507-
self._verifyProtobufCall(cw, URI, conn)
511+
self._verifyProtobufCall(cw, URI)
508512
rq_class = datastore_pb.RunQueryRequest
509513
request = rq_class()
510514
request.ParseFromString(cw['body'])
@@ -544,7 +548,7 @@ def test_run_query_wo_eventual_w_transaction(self):
544548
self.assertTrue(more)
545549
self.assertEqual(skipped, 0)
546550
cw = http._called_with
547-
self._verifyProtobufCall(cw, URI, conn)
551+
self._verifyProtobufCall(cw, URI)
548552
rq_class = datastore_pb.RunQueryRequest
549553
request = rq_class()
550554
request.ParseFromString(cw['body'])
@@ -599,7 +603,7 @@ def test_run_query_wo_namespace_empty_result(self):
599603
self.assertTrue(more)
600604
self.assertEqual(skipped, 0)
601605
cw = http._called_with
602-
self._verifyProtobufCall(cw, URI, conn)
606+
self._verifyProtobufCall(cw, URI)
603607
rq_class = datastore_pb.RunQueryRequest
604608
request = rq_class()
605609
request.ParseFromString(cw['body'])
@@ -630,7 +634,7 @@ def test_run_query_w_namespace_nonempty_result(self):
630634
pbs = conn.run_query(DATASET_ID, q_pb, 'NS')[0]
631635
self.assertEqual(len(pbs), 1)
632636
cw = http._called_with
633-
self._verifyProtobufCall(cw, URI, conn)
637+
self._verifyProtobufCall(cw, URI)
634638
rq_class = datastore_pb.RunQueryRequest
635639
request = rq_class()
636640
request.ParseFromString(cw['body'])
@@ -656,7 +660,7 @@ def test_begin_transaction_default_serialize(self):
656660
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
657661
self.assertEqual(conn.begin_transaction(DATASET_ID), TRANSACTION)
658662
cw = http._called_with
659-
self._verifyProtobufCall(cw, URI, conn)
663+
self._verifyProtobufCall(cw, URI)
660664
rq_class = datastore_pb.BeginTransactionRequest
661665
request = rq_class()
662666
request.ParseFromString(cw['body'])
@@ -681,7 +685,7 @@ def test_begin_transaction_explicit_serialize(self):
681685
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
682686
self.assertEqual(conn.begin_transaction(DATASET_ID, True), TRANSACTION)
683687
cw = http._called_with
684-
self._verifyProtobufCall(cw, URI, conn)
688+
self._verifyProtobufCall(cw, URI)
685689
rq_class = datastore_pb.BeginTransactionRequest
686690
request = rq_class()
687691
request.ParseFromString(cw['body'])
@@ -713,7 +717,7 @@ def test_commit_wo_transaction(self):
713717
self.assertEqual(result.index_updates, 0)
714718
self.assertEqual(list(result.insert_auto_id_key), [])
715719
cw = http._called_with
716-
self._verifyProtobufCall(cw, URI, conn)
720+
self._verifyProtobufCall(cw, URI)
717721
rq_class = datastore_pb.CommitRequest
718722
request = rq_class()
719723
request.ParseFromString(cw['body'])
@@ -747,7 +751,7 @@ def test_commit_w_transaction(self):
747751
self.assertEqual(result.index_updates, 0)
748752
self.assertEqual(list(result.insert_auto_id_key), [])
749753
cw = http._called_with
750-
self._verifyProtobufCall(cw, URI, conn)
754+
self._verifyProtobufCall(cw, URI)
751755
rq_class = datastore_pb.CommitRequest
752756
request = rq_class()
753757
request.ParseFromString(cw['body'])
@@ -773,7 +777,7 @@ def test_rollback_ok(self):
773777
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
774778
self.assertEqual(conn.rollback(DATASET_ID, TRANSACTION), None)
775779
cw = http._called_with
776-
self._verifyProtobufCall(cw, URI, conn)
780+
self._verifyProtobufCall(cw, URI)
777781
rq_class = datastore_pb.RollbackRequest
778782
request = rq_class()
779783
request.ParseFromString(cw['body'])
@@ -796,7 +800,7 @@ def test_allocate_ids_empty(self):
796800
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
797801
self.assertEqual(conn.allocate_ids(DATASET_ID, []), [])
798802
cw = http._called_with
799-
self._verifyProtobufCall(cw, URI, conn)
803+
self._verifyProtobufCall(cw, URI)
800804
rq_class = datastore_pb.AllocateIdsRequest
801805
request = rq_class()
802806
request.ParseFromString(cw['body'])
@@ -830,7 +834,7 @@ def test_allocate_ids_non_empty(self):
830834
self.assertEqual(conn.allocate_ids(DATASET_ID, before_key_pbs),
831835
after_key_pbs)
832836
cw = http._called_with
833-
self._verifyProtobufCall(cw, URI, conn)
837+
self._verifyProtobufCall(cw, URI)
834838
rq_class = datastore_pb.AllocateIdsRequest
835839
request = rq_class()
836840
request.ParseFromString(cw['body'])

gcloud/storage/blob.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def upload_from_file(self, file_obj, rewind=False, size=None,
292292
headers = {
293293
'Accept': 'application/json',
294294
'Accept-Encoding': 'gzip, deflate',
295-
'User-Agent': conn.USER_AGENT,
295+
'User-Agent': str(conn.USER_AGENT),
296296
}
297297

298298
upload = transfer.Upload(file_obj,

gcloud/storage/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def make_request(self, method, url, data=None, content_type=None,
252252
if content_type:
253253
headers['Content-Type'] = content_type
254254

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

257257
return self.http.request(uri=url, method=method, headers=headers,
258258
body=data)

0 commit comments

Comments
 (0)