From d53d5c732bf794b965d15458f8e578fe51864a26 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Mon, 23 Mar 2015 08:09:21 +0000 Subject: [PATCH 1/8] before_render_template signal --- flask/__init__.py | 2 +- flask/signals.py | 3 ++- flask/templating.py | 3 ++- tests/test_signals.py | 24 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index a6ef98ca..7fd7a253 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -34,7 +34,7 @@ from .templating import render_template, render_template_string from .signals import signals_available, template_rendered, request_started, \ request_finished, got_request_exception, request_tearing_down, \ appcontext_tearing_down, appcontext_pushed, \ - appcontext_popped, message_flashed + appcontext_popped, message_flashed, before_render_template # We're not exposing the actual json module but a convenient wrapper around # it. diff --git a/flask/signals.py b/flask/signals.py index ca1c1d90..4a2cfe07 100644 --- a/flask/signals.py +++ b/flask/signals.py @@ -45,6 +45,7 @@ _signals = Namespace() # Core signals. For usage examples grep the source code or consult # the API documentation in docs/api.rst as well as docs/signals.rst template_rendered = _signals.signal('template-rendered') +before_render_template = _signals.signal('before-render-template') request_started = _signals.signal('request-started') request_finished = _signals.signal('request-finished') request_tearing_down = _signals.signal('request-tearing-down') @@ -52,4 +53,4 @@ got_request_exception = _signals.signal('got-request-exception') appcontext_tearing_down = _signals.signal('appcontext-tearing-down') appcontext_pushed = _signals.signal('appcontext-pushed') appcontext_popped = _signals.signal('appcontext-popped') -message_flashed = _signals.signal('message-flashed') +message_flashed = _signals.signal('message-flashed') \ No newline at end of file diff --git a/flask/templating.py b/flask/templating.py index 1e39b932..74ff104f 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -12,7 +12,7 @@ from jinja2 import BaseLoader, Environment as BaseEnvironment, \ TemplateNotFound from .globals import _request_ctx_stack, _app_ctx_stack -from .signals import template_rendered +from .signals import template_rendered, before_render_template def _default_template_ctx_processor(): @@ -102,6 +102,7 @@ class DispatchingJinjaLoader(BaseLoader): def _render(template, context, app): """Renders the template and fires the signal""" + before_render_template.send(app, template=template, context=context) rv = template.render(context) template_rendered.send(app, template=template, context=context) return rv diff --git a/tests/test_signals.py b/tests/test_signals.py index e17acfd4..b687b6e8 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -46,6 +46,30 @@ def test_template_rendered(): finally: flask.template_rendered.disconnect(record, app) +def test_before_render_template(): + app = flask.Flask(__name__) + + @app.route('/') + def index(): + return flask.render_template('simple_template.html', whiskey=42) + + recorded = [] + + def record(sender, template, context): + context['whiskey'] = 43 + recorded.append((template, context)) + + flask.before_render_template.connect(record, app) + try: + rv = app.test_client().get('/') + assert len(recorded) == 1 + template, context = recorded[0] + assert template.name == 'simple_template.html' + assert context['whiskey'] == 43 + assert rv.data == b'

43

' + finally: + flask.before_render_template.disconnect(record, app) + def test_request_signals(): app = flask.Flask(__name__) calls = [] From 1fbeb337c467a2be0f999606373fd1ba0e200908 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Mon, 23 Mar 2015 12:25:53 +0000 Subject: [PATCH 2/8] fix endline in the signal.py --- flask/signals.py | 2 +- tests/test_signals.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/flask/signals.py b/flask/signals.py index 4a2cfe07..c9b8a210 100644 --- a/flask/signals.py +++ b/flask/signals.py @@ -53,4 +53,4 @@ got_request_exception = _signals.signal('got-request-exception') appcontext_tearing_down = _signals.signal('appcontext-tearing-down') appcontext_pushed = _signals.signal('appcontext-pushed') appcontext_popped = _signals.signal('appcontext-popped') -message_flashed = _signals.signal('message-flashed') \ No newline at end of file +message_flashed = _signals.signal('message-flashed') diff --git a/tests/test_signals.py b/tests/test_signals.py index b687b6e8..bab5b155 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -59,16 +59,14 @@ def test_before_render_template(): context['whiskey'] = 43 recorded.append((template, context)) - flask.before_render_template.connect(record, app) - try: + with flask.before_render_template.connected_to(record): rv = app.test_client().get('/') assert len(recorded) == 1 template, context = recorded[0] assert template.name == 'simple_template.html' assert context['whiskey'] == 43 assert rv.data == b'

