Skip to content

Commit 2cdaeb2

Browse files
committed
Merge pull request #852 from tseaver/825-storage_property_mixin-allow_explicit_connection
#825: Allow passing explicit connection to '_PropertyMixin.{reload,patch}'.
2 parents 2e629e0 + b44d368 commit 2cdaeb2

File tree

3 files changed

+66
-25
lines changed

3 files changed

+66
-25
lines changed

gcloud/storage/_helpers.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ class _PropertyMixin(object):
3232
- path
3333
"""
3434

35-
@property
36-
def connection(self):
37-
"""Abstract getter for the connection to use."""
38-
raise NotImplementedError
39-
4035
@property
4136
def path(self):
4237
"""Abstract getter for the object path."""
@@ -52,12 +47,19 @@ def __init__(self, name=None):
5247
self._properties = {}
5348
self._changes = set()
5449

55-
def reload(self):
56-
"""Reload properties from Cloud Storage."""
50+
def reload(self, connection=None):
51+
"""Reload properties from Cloud Storage.
52+
53+
:type connection: :class:`gcloud.storage.connection.Connection`
54+
:param connection: An explicit connection to use for the API request.
55+
If not passed, use the connection assigned to
56+
the object in its constructor.
57+
"""
58+
connection = _require_connection(connection)
5759
# Pass only '?projection=noAcl' here because 'acl' and related
5860
# are handled via custom endpoints.
5961
query_params = {'projection': 'noAcl'}
60-
api_response = self.connection.api_request(
62+
api_response = connection.api_request(
6163
method='GET', path=self.path, query_params=query_params)
6264
self._set_properties(api_response)
6365

@@ -89,16 +91,22 @@ def _set_properties(self, value):
8991
# If the values are reset, the changes must as well.
9092
self._changes = set()
9193

92-
def patch(self):
94+
def patch(self, connection=None):
9395
"""Sends all changed properties in a PATCH request.
9496
9597
Updates the ``_properties`` with the response from the backend.
98+
99+
:type connection: :class:`gcloud.storage.connection.Connection`
100+
:param connection: An explicit connection to use for the API request.
101+
If not passed, use the connection assigned to
102+
the object in its constructor.
96103
"""
104+
connection = _require_connection(connection)
97105
# Pass '?projection=full' here because 'PATCH' documented not
98106
# to work properly w/ 'noAcl'.
99107
update_properties = dict((key, self._properties[key])
100108
for key in self._changes)
101-
api_response = self.connection.api_request(
109+
api_response = connection.api_request(
102110
method='PATCH', path=self.path, data=update_properties,
103111
query_params={'projection': 'full'})
104112
self._set_properties(api_response)

gcloud/storage/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def get_bucket(bucket_name, connection=None):
160160
"""
161161
connection = _require_connection(connection)
162162
bucket = Bucket(bucket_name, connection=connection)
163-
bucket.reload()
163+
bucket.reload(connection=connection)
164164
return bucket
165165

166166

gcloud/storage/test__helpers.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,46 @@ def _getTargetClass(self):
2424
def _makeOne(self, *args, **kw):
2525
return self._getTargetClass()(*args, **kw)
2626

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

2929
class Derived(self._getTargetClass()):
3030

31-
@property
32-
def connection(self):
33-
return connection
34-
3531
@property
3632
def path(self):
3733
return path
3834

3935
return Derived
4036

41-
def test_connection_is_abstract(self):
42-
mixin = self._makeOne()
43-
self.assertRaises(NotImplementedError, lambda: mixin.connection)
37+
def _monkey(self, connection):
38+
from gcloud.storage._testing import _monkey_defaults
39+
return _monkey_defaults(connection=connection)
4440

4541
def test_path_is_abstract(self):
4642
mixin = self._makeOne()
4743
self.assertRaises(NotImplementedError, lambda: mixin.path)
4844

49-
def test_reload(self):
45+
def test_reload_w_implicit_connection(self):
46+
connection = _Connection({'foo': 'Foo'})
47+
derived = self._derivedClass('/path')()
48+
# Make sure changes is not a set, so we can observe a change.
49+
derived._changes = object()
50+
with self._monkey(connection):
51+
derived.reload()
52+
self.assertEqual(derived._properties, {'foo': 'Foo'})
53+
kw = connection._requested
54+
self.assertEqual(len(kw), 1)
55+
self.assertEqual(kw[0]['method'], 'GET')
56+
self.assertEqual(kw[0]['path'], '/path')
57+
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
58+
# Make sure changes get reset by reload.
59+
self.assertEqual(derived._changes, set())
60+
61+
def test_reload_w_explicit_connection(self):
5062
connection = _Connection({'foo': 'Foo'})
51-
derived = self._derivedClass(connection, '/path')()
63+
derived = self._derivedClass('/path')()
5264
# Make sure changes is not a set, so we can observe a change.
5365
derived._changes = object()
54-
derived.reload()
66+
derived.reload(connection)
5567
self.assertEqual(derived._properties, {'foo': 'Foo'})
5668
kw = connection._requested
5769
self.assertEqual(len(kw), 1)
@@ -66,15 +78,36 @@ def test__patch_property(self):
6678
derived._patch_property('foo', 'Foo')
6779
self.assertEqual(derived._properties, {'foo': 'Foo'})
6880

69-
def test_patch(self):
81+
def test_patch_w_implicit_connection(self):
82+
connection = _Connection({'foo': 'Foo'})
83+
derived = self._derivedClass('/path')()
84+
# Make sure changes is non-empty, so we can observe a change.
85+
BAR = object()
86+
BAZ = object()
87+
derived._properties = {'bar': BAR, 'baz': BAZ}
88+
derived._changes = set(['bar']) # Ignore baz.
89+
with self._monkey(connection):
90+
derived.patch()
91+
self.assertEqual(derived._properties, {'foo': 'Foo'})
92+
kw = connection._requested
93+
self.assertEqual(len(kw), 1)
94+
self.assertEqual(kw[0]['method'], 'PATCH')
95+
self.assertEqual(kw[0]['path'], '/path')
96+
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
97+
# Since changes does not include `baz`, we don't see it sent.
98+
self.assertEqual(kw[0]['data'], {'bar': BAR})
99+
# Make sure changes get reset by patch().
100+
self.assertEqual(derived._changes, set())
101+
102+
def test_patch_w_explicit_connection(self):
70103
connection = _Connection({'foo': 'Foo'})
71-
derived = self._derivedClass(connection, '/path')()
104+
derived = self._derivedClass('/path')()
72105
# Make sure changes is non-empty, so we can observe a change.
73106
BAR = object()
74107
BAZ = object()
75108
derived._properties = {'bar': BAR, 'baz': BAZ}
76109
derived._changes = set(['bar']) # Ignore baz.
77-
derived.patch()
110+
derived.patch(connection)
78111
self.assertEqual(derived._properties, {'foo': 'Foo'})
79112
kw = connection._requested
80113
self.assertEqual(len(kw), 1)

0 commit comments

Comments
 (0)