Skip to content

Commit 982dd56

Browse files
committed
Merge pull request #688 from dhermes/storage-properties-no-http
Removing HTTP request in _PropertyMixin.properties.
2 parents ab642a3 + 706f7ad commit 982dd56

File tree

6 files changed

+74
-162
lines changed

6 files changed

+74
-162
lines changed

gcloud/storage/_helpers.py

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,10 @@ class _PropertyMixin(object):
2525
"""Abstract mixin for cloud storage classes with associated propertties.
2626
2727
Non-abstract subclasses should implement:
28-
- CUSTOM_PROPERTY_ACCESSORS
2928
- connection
3029
- path
3130
"""
3231

33-
CUSTOM_PROPERTY_ACCESSORS = None
34-
"""Mapping of field name -> accessor for fields w/ custom accessors.
35-
36-
Expected to be set by subclasses. Fields in this mapping will cause
37-
:meth:`_get_property()` to raise a KeyError with a message to use the
38-
relevant accessor methods.
39-
"""
40-
4132
@property
4233
def connection(self):
4334
"""Abstract getter for the connection to use."""
@@ -64,12 +55,11 @@ def __init__(self, name=None, properties=None):
6455

6556
@property
6657
def properties(self):
67-
"""Ensure properties are loaded, and return a copy.
58+
"""Return a copy of properties.
6859
6960
:rtype: dict
61+
:returns: Copy of properties.
7062
"""
71-
if not self._properties:
72-
self._reload_properties()
7363
return self._properties.copy()
7464

7565
@property
@@ -131,30 +121,6 @@ def _patch_properties(self, properties):
131121
query_params={'projection': 'full'})
132122
return self
133123

134-
def _get_property(self, field, default=None):
135-
"""Return the value of a field from the server-side representation.
136-
137-
If you request a field that isn't available, and that field can
138-
be retrieved by refreshing data from Cloud Storage, this method
139-
will reload the data using :func:`_PropertyMixin._reload_properties`.
140-
141-
:type field: string
142-
:param field: A particular field to retrieve from properties.
143-
144-
:type default: anything
145-
:param default: The value to return if the field provided wasn't found.
146-
147-
:rtype: anything
148-
:returns: value of the specific field, or the default if not found.
149-
"""
150-
# Raise for fields which have custom accessors.
151-
custom = self.CUSTOM_PROPERTY_ACCESSORS.get(field)
152-
if custom is not None:
153-
message = "Use '%s' or related methods instead." % custom
154-
raise KeyError((field, message))
155-
156-
return self.properties.get(field, default)
157-
158124

159125
class _PropertyBatch(object):
160126
"""Context manager: Batch updates to object's ``_patch_properties``

gcloud/storage/blob.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,6 @@ class Blob(_PropertyMixin):
5252
:param properties: All the other data provided by Cloud Storage.
5353
"""
5454

55-
CUSTOM_PROPERTY_ACCESSORS = {
56-
'acl': 'acl',
57-
'cacheControl': 'cache_control',
58-
'contentDisposition': 'content_disposition',
59-
'contentEncoding': 'content_encoding',
60-
'contentLanguage': 'content_language',
61-
'contentType': 'content_type',
62-
'componentCount': 'component_count',
63-
'etag': 'etag',
64-
'generation': 'generation',
65-
'id': 'id',
66-
'mediaLink': 'media_link',
67-
'metageneration': 'metageneration',
68-
'name': 'name',
69-
'owner': 'owner',
70-
'selfLink': 'self_link',
71-
'size': 'size',
72-
'storageClass': 'storage_class',
73-
'timeDeleted': 'time_deleted',
74-
'updated': 'updated',
75-
}
76-
"""Map field name -> accessor for fields w/ custom accessors."""
77-
7855
CHUNK_SIZE = 1024 * 1024 # 1 MB.
7956
"""The size of a chunk of data whenever iterating (1 MB).
8057

gcloud/storage/bucket.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,6 @@ class Bucket(_PropertyMixin):
8686
_MAX_OBJECTS_FOR_BUCKET_DELETE = 256
8787
"""Maximum number of existing objects allowed in Bucket.delete()."""
8888

