Skip to content

Commit cf0fca0

Browse files
committed
Merge pull request #359 from tseaver/26-expose_bucket_iterator_w_extra_params
Fix #26: expose bucket iterator w extra params
2 parents bc8b1c4 + faf8595 commit cf0fca0

File tree

3 files changed

+121
-47
lines changed

3 files changed

+121
-47
lines changed

gcloud/storage/bucket.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ class _KeyIterator(Iterator):
2020
:type bucket: :class:`gcloud.storage.bucket.Bucket`
2121
:param bucket: The bucket from which to list keys.
2222
"""
23-
def __init__(self, bucket, connection=None, extra_params=None):
23+
def __init__(self, bucket, extra_params=None):
2424
self.bucket = bucket
25+
self.prefixes = ()
2526
super(_KeyIterator, self).__init__(
2627
connection=bucket.connection, path=bucket.path + '/o',
2728
extra_params=extra_params)
@@ -32,6 +33,7 @@ def get_items_from_response(self, response):
3233
:type response: dict
3334
:param response: The JSON API response for a page of keys.
3435
"""
36+
self.prefixes = tuple(response.get('prefixes', ()))
3537
for item in response.get('items', []):
3638
yield Key.from_dict(item, bucket=self.bucket)
3739

@@ -170,6 +172,42 @@ def get_all_keys(self):
170172
"""
171173
return list(self)
172174

175+
def iterator(self, prefix=None, delimiter=None, max_results=None,
176+
versions=None):
177+
"""Return an iterator used to find keys in the bucket.
178+
179+
:type prefix: string or None
180+
:param prefix: optional prefix used to filter keys.
181+
182+
:type delimiter: string or None
183+
:param delimiter: optional delimter, used with ``prefix`` to
184+
emulate hierarchy.
185+
186+
:type max_results: integer or None
187+
:param max_results: maximum number of keys to return.
188+
189+
:type versions: boolean or None
190+
:param versions: whether object versions should be returned as
191+
separate keys.
192+
193+
:rtype: :class:`_KeyIterator`
194+
"""
195+
extra_params = {}
196+
197+
if prefix is not None:
198+
extra_params['prefix'] = prefix
199+
200+
if delimiter is not None:
201+
extra_params['delimiter'] = delimiter
202+
203+
if max_results is not None:
204+
extra_params['maxResults'] = max_results
205+
206+
if versions is not None:
207+
extra_params['versions'] = versions
208+
209+
return self._iterator_class(self, extra_params=extra_params)
210+
173211
def new_key(self, key):
174212
"""Given path name (or Key), return a :class:`.storage.key.Key` object.
175213
@@ -665,7 +703,6 @@ def make_public(self, recursive=False, future=False):
665703
doa.save()
666704

667705
if recursive:
668-
iterator = self._iterator_class(self)
669-
for key in iterator:
706+
for key in self:
670707
key.get_acl().all().grant_read()
671708
key.save_acl()

gcloud/storage/test_bucket.py

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,49 @@
33
import unittest2
44

55

6+
class Test__KeyIterator(unittest2.TestCase):
7+
8+
def _getTargetClass(self):
9+
from gcloud.storage.bucket import _KeyIterator
10+
return _KeyIterator
11+
12+
def _makeOne(self, *args, **kw):
13+
return self._getTargetClass()(*args, **kw)
14+
15+
def test_ctor(self):
16+
connection = _Connection()
17+
bucket = _Bucket(connection)
18+
iterator = self._makeOne(bucket)
19+
self.assertTrue(iterator.bucket is bucket)
20+
self.assertTrue(iterator.connection is connection)
21+
self.assertEqual(iterator.path, '%s/o' % bucket.path)
22+
self.assertEqual(iterator.page_number, 0)
23+
self.assertEqual(iterator.next_page_token, None)
24+
self.assertEqual(iterator.prefixes, ())
25+
26+
def test_get_items_from_response_empty(self):
27+
connection = _Connection()
28+
bucket = _Bucket(connection)
29+
iterator = self._makeOne(bucket)
30+
self.assertEqual(list(iterator.get_items_from_response({})), [])
31+
self.assertEqual(iterator.prefixes, ())
32+
33+
def test_get_items_from_response_non_empty(self):
34+
from gcloud.storage.key import Key
35+
KEY = 'key'
36+
response = {'items': [{'name': KEY}], 'prefixes': ['foo']}
37+
connection = _Connection()
38+
bucket = _Bucket(connection)
39+
iterator = self._makeOne(bucket)
40+
keys = list(iterator.get_items_from_response(response))
41+
self.assertEqual(len(keys), 1)
42+
key = keys[0]
43+
self.assertTrue(isinstance(key, Key))
44+
self.assertTrue(key.connection is connection)
45+
self.assertEqual(key.name, KEY)
46+
self.assertEqual(iterator.prefixes, ('foo',))
47+
48+
649
class Test_Bucket(unittest2.TestCase):
750

