Skip to content

Commit c960b2f

Browse files
committed
Create generate_stats, an optional method similar to process_response.
generate_stats is not always called. It will only be called when the toolbar is going to insert itself into the response. The method process_response will always be called and should be used for panels that need to interrupt the request such as the RedirectsPanel. This update is to address issue #517.
1 parent 27d9aae commit c960b2f

23 files changed

+154
-13
lines changed

debug_toolbar/middleware.py

+4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ def process_response(self, request, response):
126126
# When the body ends with a newline, there's two trailing groups.
127127
bits.append(''.join(m[0] for m in matches if m[1] == ''))
128128
if len(bits) > 1:
129+
# When the toolbar will be inserted for sure, generate the stats.
130+
for panel in reversed(toolbar.enabled_panels):
131+
panel.generate_stats(request, response)
132+
129133
bits[-2] += toolbar.render_toolbar()
130134
response.content = insert_before.join(bits)
131135
if response.get('Content-Length', None):

debug_toolbar/panels/__init__.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,28 @@ def process_view(self, request, view_func, view_args, view_kwargs):
167167

168168
def process_response(self, request, response):
169169
"""
170-
Like process_response in Django's middleware.
170+
Like process_response in Django's middleware. This is similar to
171+
:meth:`generate_stats`, but will be executed on every request. It
172+
should be used when either the logic needs to be executed on every
173+
request or it needs to change the response entirely, such as
174+
:class:`RedirectsPanel`.
171175
172176
Write panel logic related to the response there. Post-process data
173177
gathered while the view executed. Save data with :meth:`record_stats`.
178+
179+
Return a response to overwrite the existing response.
180+
"""
181+
182+
def generate_stats(self, request, response):
183+
"""
184+
Similar to :meth:`process_response`, but may not be executed on every
185+
request. This will only be called if the toolbar will be inserted into
186+
the request.
187+
188+
Write panel logic related to the response there. Post-process data
189+
gathered while the view executed. Save data with :meth:`record_stats`.
190+
191+
Does not return a value.
174192
"""
175193

176194

debug_toolbar/panels/cache.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def disable_instrumentation(self):
244244
middleware_cache.caches = original_caches
245245
cache.get_cache = original_get_cache
246246

247-
def process_response(self, request, response):
247+
def generate_stats(self, request, response):
248248
self.record_stats({
249249
'total_calls': len(self.calls),
250250
'calls': self.calls,

debug_toolbar/panels/headers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def process_request(self, request):
4747
'environ': self.environ,
4848
})
4949

