-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
apps.py
273 lines (238 loc) · 9.82 KB
/
apps.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
265
266
267
268
269
270
271
272
273
import inspect
import mimetypes
from django.apps import AppConfig
from django.conf import settings
from django.core.checks import Error, Warning, register
from django.middleware.gzip import GZipMiddleware
from django.urls import NoReverseMatch, reverse
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
from debug_toolbar import APP_NAME, settings as dt_settings
from debug_toolbar.settings import CONFIG_DEFAULTS
class DebugToolbarConfig(AppConfig):
name = "debug_toolbar"
verbose_name = _("Debug Toolbar")
def ready(self):
from debug_toolbar.toolbar import DebugToolbar
# Import the panels when the app is ready and call their ready() methods. This
# allows panels like CachePanel to enable their instrumentation immediately.
for cls in DebugToolbar.get_panel_classes():
cls.ready()
def check_template_config(config):
"""
Checks if a template configuration is valid.
The toolbar requires either the toolbars to be unspecified or
``django.template.loaders.app_directories.Loader`` to be
included in the loaders.
If custom loaders are specified, then APP_DIRS must be True.
"""
def flat_loaders(loaders):
"""
Recursively flatten the settings list of template loaders.
Check for (loader, [child_loaders]) tuples.
Django's default cached loader uses this pattern.
"""
for loader in loaders:
if isinstance(loader, tuple):
yield loader[0]
yield from flat_loaders(loader[1])
else:
yield loader
app_dirs = config.get("APP_DIRS", False)
loaders = config.get("OPTIONS", {}).get("loaders", None)
if loaders:
loaders = list(flat_loaders(loaders))
# By default the app loader is included.
has_app_loaders = (
loaders is None or "django.template.loaders.app_directories.Loader" in loaders
)
return has_app_loaders or app_dirs
@register
def check_middleware(app_configs, **kwargs):
from debug_toolbar.middleware import DebugToolbarMiddleware
errors = []
gzip_index = None
debug_toolbar_indexes = []
if all(not check_template_config(config) for config in settings.TEMPLATES):
errors.append(
Warning(
"At least one DjangoTemplates TEMPLATES configuration needs "
"to use django.template.loaders.app_directories.Loader or "
"have APP_DIRS set to True.",
hint=(
"Include django.template.loaders.app_directories.Loader "
'in ["OPTIONS"]["loaders"]. Alternatively use '
"APP_DIRS=True for at least one "
"django.template.backends.django.DjangoTemplates "
"backend configuration."
),
id="debug_toolbar.W006",
)
)
# If old style MIDDLEWARE_CLASSES is being used, report an error.
if settings.is_overridden("MIDDLEWARE_CLASSES"):
errors.append(
Warning(
"debug_toolbar is incompatible with MIDDLEWARE_CLASSES setting.",
hint="Use MIDDLEWARE instead of MIDDLEWARE_CLASSES",
id="debug_toolbar.W004",
)
)
return errors
# Determine the indexes which gzip and/or the toolbar are installed at
for i, middleware in enumerate(settings.MIDDLEWARE):
if is_middleware_class(GZipMiddleware, middleware):
gzip_index = i
elif is_middleware_class(DebugToolbarMiddleware, middleware):
debug_toolbar_indexes.append(i)
if not debug_toolbar_indexes:
# If the toolbar does not appear, report an error.
errors.append(
Warning(
"debug_toolbar.middleware.DebugToolbarMiddleware is missing "
"from MIDDLEWARE.",
hint="Add debug_toolbar.middleware.DebugToolbarMiddleware to "
"MIDDLEWARE.",
id="debug_toolbar.W001",
)
)
elif len(debug_toolbar_indexes) != 1:
# If the toolbar appears multiple times, report an error.
errors.append(
Warning(
"debug_toolbar.middleware.DebugToolbarMiddleware occurs "
"multiple times in MIDDLEWARE.",
hint="Load debug_toolbar.middleware.DebugToolbarMiddleware only "
"once in MIDDLEWARE.",
id="debug_toolbar.W002",
)
)
elif gzip_index is not None and debug_toolbar_indexes[0] < gzip_index:
# If the toolbar appears before the gzip index, report an error.
errors.append(
Warning(
"debug_toolbar.middleware.DebugToolbarMiddleware occurs before "
"django.middleware.gzip.GZipMiddleware in MIDDLEWARE.",
hint="Move debug_toolbar.middleware.DebugToolbarMiddleware to "
"after django.middleware.gzip.GZipMiddleware in MIDDLEWARE.",
id="debug_toolbar.W003",
)
)
return errors
@register
def check_panel_configs(app_configs, **kwargs):
"""Allow each panel to check the toolbar's integration for their its own purposes."""
from debug_toolbar.toolbar import DebugToolbar
errors = []
for panel_class in DebugToolbar.get_panel_classes():
for check_message in panel_class.run_checks():
errors.append(check_message)
return errors
def is_middleware_class(middleware_class, middleware_path):
try:
middleware_cls = import_string(middleware_path)
except ImportError:
return
return inspect.isclass(middleware_cls) and issubclass(
middleware_cls, middleware_class
)
@register
def check_panels(app_configs, **kwargs):
errors = []
panels = dt_settings.get_panels()
if not panels:
errors.append(
Warning(
"Setting DEBUG_TOOLBAR_PANELS is empty.",
hint="Set DEBUG_TOOLBAR_PANELS to a non-empty list in your "
"settings.py.",
id="debug_toolbar.W005",
)
)
return errors
@register
def js_mimetype_check(app_configs, **kwargs):
"""
Check that JavaScript files are resolving to the correct content type.
"""
# Ideally application/javascript is returned, but text/javascript is
# acceptable.
javascript_types = {"application/javascript", "text/javascript"}
check_failed = not set(mimetypes.guess_type("toolbar.js")).intersection(
javascript_types
)
if check_failed:
return [
Warning(
"JavaScript files are resolving to the wrong content type.",
hint="The Django Debug Toolbar may not load properly while mimetypes are misconfigured. "
"See the Django documentation for an explanation of why this occurs.\n"
"https://docs.djangoproject.com/en/stable/ref/contrib/staticfiles/#static-file-development-view\n"
"\n"
"This typically occurs on Windows machines. The suggested solution is to modify "
"HKEY_CLASSES_ROOT in the registry to specify the content type for JavaScript "
"files.\n"
"\n"
"[HKEY_CLASSES_ROOT\\.js]\n"
'"Content Type"="application/javascript"',
id="debug_toolbar.W007",
)
]
return []
@register
def debug_toolbar_installed_when_running_tests_check(app_configs, **kwargs):
"""
Check that the toolbar is not being used when tests are running
"""
# Check if show toolbar callback has changed
show_toolbar_changed = (
dt_settings.get_config()["SHOW_TOOLBAR_CALLBACK"]
!= CONFIG_DEFAULTS["SHOW_TOOLBAR_CALLBACK"]
)
try:
# Check if the toolbar's urls are installed
reverse(f"{APP_NAME}:render_panel")
toolbar_urls_installed = True
except NoReverseMatch:
toolbar_urls_installed = False
# If the user is using the default SHOW_TOOLBAR_CALLBACK,
# then the middleware will respect the change to settings.DEBUG.
# However, if the user has changed the callback to:
# DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": lambda request: DEBUG}
# where DEBUG is not settings.DEBUG, then it won't pick up that Django'
# test runner has changed the value for settings.DEBUG, and the middleware
# will inject the toolbar, while the URLs aren't configured leading to a
# NoReverseMatch error.
likely_error_setup = show_toolbar_changed and not toolbar_urls_installed
if (
not settings.DEBUG
and dt_settings.get_config()["IS_RUNNING_TESTS"]
and likely_error_setup
):
return [
Error(
"The Django Debug Toolbar can't be used with tests",
hint="Django changes the DEBUG setting to False when running "
"tests. By default the Django Debug Toolbar is installed because "
"DEBUG is set to True. For most cases, you need to avoid installing "
"the toolbar when running tests. If you feel this check is in error, "
"you can set `DEBUG_TOOLBAR_CONFIG['IS_RUNNING_TESTS'] = False` to "
"bypass this check.",
id="debug_toolbar.E001",
)
]
else:
return []
@register
def check_settings(app_configs, **kwargs):
errors = []
USER_CONFIG = getattr(settings, "DEBUG_TOOLBAR_CONFIG", {})
if "OBSERVE_REQUEST_CALLBACK" in USER_CONFIG:
errors.append(
Warning(
"The deprecated OBSERVE_REQUEST_CALLBACK setting is present in DEBUG_TOOLBAR_CONFIG.",
hint="Use the UPDATE_ON_FETCH and/or SHOW_TOOLBAR_CALLBACK settings instead.",
id="debug_toolbar.W008",
)
)
return errors