forked from thisissoon/Flask-Store
-
Notifications
You must be signed in to change notification settings - Fork 0
/
__init__.py
203 lines (153 loc) · 5.6 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# -*- coding: utf-8 -*-
"""
flask_store
===========
Adds simple file handling for different providers to your application. Provides
the following providers out of the box:
* Local file storeage
* Amazon Simple File Storage (requires ``boto`` to be installed)
"""
import urlparse
from flask import current_app, send_from_directory
from flask_store.exceptions import NotConfiguredError
from importlib import import_module
from werkzeug import LocalProxy
DEFAULT_PROVIDER = 'flask_store.providers.local.LocalProvider'
Provider = LocalProxy(lambda: store_provider())
def store_provider():
""" Returns the default provider class as defined in the application
configuration.
Returns
-------
class
The provider class
"""
store = current_app.extensions['store']
return store.store.Provider
class StoreState(object):
""" Stores the state of Flask-Store from application init.
"""
def __init__(self, store, app):
self.store = store
self.app = app
class Store(object):
""" Flask-Store integration into Flask applications. Flask-Store can
be integrated in two different ways depending on how you have setup your
Flask application.
You can bind to a specific flask application::
app = Flask(__name__)
store = Store(app)
Or if you use an application factory you can use
:meth:`flask_store.Store.init_app`::
store = Store()
def create_app():
app = Flask(__name__)
store.init_app(app)
return app
"""
def __init__(self, app=None):
""" Constructor. Basically acts as a proxy to
:meth:`flask_store.Store.init_app`.
Key Arguments
-------------
app : flask.app.Flask, optional
Optional Flask application instance, default None
"""
if app:
self.init_app(app)
def init_app(self, app):
""" Sets up application default confugration options and sets a
``Provider`` property which can be used to access the default
provider class which handles the saving of files.
Arguments
---------
app : flask.app.Flask
Flask application instance
"""
app.config.setdefault('STORE_DOMAIN', None)
app.config.setdefault('STORE_PROVIDER', DEFAULT_PROVIDER)
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions['store'] = StoreState(self, app)
# Set the provider class
self.Provider = self.provider(app)
# Set configuration defaults based on provider
self.set_provider_defaults(app)
# Ensure that any required configuration vars exist
self.check_config(app)
# Register a flask route - the provider must have register_route = True
self.register_route(app)
def check_config(self, app):
""" Checks the required application configuration variables are set
in the flask application.
Arguments
---------
app : flask.app.Flask
Flask application instance
Raises
------
NotConfiguredError
In the event a required config parameter is required by the
Store.
"""
if hasattr(self.Provider, 'REQUIRED_CONFIGURATION'):
for name in self.Provider.REQUIRED_CONFIGURATION:
if app.config.get(name, None) == None:
raise NotConfiguredError(
'{0} must be configured in your flask application '
'configuration'.format(name))
def provider(self, app):
""" Fetches the provider class as defined by the application
configuration.
Arguments
---------
app : flask.app.Flask
Flask application instance
Raises
------
ImportError
If the class or module cannot be imported
Returns
-------
class
The provider class
"""
if not hasattr(self, '_provider'):
parts = app.config['STORE_PROVIDER'].split('.')
klass = parts.pop()
path = '.'.join(parts)
module = import_module(path)
if not hasattr(module, klass):
raise ImportError('{0} provider not found at {1}'.format(
klass,
path))
self._provider = getattr(module, klass)
return getattr(self, '_provider')
def set_provider_defaults(self, app):
""" If the provider has a ``app_defaults`` static method then this
simply calls that method. This will set sensible application
configuration options for the provider.
Arguments
---------
app : flask.app.Flask
Flask application instance
"""
if hasattr(self.Provider, 'app_defaults'):
self.Provider.app_defaults(app)
def register_route(self, app):
""" Registers a default route for serving uploaded assets via
Flask-Store, this is based on the absolute and relative paths
defined in the app configuration.
Arguments
---------
app : flask.app.Flask
Flask application instance
"""
def serve(filename):
return send_from_directory(app.config['STORE_PATH'], filename)
# Only do this if the Provider says so
if self.Provider.register_route:
url = urlparse.urljoin(
app.config['STORE_URL_PREFIX'].lstrip('/') + '/',
'<path:filename>')
app.add_url_rule('/' + url, 'flask.store.file', serve)