From 0514ba2de1751de1b0c9a223c96f6a7145714e6a Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Tue, 14 Jun 2016 00:59:32 +0100 Subject: [PATCH 1/5] Enable template auto-reloading in app.run() When Flask app debugging is enabled (app.debug==True), and Jinja2 template auto-reloading is not explicitly disbaled, template auto-reloading should be enabled. If the app is instantiated, the jinja_env object is accessed (thereby initialising the Jinja2 environment) and the server is then started with app.run(debug=True), template auto-reloading is *not* enabled. This is because reading the jinja_env object causes the environment initialisation function to set auto_reload to app.debug (which isn't yet True). Calling app.run(debug=True) should correct this in order to remain consistent with Flask code reloading (which is enabled within app.run() if debug == True). --- flask/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask/app.py b/flask/app.py index b1ea0464..1d32ece9 100644 --- a/flask/app.py +++ b/flask/app.py @@ -839,6 +839,8 @@ class Flask(_PackageBoundObject): options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) options.setdefault('passthrough_errors', True) + if debug: + self.jinja_env.auto_reload = True try: run_simple(host, port, self, **options) finally: From 24289e97af233f11d9c145f80604b2ee416254a1 Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Wed, 15 Jun 2016 02:15:33 +0100 Subject: [PATCH 2/5] Add test for new template auto reload debug behaviour --- tests/test_templating.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_templating.py b/tests/test_templating.py index b60a592a..e65fe834 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -14,6 +14,7 @@ import pytest import flask import logging from jinja2 import TemplateNotFound +import werkzeug.serving def test_context_processing(): @@ -346,6 +347,22 @@ def test_templates_auto_reload(): app.config['TEMPLATES_AUTO_RELOAD'] = True assert app.jinja_env.auto_reload is True +def test_templates_auto_reload_debug_run(monkeypatch): + # debug is None in config, config option is None, app.run(debug=True) + rv = {} + + # Mocks werkzeug.serving.run_simple method + def run_simple_mock(*args, **kwargs): + rv['passthrough_errors'] = kwargs.get('passthrough_errors') + + app = flask.Flask(__name__) + monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) + + assert app.config['TEMPLATES_AUTO_RELOAD'] is None + assert app.jinja_env.auto_reload is False + app.run(debug=True) + assert app.jinja_env.auto_reload is True + def test_template_loader_debugging(test_apps): from blueprintapp import app From 1a67e284d043818743ae9ef5a46b6b64a9db56f7 Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Wed, 15 Jun 2016 02:25:48 +0100 Subject: [PATCH 3/5] Remove unnecessary werkzeug mock attribs from test --- tests/test_templating.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_templating.py b/tests/test_templating.py index e65fe834..aa3d7409 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -349,11 +349,9 @@ def test_templates_auto_reload(): def test_templates_auto_reload_debug_run(monkeypatch): # debug is None in config, config option is None, app.run(debug=True) - rv = {} - # Mocks werkzeug.serving.run_simple method def run_simple_mock(*args, **kwargs): - rv['passthrough_errors'] = kwargs.get('passthrough_errors') + pass app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) From 4fc48200a58907ffdf136961b84ef7d19192e6ef Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 12:08:25 -0700 Subject: [PATCH 4/5] reconfigure the app from run command and method extract templates_auto_reload to property continues #1910 closes #1907 --- CHANGES | 3 +++ flask/app.py | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- flask/cli.py | 8 ++++++- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 59e8c4c1..7f9e968b 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,8 @@ Major release, unreleased - Removed error handler caching because it caused unexpected results for some exception inheritance hierarchies. Register handlers explicitly for each exception if you don't want to traverse the MRO. (`#2362`_) +- Template auto reloading will honor the ``run`` command's ``debug`` flag even + if ``app.jinja_env`` was already accessed. (`#2373`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -102,6 +104,7 @@ Major release, unreleased .. _#2354: https://github.com/pallets/flask/pull/2354 .. _#2358: https://github.com/pallets/flask/pull/2358 .. _#2362: https://github.com/pallets/flask/pull/2362 +.. _#2373: https://github.com/pallets/flask/pull/2373 Version 0.12.2 -------------- diff --git a/flask/app.py b/flask/app.py index 7f487fc8..04c39547 100644 --- a/flask/app.py +++ b/flask/app.py @@ -703,6 +703,28 @@ class Flask(_PackageBoundObject): """ return open(os.path.join(self.instance_path, resource), mode) + def _get_templates_auto_reload(self): + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config['TEMPLATES_AUTO_RELOAD'] + return rv if rv is not None else self.debug + + def _set_templates_auto_reload(self, value): + self.config['TEMPLATES_AUTO_RELOAD'] = value + + templates_auto_reload = property( + _get_templates_auto_reload, _set_templates_auto_reload + ) + del _get_templates_auto_reload, _set_templates_auto_reload + def create_jinja_environment(self): """Creates the Jinja2 environment based on :attr:`jinja_options` and :meth:`select_jinja_autoescape`. Since 0.7 this also adds @@ -715,13 +737,13 @@ class Flask(_PackageBoundObject): ``TEMPLATES_AUTO_RELOAD`` configuration option. """ options = dict(self.jinja_options) + if 'autoescape' not in options: options['autoescape'] = self.select_jinja_autoescape + if 'auto_reload' not in options: - if self.config['TEMPLATES_AUTO_RELOAD'] is not None: - options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD'] - else: - options['auto_reload'] = self.debug + options['auto_reload'] = self.templates_auto_reload + rv = self.jinja_environment(self, **options) rv.globals.update( url_for=url_for, @@ -806,6 +828,22 @@ class Flask(_PackageBoundObject): rv.update(processor()) return rv + def _reconfigure_for_run_debug(self, debug): + """The ``run`` commands will set the application's debug flag. Some + application configuration may already be calculated based on the + previous debug value. This method will recalculate affected values. + + Called by the :func:`flask.cli.run` command or :meth:`Flask.run` + method if the debug flag is set explicitly in the call. + + :param debug: the new value of the debug flag + + .. versionadded:: 1.0 + Reconfigures ``app.jinja_env.auto_reload``. + """ + self.debug = debug + self.jinja_env.auto_reload = self.templates_auto_reload + def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server. @@ -859,21 +897,24 @@ class Flask(_PackageBoundObject): explain_ignored_app_run() return - from werkzeug.serving import run_simple + if debug is not None: + self._reconfigure_for_run_debug(bool(debug)) + _host = '127.0.0.1' _port = 5000 server_name = self.config.get("SERVER_NAME") sn_host, sn_port = None, None + if server_name: sn_host, _, sn_port = server_name.partition(':') + host = host or sn_host or _host port = int(port or sn_port or _port) - if debug is not None: - self.debug = bool(debug) options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) - if debug: - self.jinja_env.auto_reload = True + + from werkzeug.serving import run_simple + try: run_simple(host, port, self, **options) finally: diff --git a/flask/cli.py b/flask/cli.py index 0982aa1c..4b5323c7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -319,8 +319,10 @@ class ScriptInfo(object): be returned. """ __traceback_hide__ = True + if self._loaded_app is not None: return self._loaded_app + if self.create_app is not None: rv = call_factory(self.create_app, self) else: @@ -330,10 +332,14 @@ class ScriptInfo(object): 'the FLASK_APP environment variable.\n\nFor more ' 'information see ' 'http://flask.pocoo.org/docs/latest/quickstart/') + rv = locate_app(self, self.app_import_path) + debug = get_debug_flag() + if debug is not None: - rv.debug = debug + rv._reconfigure_for_run_debug(debug) + self._loaded_app = rv return rv From 4d2a3ab2e07e36b56d09e16f0836dbd0bdd7b34c Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 12:31:56 -0700 Subject: [PATCH 5/5] test no debug flag doesn't reconfigure test templates_auto_reload property instead of config use app fixture in test --- tests/test_templating.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_templating.py b/tests/test_templating.py index 18143ba3..aaec54d7 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -386,19 +386,19 @@ def test_templates_auto_reload(app): app.config['TEMPLATES_AUTO_RELOAD'] = True assert app.jinja_env.auto_reload is True -def test_templates_auto_reload_debug_run(monkeypatch): - # debug is None in config, config option is None, app.run(debug=True) - # Mocks werkzeug.serving.run_simple method +def test_templates_auto_reload_debug_run(app, monkeypatch): def run_simple_mock(*args, **kwargs): pass - app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - assert app.config['TEMPLATES_AUTO_RELOAD'] is None - assert app.jinja_env.auto_reload is False + app.run() + assert app.templates_auto_reload == False + assert app.jinja_env.auto_reload == False + app.run(debug=True) - assert app.jinja_env.auto_reload is True + assert app.templates_auto_reload == True + assert app.jinja_env.auto_reload == True def test_template_loader_debugging(test_apps):