Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a fallback connection in Bucket #759

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gcloud/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from gcloud.storage.api import get_all_buckets
from gcloud.storage.api import get_bucket
from gcloud.storage.api import lookup_bucket
from gcloud.storage.batch import Batch
from gcloud.storage.blob import Blob
from gcloud.storage.bucket import Bucket
from gcloud.storage.connection import Connection
Expand Down Expand Up @@ -82,7 +83,7 @@ def set_default_bucket(bucket=None):
bucket_name = os.getenv(_BUCKET_ENV_VAR_NAME)
connection = get_default_connection()

if bucket_name is not None and connection is not None:
if bucket_name is not None:
bucket = Bucket(bucket_name, connection=connection)

if bucket is not None:
Expand Down
44 changes: 16 additions & 28 deletions gcloud/storage/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,11 @@ def lookup_bucket(bucket_name, connection=None):
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending requests.
If not provided, falls back to default.
If not provided, Bucket() will fall back to default.

:rtype: :class:`gcloud.storage.bucket.Bucket`
:returns: The bucket matching the name provided or None if not found.
"""
if connection is None:
connection = get_default_connection()

try:
return get_bucket(bucket_name, connection=connection)
except NotFound:
Expand All @@ -79,13 +76,12 @@ def get_all_buckets(project=None, connection=None):
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending requests.
If not provided, falls back to default.
If not provided, _BucketIterator() will fall back to
default.

:rtype: iterable of :class:`gcloud.storage.bucket.Bucket` objects.
:returns: All buckets belonging to this project.
"""
if connection is None:
connection = get_default_connection()
if project is None:
project = get_default_project()
extra_params = {'project': project}
Expand Down Expand Up @@ -116,15 +112,11 @@ def get_bucket(bucket_name, connection=None):
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending requests.
If not provided, falls back to default.
If not provided, Bucket() will fall back to default.

:rtype: :class:`gcloud.storage.bucket.Bucket`
:returns: The bucket matching the name provided.
:raises: :class:`gcloud.exceptions.NotFound`
"""
if connection is None:
connection = get_default_connection()

bucket = Bucket(bucket_name, connection=connection)
bucket._reload_properties()
return bucket
Expand All @@ -143,6 +135,9 @@ def create_bucket(bucket_name, project=None, connection=None):

This implements "storage.buckets.insert".

If the bucket already exists, will raise
:class:`gcloud.exceptions.Conflict`.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

:type project: string
:param project: Optional. The project to use when creating bucket.
If not provided, falls back to default.
Expand All @@ -153,25 +148,13 @@ def create_bucket(bucket_name, project=None, connection=None):
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending requests.
If not provided, falls back to default.
If not provided, Bucket() will fall back to default.

:rtype: :class:`gcloud.storage.bucket.Bucket`
:returns: The newly created bucket.
:raises: :class:`gcloud.exceptions.Conflict` if
there is a confict (bucket already exists, invalid name, etc.)
"""
if connection is None:
connection = get_default_connection()
if project is None:
project = get_default_project()

query_params = {'project': project}
response = connection.api_request(method='POST', path='/b',
query_params=query_params,
data={'name': bucket_name})
name = response.get('name')
bucket = Bucket(name, connection=connection)
bucket._properties = response
bucket = Bucket(bucket_name, connection=connection)
bucket.create(project)
return bucket


Expand All @@ -187,6 +170,11 @@ class _BucketIterator(Iterator):
"""

def __init__(self, connection, extra_params=None):
# If an implicit connection was intended, we pass along `None` to the
# Bucket() constructor as well.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

self._ctor_connection = connection
if connection is None:
connection = get_default_connection()
super(_BucketIterator, self).__init__(connection=connection, path='/b',
extra_params=extra_params)

Expand All @@ -198,6 +186,6 @@ def get_items_from_response(self, response):
"""
for item in response.get('items', []):
name = item.get('name')
bucket = Bucket(name, connection=self.connection)
bucket = Bucket(name, connection=self._ctor_connection)
bucket._properties = item
yield bucket
5 changes: 5 additions & 0 deletions gcloud/storage/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ def finish(self):
self._responses = list(_unpack_batch_response(response, content))
return self._responses

@staticmethod
def current():
"""Return the topmost batch, or None."""
return _BATCHES.top

def __enter__(self):
_BATCHES.push(self)
return self
Expand Down
57 changes: 56 additions & 1 deletion gcloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@
import os
import six

from gcloud._helpers import get_default_project
from gcloud.exceptions import NotFound
from gcloud.storage._helpers import _PropertyMixin
from gcloud.storage._helpers import _scalar_property
from gcloud.storage import _implicit_environ
from gcloud.storage.acl import BucketACL
from gcloud.storage.acl import DefaultObjectACL
from gcloud.storage.iterator import Iterator
from gcloud.storage.batch import Batch
from gcloud.storage.blob import Blob


Expand Down Expand Up @@ -125,6 +128,34 @@ def exists(self):
except NotFound:
return False

