Skip to content

Commit 9c78309

Browse files
committed
STORAGE: Making Connection.get_all_buckets a standalone method.
See comments in #700 for context.
1 parent 19bdb13 commit 9c78309

File tree

9 files changed

+205
-152
lines changed

9 files changed

+205
-152
lines changed

docs/_components/storage-getting-started.rst

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,8 @@ If you have a full bucket, you can delete it this way::
186186
Listing available buckets
187187
-------------------------
188188

189-
The :class:`Connection <gcloud.storage.connection.Connection>` object
190-
itself is iterable, so you can loop over it, or call ``list`` on it to get
191-
a list object::
192-
193-
>>> for bucket in connection.get_all_buckets():
189+
>>> for bucket in storage.get_all_buckets(connection):
194190
... print bucket.name
195-
>>> print list(connection)
196191

197192
Managing access control
198193
-----------------------

docs/_components/storage-quickstart.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ and instantiating the demo connection::
5555
Once you have the connection,
5656
you can create buckets and blobs::
5757

58-
>>> connection.get_all_buckets()
58+
>>> from gcloud import storage
59+
>>> storage.get_all_buckets(connection)
5960
[<Bucket: ...>, ...]
6061
>>> bucket = connection.create_bucket('my-new-bucket')
6162
>>> print bucket

gcloud/storage/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from gcloud.storage._implicit_environ import get_default_bucket
4545
from gcloud.storage._implicit_environ import get_default_connection
4646
from gcloud.storage._implicit_environ import get_default_project
47+
from gcloud.storage.api import get_all_buckets
4748
from gcloud.storage.blob import Blob
4849
from gcloud.storage.bucket import Bucket
4950
from gcloud.storage.connection import Connection

gcloud/storage/api.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2015 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Methods for interacting with Google Cloud Storage.
16+
17+
Allows interacting with Cloud Storage via user-friendly objects
18+
rather than via Connection.
19+
"""
20+
21+
from gcloud.storage._implicit_environ import get_default_connection
22+
from gcloud.storage.bucket import Bucket
23+
from gcloud.storage.iterator import Iterator
24+
25+
26+
def get_all_buckets(connection=None):
27+
"""Get all buckets in the project.
28+
29+
This will not populate the list of blobs available in each
30+
bucket.
31+
32+
>>> from gcloud import storage
33+
>>> for bucket in storage.get_all_buckets():
34+
>>> print bucket
35+
36+
This implements "storage.buckets.list".
37+
38+
:type connection: :class:`gcloud.storage.connection.Connection` or
39+
``NoneType``
40+
:param connection: Optional. The connection to use when sending requests.
41+
If not provided, falls back to default.
42+
43+
:rtype: list of :class:`gcloud.storage.bucket.Bucket` objects.
44+
:returns: All buckets belonging to this project.
45+
"""
46+
if connection is None:
47+
connection = get_default_connection()
48+
return iter(_BucketIterator(connection=connection))
49+
50+
51+
class _BucketIterator(Iterator):
52+
"""An iterator listing all buckets.
53+
54+
You shouldn't have to use this directly, but instead should use the
55+
helper methods on :class:`gcloud.storage.connection.Connection`
56+
objects.
57+
58+
:type connection: :class:`gcloud.storage.connection.Connection`
59+
:param connection: The connection to use for querying the list of buckets.
60+
"""
61+
62+
def __init__(self, connection):
63+
super(_BucketIterator, self).__init__(connection=connection, path='/b')
64+
65+
def get_items_from_response(self, response):
66+
"""Factory method which yields :class:`.Bucket` items from a response.
67+
68+
:type response: dict
69+
:param response: The JSON API response for a page of buckets.
70+
"""
71+
for item in response.get('items', []):
72+
yield Bucket(properties=item, connection=self.connection)

gcloud/storage/connection.py

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from gcloud import connection as base_connection
2222
from gcloud.exceptions import make_exception
2323
from gcloud.storage.bucket import Bucket
24-
from gcloud.storage.iterator import Iterator
2524

2625

2726
class Connection(base_connection.Connection):
@@ -57,13 +56,6 @@ class Connection(base_connection.Connection):
5756
If you want to access an existing bucket::
5857
5958
>>> bucket = connection.get_bucket('my-bucket-name')
60-
61-
You can also iterate through all :class:`gcloud.storage.bucket.Bucket`
62-
objects inside the project::
63-
64-
>>> for bucket in connection.get_all_buckets():
65-
>>> print bucket
66-
<Bucket: my-bucket-name>
6759
"""
6860

