Skip to content

Commit

Permalink
Resolving cyclic imports in storage package.
Browse files Browse the repository at this point in the history
This is accomplished by moving the definitions of
BucketIterator and KeyIterator into their respective
modules (i.e. storage.bucket and storage.key).

This is not so big an issue because the parent class
iterator.Iterator does most of the work and the children
only require a small tweak.
  • Loading branch information
dhermes committed Oct 20, 2014
1 parent 628c430 commit 3148468
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 136 deletions.
27 changes: 26 additions & 1 deletion gcloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from gcloud.storage import exceptions
from gcloud.storage.acl import BucketACL
from gcloud.storage.acl import DefaultObjectACL
from gcloud.storage.iterator import KeyIterator
from gcloud.storage.iterator import Iterator
from gcloud.storage.key import Key
from gcloud.storage.key import KeyIterator


class Bucket(object):
Expand Down Expand Up @@ -637,3 +638,27 @@ def make_public(self, recursive=False, future=False):
for key in self:
key.get_acl().all().grant_read()
key.save_acl()


class BucketIterator(Iterator):
"""An iterator listing all buckets.
You shouldn't have to use this directly,
but instead should use the helper methods
on :class:`gcloud.storage.connection.Connection` objects.
:type connection: :class:`gcloud.storage.connection.Connection`
:param connection: The connection to use for querying the list of buckets.
"""

def __init__(self, connection):
super(BucketIterator, self).__init__(connection=connection, path='/b')

def get_items_from_response(self, response):
"""Factory method which yields :class:`.Bucket` items from a response.
:type response: dict
:param response: The JSON API response for a page of buckets.
"""
for item in response.get('items', []):
yield Bucket.from_dict(item, connection=self.connection)
2 changes: 1 addition & 1 deletion gcloud/storage/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from gcloud import connection
from gcloud.storage import exceptions
from gcloud.storage.bucket import Bucket
from gcloud.storage.iterator import BucketIterator
from gcloud.storage.bucket import BucketIterator


def _utcnow(): # pragma: NO COVER testing replaces
Expand Down
54 changes: 0 additions & 54 deletions gcloud/storage/iterator.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,60 +134,6 @@ def get_items_from_response(self, response):
raise NotImplementedError


class BucketIterator(Iterator):
"""An iterator listing all buckets.
You shouldn't have to use this directly,
but instead should use the helper methods
on :class:`gcloud.storage.connection.Connection` objects.
:type connection: :class:`gcloud.storage.connection.Connection`
:param connection: The connection to use for querying the list of buckets.
"""

def __init__(self, connection):
super(BucketIterator, self).__init__(connection=connection, path='/b')

def get_items_from_response(self, response):
"""Factory method which yields :class:`.Bucket` items from a response.
:type response: dict
:param response: The JSON API response for a page of buckets.
"""

from gcloud.storage.bucket import Bucket
for item in response.get('items', []):
yield Bucket.from_dict(item, connection=self.connection)


class KeyIterator(Iterator):
"""An iterator listing keys.
You shouldn't have to use this directly,
but instead should use the helper methods
on :class:`gcloud.storage.key.Key` objects.
:type bucket: :class:`gcloud.storage.bucket.Bucket`
:param bucket: The bucket from which to list keys.
"""

def __init__(self, bucket):
self.bucket = bucket
super(KeyIterator, self).__init__(
connection=bucket.connection, path=bucket.path + '/o')

def get_items_from_response(self, response):
"""Factory method, yields :class:`.storage.key.Key` items from response.
:type response: dict
:param response: The JSON API response for a page of keys.
"""

from gcloud.storage.key import Key
for item in response.get('items', []):
yield Key.from_dict(item, bucket=self.bucket)