851
def _getTargetClass(self):
@@ -170,6 +213,41 @@ def test_get_all_keys_non_empty(self):
170213
self.assertEqual(kw['path'], '/b/%s/o' % NAME)
171214
self.assertEqual(kw['query_params'], {})
172215

216+
def test_iterator_defaults(self):
217+
NAME = 'name'
218+
connection = _Connection({'items': []})
219+
bucket = self._makeOne(connection, NAME)
220+
iterator = bucket.iterator()
221+
keys = list(iterator)
222+
self.assertEqual(keys, [])
223+
kw, = connection._requested
224+
self.assertEqual(kw['method'], 'GET')
225+
self.assertEqual(kw['path'], '/b/%s/o' % NAME)
226+
self.assertEqual(kw['query_params'], {})
227+
228+
def test_iterator_explicit(self):
229+
NAME = 'name'
230+
EXPECTED = {
231+
'prefix': 'subfolder',
232+
'delimiter': '/',
233+
'maxResults': 10,
234+
'versions': True,
235+
}
236+
connection = _Connection({'items': []})
237+
bucket = self._makeOne(connection, NAME)
238+
iterator = bucket.iterator(
239+
prefix='subfolder',
240+
delimiter='/',
241+
max_results=10,
242+
versions=True,
243+
)
244+
keys = list(iterator)
245+
self.assertEqual(keys, [])
246+
kw, = connection._requested
247+
self.assertEqual(kw['method'], 'GET')
248+
self.assertEqual(kw['path'], '/b/%s/o' % NAME)
249+
self.assertEqual(kw['query_params'], EXPECTED)
250+
173251
def test_new_key_existing(self):
174252
from gcloud.storage.key import Key
175253
existing = Key()
@@ -882,46 +960,6 @@ def get_items_from_response(self, response):
882960
self.assertEqual(kw[1]['query_params'], {})
883961

884962

885-
class Test__KeyIterator(unittest2.TestCase):
886-
887-
def _getTargetClass(self):
888-
from gcloud.storage.bucket import _KeyIterator
889-
return _KeyIterator
890-
891-
def _makeOne(self, *args, **kw):
892-
return self._getTargetClass()(*args, **kw)
893-
894-
def test_ctor(self):
895-
connection = _Connection()
896-
bucket = _Bucket(connection)
897-
iterator = self._makeOne(bucket)
898-
self.assertTrue(iterator.bucket is bucket)
899-
self.assertTrue(iterator.connection is connection)
900-
self.assertEqual(iterator.path, '%s/o' % bucket.path)
901-
self.assertEqual(iterator.page_number, 0)
902-
self.assertEqual(iterator.next_page_token, None)
903-
904-
def test_get_items_from_response_empty(self):
905-
connection = _Connection()
906-
bucket = _Bucket(connection)
907-
iterator = self._makeOne(bucket)
908-
self.assertEqual(list(iterator.get_items_from_response({})), [])
909-
910-
def test_get_items_from_response_non_empty(self):
911-
from gcloud.storage.key import Key
912-
KEY = 'key'
913-
response = {'items': [{'name': KEY}]}
914-
connection = _Connection()
915-
bucket = _Bucket(connection)
916-
iterator = self._makeOne(bucket)
917-
keys = list(iterator.get_items_from_response(response))
918-
self.assertEqual(len(keys), 1)
919-
key = keys[0]
920-
self.assertTrue(isinstance(key, Key))
921-
self.assertTrue(key.connection is connection)
922-
self.assertEqual(key.name, KEY)
923-
924-
925963
class _Connection(object):
926964
_delete_ok = False
927965

regression/storage.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,11 @@ def test_list_files(self):
190190

191191
def test_paginate_files(self):
192192
truncation_size = 1
193-
extra_params = {'maxResults': len(self.FILENAMES) - truncation_size}
194-
iterator = storage.key._KeyIterator(bucket=self.bucket,
195-
extra_params=extra_params)
193+
count = len(self.FILENAMES) - truncation_size
194+
iterator = self.bucket.iterator(max_results=count)
196195
response = iterator.get_next_page_response()
197196
keys = list(iterator.get_items_from_response(response))
198-
self.assertEqual(len(keys), extra_params['maxResults'])
197+
self.assertEqual(len(keys), count)
199198
self.assertEqual(iterator.page_number, 1)
200199
self.assertTrue(iterator.next_page_token is not None)
201200

0 commit comments

Comments
 (0)