6961
API_BASE_URL = base_connection.API_BASE_URL
@@ -268,27 +260,6 @@ def api_request(self, method, path, query_params=None,
268260

269261
return content
270262

271-
def get_all_buckets(self):
272-
"""Get all buckets in the project.
273-
274-
This will not populate the list of blobs available in each
275-
bucket.
276-
277-
You can also iterate over the connection object, so these two
278-
operations are identical::
279-
280-
>>> from gcloud import storage
281-
>>> connection = storage.get_connection(project)
282-
>>> for bucket in connection.get_all_buckets():
283-
>>> print bucket
284-
285-
This implements "storage.buckets.list".
286-
287-
:rtype: list of :class:`gcloud.storage.bucket.Bucket` objects.
288-
:returns: All buckets belonging to this project.
289-
"""
290-
return iter(_BucketIterator(connection=self))
291-
292263
def get_bucket(self, bucket_name):
293264
"""Get a bucket by name.
294265
@@ -377,27 +348,3 @@ def delete_bucket(self, bucket_name):
377348
"""
378349
bucket_path = Bucket.path_helper(bucket_name)
379350
self.api_request(method='DELETE', path=bucket_path)
380-
381-
382-
class _BucketIterator(Iterator):
383-
"""An iterator listing all buckets.
384-
385-
You shouldn't have to use this directly, but instead should use the
386-
helper methods on :class:`gcloud.storage.connection.Connection`
387-
objects.
388-
389-
:type connection: :class:`gcloud.storage.connection.Connection`
390-
:param connection: The connection to use for querying the list of buckets.
391-
"""
392-
393-
def __init__(self, connection):
394-
super(_BucketIterator, self).__init__(connection=connection, path='/b')
395-
396-
def get_items_from_response(self, response):
397-
"""Factory method which yields :class:`.Bucket` items from a response.
398-
399-
:type response: dict
400-
:param response: The JSON API response for a page of buckets.
401-
"""
402-
for item in response.get('items', []):
403-
yield Bucket(properties=item, connection=self.connection)

gcloud/storage/demo/demo.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,3 @@
1-
# Copyright 2014 Google Inc. All rights reserved.
2-
#
3-
# Licensed under the Apache License, Version 2.0 (the "License");
4-
# you may not use this file except in compliance with the License.
5-
# You may obtain a copy of the License at
6-
#
7-
# http://www.apache.org/licenses/LICENSE-2.0
8-
#
9-
# Unless required by applicable law or agreed to in writing, software
10-
# distributed under the License is distributed on an "AS IS" BASIS,
11-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
# See the License for the specific language governing permissions and
13-
# limitations under the License.
14-
15-
# pragma NO COVER
161
# Welcome to the gCloud Storage Demo! (hit enter)
172

183
# We're going to walk through some of the basics...,
@@ -21,12 +6,13 @@
216
# Let's start by importing the demo module and getting a connection:
227
import time
238

9+
from gcloud import storage
2410
from gcloud.storage import demo
2511

2612
connection = demo.get_connection()
2713

2814
# OK, now let's look at all of the buckets...
29-
print(connection.get_all_buckets()) # This might take a second...
15+
print(list(storage.get_all_buckets(connection))) # This might take a second...
3016

3117
# Now let's create a new bucket...
3218
bucket_name = ("bucket-%s" % time.time()).replace(".", "") # Get rid of dots.
@@ -35,7 +21,7 @@
3521
print(bucket)
3622

3723
# Let's look at all of the buckets again...
38-
print(connection.get_all_buckets())
24+
print(list(storage.get_all_buckets(connection)))
3925

4026
# How about we create a new blob inside this bucket.
4127
blob = bucket.new_blob("my-new-file.txt")

gcloud/storage/test_api.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Copyright 2015 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest2
16+
17+
18+
class Test__BucketIterator(unittest2.TestCase):
19+
20+
def _getTargetClass(self):
21+
from gcloud.storage.api import _BucketIterator
22+
return _BucketIterator
23+
24+
def _makeOne(self, *args, **kw):
25+
return self._getTargetClass()(*args, **kw)
26+
27+
def test_ctor(self):
28+
connection = object()
29+
iterator = self._makeOne(connection)
30+
self.assertTrue(iterator.connection is connection)
31+
self.assertEqual(iterator.path, '/b')
32+
self.assertEqual(iterator.page_number, 0)
33+
self.assertEqual(iterator.next_page_token, None)
34+
35+
def test_get_items_from_response_empty(self):
36+
connection = object()
37+
iterator = self._makeOne(connection)
38+
self.assertEqual(list(iterator.get_items_from_response({})), [])
39+
40+
def test_get_items_from_response_non_empty(self):
41+
from gcloud.storage.bucket import Bucket
42+
BLOB_NAME = 'blob-name'
43+
response = {'items': [{'name': BLOB_NAME}]}
44+
connection = object()
45+
iterator = self._makeOne(connection)
46+
buckets = list(iterator.get_items_from_response(response))
47+
self.assertEqual(len(buckets), 1)
48+
bucket = buckets[0]
49+
self.assertTrue(isinstance(bucket, Bucket))
50+
self.assertTrue(bucket.connection is connection)
51+
self.assertEqual(bucket.name, BLOB_NAME)
52+
53+
54+
class Test_get_all_buckets(unittest2.TestCase):
55+
56+
def _callFUT(self, connection=None):
57+
from gcloud.storage.api import get_all_buckets
58+
return get_all_buckets(connection=connection)
59+
60+
def test_empty(self):
61+
from gcloud.storage.connection import Connection
62+
PROJECT = 'project'
63+
conn = Connection(PROJECT)
64+
URI = '/'.join([
65+
conn.API_BASE_URL,
66+
'storage',
67+
conn.API_VERSION,
68+
'b?project=%s' % PROJECT,
69+
])
70+
http = conn._http = Http(
71+
{'status': '200', 'content-type': 'application/json'},
72+
'{}',
73+
)
74+
buckets = list(self._callFUT(conn))
75+
self.assertEqual(len(buckets), 0)
76+
self.assertEqual(http._called_with['method'], 'GET')
77+
self.assertEqual(http._called_with['uri'], URI)
78+
79+
def _get_all_buckets_non_empty_helper(self, use_default=False):
80+
from gcloud.storage._testing import _monkey_defaults
81+
from gcloud.storage.connection import Connection
82+
PROJECT = 'project'
83+
BUCKET_NAME = 'bucket-name'
84+
conn = Connection(PROJECT)
85+
URI = '/'.join([
86+
conn.API_BASE_URL,
87+
'storage',
88+
conn.API_VERSION,
89+
'b?project=%s' % PROJECT,
90+
])
91+
http = conn._http = Http(
92+
{'status': '200', 'content-type': 'application/json'},
93+
'{"items": [{"name": "%s"}]}' % BUCKET_NAME,
94+
)
95+
96+
if use_default:
97+
with _monkey_defaults(connection=conn):
98+
buckets = list(self._callFUT())
99+
else:
100+
buckets = list(self._callFUT(conn))
101+
102+
self.assertEqual(len(buckets), 1)
103+
self.assertEqual(buckets[0].name, BUCKET_NAME)
104+
self.assertEqual(http._called_with['method'], 'GET')
105+
self.assertEqual(http._called_with['uri'], URI)
106+
107+
def test_non_empty(self):
108+
self._get_all_buckets_non_empty_helper(use_default=False)
109+
110+
def test_non_use_default(self):
111+
self._get_all_buckets_non_empty_helper(use_default=True)
112+
113+
114+
class Http(object):
115+
116+
_called_with = None
117+
118+
def __init__(self, headers, content):
119+
from httplib2 import Response
120+
self._response = Response(headers)
121+
self._content = content
122+
123+
def request(self, **kw):
124+
self._called_with = kw
125+
return self._response, self._content

0 commit comments

Comments
 (0)