Source code for flask_store.providers
# -*- coding: utf-8 -*-
"""
flask_store.providers
=====================
Base store functionality and classes.
"""
import os
import shortuuid
import urlparse
from flask import current_app
from flask_store.utils import is_path, path_to_uri
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
[docs]class Provider(object):
""" Base provider class all storage providers should inherit from. This
class provides some of the base functionality for all providers. Override
as required.
"""
#: By default Providers do not require a route to be registered
register_route = False
def __init__(self, fp, location=None):
""" Constructor. When extending this class do not forget to call
``super``.
This sets up base instance variables which can be used thoughtout the
instance.
Arguments
---------
fp : werkzeug.datastructures.FileStorage, str
A FileStorage instance or absolute path to a file
Keyword Arguments
-----------------
location : str, optional
Relative location directory, this is appended to the
``STORE_PATH``, default None
"""
# The base store path for the provider
self.store_path = self.join(current_app.config['STORE_PATH'])
# Save the fp - could be a FileStorage instance or a path
self.fp = fp
# Get the filename
if is_path(fp):
self.filename = os.path.basename(fp)
else:
if not isinstance(fp, FileStorage):
raise ValueError(
'File pointer must be an instance of a '
'werkzeug.datastructures.FileStorage')
self.filename = fp.filename
# Save location
self.location = location
# Appends location to the store path
if location:
self.store_path = self.join(self.store_path, location)
@property
def relative_path(self):
""" Returns the relative path to the file, so minus the base
path but still includes the location if it is set.
Returns
-------
str
Relative path to file
"""
parts = []
if self.location:
parts.append(self.location)
parts.append(self.filename)
return self.join(*parts)
@property
def absolute_path(self):
""" Returns the absollute file path to the file.
Returns
-------
str
Absolute file path
"""
return self.join(self.store_path, self.filename)
@property
def relative_url(self):
""" Returns the relative URL, basically minus the domain.
Returns
-------
str
Realtive URL to file
"""
parts = [current_app.config['STORE_URL_PREFIX'], ]
if self.location:
parts.append(self.location)
parts.append(self.filename)
return path_to_uri(self.url_join(*parts))
@property
def absolute_url(self):
""" Absolute url contains a domain if it is set in the configuration,
the url predix, location and the actual file name.
Returns
-------
str
Full absolute URL to file
"""
if not current_app.config['STORE_DOMAIN']:
path = self.relative_url
path = urlparse.urljoin(
current_app.config['STORE_DOMAIN'],
self.relative_url)
return path_to_uri(path)
[docs] def safe_filename(self, filename):
""" If the file already exists the file will be renamed to contain a
short url safe UUID. This will avoid overwtites.
Arguments
---------
filename : str
A filename to check if it exists
Returns
-------
str
A safe filenaem to use when writting the file
"""
while self.exists(filename):
dir_name, file_name = os.path.split(filename)
file_root, file_ext = os.path.splitext(file_name)
uuid = shortuuid.uuid()
filename = secure_filename('{0}_{1}{2}'.format(
file_root,
uuid,
file_ext))
return filename
[docs] def url_join(self, *parts):
""" Safe url part joining.
Arguments
---------
\*parts : list
List of parts to join together
Returns
-------
str
Joined url parts
"""
path = ''
for i, part in enumerate(parts):
if i > 0:
part = part.lstrip('/')
path = urlparse.urljoin(path.rstrip('/') + '/', part.rstrip('/'))
return path.lstrip('/')
[docs] def join(self, *args, **kwargs):
""" Each provider needs to implement how to safely join parts of a
path together to result in a path which can be used for the provider.
Raises
------
NotImplementedError
If the "join" method has not been implemented
"""
raise NotImplementedError(
'You must define a "join" method in the {0} provider.'.format(
self.__class__.__name__))
[docs] def exists(self, *args, **kwargs):
""" Placeholder "exists" method. This should be overridden by custom
providers and return a ``boolean`` depending on if the file exists
of not for the provider.
Raises
------
NotImplementedError
If the "exists" method has not been implemented
"""
raise NotImplementedError(
'You must define a "exists" method in the {0} provider.'.format(
self.__class__.__name__))
[docs] def save(self, *args, **kwargs):
""" Placeholder "sabe" method. This should be overridden by custom
providers and save the file object to the provider.
Raises
------
NotImplementedError
If the "save" method has not been implemented
"""
raise NotImplementedError(
'You must define a "save" method in the {0} provider.'.format(
self.__class__.__name__))