Skip to content

Commit

Permalink
docs: addition of method documentation
Browse files Browse the repository at this point in the history
* Improves method documentation. (addresses #23)

Signed-off-by: Javier Delgado <javier.delgado.fernandez@cern.ch>
  • Loading branch information
JavierDelgadoFernandez committed Aug 5, 2016
1 parent 4ff3a5d commit 3093674
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 27 deletions.
150 changes: 126 additions & 24 deletions invenio_records_files/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,39 +41,69 @@ class FileObject(object):
"""Wrapper for files."""

def __init__(self, obj, data):
"""Bind to current bucket."""
"""Bind to current bucket.
:param obj: Stored object.
:param data: Metadata associated to the object.
"""
self.obj = obj
self.data = data

def get_version(self, version_id=None):
"""Return specific version ``ObjectVersion`` instance or HEAD."""
"""Return specific version ``ObjectVersion`` instance or HEAD.
:param version_id: Version ID of the object.
:returns: :class:`~invenio_files_rest.models:ObjectVersion` instance or
HEAD of the stored object.
"""
return ObjectVersion.get(bucket=self.obj.bucket, key=self.obj.key,
version_id=version_id)

def get(self, key, default=None):
"""Proxy to ``obj``."""
"""Proxy to ``obj``.
:param key: Metadata key which holds the value.
:returns: Metadata value of the specified key or default.
"""
if hasattr(self.obj, key):
return getattr(self.obj, key)
return self.data.get(key, default)

def __getattr__(self, key):
"""Proxy to ``obj``."""
"""Proxy to ``obj``.
:param key: Metadata key which holds the value.
:returns: Metadata value of the specified key.
"""
return getattr(self.obj, key)

def __getitem__(self, key):
"""Proxy to ``obj`` and ``data``."""
"""Proxy to ``obj`` and ``data``.
:param key: Metadata or object key which holds the value.
:returns: Metadata or object value of the specified key.
"""
if hasattr(self.obj, key):
return getattr(self.obj, key)
return self.data[key]

def __setitem__(self, key, value):
"""Proxy to ``data``."""
"""Proxy to ``data``.
:param key: Metadata key which will hold the file.
:param value: Value to be saved.
:raises KeyError: It occurs when the key is already used.
"""
if hasattr(self.obj, key):
raise KeyError(key)
self.data[key] = value

def dumps(self):
"""Create a dump."""
"""Create a dump.
:returns: Metadata associated to the record.
"""
self.data.update({
'bucket': str(self.obj.bucket_id),
'checksum': self.obj.file.checksum,
Expand All @@ -85,10 +115,22 @@ def dumps(self):


def _writable(method):
"""Check that record is in defined status."""
"""Check that record is in defined status.
:param method: Method to be decorated.
:returns: Function decorated.
"""
@wraps(method)
def wrapper(self, *args, **kwargs):
"""Send record for indexing."""
"""Send record for indexing.
:param args: List of arguments.
:param kwargs: Dictionary of arguments for the function.
:returns: Execution result of the decorated method.
:raises InvalidOperationError: It occurs when the bucket is locked or
deleted.
"""
if self.bucket.locked or self.bucket.deleted:
raise InvalidOperationError()
return method(self, *args, **kwargs)
Expand All @@ -99,7 +141,12 @@ class FilesIterator(object):
"""Iterator for files."""

def __init__(self, record, bucket=None, file_cls=None):
"""Initialize iterator."""
"""Initialize iterator.
:param record: Holder record of a list of files.
:param bucket: Specific bucket of files.
:param file_cls: Class that will hold each file.
"""
self._it = None
self.record = record
self.model = record.model
Expand All @@ -112,34 +159,59 @@ def __init__(self, record, bucket=None, file_cls=None):

@property
def keys(self):
"""Return file keys."""
"""Return file keys.
:returns: List of file keys.
"""
return self.filesmap.keys()

def __len__(self):
"""Get number of files."""
"""Get number of files.
:returns: Number of files that the current bucket holds.
"""
return ObjectVersion.get_by_bucket(self.bucket).count()

def __iter__(self):
"""Get iterator."""
"""Get iterator.
:returns: Iterator over the sorted filed of the bucket.
"""
self._it = iter(sorted_files_from_bucket(self.bucket, self.keys))
return self

def next(self):
"""Python 2.7 compatibility."""
"""Python 2.7 compatibility.
:returns: Next object of the iteration.
"""
return self.__next__() # pragma: no cover

def __next__(self):
"""Get next file item."""
"""Get next file item.
:returns: Next object of the iteration.
"""
obj = next(self._it)
return self.file_cls(obj, self.filesmap.get(obj.key, {}))

def __contains__(self, key):
"""Test if file exists."""
"""Test if file exists.
:param key: Key to check if it is in the bucket.
:returns: If the key has been found in the bucket.
"""
return ObjectVersion.get_by_bucket(
self.bucket).filter_by(key=str(key)).count()

def __getitem__(self, key):
"""Get a specific file."""
"""Get a specific file.
:param key: File key which holds the file.
:returns: Instance of :py:data:`self.file_cls` containing the file.
:raises KeyError: It occurs when the key is not found in the bucket.
"""
obj = ObjectVersion.get(self.bucket, key)
if obj:
return self.file_cls(obj, self.filesmap.get(obj.key, {}))
Expand All @@ -151,7 +223,11 @@ def flush(self):

@_writable
def __setitem__(self, key, stream):
"""Add file inside a deposit."""
"""Add file inside a deposit.
:param key: File key which will hold the file.
:param stream: Content file stream.
"""
with db.session.begin_nested():
# save the file
obj = ObjectVersion.create(
Expand All @@ -161,7 +237,11 @@ def __setitem__(self, key, stream):

@_writable
def __delitem__(self, key):
"""Delete a file from the deposit."""
"""Delete a file from the deposit.
:param key: File key which will be deleted.
:raises KeyError: It occurs when the key is not found in the bucket.
"""
obj = ObjectVersion.delete(bucket=self.bucket, key=key)

if obj is None:
Expand All @@ -172,7 +252,10 @@ def __delitem__(self, key):
self.flush()

def sort_by(self, *ids):
"""Update files order."""
"""Update files order.
:param ids: List of ids specifying the final status of the list.
"""
# Support sorting by file_ids or keys.
files = {str(f_.file_id): f_.key for f_ in self}
# self.record['_files'] = [{'key': files.get(id_, id_)} for id_ in ids]
Expand All @@ -184,7 +267,12 @@ def sort_by(self, *ids):

@_writable
def rename(self, old_key, new_key):
"""Rename a file."""
"""Rename a file.
:param old_key: Old key that holds the object.
:param new_key: New key that will hold the object.
:returns: The object that has been renamed.
"""
assert new_key not in self
assert old_key != new_key

Expand All @@ -204,7 +292,11 @@ def rename(self, old_key, new_key):
return obj

def dumps(self, bucket=None):
"""Serialize files from a bucket."""
"""Serialize files from a bucket.
:param bucket: Specific bucket of files, otherwise it will take the .
:returns: List of files serialized.
"""
return [
self.file_cls(o, self.filesmap.get(o.key, {})).dumps()
for o in sorted_files_from_bucket(bucket or self.bucket, self.keys)
Expand All @@ -221,18 +313,24 @@ class FilesMixin(object):
"""

file_cls = FileObject

files_iter_cls = FilesIterator

def _create_bucket(self):
"""Return an instance of ``Bucket`` class.
.. note:: Reimplement in children class for custom behavior.
:returns: Instance of ``Bucket``.
"""
return None

@property
def files(self):
"""Get files iterator."""
"""Get files iterator.
:returns: Files iterator.
"""
if self.model is None:
raise MissingModelError()

Expand All @@ -254,7 +352,11 @@ class Record(_Record, FilesMixin):
"""Define API for files manipulation using ``FilesMixin``."""

def delete(self, force=False):
"""Delete a record and also remove the RecordsBuckets if necessary."""
"""Delete a record and also remove the RecordsBuckets if necessary.
:param force: True to remove also the RecordBuckets object.
:returns: Deleted record.
"""
if force:
RecordsBuckets.query.filter_by(
record=self.model,
Expand Down
12 changes: 11 additions & 1 deletion invenio_records_files/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,30 @@ class RecordsBuckets(db.Model):
nullable=False,
# NOTE no unique constrain for better future ...
)
"""Record related with the bucket."""

bucket_id = db.Column(
UUIDType,
db.ForeignKey(Bucket.id),
primary_key=True,
nullable=False,
)
"""Bucket related with the record."""

bucket = db.relationship(Bucket)
"""Relationship to the bucket."""

record = db.relationship(RecordMetadata)
"""It is used by SQLAlchemy for optimistic concurrency control."""

@classmethod
def create(cls, record, bucket):
"""Create a new RecordsBuckets and adds it to the session."""
"""Create a new RecordsBuckets and adds it to the session.
:param record: Record used to relate with the ``Bucket``.
:param bucket: Bucket used to relate with the ``Record``.
:returns: The ``RecordsBuckets`` object created.
"""
rb = cls(record=record, bucket=bucket)
db.session.add(rb)
return rb
16 changes: 14 additions & 2 deletions invenio_records_files/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@


def sorted_files_from_bucket(bucket, keys=None):
"""Return files from bucket sorted by given keys."""
"""Return files from bucket sorted by given keys.
:param bucket: :class:`~invenio_records_files.models:Bucket` containing the
files.
:param keys: Keys order to be used.
:returns: Sorted of bucket items.
"""
keys = keys or []
total = len(keys)
sortby = dict(zip(keys, range(total)))
Expand All @@ -40,7 +46,13 @@ def sorted_files_from_bucket(bucket, keys=None):


def record_file_factory(pid, record, filename):
"""Get file from a record."""
"""Get file from a record.
:param pid: Not used. It keeps the function signature.
:param record: Record which contains the files.
:param filename: Name of the file to be returned.
:returns: File object or ``None`` if not found.
"""
try:
if not (hasattr(record, 'files') and record.files):
return None
Expand Down

0 comments on commit 3093674

Please sign in to comment.