def create(self, project=None):
"""Creates current bucket.

If the bucket already exists, will raise
:class:`gcloud.exceptions.Conflict`.

This implements "storage.buckets.insert".

:type project: string
:param project: Optional. The project to use when creating bucket.
If not provided, falls back to default.

:rtype: :class:`gcloud.storage.bucket.Bucket`
:returns: The newly created bucket.
:raises: :class:`EnvironmentError` if the project is not given and

This comment was marked as spam.

This comment was marked as spam.

can't be inferred.
"""
if project is None:
project = get_default_project()
if project is None:
raise EnvironmentError('Project could not be inferred '
'from environment.')

query_params = {'project': project}
self._properties = self.connection.api_request(
method='POST', path='/b', query_params=query_params,
data={'name': self.name})

@property
def acl(self):
"""Create our ACL on demand."""
Expand All @@ -146,7 +177,7 @@ def connection(self):
:rtype: :class:`gcloud.storage.connection.Connection`
:returns: The connection to use.
"""
return self._connection
return _require_connection(self._connection)

@staticmethod
def path_helper(bucket_name):
Expand Down Expand Up @@ -746,3 +777,27 @@ def make_public(self, recursive=False, future=False):
for blob in self:
blob.acl.all().grant_read()
blob.save_acl()


def _require_connection(connection=None):
"""Infer a connection from the environment, if not passed explicitly.

:type connection: :class:`gcloud.storage.connection.Connection`
:param connection: Optional.

:rtype: :class:`gcloud.storage.connection.Connection`
:returns: A connection based on the current environment.
:raises: :class:`EnvironmentError` if ``connection`` is ``None``, and
cannot be inferred from the environment.
"""
# NOTE: We use current Batch directly since it inherits from Connection.
if connection is None:
connection = Batch.current()

if connection is None:
connection = _implicit_environ.get_default_connection()

if connection is None:
raise EnvironmentError('Connection could not be inferred.')

return connection
31 changes: 25 additions & 6 deletions gcloud/storage/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ def _lookup_bucket_hit_helper(self, use_default=False):
if use_default:
with _monkey_defaults(connection=conn):
bucket = self._callFUT(BLOB_NAME)
# In the default case, the bucket never has a connection
# bound to it.
self.assertTrue(bucket._connection is None)
else:
bucket = self._callFUT(BLOB_NAME, connection=conn)
# In the explicit case, the bucket has a connection bound to it.
self.assertTrue(bucket._connection is conn)

self.assertTrue(isinstance(bucket, Bucket))
self.assertTrue(bucket.connection is conn)
self.assertEqual(bucket.name, BLOB_NAME)
self.assertEqual(http._called_with['method'], 'GET')
self.assertEqual(http._called_with['uri'], URI)
Expand Down Expand Up @@ -187,11 +191,15 @@ def _get_bucket_hit_helper(self, use_default=False):
if use_default:
with _monkey_defaults(connection=conn):
bucket = self._callFUT(BLOB_NAME)
# In the default case, the bucket never has a connection
# bound to it.
self.assertTrue(bucket._connection is None)
else:
bucket = self._callFUT(BLOB_NAME, connection=conn)
# In the explicit case, the bucket has a connection bound to it.
self.assertTrue(bucket._connection is conn)

self.assertTrue(isinstance(bucket, Bucket))
self.assertTrue(bucket.connection is conn)
self.assertEqual(bucket.name, BLOB_NAME)
self.assertEqual(http._called_with['method'], 'GET')
self.assertEqual(http._called_with['uri'], URI)
Expand Down Expand Up @@ -232,11 +240,15 @@ def _create_bucket_success_helper(self, project, use_default=False):
with _base_monkey_defaults(project=project):
with _monkey_defaults(connection=conn):
bucket = self._callFUT(BLOB_NAME)
# In the default case, the bucket never has a connection
# bound to it.
self.assertTrue(bucket._connection is None)
else:
bucket = self._callFUT(BLOB_NAME, project=project, connection=conn)
# In the explicit case, the bucket has a connection bound to it.
self.assertTrue(bucket._connection is conn)

self.assertTrue(isinstance(bucket, Bucket))
self.assertTrue(bucket.connection is conn)
self.assertEqual(bucket.name, BLOB_NAME)
self.assertEqual(http._called_with['method'], 'POST')
self.assertEqual(http._called_with['uri'], URI)
Expand Down Expand Up @@ -270,19 +282,26 @@ def test_get_items_from_response_empty(self):
iterator = self._makeOne(connection)
self.assertEqual(list(iterator.get_items_from_response({})), [])

def test_get_items_from_response_non_empty(self):
def _get_items_helper(self, connection):
from gcloud.storage.bucket import Bucket
BLOB_NAME = 'blob-name'
response = {'items': [{'name': BLOB_NAME}]}
connection = object()
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.assertTrue(bucket._connection is connection)
self.assertEqual(bucket.name, BLOB_NAME)

def test_get_items_from_response_non_empty(self):
connection = object()
self._get_items_helper(connection)

def test_get_items_from_response_implicit_connection(self):
connection = None
self._get_items_helper(connection)


class Http(object):

Expand Down
Loading