This repository has been archived by the owner on Sep 15, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
flask_fanstatic.py
160 lines (120 loc) · 4.24 KB
/
flask_fanstatic.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
from fanstatic import (init_needed, del_needed, Publisher, get_library_registry,
DEFAULT_SIGNATURE, Resource, Library, Group)
from flask import Blueprint, g, Markup, current_app, request
from werkzeug import cached_property
from werkzeug.utils import import_string
from werkzeug.wsgi import DispatcherMiddleware
class Fanstatic(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
if app is not None and app.has_static_folder:
self.library = Library(app.name, app.static_folder)
get_library_registry().add(self.library)
else:
self.library = None
self.resources = {}
def init_app(self, app):
if isinstance(app, Blueprint):
app.record_once(lambda s: self._configure_app(s.app, blueprint=app))
else:
self._configure_app(app)
def _configure_app(self, app, blueprint=None):
if not hasattr(app, 'extensions'):
app.extensions = {}
if 'fanstatic' not in app.extensions:
app.extensions['fanstatic'] = _FanstaticManager(app)
app.extensions['fanstatic'].register(self, blueprint=blueprint)
def resource(self, *args, **kwargs):
if self.app is None:
raise AssertionError('Cannot provide resources: '
'not initialized with an app')
if self.library is None:
raise AssertionError('Cannot provide resources: '
'app does not have a static folder')
name = kwargs.pop('name', None)
resource = Resource(self.library, *args, **kwargs)
if name:
self.resources[name] = resource
return resource
def group(self, name, items):
items = [
self.resources[i] if isinstance(i, basestring) else i
for i in items
]
group = self.resources[name] = Group(items)
return group
class _FanstaticManager(object):
def __init__(self, app):
self.publisher = Publisher(get_library_registry())
app.before_request(self.before_request)
teardown_request = getattr(app, 'teardown_request', app.after_request)
teardown_request(self.teardown_request)
options = app.config.get('FANSTATIC_OPTIONS', {})
publisher_signature = options.get('publisher_signature', DEFAULT_SIGNATURE)
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {
'/%s' % publisher_signature: self.publisher,
})
self.resource_sets = {}
def register(self, fanstatic, blueprint=None):
if blueprint:
prefix = blueprint.name
else:
prefix = None
self.resource_sets[prefix] = fanstatic
def find_resource(self, name):
if ':' in name:
return import_string(name)
blueprint, sep, resource = name.rpartition('.')
if not sep:
blueprint = None
elif not blueprint:
blueprint = request.blueprint
return self.resource_sets[blueprint].resources[resource]
def before_request(self):
g.fanstatic = _FanstaticContext(
self, script_name=request.script_root,
**current_app.config.get('FANSTATIC_OPTIONS', {})
)
def teardown_request(self, *args):
del_needed()
class _FanstaticContext(object):
def __init__(self, manager, **config):
# call this first, to pop some items from the config
self._injector = _make_injector(config)
self._needed = init_needed(**config)
self._manager = manager
self._rendered = False
def needs(self, *resources):
if self._rendered:
raise AssertionError('Invalid state: already rendered Fanstatic resources')
for name in resources:
self._manager.find_resource(name).need()
return ''
@property
def top(self):
return Markup(self._topbottom[0])
@property
def bottom(self):
return Markup(self._topbottom[1])
@cached_property
def _topbottom(self):
self._rendered = True
if not self._needed.has_resources():
return '', ''
elif self._injector:
top, bottom = self._injector.group(self._needed)
return top.render(), bottom.render()
else:
return self._needed.render_topbottom()
# Fanstatic 1.0 changes the API a bit, so provide compatible implementations:
try:
# for 1.0
from fanstatic.injector import TopBottomInjector
def _make_injector(config):
return TopBottomInjector(config)
except ImportError:
# for Pre-1.0
def _make_injector(config):
return None