Skip to content

Commit 3a1a1c6

Browse files
authored
Merge pull request #739 from TMiguelT/master
Add separate init_app function for providing the flask app
2 parents 49100d8 + 9b18311 commit 3a1a1c6

File tree

2 files changed

+89
-62
lines changed

2 files changed

+89
-62
lines changed

dash/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Unreleased
22
### Changed
3+
- [#739](https://github.com/plotly/dash/pull/739) Allow the Flask app to be provided to Dash after object initialization. This allows users to define Dash layouts etc when using the app factory pattern, or any other pattern that inhibits access to the app object. This broadly complies with the flask extension API, allowing Dash to be considered as a Flask extension where it needs to be.
4+
35
- [#722](https://github.com/plotly/dash/pull/722) Assets are served locally by default. Both JS scripts and CSS files are affected. This improves robustness and flexibility in numerous situations, but in certain cases initial loading could be slowed. To restore the previous CDN serving, set `app.scripts.config.serve_locally = False` (and similarly with `app.css`, but this is generally less important).
46

57
- Undo/redo toolbar is removed by default, you can enable it with `app=Dash(show_undo_redo=true)`. The CSS hack `._dash-undo-redo:{display:none;}` is no longer needed [#724](https://github.com/plotly/dash/pull/724)

dash/dash.py

+87-62
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Dash(object):
8989
def __init__(
9090
self,
9191
name='__main__',
92-
server=None,
92+
server=True,
9393
static_folder='static',
9494
assets_folder='assets',
9595
assets_url_path='/assets',
@@ -110,6 +110,10 @@ def __init__(
110110
plugins=None,
111111
**kwargs):
112112

113+
# Store some flask-related parameters for use in init_app()
114+
self.compress = compress
115+
self.name = name
116+
113117
# pylint-disable: too-many-instance-attributes
114118
if 'csrf_protect' in kwargs:
115119
warnings.warn('''
@@ -125,8 +129,17 @@ def __init__(
125129
)
126130
self._assets_url_path = assets_url_path
127131

128-
# allow users to supply their own flask server
129-
self.server = server or Flask(name, static_folder=static_folder)
132+
# We have 3 cases: server is either True (we create the server), False
133+
# (defer server creation) or a Flask app instance (we use their server)
134+
if isinstance(server, bool):
135+
if server:
136+
self.server = Flask(name, static_folder=static_folder)
137+
else:
138+
self.server = None
139+
elif isinstance(server, Flask):
140+
self.server = server
141+
else:
142+
raise ValueError('server must be a Flask app, or a boolean')
130143

131144
url_base_pathname, routes_pathname_prefix, requests_pathname_prefix = \
132145
pathname_configs(
@@ -154,22 +167,6 @@ def __init__(
154167
'show_undo_redo': show_undo_redo
155168
})
156169

157-
assets_blueprint_name = '{}{}'.format(
158-
self.config.routes_pathname_prefix.replace('/', '_'),
159-
'dash_assets'
160-
)
161-
162-
self.server.register_blueprint(
163-
flask.Blueprint(
164-
assets_blueprint_name, name,
165-
static_folder=self._assets_folder,
166-
static_url_path='{}{}'.format(
167-
self.config.routes_pathname_prefix,
168-
assets_url_path.lstrip('/')
169-
)
170-
)
171-
)
172-
173170
# list of dependencies
174171
self.callback_map = {}
175172

@@ -181,15 +178,6 @@ def __init__(
181178
# default renderer string
182179
self.renderer = 'var renderer = new DashRenderer();'
183180

184-
if compress:
185-
# gzip
186-
Compress(self.server)
187-
188-
@self.server.errorhandler(exceptions.PreventUpdate)
189-
def _handle_error(_):
190-
"""Handle a halted callback and return an empty 204 response"""
191-
return '', 204
192-
193181
# static files from the packages
194182
self.css = Css()
195183
self.scripts = Scripts()
@@ -204,8 +192,79 @@ def _handle_error(_):
204192
# urls
205193
self.routes = []
206194

195+
self._layout = None
196+
self._cached_layout = None
197+
self._dev_tools = _AttributeDict({
198+
'serve_dev_bundles': False,
199+
'hot_reload': False,
200+
'hot_reload_interval': 3000,
201+
'hot_reload_watch_interval': 0.5,
202+
'hot_reload_max_retry': 8,
203+
'ui': False,
204+
'props_check': False,
205+
})
206+
207+
self._assets_files = []
208+
209+
# hot reload
210+
self._reload_hash = None
211+
self._hard_reload = False
212+
self._lock = threading.RLock()
213+
self._watch_thread = None
214+
self._changed_assets = []
215+
216+
self.logger = logging.getLogger(name)
217+
self.logger.addHandler(logging.StreamHandler(stream=sys.stdout))
218+
219+
if isinstance(plugins, _patch_collections_abc('Iterable')):
220+
for plugin in plugins:
221+
plugin.plug(self)
222+
223+
if self.server is not None:
224+
self.init_app()
225+
226+
def init_app(self, app=None):
227+
"""
228+
Initialize the parts of Dash that require a flask app
229+
"""
230+
231+
if app is not None:
232+
self.server = app
233+
234+
assets_blueprint_name = '{}{}'.format(
235+
self.config.routes_pathname_prefix.replace('/', '_'),
236+
'dash_assets'
237+
)
238+
239+
self.server.register_blueprint(
240+
flask.Blueprint(
241+
assets_blueprint_name,
242+
self.name,
243+
static_folder=self._assets_folder,
244+
static_url_path='{}{}'.format(
245+
self.config.routes_pathname_prefix,
246+
self._assets_url_path.lstrip('/')
247+
)
248+
)
249+
)
250+
251+
if self.compress:
252+
# gzip
253+
Compress(self.server)
254+
255+
@self.server.errorhandler(exceptions.PreventUpdate)
256+
def _handle_error(_):
257+
"""Handle a halted callback and return an empty 204 response"""
258+
return '', 204
259+
207260
prefix = self.config['routes_pathname_prefix']
208261

262+
self.server.before_first_request(self._setup_server)
263+
264+
# add a handler for components suites errors to return 404
265+
self.server.errorhandler(exceptions.InvalidResourceError)(
266+
self._invalid_resources_handler)
267+
209268
self._add_url('{}_dash-layout'.format(prefix), self.serve_layout)
210269

211270
self._add_url('{}_dash-dependencies'.format(prefix), self.dependencies)
@@ -236,40 +295,6 @@ def _handle_error(_):
236295
'{}_favicon.ico'.format(prefix),
237296
self._serve_default_favicon)
238297

239-
self.server.before_first_request(self._setup_server)
240-
241-
self._layout = None
242-
self._cached_layout = None
243-
self._dev_tools = _AttributeDict({
244-
'serve_dev_bundles': False,
245-
'hot_reload': False,
246-
'hot_reload_interval': 3000,
247-
'hot_reload_watch_interval': 0.5,
248-
'hot_reload_max_retry': 8,
249-
'ui': False,
250-
'props_check': False,
251-
})
252-
253-
# add a handler for components suites errors to return 404
254-
self.server.errorhandler(exceptions.InvalidResourceError)(
255-
self._invalid_resources_handler)
256-
257-
self._assets_files = []
258-
259-
# hot reload
260-
self._reload_hash = None
261-
self._hard_reload = False
262-
self._lock = threading.RLock()
263-
self._watch_thread = None
264-
self._changed_assets = []
265-
266-
self.logger = logging.getLogger(name)
267-
self.logger.addHandler(logging.StreamHandler(stream=sys.stdout))
268-
269-
if isinstance(plugins, _patch_collections_abc('Iterable')):
270-
for plugin in plugins:
271-
plugin.plug(self)
272-
273298
def _add_url(self, name, view_func, methods=('GET',)):
274299
self.server.add_url_rule(
275300
name,

0 commit comments

Comments
 (0)