43

' - finally: - flask.before_render_template.disconnect(record, app) + def test_request_signals(): app = flask.Flask(__name__) From 967907ee81cbc671cd8982bdc02a9d3335fdde11 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 24 Mar 2015 12:20:28 +0000 Subject: [PATCH 3/8] before_render_template signal can override render template. --- flask/templating.py | 15 +++++++++++++-- tests/test_signals.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/flask/templating.py b/flask/templating.py index 74ff104f..d76d82a0 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -101,8 +101,19 @@ class DispatchingJinjaLoader(BaseLoader): def _render(template, context, app): - """Renders the template and fires the signal""" - before_render_template.send(app, template=template, context=context) + """Renders the template and fires signals. + + It is possible to override render template in the before_render_template signal. + It can be done only if exactly one receiver and it return not None result.""" + + brt_resp = before_render_template.send(app, template=template, context=context) + + if len(brt_resp) == 1: + first_resp = brt_resp[0] + + if len(first_resp) == 2 and first_resp[1] is not None: + return first_resp[1] + rv = template.render(context) template_rendered.send(app, template=template, context=context) return rv diff --git a/tests/test_signals.py b/tests/test_signals.py index bab5b155..f77e645f 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -67,6 +67,22 @@ def test_before_render_template(): assert context['whiskey'] == 43 assert rv.data == b'

43

' +def test_before_render_template_signal_not_None_result_render_skip_render_template(): + app = flask.Flask(__name__) + + @app.route('/') + def index(): + return flask.render_template('simple_template.html', whiskey=42) + + recorded = [] + + def record(sender, template, context): + recorded.append((template, context)) + return 'Not template string' + + with flask.before_render_template.connected_to(record): + rv = app.test_client().get('/') + assert rv.data == 'Not template string' def test_request_signals(): app = flask.Flask(__name__) From e57199e0c4e18da2a87b15ed6d6e35668ae1b763 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 24 Mar 2015 13:49:39 +0000 Subject: [PATCH 4/8] fix test_signals --- tests/test_signals.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_signals.py b/tests/test_signals.py index f77e645f..7192adc8 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -59,13 +59,16 @@ def test_before_render_template(): context['whiskey'] = 43 recorded.append((template, context)) - with flask.before_render_template.connected_to(record): + flask.before_render_template.connect(record, app) + try: rv = app.test_client().get('/') assert len(recorded) == 1 template, context = recorded[0] assert template.name == 'simple_template.html' assert context['whiskey'] == 43 assert rv.data == b'

43

