Skip to content

Commit

Permalink
Improve metadata mime type handling
Browse files Browse the repository at this point in the history
  • Loading branch information
noirbizarre committed Mar 24, 2018
1 parent 626a6ad commit 273baed
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 13 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Changelog
Current
-------

- Added ``copy`` and ``move`` operations
- `delete` now supports directories (or prefixes for key/value stores)
- Added ``copy()`` and ``move()`` operations
- `delete()` now supports directories (or prefixes for key/value stores)
- Improve `metadata()` `mime` handling

0.5.1 (2018-03-12)
------------------
Expand Down
18 changes: 18 additions & 0 deletions flask_fs/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import six

from flask_fs import files

__all__ = [i.encode('ascii') for i in ('BaseBackend', 'DEFAULT_BACKEND')]


Expand All @@ -14,6 +16,7 @@ class BaseBackend(object):
Abstract class to implement backend.
'''
root = None
DEFAULT_MIME = 'application/octet-stream'

def __init__(self, name, config):
self.name = name
Expand Down Expand Up @@ -66,6 +69,21 @@ def save(self, file_or_wfs, filename, overwrite=False):
self.write(filename, file_or_wfs.read())
return filename

def metadata(self, filename):
'''
Fetch all available metadata for a given file
'''
meta = self.get_metadata(filename)
# Fix backend mime misdetection
meta['mime'] = meta.get('mime') or files.mime(filename, self.DEFAULT_MIME)
return meta

def get_metadata(self, filename):
'''
Backend specific method to retrieve metadata for a given file
'''
raise NotImplementedError('Copy operation is not implemented')

def serve(self, filename):
'''Serve a file given its filename'''
raise NotImplementedError('serve operation is not implemented')
Expand Down
6 changes: 2 additions & 4 deletions flask_fs/backends/gridfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
from gridfs import GridFS
from pymongo import MongoClient

from flask_fs import files

from . import BaseBackend

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -75,11 +73,11 @@ def serve(self, filename):
file = self.fs.get_last_version(filename)
return send_file(file, mimetype=file.content_type)

def metadata(self, filename):
def get_metadata(self, filename):
f = self.fs.get_last_version(filename)
return {
'checksum': 'md5:{0}'.format(f.md5),
'size': f.length,
'mime': f.content_type or files.mime(filename),
'mime': f.content_type,
'modified': f.upload_date,
}
4 changes: 2 additions & 2 deletions flask_fs/backends/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ def serve(self, filename):
'''Serve files for storages with direct file access'''
return send_from_directory(self.root, filename)

def metadata(self, filename):
'''Fetch all availabe metadata'''
def get_metadata(self, filename):
'''Fetch all available metadata'''
dest = self.path(filename)
with open(dest, 'rb', buffering=0) as f:
checksum = 'sha1:{0}'.format(sha1(f))
Expand Down
5 changes: 3 additions & 2 deletions flask_fs/backends/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,15 @@ def list_files(self):
for f in self.bucket.objects.all():
yield f.key

def metadata(self, filename):
def get_metadata(self, filename):
'''Fetch all availabe metadata'''
obj = self.bucket.Object(filename)
checksum = 'md5:{0}'.format(obj.e_tag[1:-1])
mime = obj.content_type.split(';', 1)[0] if obj.content_type else None
return {
'checksum': checksum,
'size': obj.content_length,
'mime': obj.content_type,
'mime': mime,
'modified': obj.last_modified,
}

Expand Down
2 changes: 1 addition & 1 deletion flask_fs/backends/swift.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def list_files(self):
for i in items:
yield i['name']

def metadata(self, filename):
def get_metadata(self, filename):
data = self.conn.head_object(self.name, filename)
return {
'checksum': 'md5:{0}'.format(data['etag']),
Expand Down
4 changes: 2 additions & 2 deletions flask_fs/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ def lower_extension(filename):
return filename


def mime(filename):
def mime(filename, default=None):
'''
A basic helper to guess mime type from a filename or url
'''
return mimetypes.guess_type(filename)[0]
return mimetypes.guess_type(filename)[0] or default


class All(object):
Expand Down
7 changes: 7 additions & 0 deletions tests/test_backend_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ def test_metadata(self, app, faker):
assert metadata['mime'] == 'text/plain'
assert isinstance(metadata['modified'], datetime)

def test_metadata_unknown_mime(self, app, faker):
content = six.text_type(faker.sentence())
self.put_file('file.whatever', content)

metadata = self.backend.metadata('file.whatever')
assert metadata['mime'] in ('application/octet-stream', 'text/plain')

def test_copy(self, faker):
content = faker.sentence()
self.put_file('file.test', content)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,18 @@ def test_all_except():
all_except = files.AllExcept('exe')
assert 'csv' in all_except
assert 'exe' not in all_except


def test_mime_known_type():
assert files.mime('test.txt') == 'text/plain'
assert files.mime('test.csv') == 'text/csv'


def test_mime_default_to_none():
assert files.mime('test') is None
assert files.mime('test', default=None) is None


def test_mime_default_to_custom():
default = 'application/octet-stream'
assert files.mime('test', default=default) == default

0 comments on commit 273baed

Please sign in to comment.