Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d52d81f
Add support for the Bucket's 'versioning' field.
tseaver Nov 4, 2014
8477e85
Add tests asserting that 'get_metatdata' raises w/ 'versioning'.
tseaver Nov 4, 2014
037a732
Add support for the Bucket's 'logging' field.
tseaver Nov 4, 2014
c586a7c
Add CORS support to buckets.
tseaver Nov 4, 2014
b721f42
Add location support to buckets.
tseaver Nov 4, 2014
c9fa1f3
Expanded / explicit coverage and assertions for '_helpers._MetadataMi…
tseaver Nov 5, 2014
2c3725d
Remove redundant coverage for mixin methods.
tseaver Nov 5, 2014
36966e8
Stop using 'metadata' to refer to all properties in bucket/key repres…
tseaver Nov 5, 2014
79e0b05
Wrap method / attr name in single quotes.
tseaver Nov 5, 2014
7533899
Add read-only properties for non-writable bucket fields.
tseaver Nov 5, 2014
9d7fc2f
Merge branch '314-bucket_versioning_support' into 314-omnibus
tseaver Nov 5, 2014
40c03c5
Merge branch '314-bucket_logging_support' into 314-omnibus
tseaver Nov 5, 2014
1fdacb4
Merge branch '314-bucket_cors_support' into 314-omnibus
tseaver Nov 5, 2014
48677ee
Merge branch '314-bucket_location_support' into 314-omnibus
tseaver Nov 5, 2014
c3fcb2e
Re-order methods to match field order.
tseaver Nov 5, 2014
aa7afbb
Make 'lazy' test less eager.
tseaver Nov 5, 2014
5f1667a
Fix up accessor / mutators methods to use new helpers.
tseaver Nov 5, 2014
72f3e5a
Bucket has more fields than pylint wants to allow.
tseaver Nov 5, 2014
f77e593
Remove backward-compatibility aliases.
tseaver Nov 6, 2014
b8ea274
Docstring fixes.
tseaver Nov 6, 2014
262b413
Drop (unused-and-confusing) '_has_property' method.
tseaver Nov 6, 2014
25b7b5c
Use 'properties' to ensure loading before lookup.
tseaver Nov 6, 2014
13b28c2
Fix copy-paste errors in 'Bucket' accessor docstrings.
tseaver Nov 6, 2014
3eaf39d
Avoid remapping names of keys in server-side properties.
tseaver Nov 6, 2014
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
137 changes: 63 additions & 74 deletions gcloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,23 @@
"""


class _MetadataMixin(object):
"""Abstract mixin for cloud storage classes with associated metadata.
class _PropertyMixin(object):
"""Abstract mixin for cloud storage classes with associated propertties.

Non-abstract subclasses should implement:
- CUSTOM_METADATA_FIELDS
- CUSTOM_PROPERTY_ACCESSORS
- connection
- path
"""

CUSTOM_METADATA_FIELDS = None
CUSTOM_PROPERTY_ACCESSORS = None
"""Mapping of field name -> accessor for fields w/ custom accessors.

Expected to be set by subclasses. Fields in this mapping will cause
`get_metadata()` to raise a KeyError with a message to use the relevant
accessor methods.
:meth:`_get_property()` to raise a KeyError with a message to use the
relevant accessor methods.
"""

def __init__(self, name=None, metadata=None):
"""_MetadataMixin constructor.

:type name: string
:param name: The name of the object.

:type metadata: dict
:param metadata: All the other data provided by Cloud Storage.
"""
self.name = name
self.metadata = metadata

@property
def connection(self):
"""Abstract getter for the connection to use."""
Expand All @@ -43,90 +31,91 @@ def path(self):
"""Abstract getter for the object path."""
raise NotImplementedError

def has_metadata(self, field=None):
"""Check if metadata is available.
def __init__(self, name=None, properties=None):
"""_PropertyMixin constructor.

:type field: string
:param field: (optional) the particular field to check for.
:type name: string
:param name: The name of the object.

:rtype: bool
:returns: Whether metadata is available locally.
:type properties: dict
:param properties: All the other data provided by Cloud Storage.
"""
if not self.metadata:
return False
elif field and field not in self.metadata:
return False
else:
return True
self.name = name
self._properties = {}
if properties is not None:
self._properties.update(properties)

def reload_metadata(self):
"""Reload metadata from Cloud Storage.
@property
def properties(self):
"""Ensure properties are loaded, and return a copy.

This comment was marked as spam.


:rtype: dict
"""
if not self._properties:
self._reload_properties()
return self._properties.copy()

:rtype: :class:`_MetadataMixin`
def _reload_properties(self):
"""Reload properties from Cloud Storage.

:rtype: :class:`_PropertyMixin`
:returns: The object you just reloaded data for.
"""
# Pass only '?projection=noAcl' here because 'acl' and related
# are handled via 'get_acl()' etc.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

query_params = {'projection': 'noAcl'}
self.metadata = self.connection.api_request(
self._properties = self.connection.api_request(
method='GET', path=self.path, query_params=query_params)
return self

def get_metadata(self, field=None, default=None):
"""Get all metadata or a specific field.
def _patch_properties(self, properties):
"""Update particular fields of this object's properties.

This method will only update the fields provided and will not
touch the other fields.

It will also reload the properties locally based on the server's
response.

:type properties: dict
:param properties: The dictionary of values to update.

:rtype: :class:`_PropertyMixin`
:returns: The current object.
"""
# Pass '?projection=full' here because 'PATCH' documented not
# to work properly w/ 'noAcl'.
self._properties = self.connection.api_request(
method='PATCH', path=self.path, data=properties,
query_params={'projection': 'full'})
return self

def _get_property(self, field, default=None):
"""Return the value of a field from the server-side representation.

If you request a field that isn't available, and that field can
be retrieved by refreshing data from Cloud Storage, this method
will reload the data using :func:`_MetadataMixin.reload_metadata`.
will reload the data using :func:`_PropertyMixin._reload_properties`.

:type field: string
:param field: (optional) A particular field to retrieve from metadata.
:param field: A particular field to retrieve from properties.

:type default: anything
:param default: The value to return if the field provided wasn't found.

:rtype: dict or anything
:returns: All metadata or the value of the specific field.

:raises: :class:`KeyError` if the field is in CUSTOM_METADATA_FIELDS.
:rtype: anything
:returns: value of the specific field, or the default if not found.
"""
# We ignore 'acl' and related fields because they are meant to be
# handled via 'get_acl()' and related methods.
custom = self.CUSTOM_METADATA_FIELDS.get(field)
# Raise for fields which have custom accessors.
custom = self.CUSTOM_PROPERTY_ACCESSORS.get(field)
if custom is not None:
message = 'Use %s or related methods instead.' % custom
message = "Use '%s' or related methods instead." % custom
raise KeyError((field, message))

if not self.has_metadata(field=field):
self.reload_metadata()

if field:
return self.metadata.get(field, default)
else:
return self.metadata

def patch_metadata(self, metadata):
"""Update particular fields of this object's metadata.

This method will only update the fields provided and will not
touch the other fields.

It will also reload the metadata locally based on the server's
response.

:type metadata: dict
:param metadata: The dictionary of values to update.

:rtype: :class:`_MetadataMixin`
:returns: The current object.
"""
self.metadata = self.connection.api_request(
method='PATCH', path=self.path, data=metadata,
query_params={'projection': 'full'})
return self
return self.properties.get(field, default)

def get_acl(self):
"""Get ACL metadata as an object.
"""Get ACL as an object.

:returns: An ACL object for the current object.
"""
Expand Down
Loading