Skip to content

Commit 6154046

Browse files
committed
Refs django-commons#910 - Rather than stringify the sorted version of a given layer, and store that, it looks like we can instead take a reference to the whole Context, which is repeatedly passed around, and only run the necessary pformat calls once per Context instance.
1 parent 3a18bff commit 6154046

File tree

1 file changed

+59
-56
lines changed

1 file changed

+59
-56
lines changed

debug_toolbar/panels/templates/panel.py

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ def __init__(self, *args, **kwargs):
7070
super(TemplatesPanel, self).__init__(*args, **kwargs)
7171
self.templates = []
7272
# Refs GitHub issue #910
73-
# Holds a collection of unique context layers, keyed by a string
74-
# version of them, with the value holding the `pformat` output
75-
# of the original dictionary. See _store_template_info.
76-
self.pformats = {}
73+
# Holds a collection of unique contexts, keyed by the id()
74+
# of them, with the value holding a list of `pformat` output
75+
# of the original layers. See _store_template_info.
76+
self.seen_contexts = {}
77+
7778

7879
def _store_template_info(self, sender, **kwargs):
7980
template, context = kwargs['template'], kwargs['context']
@@ -83,63 +84,65 @@ def _store_template_info(self, sender, **kwargs):
8384
template.name.startswith('debug_toolbar/')):
8485
return
8586

86-
context_list = []
87-
for context_layer in context.dicts:
88-
temp_layer = {}
89-
if hasattr(context_layer, 'items'):
90-
for key, value in context_layer.items():
91-
# Replace any request elements - they have a large
92-
# unicode representation and the request data is
93-
# already made available from the Request panel.
94-
if isinstance(value, http.HttpRequest):
95-
temp_layer[key] = '<<request>>'
96-
# Replace the debugging sql_queries element. The SQL
97-
# data is already made available from the SQL panel.
98-
elif key == 'sql_queries' and isinstance(value, list):
99-
temp_layer[key] = '<<sql_queries>>'
100-
# Replace LANGUAGES, which is available in i18n context processor
101-
elif key == 'LANGUAGES' and isinstance(value, tuple):
102-
temp_layer[key] = '<<languages>>'
103-
# QuerySet would trigger the database: user can run the query from SQL Panel
104-
elif isinstance(value, (QuerySet, RawQuerySet)):
105-
model_name = "%s.%s" % (
106-
value.model._meta.app_label, value.model.__name__)
107-
temp_layer[key] = '<<%s of %s>>' % (
108-
value.__class__.__name__.lower(), model_name)
109-
else:
110-
try:
111-
recording(False)
112-
force_text(value) # this MAY trigger a db query
113-
except SQLQueryTriggered:
114-
temp_layer[key] = '<<triggers database query>>'
115-
except UnicodeEncodeError:
116-
temp_layer[key] = '<<unicode encode error>>'
117-
except Exception:
118-
temp_layer[key] = '<<unhandled exception>>'
87+
# Refs #910
88+
# The same Context instance may be passed around a lot, so if we've
89+
# seen it before, just re-use the previous output.
90+
context_id = id(context)
91+
if context_id in self.seen_contexts:
92+
context_list = self.seen_contexts[context_id]
93+
else:
94+
context_list = []
95+
for context_layer in context.dicts:
96+
temp_layer = {}
97+
if hasattr(context_layer, 'items'):
98+
for key, value in context_layer.items():
99+
# Replace any request elements - they have a large
100+
# unicode representation and the request data is
101+
# already made available from the Request panel.
102+
if isinstance(value, http.HttpRequest):
103+
temp_layer[key] = '<<request>>'
104+
# Replace the debugging sql_queries element. The SQL
105+
# data is already made available from the SQL panel.
106+
elif key == 'sql_queries' and isinstance(value, list):
107+
temp_layer[key] = '<<sql_queries>>'
108+
# Replace LANGUAGES, which is available in i18n context processor
109+
elif key == 'LANGUAGES' and isinstance(value, tuple):
110+
temp_layer[key] = '<<languages>>'
111+
# QuerySet would trigger the database: user can run the query from SQL Panel
112+
elif isinstance(value, (QuerySet, RawQuerySet)):
113+
model_name = "%s.%s" % (
114+
value.model._meta.app_label, value.model.__name__)
115+
temp_layer[key] = '<<%s of %s>>' % (
116+
value.__class__.__name__.lower(), model_name)
119117
else:
120-
temp_layer[key] = value
121-
finally:
122-
recording(True)
118+
try:
119+
recording(False)
120+
force_text(value) # this MAY trigger a db query
121+
except SQLQueryTriggered:
122+
temp_layer[key] = '<<triggers database query>>'
123+
except UnicodeEncodeError:
124+
temp_layer[key] = '<<unicode encode error>>'
125+
except Exception:
126+
temp_layer[key] = '<<unhandled exception>>'
127+
else:
128+
temp_layer[key] = value
129+
finally:
130+
recording(True)
131+
try:
132+
prettified_layer = force_text(pformat(temp_layer))
133+
except UnicodeEncodeError:
134+
pass
135+
else:
136+
context_list.append(prettified_layer)
123137
# Refs GitHub issue #910
124-
# After Django introduced template based form widget rendering,
138+
# After Django introduced template based form widget rendering,
125139
# djdt has to collect and format far more contexts, many of which
126140
# are duplicates, and don't need formatting if we've already seen
127141
# the exact same context.
128-
# We sort the `temp_layer` dictionary as a 2-tuple to ensure that
129-
# ordering is consistent, before simply converting it to a string
130-
# representation to ensure UnicodeEncodeError can be raised as it
131-
# was previously.
132-
# If the stringification succeeded, and we've not seen the key
133-
# before, pformat it. If we've seen it before, we should be able
134-
# to just re-use it.
135-
try:
136-
forced = force_text(sorted(temp_layer.items()))
137-
except UnicodeEncodeError:
138-
continue
139-
else:
140-
if forced not in self.pformats:
141-
self.pformats[forced] = force_text(pformat(temp_layer))
142-
context_list.append(self.pformats[forced])
142+
# At this point, we know this is the first time we've seen and
143+
# collected the layers of this context, so we store the value into
144+
# a dictionary whose key is the id() of the Context instance.
145+
self.seen_contexts[context_id] = context_list
143146

144147
kwargs['context'] = context_list
145148
kwargs['context_processors'] = getattr(context, 'context_processors', None)

0 commit comments

Comments
 (0)