' + finally: + flask.before_render_template.disconnect(record, app) def test_before_render_template_signal_not_None_result_render_skip_render_template(): app = flask.Flask(__name__) @@ -80,9 +83,12 @@ def test_before_render_template_signal_not_None_result_render_skip_render_templa recorded.append((template, context)) return 'Not template string' - with flask.before_render_template.connected_to(record): + flask.before_render_template.connect(record, app) + try: rv = app.test_client().get('/') assert rv.data == 'Not template string' + finally: + flask.before_render_template.disconnect(record, app) def test_request_signals(): app = flask.Flask(__name__) From eae37b575d2106ae80edea823d50c0e4ebfebec3 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 24 Mar 2015 14:28:33 +0000 Subject: [PATCH 5/8] fix test_signals --- tests/test_signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_signals.py b/tests/test_signals.py index 7192adc8..e85c66e6 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -86,7 +86,7 @@ def test_before_render_template_signal_not_None_result_render_skip_render_templa flask.before_render_template.connect(record, app) try: rv = app.test_client().get('/') - assert rv.data == 'Not template string' + assert rv.data == b'Not template string' finally: flask.before_render_template.disconnect(record, app) From 883f82f2617f893d024b3fcee8fd9bbe0b0d9341 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Mon, 30 Mar 2015 10:49:55 +0000 Subject: [PATCH 6/8] template overrides handling changed --- flask/templating.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/flask/templating.py b/flask/templating.py index d76d82a0..0c54cbd4 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -108,11 +108,12 @@ def _render(template, context, app): brt_resp = before_render_template.send(app, template=template, context=context) - if len(brt_resp) == 1: - first_resp = brt_resp[0] - - if len(first_resp) == 2 and first_resp[1] is not None: - return first_resp[1] + overrides = [rv for _, rv in brt_resp if rv is not None] + if len(overrides) == 1: + return overrides[0] + elif len(overrides) > 1: + raise RuntimeError('More than one before_render_template signal ' + 'returned data') rv = template.render(context) template_rendered.send(app, template=template, context=context) From 5e12748d0efb5fc3d1f6a68d7bf05fd8980a2fd4 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 7 Apr 2015 07:18:15 +0000 Subject: [PATCH 7/8] Ignore before_render_template return values --- flask/templating.py | 15 ++------------- tests/test_signals.py | 20 -------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/flask/templating.py b/flask/templating.py index 0c54cbd4..59fd988e 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -101,20 +101,9 @@ class DispatchingJinjaLoader(BaseLoader): def _render(template, context, app): - """Renders the template and fires signals. - - It is possible to override render template in the before_render_template signal. - It can be done only if exactly one receiver and it return not None result.""" - - brt_resp = before_render_template.send(app, template=template, context=context) - - overrides = [rv for _, rv in brt_resp if rv is not None] - if len(overrides) == 1: - return overrides[0] - elif len(overrides) > 1: - raise RuntimeError('More than one before_render_template signal ' - 'returned data') + """Renders the template and fires the signal""" + before_render_template.send(app, template=template, context=context) rv = template.render(context) template_rendered.send(app, template=template, context=context) return rv diff --git a/tests/test_signals.py b/tests/test_signals.py index e85c66e6..b687b6e8 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -70,26 +70,6 @@ def test_before_render_template(): finally: flask.before_render_template.disconnect(record, app) -def test_before_render_template_signal_not_None_result_render_skip_render_template(): - app = flask.Flask(__name__) - - @app.route('/') - def index(): - return flask.render_template('simple_template.html', whiskey=42) - - recorded = [] - - def record(sender, template, context): - recorded.append((template, context)) - return 'Not template string' - - flask.before_render_template.connect(record, app) - try: - rv = app.test_client().get('/') - assert rv.data == b'Not template string' - finally: - flask.before_render_template.disconnect(record, app) - def test_request_signals(): app = flask.Flask(__name__) calls = [] From a9066a37563f40ec6757e99c458f5883bb000bd1 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Wed, 17 Jun 2015 11:59:04 +0000 Subject: [PATCH 8/8] Changes and docs are modified. --- CHANGES | 1 + docs/api.rst | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGES b/CHANGES index 284c95d0..a8543e6c 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ Version 1.0 (release date to be announced, codename to be selected) +- Added before_render_template signal. - Added `**kwargs` to :meth:`flask.Test.test_client` to support passing additional keyword arguments to the constructor of :attr:`flask.Flask.test_client_class`. diff --git a/docs/api.rst b/docs/api.rst index 69ef38b5..70be5ca2 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -538,6 +538,23 @@ The following signals exist in Flask: from flask import template_rendered template_rendered.connect(log_template_renders, app) +.. data:: flask.before_render_template + :noindex: + + This signal is sent before template rendering process. The + signal is invoked with the instance of the template as `template` + and the context as dictionary (named `context`). + + Example subscriber:: + + def log_template_renders(sender, template, context, **extra): + sender.logger.debug('Rendering template "%s" with context %s', + template.name or 'string template', + context) + + from flask import before_render_template + before_render_template.connect(log_template_renders, app) + .. data:: request_started This signal is sent when the request context is set up, before