89-
CUSTOM_PROPERTY_ACCESSORS = {
90-
'acl': 'acl',
91-
'cors': 'get_cors()',
92-
'defaultObjectAcl': 'get_default_object_acl()',
93-
'etag': 'etag',
94-
'id': 'id',
95-
'lifecycle': 'get_lifecycle()',
96-
'location': 'location',
97-
'logging': 'get_logging()',
98-
'metageneration': 'metageneration',
99-
'name': 'name',
100-
'owner': 'owner',
101-
'projectNumber': 'project_number',
102-
'selfLink': 'self_link',
103-
'storageClass': 'storage_class',
104-
'timeCreated': 'time_created',
105-
'versioning': 'versioning_enabled',
106-
}
107-
"""Map field name -> accessor for fields w/ custom accessors."""
108-
10989
# ACL rules are lazily retrieved.
11090
_acl = _default_object_acl = None
11191

@@ -594,6 +574,7 @@ def get_logging(self):
594574
:returns: a dict w/ keys, ``logBucket`` and ``logObjectPrefix``
595575
(if logging is enabled), or None (if not).
596576
"""
577+
self._reload_properties()
597578
info = self.properties.get('logging')
598579
if info is not None:
599580
return info.copy()

gcloud/storage/test__helpers.py

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ def _getTargetClass(self):
2424
def _makeOne(self, *args, **kw):
2525
return self._getTargetClass()(*args, **kw)
2626

27-
def _derivedClass(self, connection=None, path=None, **custom_fields):
27+
def _derivedClass(self, connection=None, path=None):
2828

2929
class Derived(self._getTargetClass()):
30-
CUSTOM_PROPERTY_ACCESSORS = custom_fields
3130

3231
@property
3332
def connection(self):
@@ -39,18 +38,14 @@ def path(self):
3938

4039
return Derived
4140

42-
def test_connetction_is_abstract(self):
41+
def test_connection_is_abstract(self):
4342
mixin = self._makeOne()
4443
self.assertRaises(NotImplementedError, lambda: mixin.connection)
4544

4645
def test_path_is_abstract(self):
4746
mixin = self._makeOne()
4847
self.assertRaises(NotImplementedError, lambda: mixin.path)
4948

50-
def test_properties_eager(self):
51-
derived = self._derivedClass()(properties={'extant': False})
52-
self.assertEqual(derived.properties, {'extant': False})
53-
5449
def test_batch(self):
5550
connection = _Connection({'foo': 'Qux', 'bar': 'Baz'})
5651
derived = self._derivedClass(connection, '/path')()
@@ -65,9 +60,11 @@ def test_batch(self):
6560
self.assertEqual(kw[0]['data'], {'foo': 'Qux', 'bar': 'Baz'})
6661
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
6762

68-
def test_properties_lazy(self):
63+
def test_properties_no_fetch(self):
6964
connection = _Connection({'foo': 'Foo'})
7065
derived = self._derivedClass(connection, '/path')()
66+
self.assertEqual(derived.properties, {})
67+
derived._reload_properties()
7168
self.assertEqual(derived.properties, {'foo': 'Foo'})
7269
kw = connection._requested
7370
self.assertEqual(len(kw), 1)
@@ -86,40 +83,6 @@ def test__reload_properties(self):
8683
self.assertEqual(kw[0]['path'], '/path')
8784
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
8885

89-
def test__get_property_eager_hit(self):
90-
derived = self._derivedClass()(properties={'foo': 'Foo'})
91-
self.assertEqual(derived._get_property('foo'), 'Foo')
92-
93-
def test__get_property_eager_miss_w_default(self):
94-
connection = _Connection({'foo': 'Foo'})
95-
derived = self._derivedClass(connection, '/path')()
96-
default = object()
97-
self.assertTrue(derived._get_property('nonesuch', default) is default)
98-
kw = connection._requested
99-
self.assertEqual(len(kw), 1)
100-
self.assertEqual(kw[0]['method'], 'GET')
101-
self.assertEqual(kw[0]['path'], '/path')
102-
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
103-
104-
def test__get_property_lazy_hit(self):
105-
connection = _Connection({'foo': 'Foo'})
106-
derived = self._derivedClass(connection, '/path')()
107-
self.assertTrue(derived._get_property('nonesuch') is None)
108-
kw = connection._requested
109-
self.assertEqual(len(kw), 1)
110-
self.assertEqual(kw[0]['method'], 'GET')
111-
self.assertEqual(kw[0]['path'], '/path')
112-
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
113-
114-
def test__get_property_w_custom_field(self):
115-
derived = self._derivedClass(foo='get_foo')()
116-
try:
117-
derived._get_property('foo')
118-
except KeyError as e:
119-
self.assertTrue('get_foo' in str(e))
120-
else: # pragma: NO COVER
121-
self.assert_('KeyError not raised')
122-
12386
def test__patch_properties(self):
12487
connection = _Connection({'foo': 'Foo'})
12588
derived = self._derivedClass(connection, '/path')()
@@ -141,11 +104,10 @@ def _getTargetClass(self):
141104
def _makeOne(self, wrapped):
142105
return self._getTargetClass()(wrapped)
143106

144-
def _makeWrapped(self, connection=None, path=None, **custom_fields):
107+
def _makeWrapped(self, connection=None, path=None):
145108
from gcloud.storage._helpers import _PropertyMixin
146109

147110
class Wrapped(_PropertyMixin):
148-
CUSTOM_PROPERTY_ACCESSORS = custom_fields
149111

150112
@property
151113
def connection(self):

gcloud/storage/test_bucket.py

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ def test_get_cors_lazy(self):
633633
after = {'cors': [CORS_ENTRY]}
634634
connection = _Connection(after)
635635
bucket = self._makeOne(connection, NAME)
636+
bucket._reload_properties()
636637
entries = bucket.get_cors()
637638
self.assertEqual(len(entries), 1)
638639
self.assertEqual(entries[0]['maxAgeSeconds'],
@@ -725,6 +726,7 @@ def test_get_lifecycle_lazy(self):
725726
after = {'lifecycle': {'rule': [LC_RULE]}}
726727
connection = _Connection(after)
727728
bucket = self._makeOne(connection, NAME)
729+
bucket._reload_properties()
728730
entries = bucket.get_lifecycle()
729731
self.assertEqual(len(entries), 1)
730732
self.assertEqual(entries[0]['action']['type'], 'Delete')
@@ -776,30 +778,22 @@ def test_location_setter(self):
776778
self.assertEqual(kw[0]['data'], {'location': 'AS'})
777779
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
778780

779-
def test_get_logging_eager_w_prefix(self):
781+
def test_get_logging_w_prefix(self):
780782
NAME = 'name'
781783
LOG_BUCKET = 'logs'
782784
LOG_PREFIX = 'pfx'
783785
before = {
784-
'logging': {'logBucket': LOG_BUCKET,
785-
'logObjectPrefix': LOG_PREFIX}}
786-
connection = _Connection()
787-
bucket = self._makeOne(connection, NAME, before)
788-
info = bucket.get_logging()
789-
self.assertEqual(info['logBucket'], LOG_BUCKET)
790-
self.assertEqual(info['logObjectPrefix'], LOG_PREFIX)
791-
kw = connection._requested
792-
self.assertEqual(len(kw), 0)
793-
794-
def test_get_logging_lazy_wo_prefix(self):
795-
NAME = 'name'
796-
LOG_BUCKET = 'logs'
797-
after = {'logging': {'logBucket': LOG_BUCKET}}
798-
connection = _Connection(after)
786+
'logging': {
787+
'logBucket': LOG_BUCKET,
788+
'logObjectPrefix': LOG_PREFIX,
789+
},
790+
}
791+
resp_to_reload = before
792+
connection = _Connection(resp_to_reload)
799793
bucket = self._makeOne(connection, NAME)
800794
info = bucket.get_logging()
801795
self.assertEqual(info['logBucket'], LOG_BUCKET)
802-
self.assertEqual(info.get('logObjectPrefix'), None)
796+
self.assertEqual(info['logObjectPrefix'], LOG_PREFIX)
803797
kw = connection._requested
804798
self.assertEqual(len(kw), 1)
805799
self.assertEqual(kw[0]['method'], 'GET')
@@ -810,57 +804,85 @@ def test_enable_logging_defaults(self):
810804
NAME = 'name'
811805
LOG_BUCKET = 'logs'
812806
before = {'logging': None}
813-
after = {'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': ''}}
814-
connection = _Connection(after)
807+
resp_to_reload = before
808+
resp_to_enable_logging = {
809+
'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': ''},
810+
}
811+
connection = _Connection(resp_to_reload, resp_to_enable_logging,
812+
resp_to_enable_logging)
815813
bucket = self._makeOne(connection, NAME, before)
816814
self.assertTrue(bucket.get_logging() is None)
817815
bucket.enable_logging(LOG_BUCKET)
818816
info = bucket.get_logging()
819817
self.assertEqual(info['logBucket'], LOG_BUCKET)
820818
self.assertEqual(info['logObjectPrefix'], '')
821819
kw = connection._requested
822-
self.assertEqual(len(kw), 1)
823-
self.assertEqual(kw[0]['method'], 'PATCH')
820+
self.assertEqual(len(kw), 3)
821+
self.assertEqual(kw[0]['method'], 'GET')
824822
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
825-
self.assertEqual(kw[0]['data'], after)
826-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
823+
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
824+
self.assertEqual(kw[1]['method'], 'PATCH')
825+
self.assertEqual(kw[1]['path'], '/b/%s' % NAME)
826+
self.assertEqual(kw[1]['data'], resp_to_enable_logging)
827+
self.assertEqual(kw[1]['query_params'], {'projection': 'full'})
828+
self.assertEqual(kw[2]['method'], 'GET')
829+
self.assertEqual(kw[2]['path'], '/b/%s' % NAME)
830+
self.assertEqual(kw[2]['query_params'], {'projection': 'noAcl'})
827831

828832
def test_enable_logging_explicit(self):
829833
NAME = 'name'
830834
LOG_BUCKET = 'logs'
831835
LOG_PFX = 'pfx'
832836
before = {'logging': None}
833-
after = {
834-
'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': LOG_PFX}}
835-
connection = _Connection(after)
837+
resp_to_reload = before
838+
resp_to_enable_logging = {
839+
'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': LOG_PFX},
840+
}
841+
connection = _Connection(resp_to_reload,
842+
resp_to_enable_logging,
843+
resp_to_enable_logging)
836844
bucket = self._makeOne(connection, NAME, before)
837845
self.assertTrue(bucket.get_logging() is None)
838846
bucket.enable_logging(LOG_BUCKET, LOG_PFX)
839847
info = bucket.get_logging()
840848
self.assertEqual(info['logBucket'], LOG_BUCKET)
841849
self.assertEqual(info['logObjectPrefix'], LOG_PFX)
842850
kw = connection._requested
843-
self.assertEqual(len(kw), 1)
844-
self.assertEqual(kw[0]['method'], 'PATCH')
851+
self.assertEqual(len(kw), 3)
852+
self.assertEqual(kw[0]['method'], 'GET')
845853
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
846-
self.assertEqual(kw[0]['data'], after)
847-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
854+
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
855+
self.assertEqual(kw[1]['method'], 'PATCH')
856+
self.assertEqual(kw[1]['path'], '/b/%s' % NAME)
857+
self.assertEqual(kw[1]['data'], resp_to_enable_logging)
858+
self.assertEqual(kw[1]['query_params'], {'projection': 'full'})
859+
self.assertEqual(kw[2]['method'], 'GET')
860+
self.assertEqual(kw[2]['path'], '/b/%s' % NAME)
861+
self.assertEqual(kw[2]['query_params'], {'projection': 'noAcl'})
848862

849863
def test_disable_logging(self):
850864
NAME = 'name'
851865
before = {'logging': {'logBucket': 'logs', 'logObjectPrefix': 'pfx'}}
852-
after = {'logging': None}
853-
connection = _Connection(after)
866+
resp_to_reload = before
867+
resp_to_disable_logging = {'logging': None}
868+
connection = _Connection(resp_to_reload, resp_to_disable_logging,
869+
resp_to_disable_logging)
854870
bucket = self._makeOne(connection, NAME, before)
855871
self.assertTrue(bucket.get_logging() is not None)
856872
bucket.disable_logging()
857873
self.assertTrue(bucket.get_logging() is None)
858874
kw = connection._requested
859-
self.assertEqual(len(kw), 1)
860-
self.assertEqual(kw[0]['method'], 'PATCH')
875+
self.assertEqual(len(kw), 3)
876+
self.assertEqual(kw[0]['method'], 'GET')
861877
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
862-
self.assertEqual(kw[0]['data'], {'logging': None})
863-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
878+
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
879+
self.assertEqual(kw[1]['method'], 'PATCH')
880+
self.assertEqual(kw[1]['path'], '/b/%s' % NAME)
881+
self.assertEqual(kw[1]['data'], {'logging': None})
882+
self.assertEqual(kw[1]['query_params'], {'projection': 'full'})
883+
self.assertEqual(kw[2]['method'], 'GET')
884+
self.assertEqual(kw[2]['path'], '/b/%s' % NAME)
885+
self.assertEqual(kw[2]['query_params'], {'projection': 'noAcl'})
864886

865887
def test_metageneration(self):
866888
METAGENERATION = 42
@@ -904,6 +926,7 @@ def test_versioning_enabled_getter_missing(self):
904926
NAME = 'name'
905927
connection = _Connection({})
906928
bucket = self._makeOne(connection, NAME)
929+
bucket._reload_properties()
907930
self.assertEqual(bucket.versioning_enabled, False)
908931
kw = connection._requested
909932
self.assertEqual(len(kw), 1)

0 commit comments

Comments
 (0)