Skip to content

Commit a961216

Browse files
committed
Improve template wrapping
Fix the issue seen in scoutapp/scout_apm_python#231 by using improved decoration of the `render()` methods on the template classes using the famous `wrapt` module. Also improve the speed of getting the stack depth by avoiding pulling the whole of `inspect.stack()`, which does some reformatting work, and instead working with raw frames instaed.
1 parent 44d4150 commit a961216

File tree

5 files changed

+58
-47
lines changed

5 files changed

+58
-47
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
packages=find_packages(),
1515
install_requires=[
1616
'django-debug-toolbar>=1.1,<2.0',
17+
'wrapt',
1718
],
1819
include_package_data=True,
1920
zip_safe=False, # because we're including static files

template_profiler_panel/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
default_app_config = "template_profiler_panel.apps.TemplateProfilerPanelAppConfig"

template_profiler_panel/apps.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import inspect
2+
from time import time
3+
4+
import wrapt
5+
from django.apps import AppConfig
6+
7+
from template_profiler_panel.signals import template_rendered
8+
9+
10+
class TemplateProfilerPanelAppConfig(AppConfig):
11+
name = "template_profiler_panel"
12+
verbose_name = "Debug Toolbar Template Profiler Panel"
13+
14+
def ready(self):
15+
self.monkey_patch_template_classes()
16+
17+
def monkey_patch_template_classes(self):
18+
from django.template import Template as DjangoTemplate
19+
template_classes = [DjangoTemplate]
20+
21+
try:
22+
from jinja2 import Template as Jinja2Template
23+
except ImportError:
24+
pass
25+
else:
26+
template_classes.append(Jinja2Template)
27+
28+
@wrapt.decorator
29+
def render_wrapper(wrapped, instance, args, kwargs):
30+
start = time()
31+
result = wrapped(*args, **kwargs)
32+
end = time()
33+
34+
stack_depth = 1
35+
current_frame = inspect.currentframe()
36+
while True:
37+
current_frame = current_frame.f_back
38+
if current_frame is None:
39+
break
40+
stack_depth += 1
41+
42+
template_rendered.send(
43+
sender=instance.__class__,
44+
instance=instance,
45+
start=start,
46+
end=end,
47+
level=stack_depth,
48+
)
49+
return result
50+
51+
for template_class in template_classes:
52+
template_class.render = render_wrapper(template_class.render)

template_profiler_panel/panels/template.py

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,11 @@
1-
from time import time
21
from collections import defaultdict
3-
from inspect import stack
42

5-
from django.dispatch import Signal
63
from django.utils.translation import ugettext_lazy as _
74

85
from debug_toolbar.panels import Panel
96
from debug_toolbar.panels.sql.utils import contrasting_color_generator
107

11-
from django.template import Template as DjangoTemplate
12-
13-
try:
14-
jinja_import = True
15-
from jinja2 import Template as JinjaTemplate
16-
except ImportError:
17-
jinja_import = False
18-
19-
template_rendered = Signal(
20-
providing_args=['instance', 'start', 'end', 'level'])
21-
22-
23-
def template_render_wrapper_django(self, context):
24-
t_start = time()
25-
result = DjangoTemplate.tp_saved_render(self, context)
26-
t_end = time()
27-
28-
template_rendered.send(
29-
sender=DjangoTemplate, instance=self, start=t_start, end=t_end,
30-
level=len(stack()))
31-
32-
return result
33-
34-
35-
def template_render_wrapper_jinja(self, context):
36-
t_start = time()
37-
result = JinjaTemplate.tp_saved_render(self, context)
38-
t_end = time()
39-
40-
template_rendered.send(
41-
sender=JinjaTemplate, instance=self, start=t_start, end=t_end,
42-
level=len(stack()))
43-
44-
return result
45-
46-
47-
DjangoTemplate.engine = DjangoTemplate
48-
DjangoTemplate.tp_saved_render = DjangoTemplate.render
49-
DjangoTemplate.render = template_render_wrapper_django
50-
51-
if jinja_import:
52-
JinjaTemplate.engine = JinjaTemplate
53-
JinjaTemplate.tp_saved_render = JinjaTemplate.render
54-
JinjaTemplate.render = template_render_wrapper_jinja
8+
from template_profiler_panel.signals import template_rendered
559

5610

5711
class TemplateProfilerPanel(Panel):

template_profiler_panel/signals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.dispatch import Signal
2+
3+
template_rendered = Signal(providing_args=['instance', 'start', 'end', 'level'])

0 commit comments

Comments
 (0)