50-
def process_response(self, request, response):
50+
def generate_stats(self, request, response):
5151
self.response_headers = OrderedDict(sorted(response.items()))
5252
self.record_stats({
5353
'response_headers': self.response_headers,

debug_toolbar/panels/logging.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def nav_subtitle(self):
7474
def process_request(self, request):
7575
collector.clear_collection()
7676

77-
def process_response(self, request, response):
77+
def generate_stats(self, request, response):
7878
records = collector.get_collection()
7979
self._records[threading.currentThread()] = records
8080
collector.clear_collection()

debug_toolbar/panels/profiling.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def add_node(self, func_list, func, max_depth, cum_time=0.1):
142142
func.has_subfuncs = True
143143
self.add_node(func_list, subfunc, max_depth, cum_time=cum_time)
144144

145-
def process_response(self, request, response):
145+
def generate_stats(self, request, response):
146146
if not hasattr(self, 'profiler'):
147147
return None
148148
# Could be delayed until the panel content is requested (perf. optim.)

debug_toolbar/panels/request.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def nav_subtitle(self):
2525
view_func = self.get_stats().get('view_func', '')
2626
return view_func.rsplit('.', 1)[-1]
2727

28-
def process_response(self, request, response):
28+
def generate_stats(self, request, response):
2929
self.record_stats({
3030
'get': [(k, request.GET.getlist(k)) for k in sorted(request.GET)],
3131
'post': [(k, request.POST.getlist(k)) for k in sorted(request.POST)],

debug_toolbar/panels/settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class SettingsPanel(Panel):
1919
def title(self):
2020
return _("Settings from <code>%s</code>") % settings.SETTINGS_MODULE
2121

22-
def process_response(self, request, response):
22+
def generate_stats(self, request, response):
2323
self.record_stats({
2424
'settings': OrderedDict(sorted(get_safe_settings().items(),
2525
key=lambda s: s[0])),

debug_toolbar/panels/signals.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def signals(self):
5858
signals[signal_name] = getattr(signals_mod, signal_name)
5959
return signals
6060

61-
def process_response(self, request, response):
61+
def generate_stats(self, request, response):
6262
signals = []
6363
for name, signal in sorted(self.signals.items(), key=lambda x: x[0]):
6464
if signal is None:

debug_toolbar/panels/sql/panel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def disable_instrumentation(self):
136136
for connection in connections.all():
137137
unwrap_cursor(connection)
138138

139-
def process_response(self, request, response):
139+
def generate_stats(self, request, response):
140140
colors = contrasting_color_generator()
141141
trace_colors = defaultdict(lambda: next(colors))
142142
query_duplicates = defaultdict(lambda: defaultdict(int))

debug_toolbar/panels/staticfiles.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def nav_subtitle(self):
113113
def process_request(self, request):
114114
collector.clear_collection()
115115

116-
def process_response(self, request, response):
116+
def generate_stats(self, request, response):
117117
used_paths = collector.get_collection()
118118
self._paths[threading.currentThread()] = used_paths
119119

debug_toolbar/panels/templates/panel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def enable_instrumentation(self):
195195
def disable_instrumentation(self):
196196
template_rendered.disconnect(self._store_template_info)
197197

198-
def process_response(self, request, response):
198+
def generate_stats(self, request, response):
199199
template_context = []
200200
for template_data in self.templates:
201201
info = {}

debug_toolbar/panels/timer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def process_request(self, request):
5252
if self.has_content:
5353
self._start_rusage = resource.getrusage(resource.RUSAGE_SELF)
5454

55-
def process_response(self, request, response):
55+
def generate_stats(self, request, response):
5656
stats = {}
5757
if hasattr(self, '_start_time'):
5858
stats['total_time'] = (time.time() - self._start_time) * 1000

debug_toolbar/panels/versions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def nav_subtitle(self):
2222

2323
template = 'debug_toolbar/panels/versions.html'
2424

25-
def process_response(self, request, response):
25+
def generate_stats(self, request, response):
2626
versions = [
2727
('Python', '%d.%d.%d' % sys.version_info[:3]),
2828
('Django', self.get_app_version(django)),

tests/panels/test_cache.py

+13
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,16 @@ def test_recording_get_cache(self):
4646
default_cache.set('foo', 'bar')
4747
second_cache.get('foo')
4848
self.assertEqual(len(self.panel.calls), 2)
49+
50+
def test_insert_content(self):
51+
"""
52+
Test that the panel only inserts content after generate_stats and
53+
not the process_response.
54+
"""
55+
cache.cache.get('café')
56+
self.panel.process_response(self.request, self.response)
57+
# ensure the panel does not have content yet.
58+
self.assertNotIn('café', self.panel.content)
59+
self.panel.generate_stats(self.request, self.response)
60+
# ensure the panel renders correctly.
61+
self.assertIn('café', self.panel.content)

tests/panels/test_logging.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# coding: utf-8
2+
13
from __future__ import absolute_import, unicode_literals
24

35
import logging
@@ -24,6 +26,7 @@ def test_happy_case(self):
2426
self.logger.info('Nothing to see here, move along!')
2527

2628
self.panel.process_response(self.request, self.response)
29+
self.panel.generate_stats(self.request, self.response)
2730
records = self.panel.get_stats()['records']
2831

2932
self.assertEqual(1, len(records))
@@ -34,12 +37,26 @@ def test_formatting(self):
3437
self.logger.info('There are %d %s', 5, 'apples')
3538

3639
self.panel.process_response(self.request, self.response)
40+
self.panel.generate_stats(self.request, self.response)
3741
records = self.panel.get_stats()['records']
3842

3943
self.assertEqual(1, len(records))
4044
self.assertEqual('There are 5 apples',
4145
records[0]['message'])
4246

47+
def test_insert_content(self):
48+
"""
49+
Test that the panel only inserts content after generate_stats and
50+
not the process_response.
51+
"""
52+
self.logger.info('café')
53+
self.panel.process_response(self.request, self.response)
54+
# ensure the panel does not have content yet.
55+
self.assertNotIn('café', self.panel.content)
56+
self.panel.generate_stats(self.request, self.response)
57+
# ensure the panel renders correctly.
58+
self.assertIn('café', self.panel.content)
59+
4360
def test_failing_formatting(self):
4461
class BadClass(object):
4562
def __str__(self):
@@ -49,6 +66,7 @@ def __str__(self):
4966
self.logger.debug('This class is misbehaving: %s', BadClass())
5067

5168
self.panel.process_response(self.request, self.response)
69+
self.panel.generate_stats(self.request, self.response)
5270
records = self.panel.get_stats()['records']
5371

5472
self.assertEqual(1, len(records))

tests/panels/test_profiling.py

+14
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,23 @@ def setUp(self):
2323
def test_regular_view(self):
2424
self.panel.process_view(self.request, regular_view, ('profiling',), {})
2525
self.panel.process_response(self.request, self.response)
26+
self.panel.generate_stats(self.request, self.response)
2627
self.assertIn('func_list', self.panel.get_stats())
2728
self.assertIn('regular_view', self.panel.content)
2829

30+
def test_insert_content(self):
31+
"""
32+
Test that the panel only inserts content after generate_stats and
33+
not the process_response.
34+
"""
35+
self.panel.process_view(self.request, regular_view, ('profiling',), {})
36+
self.panel.process_response(self.request, self.response)
37+
# ensure the panel does not have content yet.
38+
self.assertNotIn('regular_view', self.panel.content)
39+
self.panel.generate_stats(self.request, self.response)
40+
# ensure the panel renders correctly.
41+
self.assertIn('regular_view', self.panel.content)
42+
2943

3044
@override_settings(DEBUG=True,
3145
DEBUG_TOOLBAR_PANELS=['debug_toolbar.panels.profiling.ProfilingPanel'])

tests/panels/test_redirects.py

+11
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,14 @@ def test_unknown_status_code_with_reason(self):
5858
redirect['Location'] = 'http://somewhere/else/'
5959
response = self.panel.process_response(self.request, redirect)
6060
self.assertContains(response, '369 Look Ma!')
61+
62+
def test_insert_content(self):
63+
"""
64+
Test that the panel only inserts content after generate_stats and
65+
not the process_response.
66+
"""
67+
redirect = HttpResponse(status=304)
68+
response = self.panel.process_response(self.request, redirect)
69+
self.assertIsNotNone(response)
70+
response = self.panel.generate_stats(self.request, redirect)
71+
self.assertIsNone(response)

tests/panels/test_request.py

+15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def test_non_ascii_session(self):
1919
self.request.session['là'.encode('utf-8')] = 'là'.encode('utf-8')
2020
self.panel.process_request(self.request)
2121
self.panel.process_response(self.request, self.response)
22+
self.panel.generate_stats(self.request, self.response)
2223
content = self.panel.content
2324
if six.PY3:
2425
self.assertIn('où', content)
@@ -30,4 +31,18 @@ def test_object_with_non_ascii_repr_in_request_params(self):
3031
self.request.path = '/non_ascii_request/'
3132
self.panel.process_request(self.request)
3233
self.panel.process_response(self.request, self.response)
34+
self.panel.generate_stats(self.request, self.response)
35+
self.assertIn('nôt åscíì', self.panel.content)
36+
37+
def test_insert_content(self):
38+
"""
39+
Test that the panel only inserts content after generate_stats and
40+
not the process_response.
41+
"""
42+
self.request.path = '/non_ascii_request/'
43+
self.panel.process_response(self.request, self.response)
44+
# ensure the panel does not have content yet.
45+
self.assertNotIn('nôt åscíì', self.panel.content)
46+
self.panel.generate_stats(self.request, self.response)
47+
# ensure the panel renders correctly.
3348
self.assertIn('nôt åscíì', self.panel.content)

tests/panels/test_sql.py

+14
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,24 @@ def test_non_ascii_query(self):
6464
self.assertEqual(len(self.panel._queries), 3)
6565

6666
self.panel.process_response(self.request, self.response)
67+
self.panel.generate_stats(self.request, self.response)
6768

6869
# ensure the panel renders correctly
6970
self.assertIn('café', self.panel.content)
7071

72+
def test_insert_content(self):
73+
"""
74+
Test that the panel only inserts content after generate_stats and
75+
not the process_response.
76+
"""
77+
list(User.objects.filter(username='café'.encode('utf-8')))
78+
self.panel.process_response(self.request, self.response)
79+
# ensure the panel does not have content yet.
80+
self.assertNotIn('café', self.panel.content)
81+
self.panel.generate_stats(self.request, self.response)
82+
# ensure the panel renders correctly.
83+
self.assertIn('café', self.panel.content)
84+
7185
@unittest.skipUnless(connection.vendor == 'postgresql',
7286
'Test valid only on PostgreSQL')
7387
def test_erroneous_query(self):

tests/panels/test_staticfiles.py

+16
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def setUp(self):
1616
def test_default_case(self):
1717
self.panel.process_request(self.request)
1818
self.panel.process_response(self.request, self.response)
19+
self.panel.generate_stats(self.request, self.response)
1920
self.assertIn('django.contrib.staticfiles.finders.'
2021
'AppDirectoriesFinder', self.panel.content)
2122
self.assertIn('django.contrib.staticfiles.finders.'
@@ -26,3 +27,18 @@ def test_default_case(self):
2627
['django.contrib.admin', 'debug_toolbar'])
2728
self.assertEqual(self.panel.get_staticfiles_dirs(),
2829
finders.FileSystemFinder().locations)
30+
31+
def test_insert_content(self):
32+
"""
33+
Test that the panel only inserts content after generate_stats and
34+
not the process_response.
35+
"""
36+
self.panel.process_request(self.request)
37+
self.panel.process_response(self.request, self.response)
38+
# ensure the panel does not have content yet.
39+
self.assertNotIn('django.contrib.staticfiles.finders.'
40+
'AppDirectoriesFinder', self.panel.content)
41+
self.panel.generate_stats(self.request, self.response)
42+
# ensure the panel renders correctly.
43+
self.assertIn('django.contrib.staticfiles.finders.'
44+
'AppDirectoriesFinder', self.panel.content)

tests/panels/test_template.py

+17
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ def test_object_with_non_ascii_repr_in_context(self):
4848
c = Context({'object': NonAsciiRepr()})
4949
t.render(c)
5050
self.panel.process_response(self.request, self.response)
51+
self.panel.generate_stats(self.request, self.response)
52+
self.assertIn('nôt åscíì', self.panel.content)
53+
54+
def test_insert_content(self):
55+
"""
56+
Test that the panel only inserts content after generate_stats and
57+
not the process_response.
58+
"""
59+
t = Template("{{ object }}")
60+
c = Context({'object': NonAsciiRepr()})
61+
t.render(c)
62+
self.panel.process_response(self.request, self.response)
63+
# ensure the panel does not have content yet.
64+
self.assertNotIn('nôt åscíì', self.panel.content)
65+
self.panel.generate_stats(self.request, self.response)
66+
# ensure the panel renders correctly.
5167
self.assertIn('nôt åscíì', self.panel.content)
5268

5369
def test_custom_context_processor(self):
@@ -56,6 +72,7 @@ def test_custom_context_processor(self):
5672
c = RequestContext(self.request, processors=[context_processor])
5773
t.render(c)
5874
self.panel.process_response(self.request, self.response)
75+
self.panel.generate_stats(self.request, self.response)
5976
self.assertIn('tests.panels.test_template.context_processor', self.panel.content)
6077

6178
def test_disabled(self):

tests/test_integration.py

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def _resolve_stats(self, path):
4545
panel = self.toolbar.get_panel_by_id('RequestPanel')
4646
panel.process_request(self.request)
4747
panel.process_response(self.request, self.response)
48+
panel.generate_stats(self.request, self.response)
4849
return panel.get_stats()
4950

5051
def test_url_resolving_positional(self):

0 commit comments

Comments
 (0)