Skip to content

Commit 7305b04

Browse files
authored
Monkey patch templates only when the panel is used (#12)
Fixes #6. This moves the monkey patching code from the AppConfig to the panel class, so that when the panel is first instantiated, it then does the monkey patching. This allows the app to be installed with the panel disabled without any overhead.
1 parent d07d9f7 commit 7305b04

File tree

4 files changed

+62
-55
lines changed

4 files changed

+62
-55
lines changed

HISTORY.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Pending Release
1717
* Skip templates as configured in django-debug-toolbar's
1818
``SKIP_TEMPLATE_PREFIXES`` setting
1919
(`PR #11 <https://github.com/node13h/django-debug-toolbar-template-profiler/pull/11>`__).
20+
* Only monkey patch ``Template.render()`` methods if the panel is in use
21+
(`PR #12 <https://github.com/node13h/django-debug-toolbar-template-profiler/pull/12>`__).
2022

2123
1.0.2 (2017-05-03)
2224
------------------

template_profiler_panel/apps.py

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,6 @@
1-
import inspect
2-
from time import time
3-
4-
import wrapt
51
from django.apps import AppConfig
62

7-
from template_profiler_panel.signals import template_rendered
8-
93

104
class TemplateProfilerPanelAppConfig(AppConfig):
115
name = "template_profiler_panel"
126
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: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
import inspect
12
from collections import defaultdict
3+
from time import time
24

3-
from django.conf import settings
5+
import wrapt
6+
from django.dispatch import Signal
47
from django.utils.translation import ugettext_lazy as _
58

69
from debug_toolbar.panels import Panel
710
from debug_toolbar.panels.sql.utils import contrasting_color_generator
811

9-
from template_profiler_panel.signals import template_rendered
12+
13+
template_rendered = Signal(providing_args=['instance', 'start', 'end', 'level'])
1014

1115

1216
class TemplateProfilerPanel(Panel):
@@ -23,8 +27,56 @@ def __init__(self, *args, **kwargs):
2327
self.t_min = 0
2428
self.t_max = 0
2529
self.total = 0
30+
self.monkey_patch_template_classes()
31+
self.is_enabled = False
32+
template_rendered.connect(self.record)
2633
super(TemplateProfilerPanel, self).__init__(*args, **kwargs)
2734

35+
have_monkey_patched_template_classes = False
36+
37+
@classmethod
38+
def monkey_patch_template_classes(cls):
39+
if cls.have_monkey_patched_template_classes:
40+
return
41+
42+
from django.template import Template as DjangoTemplate
43+
template_classes = [DjangoTemplate]
44+
45+
try:
46+
from jinja2 import Template as Jinja2Template
47+
except ImportError:
48+
pass
49+
else:
50+
template_classes.append(Jinja2Template)
51+
52+
@wrapt.decorator
53+
def render_wrapper(wrapped, instance, args, kwargs):
54+
start = time()
55+
result = wrapped(*args, **kwargs)
56+
end = time()
57+
58+
stack_depth = 1
59+
current_frame = inspect.currentframe()
60+
while True:
61+
current_frame = current_frame.f_back
62+
if current_frame is None:
63+
break
64+
stack_depth += 1
65+
66+
template_rendered.send(
67+
sender=instance.__class__,
68+
instance=instance,
69+
start=start,
70+
end=end,
71+
level=stack_depth,
72+
)
73+
return result
74+
75+
for template_class in template_classes:
76+
template_class.render = render_wrapper(template_class.render)
77+
78+
cls.have_monkey_patched_template_classes = True
79+
2880
@property
2981
def nav_title(self):
3082
return _('Template Profiler')
@@ -41,9 +93,11 @@ def title(self):
4193
def _get_color(self, level):
4294
return self.colors.setdefault(level, next(self.color_generator))
4395

44-
def record(self, sender, instance, start, end, level, **kwargs):
45-
template_name = instance.name
96+
def record(self, instance, start, end, level, **kwargs):
97+
if not self.enabled:
98+
return
4699

100+
template_name = instance.name
47101
# Logic copied from django-debug-toolbar:
48102
# https://github.com/jazzband/django-debug-toolbar/blob/5d095f66fde8f10b45a93c0b35be0a85762b0458/debug_toolbar/panels/templates/panel.py#L77
49103
is_skipped_template = isinstance(template_name, str) and (
@@ -69,10 +123,10 @@ def record(self, sender, instance, start, end, level, **kwargs):
69123
})
70124

71125
def enable_instrumentation(self):
72-
template_rendered.connect(self.record)
126+
self.is_enabled = True
73127

74128
def disable_instrumentation(self):
75-
template_rendered.disconnect(self.record)
129+
self.is_enabled = False
76130

77131
def _calc_p(self, part, whole):
78132
return (part / whole) * 100.0

template_profiler_panel/signals.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)