-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy path__init__.py
264 lines (204 loc) · 7.92 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
from django.template.loader import render_to_string
from debug_toolbar import settings as dt_settings
from debug_toolbar.utils import get_name_from_obj
class Panel:
"""
Base class for panels.
"""
def __init__(self, toolbar, get_response):
self.toolbar = toolbar
self.get_response = get_response
# Private panel properties
@property
def panel_id(self):
return self.__class__.__name__
@property
def enabled(self) -> bool:
# The user's cookies should override the default value
cookie_value = self.toolbar.request.COOKIES.get("djdt" + self.panel_id)
if cookie_value is not None:
return cookie_value == "on"
# Check to see if settings has a default value for it
disabled_panels = dt_settings.get_config()["DISABLE_PANELS"]
panel_path = get_name_from_obj(self)
# Some panels such as the SQLPanel and TemplatesPanel exist in a
# panel module, but can be disabled without panel in the path.
# For that reason, replace .panel. in the path and check for that
# value in the disabled panels as well.
return (
panel_path not in disabled_panels
and panel_path.replace(".panel.", ".") not in disabled_panels
)
# Titles and content
@property
def nav_title(self):
"""
Title shown in the side bar. Defaults to :attr:`title`.
"""
return self.title
@property
def nav_subtitle(self):
"""
Subtitle shown in the side bar. Defaults to the empty string.
"""
return ""
@property
def has_content(self):
"""
``True`` if the panel can be displayed in full screen, ``False`` if
it's only shown in the side bar. Defaults to ``True``.
"""
return True
@property
def is_historical(self):
"""
Panel supports rendering historical values.
Defaults to :attr:`has_content`.
"""
return self.has_content
@property
def title(self):
"""
Title shown in the panel when it's displayed in full screen.
Mandatory, unless the panel sets :attr:`has_content` to ``False``.
"""
raise NotImplementedError
@property
def template(self):
"""
Template used to render :attr:`content`.
Mandatory, unless the panel sets :attr:`has_content` to ``False`` or
overrides :attr:`content`.
"""
raise NotImplementedError
@property
def content(self):
"""
Content of the panel when it's displayed in full screen.
By default this renders the template defined by :attr:`template`.
Statistics stored with :meth:`record_stats` are available in the
template's context.
"""
if self.has_content:
return render_to_string(self.template, self.get_stats())
@property
def scripts(self):
"""
Scripts used by the HTML content of the panel when it's displayed.
When a panel is rendered on the frontend, the ``djdt.panel.render``
JavaScript event will be dispatched. The scripts can listen for
this event to support dynamic functionality.
"""
return []
# Panel early initialization
@classmethod
def ready(cls):
"""
Perform early initialization for the panel.
This should only include initialization or instrumentation that needs to
be done unconditionally for the panel regardless of whether it is
enabled for a particular request. It should be idempotent.
"""
# URLs for panel-specific views
@classmethod
def get_urls(cls):
"""
Return URLpatterns, if the panel has its own views.
"""
return []
# Enable and disable (expensive) instrumentation, must be idempotent
def enable_instrumentation(self):
"""
Enable instrumentation to gather data for this panel.
This usually means monkey-patching (!) or registering signal
receivers. Any instrumentation with a non-negligible effect on
performance should be installed by this method rather than at import
time.
Unless the toolbar or this panel is disabled, this method will be
called early in ``DebugToolbarMiddleware``. It should be idempotent.
"""
def disable_instrumentation(self):
"""
Disable instrumentation to gather data for this panel.
This is the opposite of :meth:`enable_instrumentation`.
Unless the toolbar or this panel is disabled, this method will be
called late in the middleware. It should be idempotent.
"""
# Store and retrieve stats (shared between panels for no good reason)
def record_stats(self, stats):
"""
Store data gathered by the panel. ``stats`` is a :class:`dict`.
Each call to ``record_stats`` updates the statistics dictionary.
"""
self.toolbar.stats.setdefault(self.panel_id, {}).update(stats)
def get_stats(self):
"""
Access data stored by the panel. Returns a :class:`dict`.
"""
return self.toolbar.stats.get(self.panel_id, {})
def record_server_timing(self, key, title, value):
"""
Store data gathered by the panel. ``stats`` is a :class:`dict`.
Each call to ``record_stats`` updates the statistics dictionary.
"""
data = {key: {"title": title, "value": value}}
self.toolbar.server_timing_stats.setdefault(self.panel_id, {}).update(data)
def get_server_timing_stats(self):
"""
Access data stored by the panel. Returns a :class:`dict`.
"""
return self.toolbar.server_timing_stats.get(self.panel_id, {})
# Standard middleware methods
def process_request(self, request):
"""
Like __call__ in Django's middleware.
Write panel logic related to the request there. Save data with
:meth:`record_stats`.
Return the existing response or overwrite it.
"""
return self.get_response(request)
def get_headers(self, request):
"""
Get headers the panel needs to set.
Called after :meth:`process_request
<debug_toolbar.panels.Panel.generate_stats>` and
:meth:`process_request<debug_toolbar.panels.Panel.generate_stats>`
Header values will be appended if multiple panels need to set it.
By default it sets the Server-Timing header.
Return dict of headers to be appended.
"""
headers = {}
stats = self.get_server_timing_stats()
if stats:
headers["Server-Timing"] = ", ".join(
# example: `SQLPanel_sql_time;dur=0;desc="SQL 0 queries"`
'{}_{};dur={};desc="{}"'.format(
self.panel_id, key, record.get("value"), record.get("title")
)
for key, record in stats.items()
)
return headers
def generate_stats(self, request, response):
"""
Write panel logic related to the response there. Post-process data
gathered while the view executed. Save data with :meth:`record_stats`.
Called after :meth:`process_request
<debug_toolbar.panels.Panel.process_request>`.
Does not return a value.
"""
def generate_server_timing(self, request, response):
"""
Similar to :meth:`generate_stats
<debug_toolbar.panels.Panel.generate_stats>`,
Generate stats for Server Timing https://w3c.github.io/server-timing/
Does not return a value.
"""
@classmethod
def run_checks(cls):
"""
Check that the integration is configured correctly for the panel.
This will be called as a part of the Django checks system when the
application is being setup.
Return a list of :class:`django.core.checks.CheckMessage` instances.
"""
return []