Skip to content

Commit

Permalink
Binding client to Bucket and using clients throughout.
Browse files Browse the repository at this point in the history
Towards googleapis#952.

Not all tests (cover and system-tests) are passing since implicit
code paths are no longer being traced and since storage.Batch()
is not fully supported yet.
  • Loading branch information
dhermes committed Jul 11, 2015
1 parent fac1e2c commit 22c0d3f
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 584 deletions.
2 changes: 1 addition & 1 deletion gcloud/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def set_default_bucket(bucket=None):
bucket_name = os.getenv(_BUCKET_ENV_VAR_NAME)

if bucket_name is not None:
bucket = Bucket(bucket_name)
bucket = Bucket(client=None, name=bucket_name)

if bucket is not None:
_implicit_environ._DEFAULTS.bucket = bucket
Expand Down
78 changes: 26 additions & 52 deletions gcloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,61 +20,59 @@
from Crypto.Hash import MD5
import base64

from gcloud.storage._implicit_environ import get_default_connection
from gcloud.storage.batch import Batch


class _PropertyMixin(object):
"""Abstract mixin for cloud storage classes with associated propertties.
Non-abstract subclasses should implement:
- connection
- client
- path
:type name: string
:param name: The name of the object.
"""

@property
def path(self):
"""Abstract getter for the object path."""
raise NotImplementedError

def __init__(self, name=None):
self.name = name
self._properties = {}
self._changes = set()

@staticmethod
def _client_or_connection(client):
"""Temporary method to get a connection from a client.
@property
def path(self):
"""Abstract getter for the object path."""
raise NotImplementedError

If the client is null, gets the connection from the environment.
@property
def client(self):
"""Abstract getter for the object client."""
raise NotImplementedError

def _require_client(self, client):
"""Check client or verify over-ride.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current object.
:rtype: :class:`gcloud.storage.connection.Connection`
:returns: The connection determined from the ``client`` or environment.
:rtype: :class:`gcloud.storage.client.Client`
:returns: The client passed in or the currently bound client.
"""
if client is None:
return _require_connection()
else:
return client.connection
client = self.client
return client

def reload(self, client=None):
"""Reload properties from Cloud Storage.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current object.
"""
connection = self._client_or_connection(client)
client = self._require_client(client)
# Pass only '?projection=noAcl' here because 'acl' and related
# are handled via custom endpoints.
query_params = {'projection': 'noAcl'}
api_response = connection.api_request(
api_response = client.connection.api_request(
method='GET', path=self.path, query_params=query_params,
_target_object=self)
self._set_properties(api_response)
Expand Down Expand Up @@ -113,44 +111,20 @@ def patch(self, client=None):
Updates the ``_properties`` with the response from the backend.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current object.
"""
connection = self._client_or_connection(client)
client = self._require_client(client)
# Pass '?projection=full' here because 'PATCH' documented not
# to work properly w/ 'noAcl'.
update_properties = dict((key, self._properties[key])
for key in self._changes)
api_response = connection.api_request(
api_response = client.connection.api_request(
method='PATCH', path=self.path, data=update_properties,
query_params={'projection': 'full'}, _target_object=self)
self._set_properties(api_response)


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 = get_default_connection()

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

return connection


def _scalar_property(fieldname):
"""Create a property descriptor around the :class:`_PropertyMixin` helpers.
"""
Expand Down
49 changes: 29 additions & 20 deletions gcloud/storage/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@
when sending metadata for ACLs to the API.
"""

from gcloud.storage._helpers import _require_connection


class _ACLEntity(object):
"""Class representing a set of roles for an entity.
Expand Down Expand Up @@ -351,37 +349,38 @@ def get_entities(self):
self._ensure_loaded()
return list(self.entities.values())

@staticmethod
def _client_or_connection(client):
"""Temporary method to get a connection from a client.
@property
def client(self):
"""Abstract getter for the object client."""
raise NotImplementedError

If the client is null, gets the connection from the environment.
def _require_client(self, client):
"""Check client or verify over-ride.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current ACL.
:rtype: :class:`gcloud.storage.connection.Connection`
:returns: The connection determined from the ``client`` or environment.
:rtype: :class:`gcloud.storage.client.Client`
:returns: The client passed in or the currently bound client.
"""
if client is None:
return _require_connection()
else:
return client.connection
client = self.client
return client

def reload(self, client=None):
"""Reload the ACL data from Cloud Storage.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
to the ``client`` stored on the ACL's parent.
"""
path = self.reload_path
connection = self._client_or_connection(client)
client = self._require_client(client)

self.entities.clear()

found = connection.api_request(method='GET', path=path)
found = client.connection.api_request(method='GET', path=path)
self.loaded = True
for entry in found.get('items', ()):
self.add_entity(self.entity_from_dict(entry))
Expand All @@ -395,7 +394,7 @@ def save(self, acl=None, client=None):
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
to the ``client`` stored on the ACL's parent.
"""
if acl is None:
acl = self
Expand All @@ -405,8 +404,8 @@ def save(self, acl=None, client=None):

if save_to_backend:
path = self.save_path
connection = self._client_or_connection(client)
result = connection.api_request(
client = self._require_client(client)
result = client.connection.api_request(
method='PATCH',
path=path,
data={self._URL_PATH_ELEM: list(acl)},
Expand All @@ -426,7 +425,7 @@ def clear(self, client=None):
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
to the ``client`` stored on the ACL's parent.
"""
self.save([], client=client)

Expand All @@ -442,6 +441,11 @@ def __init__(self, bucket):
super(BucketACL, self).__init__()
self.bucket = bucket

@property
def client(self):
"""The client bound to this ACL's bucket."""
return self.bucket.client

@property
def reload_path(self):
"""Compute the path for GET API requests for this ACL."""
Expand Down Expand Up @@ -470,6 +474,11 @@ def __init__(self, blob):
super(ObjectACL, self).__init__()
self.blob = blob

@property
def client(self):
"""The client bound to this ACL's blob."""
return self.blob.client

@property
def reload_path(self):
"""Compute the path for GET API requests for this ACL."""
Expand Down
Loading

0 comments on commit 22c0d3f

Please sign in to comment.