class KeyDataIterator(object):
"""An iterator listing data stored in a key.
Expand Down
27 changes: 27 additions & 0 deletions gcloud/storage/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from StringIO import StringIO

from gcloud.storage.acl import ObjectACL
from gcloud.storage.iterator import Iterator
from gcloud.storage.iterator import KeyDataIterator


Expand Down Expand Up @@ -459,3 +460,29 @@ def make_public(self):
self.get_acl().all().grant_read()
self.save_acl()
return self


class KeyIterator(Iterator):
"""An iterator listing keys.
You shouldn't have to use this directly,
but instead should use the helper methods
on :class:`gcloud.storage.key.Key` objects.
:type bucket: :class:`gcloud.storage.bucket.Bucket`
:param bucket: The bucket from which to list keys.
"""

def __init__(self, bucket):
self.bucket = bucket
super(KeyIterator, self).__init__(
connection=bucket.connection, path=bucket.path + '/o')

def get_items_from_response(self, response):
"""Factory method, yields :class:`.storage.key.Key` items from response.
:type response: dict
:param response: The JSON API response for a page of keys.
"""
for item in response.get('items', []):
yield Key.from_dict(item, bucket=self.bucket)
40 changes: 38 additions & 2 deletions gcloud/storage/test_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ def test_make_public_w_future(self):
def test_make_public_recursive(self):
from gcloud.storage.acl import _ACLEntity
from gcloud._testing import _Monkey
from gcloud.storage import iterator
from gcloud.storage import key
from gcloud.storage import bucket as MUT
_saved = []

Expand All @@ -863,7 +863,7 @@ def grant_read(self):
def save_acl(self):
_saved.append((self._bucket, self._name, self._granted))

class _KeyIterator(iterator.KeyIterator):
class _KeyIterator(key.KeyIterator):
def get_items_from_response(self, response):
for item in response.get('items', []):
yield _Key(self.bucket, item['name'])
Expand Down Expand Up @@ -892,6 +892,42 @@ def get_items_from_response(self, response):
self.assertEqual(kw[1]['query_params'], None)


class TestBucketIterator(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.storage.bucket import BucketIterator
return BucketIterator

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_ctor(self):
connection = _Connection()
iterator = self._makeOne(connection)
self.assertTrue(iterator.connection is connection)
self.assertEqual(iterator.path, '/b')
self.assertEqual(iterator.page_number, 0)
self.assertEqual(iterator.next_page_token, None)

def test_get_items_from_response_empty(self):
connection = _Connection()
iterator = self._makeOne(connection)
self.assertEqual(list(iterator.get_items_from_response({})), [])

def test_get_items_from_response_non_empty(self):
from gcloud.storage.bucket import Bucket
KEY = 'key'
response = {'items': [{'name': KEY}]}
connection = _Connection()
iterator = self._makeOne(connection)
buckets = list(iterator.get_items_from_response(response))
self.assertEqual(len(buckets), 1)
bucket = buckets[0]
self.assertTrue(isinstance(bucket, Bucket))
self.assertTrue(bucket.connection is connection)
self.assertEqual(bucket.name, KEY)


class _Connection(object):
_delete_ok = False

Expand Down
76 changes: 0 additions & 76 deletions gcloud/storage/test_iterator.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,82 +118,6 @@ def test_get_items_from_response_raises_NotImplementedError(self):
iterator.get_items_from_response, object())


class TestBucketIterator(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.storage.iterator import BucketIterator
return BucketIterator

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_ctor(self):
connection = _Connection()
iterator = self._makeOne(connection)
self.assertTrue(iterator.connection is connection)
self.assertEqual(iterator.path, '/b')
self.assertEqual(iterator.page_number, 0)
self.assertEqual(iterator.next_page_token, None)

def test_get_items_from_response_empty(self):
connection = _Connection()
iterator = self._makeOne(connection)
self.assertEqual(list(iterator.get_items_from_response({})), [])

def test_get_items_from_response_non_empty(self):
from gcloud.storage.bucket import Bucket
KEY = 'key'
response = {'items': [{'name': KEY}]}
connection = _Connection()
iterator = self._makeOne(connection)
buckets = list(iterator.get_items_from_response(response))
self.assertEqual(len(buckets), 1)
bucket = buckets[0]
self.assertTrue(isinstance(bucket, Bucket))
self.assertTrue(bucket.connection is connection)
self.assertEqual(bucket.name, KEY)


class TestKeyIterator(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.storage.iterator import KeyIterator
return KeyIterator

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_ctor(self):
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
self.assertTrue(iterator.bucket is bucket)
self.assertTrue(iterator.connection is connection)
self.assertEqual(iterator.path, '%s/o' % bucket.path)
self.assertEqual(iterator.page_number, 0)
self.assertEqual(iterator.next_page_token, None)

def test_get_items_from_response_empty(self):
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
self.assertEqual(list(iterator.get_items_from_response({})), [])

def test_get_items_from_response_non_empty(self):
from gcloud.storage.key import Key
KEY = 'key'
response = {'items': [{'name': KEY}]}
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
keys = list(iterator.get_items_from_response(response))
self.assertEqual(len(keys), 1)
key = keys[0]
self.assertTrue(isinstance(key, Key))
self.assertTrue(key.connection is connection)
self.assertEqual(key.name, KEY)


class TestKeyDataIterator(unittest2.TestCase):

def _getTargetClass(self):
Expand Down
40 changes: 40 additions & 0 deletions gcloud/storage/test_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,46 @@ def test_make_public(self):
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})


class TestKeyIterator(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.storage.key import KeyIterator
return KeyIterator

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_ctor(self):
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
self.assertTrue(iterator.bucket is bucket)
self.assertTrue(iterator.connection is connection)
self.assertEqual(iterator.path, '%s/o' % bucket.path)
self.assertEqual(iterator.page_number, 0)
self.assertEqual(iterator.next_page_token, None)

def test_get_items_from_response_empty(self):
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
self.assertEqual(list(iterator.get_items_from_response({})), [])

def test_get_items_from_response_non_empty(self):
from gcloud.storage.key import Key
KEY = 'key'
response = {'items': [{'name': KEY}]}
connection = _Connection()
bucket = _Bucket(connection)
iterator = self._makeOne(bucket)
keys = list(iterator.get_items_from_response(response))
self.assertEqual(len(keys), 1)
key = keys[0]
self.assertTrue(isinstance(key, Key))
self.assertTrue(key.connection is connection)
self.assertEqual(key.name, KEY)


class _Connection(object):
API_BASE_URL = 'http://example.com'

Expand Down
2 changes: 1 addition & 1 deletion pylintrc_default
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ignore = datastore_v1_pb2.py
[MESSAGES CONTROL]
disable = I, protected-access, maybe-no-member, no-member,
redefined-builtin, star-args, missing-format-attribute,
similarities, cyclic-import, arguments-differ,
similarities, arguments-differ,



Expand Down
2 changes: 1 addition & 1 deletion pylintrc_reduced
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ignore = datastore_v1_pb2.py
[MESSAGES CONTROL]
disable = I, protected-access, maybe-no-member, no-member,
redefined-builtin, star-args, missing-format-attribute,
similarities, cyclic-import, arguments-differ,
similarities, arguments-differ,
invalid-name, missing-docstring, too-many-public-methods,
too-few-public-methods, attribute-defined-outside-init,
unbalanced-tuple-unpacking, too-many-locals, exec-used,
Expand Down

0 comments on commit 3148468

Please sign in to comment.