Imported Upstream version 1.4
Andrew Starr-Bochicchio
8 years ago
0 | 0 | language: python |
1 | sudo: false | |
1 | 2 | python: |
2 | - "2.6" | |
3 | 3 | - "2.7" |
4 | 4 | - "3.2" |
5 | 5 | - "3.3" |
6 | 6 | - "3.4" |
7 | - "3.5" | |
7 | 8 | env: |
8 | - DJANGO_VERSION=1.4.10 | |
9 | - DJANGO_VERSION=1.5.5 | |
10 | - DJANGO_VERSION=1.6.1 | |
9 | - DJANGO="Django>=1.7.0,<1.8.0" | |
10 | - DJANGO="Django>=1.8.0,<1.9.0" | |
11 | - DJANGO="https://www.djangoproject.com/download/1.9a1/tarball/" | |
11 | 12 | matrix: |
12 | 13 | exclude: |
13 | 14 | - python: "3.2" |
14 | env: DJANGO_VERSION=1.4.10 | |
15 | env: DJANGO="https://www.djangoproject.com/download/1.9a1/tarball/" | |
15 | 16 | - python: "3.3" |
16 | env: DJANGO_VERSION=1.4.10 | |
17 | - python: "3.4" | |
18 | env: DJANGO_VERSION=1.4.10 | |
17 | env: DJANGO="https://www.djangoproject.com/download/1.9a1/tarball/" | |
18 | - python: "3.5" | |
19 | env: DJANGO="Django>=1.7.0,<1.8.0" | |
20 | - python: "3.5" | |
21 | env: DJANGO="Django>=1.8.0,<1.9.0" | |
19 | 22 | install: |
20 | 23 | - pip install -e . |
21 | - pip install Django==$DJANGO_VERSION django-discover-runner sqlparse | |
24 | - travis_retry pip install $DJANGO django-discover-runner sqlparse | |
22 | 25 | script: make test |
2 | 2 | flake8: |
3 | 3 | flake8 debug_toolbar example tests |
4 | 4 | |
5 | isort: | |
6 | isort -rc debug_toolbar example tests | |
7 | ||
8 | isort_check_only: | |
9 | isort -rc -c debug_toolbar example tests | |
10 | ||
5 | 11 | example: |
6 | 12 | DJANGO_SETTINGS_MODULE=example.settings \ |
7 | django-admin.py runserver | |
13 | django-admin runserver | |
8 | 14 | |
9 | 15 | test: |
10 | 16 | DJANGO_SETTINGS_MODULE=tests.settings \ |
11 | django-admin.py test tests | |
17 | django-admin test tests | |
12 | 18 | |
13 | 19 | test_selenium: |
14 | 20 | DJANGO_SELENIUM_TESTS=true DJANGO_SETTINGS_MODULE=tests.settings \ |
15 | django-admin.py test tests | |
21 | django-admin test tests | |
16 | 22 | |
17 | 23 | coverage: |
18 | 24 | coverage erase |
19 | 25 | DJANGO_SETTINGS_MODULE=tests.settings \ |
20 | coverage run --branch --source=debug_toolbar `which django-admin.py` test tests | |
26 | coverage run --branch --source=debug_toolbar `which django-admin` test tests | |
21 | 27 | coverage html |
22 | 28 | |
23 | 29 | translatable_strings: |
24 | cd debug_toolbar && django-admin.py makemessages -l en --no-obsolete | |
30 | cd debug_toolbar && django-admin makemessages -l en --no-obsolete | |
25 | 31 | @echo "Please commit changes and run 'tx push -s' (or wait for Transifex to pick them)" |
26 | 32 | |
27 | 33 | update_translations: |
28 | 34 | tx pull -a --minimum-perc=10 |
29 | cd debug_toolbar && django-admin.py compilemessages | |
35 | cd debug_toolbar && django-admin compilemessages |
18 | 18 | In addition to the built-in panels, a number of third-party panels are |
19 | 19 | contributed by the community. |
20 | 20 | |
21 | The current version of the Debug Toolbar is 1.2.1. It works on Django 1.4 to 1.7. | |
22 | ||
23 | If you're using Django 1.4, you will need Django ≥ 1.4.2 and Python ≥ 2.6.5. | |
24 | If you're using Django ≥ 1.5, there aren't any restrictions. | |
21 | The current version of the Debug Toolbar is 1.4. It works on Django ≥ 1.7. | |
25 | 22 | |
26 | 23 | Documentation, including installation and configuration instructions, is |
27 | 24 | available at http://django-debug-toolbar.readthedocs.org/. |
12 | 12 | def ready(self): |
13 | 13 | if dt_settings.PATCH_SETTINGS: |
14 | 14 | dt_settings.patch_all() |
15 | dt_settings.check_middleware() |
0 | """ | |
1 | This file exists to contain all Django and Python compatibility issues. | |
2 | ||
3 | In order to avoid circular references, nothing should be imported from | |
4 | debug_toolbar. | |
5 | """ | |
6 | ||
7 | from django.conf import settings | |
8 | from django.core.exceptions import ImproperlyConfigured | |
9 | ||
10 | try: | |
11 | from django.template.base import linebreak_iter # NOQA | |
12 | except ImportError: # Django < 1.9 | |
13 | from django.views.debug import linebreak_iter # NOQA | |
14 | ||
15 | try: | |
16 | from django.template.engine import Engine | |
17 | except ImportError: # Django < 1.8 | |
18 | Engine = None | |
19 | from django.template.context import get_standard_processors # NOQA | |
20 | from django.template.loader import find_template_loader # NOQA | |
21 | ||
22 | ||
23 | def get_template_dirs(): | |
24 | """Compatibility method to fetch the template directories.""" | |
25 | if Engine: | |
26 | try: | |
27 | engine = Engine.get_default() | |
28 | except ImproperlyConfigured: | |
29 | template_dirs = [] | |
30 | else: | |
31 | template_dirs = engine.dirs | |
32 | else: # Django < 1.8 | |
33 | template_dirs = settings.TEMPLATE_DIRS | |
34 | return template_dirs | |
35 | ||
36 | ||
37 | def get_template_loaders(): | |
38 | """Compatibility method to fetch the template loaders.""" | |
39 | if Engine: | |
40 | try: | |
41 | engine = Engine.get_default() | |
42 | except ImproperlyConfigured: | |
43 | loaders = [] | |
44 | else: | |
45 | loaders = engine.template_loaders | |
46 | else: # Django < 1.8 | |
47 | loaders = [ | |
48 | find_template_loader(loader_name) | |
49 | for loader_name in settings.TEMPLATE_LOADERS] | |
50 | return loaders | |
51 | ||
52 | ||
53 | def get_template_context_processors(): | |
54 | """Compatibility method to fetch the template context processors.""" | |
55 | if Engine: | |
56 | try: | |
57 | engine = Engine.get_default() | |
58 | except ImproperlyConfigured: | |
59 | context_processors = [] | |
60 | else: | |
61 | context_processors = engine.template_context_processors | |
62 | else: # Django < 1.8 | |
63 | context_processors = get_standard_processors() | |
64 | return context_processors |
5 | 5 | msgstr "" |
6 | 6 | "Project-Id-Version: Django Debug Toolbar\n" |
7 | 7 | "Report-Msgid-Bugs-To: \n" |
8 | "POT-Creation-Date: 2014-04-25 21:52+0200\n" | |
8 | "POT-Creation-Date: 2015-07-06 16:50-0400\n" | |
9 | 9 | "PO-Revision-Date: 2012-03-31 20:10+0000\n" |
10 | 10 | "Last-Translator: \n" |
11 | 11 | "Language-Team: \n" |
19 | 19 | msgid "Debug Toolbar" |
20 | 20 | msgstr "" |
21 | 21 | |
22 | #: views.py:14 | |
23 | msgid "" | |
24 | "Data for this panel isn't available anymore. Please reload the page and " | |
25 | "retry." | |
26 | msgstr "" | |
27 | ||
28 | #: panels/cache.py:191 | |
22 | #: panels/cache.py:209 | |
29 | 23 | msgid "Cache" |
30 | 24 | msgstr "" |
31 | 25 | |
32 | #: panels/cache.py:196 | |
26 | #: panels/cache.py:214 | |
33 | 27 | #, python-format |
34 | 28 | msgid "%(cache_calls)d call in %(time).2fms" |
35 | 29 | msgid_plural "%(cache_calls)d calls in %(time).2fms" |
36 | 30 | msgstr[0] "" |
37 | 31 | msgstr[1] "" |
38 | 32 | |
39 | #: panels/cache.py:204 | |
33 | #: panels/cache.py:222 | |
40 | 34 | #, python-format |
41 | 35 | msgid "Cache calls from %(count)d backend" |
42 | 36 | msgid_plural "Cache calls from %(count)d backends" |
43 | 37 | msgstr[0] "" |
44 | 38 | msgstr[1] "" |
45 | 39 | |
46 | #: panels/headers.py:35 | |
40 | #: panels/headers.py:33 | |
47 | 41 | msgid "Headers" |
48 | 42 | msgstr "" |
49 | 43 | |
50 | #: panels/logging.py:64 | |
44 | #: panels/logging.py:63 | |
51 | 45 | msgid "Logging" |
52 | 46 | msgstr "" |
53 | 47 | |
54 | #: panels/logging.py:70 | |
48 | #: panels/logging.py:69 | |
55 | 49 | #, python-format |
56 | 50 | msgid "%(count)s message" |
57 | 51 | msgid_plural "%(count)s messages" |
58 | 52 | msgstr[0] "" |
59 | 53 | msgstr[1] "" |
60 | 54 | |
61 | #: panels/logging.py:73 | |
55 | #: panels/logging.py:72 | |
62 | 56 | msgid "Log messages" |
63 | 57 | msgstr "" |
64 | 58 | |
82 | 76 | msgid "<unavailable>" |
83 | 77 | msgstr "" |
84 | 78 | |
79 | #: panels/settings.py:17 | |
80 | msgid "Settings" | |
81 | msgstr "" | |
82 | ||
85 | 83 | #: panels/settings.py:20 |
86 | msgid "Settings" | |
87 | msgstr "" | |
88 | ||
89 | #: panels/settings.py:23 | |
90 | 84 | #, python-format |
91 | 85 | msgid "Settings from <code>%s</code>" |
92 | 86 | msgstr "" |
93 | 87 | |
94 | #: panels/signals.py:45 | |
88 | #: panels/signals.py:42 | |
95 | 89 | #, python-format |
96 | 90 | msgid "%(num_receivers)d receiver of 1 signal" |
97 | 91 | msgid_plural "%(num_receivers)d receivers of 1 signal" |
98 | 92 | msgstr[0] "" |
99 | 93 | msgstr[1] "" |
100 | 94 | |
101 | #: panels/signals.py:48 | |
95 | #: panels/signals.py:45 | |
102 | 96 | #, python-format |
103 | 97 | msgid "%(num_receivers)d receiver of %(num_signals)d signals" |
104 | 98 | msgid_plural "%(num_receivers)d receivers of %(num_signals)d signals" |
105 | 99 | msgstr[0] "" |
106 | 100 | msgstr[1] "" |
107 | 101 | |
108 | #: panels/signals.py:53 | |
102 | #: panels/signals.py:50 | |
109 | 103 | msgid "Signals" |
110 | 104 | msgstr "" |
111 | 105 | |
112 | #: panels/staticfiles.py:89 | |
106 | #: panels/sql/panel.py:23 | |
107 | msgid "Autocommit" | |
108 | msgstr "" | |
109 | ||
110 | #: panels/sql/panel.py:24 | |
111 | msgid "Read uncommitted" | |
112 | msgstr "" | |
113 | ||
114 | #: panels/sql/panel.py:25 | |
115 | msgid "Read committed" | |
116 | msgstr "" | |
117 | ||
118 | #: panels/sql/panel.py:26 | |
119 | msgid "Repeatable read" | |
120 | msgstr "" | |
121 | ||
122 | #: panels/sql/panel.py:27 | |
123 | msgid "Serializable" | |
124 | msgstr "" | |
125 | ||
126 | #: panels/sql/panel.py:38 | |
127 | msgid "Idle" | |
128 | msgstr "" | |
129 | ||
130 | #: panels/sql/panel.py:39 | |
131 | msgid "Active" | |
132 | msgstr "" | |
133 | ||
134 | #: panels/sql/panel.py:40 | |
135 | msgid "In transaction" | |
136 | msgstr "" | |
137 | ||
138 | #: panels/sql/panel.py:41 | |
139 | msgid "In error" | |
140 | msgstr "" | |
141 | ||
142 | #: panels/sql/panel.py:42 | |
143 | msgid "Unknown" | |
144 | msgstr "" | |
145 | ||
146 | #: panels/sql/panel.py:106 | |
147 | msgid "SQL" | |
148 | msgstr "" | |
149 | ||
150 | #: panels/staticfiles.py:86 | |
113 | 151 | #, python-format |
114 | 152 | msgid "Static files (%(num_found)s found, %(num_used)s used)" |
115 | 153 | msgstr "" |
116 | 154 | |
117 | #: panels/staticfiles.py:107 | |
155 | #: panels/staticfiles.py:104 | |
118 | 156 | msgid "Static files" |
119 | 157 | msgstr "" |
120 | 158 | |
121 | #: panels/staticfiles.py:112 | |
159 | #: panels/staticfiles.py:109 | |
122 | 160 | #, python-format |
123 | 161 | msgid "%(num_used)s file used" |
124 | 162 | msgid_plural "%(num_used)s files used" |
125 | 163 | msgstr[0] "" |
126 | 164 | msgstr[1] "" |
165 | ||
166 | #: panels/templates/panel.py:171 | |
167 | msgid "Templates" | |
168 | msgstr "" | |
169 | ||
170 | #: panels/templates/panel.py:176 | |
171 | #, python-format | |
172 | msgid "Templates (%(num_templates)s rendered)" | |
173 | msgstr "" | |
174 | ||
175 | #: panels/templates/panel.py:207 | |
176 | msgid "No origin" | |
177 | msgstr "" | |
127 | 178 | |
128 | 179 | #: panels/timer.py:23 |
129 | 180 | #, python-format |
187 | 238 | msgid "%(vcsw)d voluntary, %(ivcsw)d involuntary" |
188 | 239 | msgstr "" |
189 | 240 | |
190 | #: panels/versions.py:25 | |
241 | #: panels/versions.py:21 | |
191 | 242 | msgid "Versions" |
192 | msgstr "" | |
193 | ||
194 | #: panels/sql/panel.py:22 | |
195 | msgid "Autocommit" | |
196 | msgstr "" | |
197 | ||
198 | #: panels/sql/panel.py:23 | |
199 | msgid "Read uncommitted" | |
200 | msgstr "" | |
201 | ||
202 | #: panels/sql/panel.py:24 | |
203 | msgid "Read committed" | |
204 | msgstr "" | |
205 | ||
206 | #: panels/sql/panel.py:25 | |
207 | msgid "Repeatable read" | |
208 | msgstr "" | |
209 | ||
210 | #: panels/sql/panel.py:26 | |
211 | msgid "Serializable" | |
212 | msgstr "" | |
213 | ||
214 | #: panels/sql/panel.py:37 | |
215 | msgid "Idle" | |
216 | msgstr "" | |
217 | ||
218 | #: panels/sql/panel.py:38 | |
219 | msgid "Active" | |
220 | msgstr "" | |
221 | ||
222 | #: panels/sql/panel.py:39 | |
223 | msgid "In transaction" | |
224 | msgstr "" | |
225 | ||
226 | #: panels/sql/panel.py:40 | |
227 | msgid "In error" | |
228 | msgstr "" | |
229 | ||
230 | #: panels/sql/panel.py:41 | |
231 | msgid "Unknown" | |
232 | msgstr "" | |
233 | ||
234 | #: panels/sql/panel.py:105 | |
235 | msgid "SQL" | |
236 | msgstr "" | |
237 | ||
238 | #: panels/templates/panel.py:141 | |
239 | msgid "Templates" | |
240 | msgstr "" | |
241 | ||
242 | #: panels/templates/panel.py:146 | |
243 | #, python-format | |
244 | msgid "Templates (%(num_templates)s rendered)" | |
245 | 243 | msgstr "" |
246 | 244 | |
247 | 245 | #: templates/debug_toolbar/base.html:19 |
264 | 262 | msgid "Show toolbar" |
265 | 263 | msgstr "" |
266 | 264 | |
267 | #: templates/debug_toolbar/base.html:53 | |
268 | msgid "Close" | |
269 | msgstr "" | |
270 | ||
271 | #: templates/debug_toolbar/redirect.html:8 | |
272 | msgid "Location:" | |
273 | msgstr "" | |
274 | ||
275 | #: templates/debug_toolbar/redirect.html:10 | |
276 | msgid "" | |
277 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " | |
278 | "debug viewing purposes. You can click the above link to continue with the " | |
279 | "redirect as normal." | |
280 | msgstr "" | |
281 | ||
282 | 265 | #: templates/debug_toolbar/panels/cache.html:2 |
283 | 266 | msgid "Summary" |
284 | 267 | msgstr "" |
308 | 291 | msgstr "" |
309 | 292 | |
310 | 293 | #: templates/debug_toolbar/panels/cache.html:43 |
311 | #: templates/debug_toolbar/panels/sql.html:20 | |
294 | #: templates/debug_toolbar/panels/sql.html:23 | |
312 | 295 | msgid "Time (ms)" |
313 | 296 | msgstr "" |
314 | 297 | |
482 | 465 | msgstr[0] "" |
483 | 466 | msgstr[1] "" |
484 | 467 | |
485 | #: templates/debug_toolbar/panels/sql.html:18 | |
468 | #: templates/debug_toolbar/panels/sql.html:9 | |
469 | #, python-format | |
470 | msgid "including %(dupes)s duplicates" | |
471 | msgstr "" | |
472 | ||
473 | #: templates/debug_toolbar/panels/sql.html:21 | |
486 | 474 | msgid "Query" |
487 | 475 | msgstr "" |
488 | 476 | |
489 | #: templates/debug_toolbar/panels/sql.html:19 | |
477 | #: templates/debug_toolbar/panels/sql.html:22 | |
490 | 478 | #: templates/debug_toolbar/panels/timer.html:36 |
491 | 479 | msgid "Timeline" |
492 | 480 | msgstr "" |
493 | 481 | |
494 | #: templates/debug_toolbar/panels/sql.html:21 | |
482 | #: templates/debug_toolbar/panels/sql.html:24 | |
495 | 483 | msgid "Action" |
496 | 484 | msgstr "" |
497 | 485 | |
498 | #: templates/debug_toolbar/panels/sql.html:64 | |
486 | #: templates/debug_toolbar/panels/sql.html:39 | |
487 | #, python-format | |
488 | msgid "Duplicated %(dupes)s times." | |
489 | msgstr "" | |
490 | ||
491 | #: templates/debug_toolbar/panels/sql.html:71 | |
499 | 492 | msgid "Connection:" |
500 | 493 | msgstr "" |
501 | 494 | |
502 | #: templates/debug_toolbar/panels/sql.html:66 | |
495 | #: templates/debug_toolbar/panels/sql.html:73 | |
503 | 496 | msgid "Isolation level:" |
504 | 497 | msgstr "" |
505 | 498 | |
506 | #: templates/debug_toolbar/panels/sql.html:69 | |
499 | #: templates/debug_toolbar/panels/sql.html:76 | |
507 | 500 | msgid "Transaction status:" |
508 | 501 | msgstr "" |
509 | 502 | |
510 | #: templates/debug_toolbar/panels/sql.html:83 | |
503 | #: templates/debug_toolbar/panels/sql.html:90 | |
511 | 504 | msgid "(unknown)" |
512 | 505 | msgstr "" |
513 | 506 | |
514 | #: templates/debug_toolbar/panels/sql.html:92 | |
507 | #: templates/debug_toolbar/panels/sql.html:99 | |
515 | 508 | msgid "No SQL queries were recorded during this request." |
516 | msgstr "" | |
517 | ||
518 | #: templates/debug_toolbar/panels/sql_explain.html:3 | |
519 | #: templates/debug_toolbar/panels/sql_profile.html:3 | |
520 | #: templates/debug_toolbar/panels/sql_select.html:3 | |
521 | #: templates/debug_toolbar/panels/template_source.html:3 | |
522 | msgid "Back" | |
523 | 509 | msgstr "" |
524 | 510 | |
525 | 511 | #: templates/debug_toolbar/panels/sql_explain.html:4 |
651 | 637 | #: templates/debug_toolbar/panels/versions.html:6 |
652 | 638 | msgid "Version" |
653 | 639 | msgstr "" |
640 | ||
641 | #: templates/debug_toolbar/redirect.html:8 | |
642 | msgid "Location:" | |
643 | msgstr "" | |
644 | ||
645 | #: templates/debug_toolbar/redirect.html:10 | |
646 | msgid "" | |
647 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " | |
648 | "debug viewing purposes. You can click the above link to continue with the " | |
649 | "redirect as normal." | |
650 | msgstr "" | |
651 | ||
652 | #: views.py:14 | |
653 | msgid "" | |
654 | "Data for this panel isn't available anymore. Please reload the page and " | |
655 | "retry." | |
656 | msgstr "" |
1 | 1 | |
2 | 2 | from time import time |
3 | 3 | |
4 | import sqlparse | |
4 | 5 | # 'debugsqlshell' is the same as the 'shell'. |
5 | from django.core.management.commands.shell import Command # noqa | |
6 | try: | |
7 | from django.db.backends import utils | |
8 | except ImportError: | |
9 | from django.db.backends import util as utils | |
10 | ||
11 | import sqlparse | |
6 | from django.core.management.commands.shell import Command # noqa | |
7 | from django.db.backends import utils as db_backends_utils | |
12 | 8 | |
13 | 9 | |
14 | class PrintQueryWrapper(utils.CursorDebugWrapper): | |
10 | class PrintQueryWrapper(db_backends_utils.CursorDebugWrapper): | |
15 | 11 | def execute(self, sql, params=()): |
16 | 12 | start_time = time() |
17 | 13 | try: |
24 | 20 | print('%s [%.2fms]' % (formatted_sql, duration)) |
25 | 21 | |
26 | 22 | |
27 | utils.CursorDebugWrapper = PrintQueryWrapper | |
23 | db_backends_utils.CursorDebugWrapper = PrintQueryWrapper |
7 | 7 | import threading |
8 | 8 | |
9 | 9 | from django.conf import settings |
10 | from django.utils import six | |
10 | 11 | from django.utils.encoding import force_text |
11 | from django.utils.importlib import import_module | |
12 | from django.utils.module_loading import import_string | |
12 | 13 | |
14 | from debug_toolbar import settings as dt_settings | |
13 | 15 | from debug_toolbar.toolbar import DebugToolbar |
14 | from debug_toolbar import settings as dt_settings | |
15 | 16 | |
16 | 17 | _HTML_TYPES = ('text/html', 'application/xhtml+xml') |
17 | 18 | # Handles python threading module bug - http://bugs.python.org/issue14308 |
38 | 39 | """ |
39 | 40 | debug_toolbars = {} |
40 | 41 | |
42 | def __init__(self): | |
43 | # If SHOW_TOOLBAR_CALLBACK is a string, which is the recommended | |
44 | # setup, resolve it to the corresponding callable. | |
45 | func_or_path = dt_settings.CONFIG['SHOW_TOOLBAR_CALLBACK'] | |
46 | if isinstance(func_or_path, six.string_types): | |
47 | self.show_toolbar = import_string(func_or_path) | |
48 | else: | |
49 | self.show_toolbar = func_or_path | |
50 | ||
41 | 51 | def process_request(self, request): |
42 | 52 | # Decide whether the toolbar is active for this request. |
43 | func_path = dt_settings.CONFIG['SHOW_TOOLBAR_CALLBACK'] | |
44 | # Replace this with import_by_path in Django >= 1.6. | |
45 | mod_path, func_name = func_path.rsplit('.', 1) | |
46 | show_toolbar = getattr(import_module(mod_path), func_name) | |
47 | if not show_toolbar(request): | |
53 | if not self.show_toolbar(request): | |
48 | 54 | return |
49 | 55 | |
50 | 56 | toolbar = DebugToolbar(request) |
117 | 123 | # When the body ends with a newline, there's two trailing groups. |
118 | 124 | bits.append(''.join(m[0] for m in matches if m[1] == '')) |
119 | 125 | if len(bits) > 1: |
126 | # When the toolbar will be inserted for sure, generate the stats. | |
127 | for panel in reversed(toolbar.enabled_panels): | |
128 | panel.generate_stats(request, response) | |
129 | ||
120 | 130 | bits[-2] += toolbar.render_toolbar() |
121 | 131 | response.content = insert_before.join(bits) |
122 | 132 | if response.get('Content-Length', None): |
0 | from __future__ import absolute_import, unicode_literals | |
1 | ||
2 | import django | |
3 | ||
4 | from debug_toolbar import settings as dt_settings | |
5 | ||
6 | ||
7 | if dt_settings.PATCH_SETTINGS and django.VERSION[:2] < (1, 7): | |
8 | dt_settings.patch_all() |
23 | 23 | @property |
24 | 24 | def enabled(self): |
25 | 25 | # Check to see if settings has a default value for it |
26 | if get_name_from_obj(self) in dt_settings.CONFIG['DISABLE_PANELS']: | |
26 | disabled_panels = dt_settings.CONFIG['DISABLE_PANELS'] | |
27 | panel_path = get_name_from_obj(self) | |
28 | # Some panels such as the SQLPanel and TemplatesPanel exist in a | |
29 | # panel module, but can be disabled without panel in the path. | |
30 | # For that reason, replace .panel. in the path and check for that | |
31 | # value in the disabled panels as well. | |
32 | disable_panel = ( | |
33 | panel_path in disabled_panels or | |
34 | panel_path.replace('.panel.', '.') in disabled_panels) | |
35 | if disable_panel: | |
27 | 36 | default = 'off' |
28 | 37 | else: |
29 | 38 | default = 'on' |
157 | 166 | |
158 | 167 | def process_response(self, request, response): |
159 | 168 | """ |
160 | Like process_response in Django's middleware. | |
169 | Like process_response in Django's middleware. This is similar to | |
170 | :meth:`generate_stats <debug_toolbar.panels.Panel.generate_stats>`, | |
171 | but will be executed on every request. It should be used when either | |
172 | the logic needs to be executed on every request or it needs to change | |
173 | the response entirely, such as :class:`RedirectsPanel`. | |
161 | 174 | |
162 | 175 | Write panel logic related to the response there. Post-process data |
163 | 176 | gathered while the view executed. Save data with :meth:`record_stats`. |
177 | ||
178 | Return a response to overwrite the existing response. | |
179 | """ | |
180 | ||
181 | def generate_stats(self, request, response): | |
182 | """ | |
183 | Similar to :meth:`process_response | |
184 | <debug_toolbar.panels.Panel.process_response>`, | |
185 | but may not be executed on every request. This will only be called if | |
186 | the toolbar will be inserted into the request. | |
187 | ||
188 | Write panel logic related to the response there. Post-process data | |
189 | gathered while the view executed. Save data with :meth:`record_stats`. | |
190 | ||
191 | Does not return a value. | |
164 | 192 | """ |
165 | 193 | |
166 | 194 |
2 | 2 | import inspect |
3 | 3 | import sys |
4 | 4 | import time |
5 | ||
5 | from collections import OrderedDict | |
6 | ||
7 | import django | |
6 | 8 | from django.conf import settings |
7 | 9 | from django.core import cache |
8 | from django.core.cache import cache as original_cache, get_cache as original_get_cache | |
10 | from django.core.cache import CacheHandler, caches as original_caches | |
9 | 11 | from django.core.cache.backends.base import BaseCache |
10 | 12 | from django.dispatch import Signal |
11 | from django.template import Node | |
13 | from django.middleware import cache as middleware_cache | |
12 | 14 | from django.utils.translation import ugettext_lazy as _, ungettext |
13 | try: | |
14 | from collections import OrderedDict | |
15 | except ImportError: | |
16 | from django.utils.datastructures import SortedDict as OrderedDict | |
17 | ||
15 | ||
16 | from debug_toolbar import settings as dt_settings | |
18 | 17 | from debug_toolbar.panels import Panel |
19 | from debug_toolbar.utils import (tidy_stacktrace, render_stacktrace, | |
20 | get_template_info, get_stack) | |
21 | from debug_toolbar import settings as dt_settings | |
22 | ||
18 | from debug_toolbar.utils import ( | |
19 | get_stack, get_template_info, render_stacktrace, tidy_stacktrace, | |
20 | ) | |
21 | ||
22 | if django.VERSION[:2] < (1, 9): | |
23 | from django.core.cache import get_cache as original_get_cache | |
23 | 24 | |
24 | 25 | cache_called = Signal(providing_args=[ |
25 | 26 | "time_taken", "name", "return_value", "args", "kwargs", "trace"]) |
36 | 37 | else: |
37 | 38 | stacktrace = [] |
38 | 39 | |
39 | template_info = None | |
40 | cur_frame = sys._getframe().f_back | |
41 | try: | |
42 | while cur_frame is not None: | |
43 | if cur_frame.f_code.co_name == 'render': | |
44 | node = cur_frame.f_locals['self'] | |
45 | if isinstance(node, Node): | |
46 | template_info = get_template_info(node.source) | |
47 | break | |
48 | cur_frame = cur_frame.f_back | |
49 | except Exception: | |
50 | pass | |
51 | del cur_frame | |
40 | template_info = get_template_info() | |
52 | 41 | cache_called.send(sender=self.__class__, time_taken=t, |
53 | 42 | name=method.__name__, return_value=value, |
54 | 43 | args=args, kwargs=kwargs, trace=stacktrace, |
93 | 82 | return self.cache.delete(*args, **kwargs) |
94 | 83 | |
95 | 84 | @send_signal |
85 | def clear(self, *args, **kwargs): | |
86 | return self.cache.clear(*args, **kwargs) | |
87 | ||
88 | @send_signal | |
96 | 89 | def has_key(self, *args, **kwargs): |
97 | return self.cache.has_key(*args, **kwargs) | |
90 | # Ignore flake8 rules for has_key since we need to support caches | |
91 | # that may be using has_key. | |
92 | return self.cache.has_key(*args, **kwargs) # noqa | |
98 | 93 | |
99 | 94 | @send_signal |
100 | 95 | def incr(self, *args, **kwargs): |
125 | 120 | return self.cache.decr_version(*args, **kwargs) |
126 | 121 | |
127 | 122 | |
128 | def get_cache(*args, **kwargs): | |
129 | return CacheStatTracker(original_get_cache(*args, **kwargs)) | |
123 | if django.VERSION[:2] < (1, 9): | |
124 | def get_cache(*args, **kwargs): | |
125 | return CacheStatTracker(original_get_cache(*args, **kwargs)) | |
126 | ||
127 | ||
128 | class CacheHandlerPatch(CacheHandler): | |
129 | def __getitem__(self, alias): | |
130 | actual_cache = super(CacheHandlerPatch, self).__getitem__(alias) | |
131 | return CacheStatTracker(actual_cache) | |
132 | ||
133 | ||
134 | # Must monkey patch the middleware's cache module as well in order to | |
135 | # cover per-view level caching. This needs to be monkey patched outside | |
136 | # of the enable_instrumentation method since the django's | |
137 | # decorator_from_middleware_with_args will store the cache from core.caches | |
138 | # when it wraps the view. | |
139 | if django.VERSION[:2] < (1, 9): | |
140 | middleware_cache.get_cache = get_cache | |
141 | middleware_cache.caches = CacheHandlerPatch() | |
130 | 142 | |
131 | 143 | |
132 | 144 | class CachePanel(Panel): |
146 | 158 | ('get', 0), |
147 | 159 | ('set', 0), |
148 | 160 | ('delete', 0), |
161 | ('clear', 0), | |
149 | 162 | ('get_many', 0), |
150 | 163 | ('set_many', 0), |
151 | 164 | ('delete_many', 0), |
205 | 218 | count) % dict(count=count) |
206 | 219 | |
207 | 220 | def enable_instrumentation(self): |
208 | # This isn't thread-safe because cache connections aren't thread-local | |
209 | # in Django, unlike database connections. | |
210 | cache.cache = CacheStatTracker(original_cache) | |
211 | cache.get_cache = get_cache | |
221 | if django.VERSION[:2] < (1, 9): | |
222 | cache.get_cache = get_cache | |
223 | if isinstance(middleware_cache.caches, CacheHandlerPatch): | |
224 | cache.caches = middleware_cache.caches | |
225 | else: | |
226 | cache.caches = CacheHandlerPatch() | |
212 | 227 | |
213 | 228 | def disable_instrumentation(self): |
214 | cache.cache = original_cache | |
215 | cache.get_cache = original_get_cache | |
216 | ||
217 | def process_response(self, request, response): | |
229 | if django.VERSION[:2] < (1, 9): | |
230 | cache.get_cache = original_get_cache | |
231 | cache.caches = original_caches | |
232 | # While it can be restored to the original, any views that were | |
233 | # wrapped with the cache_page decorator will continue to use a | |
234 | # monkey patched cache. | |
235 | middleware_cache.caches = original_caches | |
236 | ||
237 | def generate_stats(self, request, response): | |
218 | 238 | self.record_stats({ |
219 | 239 | 'total_calls': len(self.calls), |
220 | 240 | 'calls': self.calls, |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | try: | |
3 | from collections import OrderedDict | |
4 | except ImportError: | |
5 | from django.utils.datastructures import SortedDict as OrderedDict | |
2 | from collections import OrderedDict | |
3 | ||
6 | 4 | from django.utils.translation import ugettext_lazy as _ |
5 | ||
7 | 6 | from debug_toolbar.panels import Panel |
8 | 7 | |
9 | 8 | |
48 | 47 | 'environ': self.environ, |
49 | 48 | }) |
50 | 49 | |
51 | def process_response(self, request, response): | |
50 | def generate_stats(self, request, response): | |
52 | 51 | self.response_headers = OrderedDict(sorted(response.items())) |
53 | 52 | self.record_stats({ |
54 | 53 | 'response_headers': self.response_headers, |
1 | 1 | |
2 | 2 | import datetime |
3 | 3 | import logging |
4 | ||
5 | from django.utils.translation import ugettext_lazy as _, ungettext | |
6 | ||
7 | from debug_toolbar.panels import Panel | |
8 | from debug_toolbar.utils import ThreadCollector | |
9 | ||
4 | 10 | try: |
5 | 11 | import threading |
6 | 12 | except ImportError: |
7 | 13 | threading = None |
8 | from django.utils.translation import ungettext, ugettext_lazy as _ | |
9 | from debug_toolbar.panels import Panel | |
10 | from debug_toolbar.utils import ThreadCollector | |
11 | 14 | |
12 | 15 | MESSAGE_IF_STRING_REPRESENTATION_INVALID = '[Could not get log message]' |
13 | 16 | |
49 | 52 | |
50 | 53 | collector = LogCollector() |
51 | 54 | logging_handler = ThreadTrackingHandler(collector) |
52 | logging.root.setLevel(logging.NOTSET) | |
53 | 55 | logging.root.addHandler(logging_handler) |
54 | 56 | |
55 | 57 | |
74 | 76 | def process_request(self, request): |
75 | 77 | collector.clear_collection() |
76 | 78 | |
77 | def process_response(self, request, response): | |
79 | def generate_stats(self, request, response): | |
78 | 80 | records = collector.get_collection() |
79 | 81 | self._records[threading.currentThread()] = records |
80 | 82 | collector.clear_collection() |
0 | 0 | from __future__ import absolute_import, division, unicode_literals |
1 | 1 | |
2 | import cProfile | |
3 | import os | |
4 | from colorsys import hsv_to_rgb | |
5 | from pstats import Stats | |
6 | ||
7 | from django.utils import six | |
8 | from django.utils.safestring import mark_safe | |
2 | 9 | from django.utils.translation import ugettext_lazy as _ |
3 | from django.utils.safestring import mark_safe | |
10 | ||
11 | from debug_toolbar import settings as dt_settings | |
4 | 12 | from debug_toolbar.panels import Panel |
5 | from debug_toolbar import settings as dt_settings | |
6 | 13 | |
7 | import cProfile | |
8 | from pstats import Stats | |
9 | from colorsys import hsv_to_rgb | |
10 | import os | |
14 | # Occasionally the disable method on the profiler is listed before | |
15 | # the actual view functions. This function call should be ignored as | |
16 | # it leads to an error within the tests. | |
17 | INVALID_PROFILER_FUNC = '_lsprof.Profiler' | |
18 | ||
19 | ||
20 | def contains_profiler(func_tuple): | |
21 | """Helper function that checks to see if the tuple contains | |
22 | the INVALID_PROFILE_FUNC in any string value of the tuple.""" | |
23 | has_profiler = False | |
24 | for value in func_tuple: | |
25 | if isinstance(value, six.string_types): | |
26 | has_profiler |= INVALID_PROFILER_FUNC in value | |
27 | return has_profiler | |
11 | 28 | |
12 | 29 | |
13 | 30 | class DjangoDebugToolbarStats(Stats): |
16 | 33 | def get_root_func(self): |
17 | 34 | if self.__root is None: |
18 | 35 | for func, (cc, nc, tt, ct, callers) in self.stats.items(): |
19 | if len(callers) == 0: | |
36 | if len(callers) == 0 and not contains_profiler(func): | |
20 | 37 | self.__root = func |
21 | 38 | break |
22 | 39 | return self.__root |
61 | 78 | file_path, file_name = file_name.rsplit(os.sep, 1) |
62 | 79 | |
63 | 80 | return mark_safe( |
64 | '<span class="path">{0}/</span>' | |
65 | '<span class="file">{1}</span>' | |
66 | ' in <span class="func">{3}</span>' | |
67 | '(<span class="lineno">{2}</span>)'.format( | |
81 | '<span class="djdt-path">{0}/</span>' | |
82 | '<span class="djdt-file">{1}</span>' | |
83 | ' in <span class="djdt-func">{3}</span>' | |
84 | '(<span class="djdt-lineno">{2}</span>)'.format( | |
68 | 85 | file_path, |
69 | 86 | file_name, |
70 | 87 | line_num, |
141 | 158 | func.has_subfuncs = True |
142 | 159 | self.add_node(func_list, subfunc, max_depth, cum_time=cum_time) |
143 | 160 | |
144 | def process_response(self, request, response): | |
161 | def generate_stats(self, request, response): | |
145 | 162 | if not hasattr(self, 'profiler'): |
146 | 163 | return None |
147 | 164 | # Could be delayed until the panel content is requested (perf. optim.) |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | from django.core.handlers.wsgi import STATUS_CODE_TEXT | |
3 | 2 | from django.shortcuts import render_to_response |
4 | 3 | from django.utils.translation import ugettext_lazy as _ |
5 | 4 | |
19 | 18 | if 300 <= int(response.status_code) < 400: |
20 | 19 | redirect_to = response.get('Location', None) |
21 | 20 | if redirect_to: |
22 | try: # Django >= 1.6 | |
23 | reason_phrase = response.reason_phrase | |
24 | except AttributeError: | |
25 | reason_phrase = STATUS_CODE_TEXT.get(response.status_code, | |
26 | 'UNKNOWN STATUS CODE') | |
27 | status_line = '%s %s' % (response.status_code, reason_phrase) | |
21 | status_line = '%s %s' % (response.status_code, response.reason_phrase) | |
28 | 22 | cookies = response.cookies |
29 | 23 | context = {'redirect_to': redirect_to, 'status_line': status_line} |
30 | 24 | response = render_to_response('debug_toolbar/redirect.html', context) |
24 | 24 | view_func = self.get_stats().get('view_func', '') |
25 | 25 | return view_func.rsplit('.', 1)[-1] |
26 | 26 | |
27 | def process_response(self, request, response): | |
27 | def generate_stats(self, request, response): | |
28 | 28 | self.record_stats({ |
29 | 29 | 'get': [(k, request.GET.getlist(k)) for k in sorted(request.GET)], |
30 | 30 | 'post': [(k, request.POST.getlist(k)) for k in sorted(request.POST)], |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | from collections import OrderedDict | |
3 | ||
2 | 4 | from django.conf import settings |
5 | from django.utils.translation import ugettext_lazy as _ | |
3 | 6 | from django.views.debug import get_safe_settings |
4 | from django.utils.translation import ugettext_lazy as _ | |
5 | try: | |
6 | from collections import OrderedDict | |
7 | except ImportError: | |
8 | from django.utils.datastructures import SortedDict as OrderedDict | |
9 | 7 | |
10 | 8 | from debug_toolbar.panels import Panel |
11 | 9 | |
21 | 19 | def title(self): |
22 | 20 | return _("Settings from <code>%s</code>") % settings.SETTINGS_MODULE |
23 | 21 | |
24 | def process_response(self, request, response): | |
22 | def generate_stats(self, request, response): | |
25 | 23 | self.record_stats({ |
26 | 24 | 'settings': OrderedDict(sorted(get_safe_settings().items(), |
27 | 25 | key=lambda s: s[0])), |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | import weakref | |
3 | from importlib import import_module | |
4 | ||
2 | 5 | from django.core.signals import ( |
3 | request_started, request_finished, got_request_exception) | |
6 | got_request_exception, request_finished, request_started, | |
7 | ) | |
4 | 8 | from django.db.backends.signals import connection_created |
5 | 9 | from django.db.models.signals import ( |
6 | class_prepared, pre_init, post_init, pre_save, post_save, | |
7 | pre_delete, post_delete, post_syncdb) | |
8 | try: | |
9 | from django.dispatch.dispatcher import WEAKREF_TYPES | |
10 | except ImportError: | |
11 | import weakref | |
12 | WEAKREF_TYPES = weakref.ReferenceType, | |
10 | class_prepared, post_delete, post_init, post_migrate, post_save, | |
11 | pre_delete, pre_init, pre_save, | |
12 | ) | |
13 | 13 | from django.utils.translation import ugettext_lazy as _, ungettext |
14 | from django.utils.importlib import import_module | |
15 | 14 | |
16 | 15 | from debug_toolbar.panels import Panel |
17 | 16 | |
31 | 30 | 'post_save': post_save, |
32 | 31 | 'pre_delete': pre_delete, |
33 | 32 | 'post_delete': post_delete, |
34 | 'post_syncdb': post_syncdb, | |
33 | 'post_migrate': post_migrate, | |
35 | 34 | } |
36 | 35 | |
37 | 36 | def nav_subtitle(self): |
60 | 59 | signals[signal_name] = getattr(signals_mod, signal_name) |
61 | 60 | return signals |
62 | 61 | |
63 | def process_response(self, request, response): | |
62 | def generate_stats(self, request, response): | |
64 | 63 | signals = [] |
65 | 64 | for name, signal in sorted(self.signals.items(), key=lambda x: x[0]): |
66 | 65 | if signal is None: |
68 | 67 | receivers = [] |
69 | 68 | for receiver in signal.receivers: |
70 | 69 | receiver = receiver[1] |
71 | if isinstance(receiver, WEAKREF_TYPES): | |
70 | if isinstance(receiver, weakref.ReferenceType): | |
72 | 71 | receiver = receiver() |
73 | 72 | if receiver is None: |
74 | 73 | continue |
0 | from debug_toolbar.panels.sql.panel import SQLPanel # noqa | |
0 | from debug_toolbar.panels.sql.panel import SQLPanel # noqa |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | import hashlib | |
2 | 3 | import json |
3 | import hashlib | |
4 | 4 | |
5 | 5 | from django import forms |
6 | 6 | from django.conf import settings |
7 | from django.core.exceptions import ValidationError | |
7 | 8 | from django.db import connections |
8 | 9 | from django.utils.encoding import force_text |
9 | 10 | from django.utils.functional import cached_property |
10 | from django.core.exceptions import ValidationError | |
11 | 11 | |
12 | 12 | from debug_toolbar.panels.sql.utils import reformat_sql |
13 | 13 |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | 2 | import uuid |
3 | from collections import defaultdict | |
3 | 4 | from copy import copy |
4 | from collections import defaultdict | |
5 | ||
6 | from django.conf.urls import patterns, url | |
5 | ||
6 | from django.conf.urls import url | |
7 | 7 | from django.db import connections |
8 | 8 | from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __ |
9 | 9 | |
10 | 10 | from debug_toolbar.panels import Panel |
11 | from debug_toolbar.panels.sql import views | |
11 | 12 | from debug_toolbar.panels.sql.forms import SQLSelectForm |
13 | from debug_toolbar.panels.sql.tracking import unwrap_cursor, wrap_cursor | |
14 | from debug_toolbar.panels.sql.utils import ( | |
15 | contrasting_color_generator, reformat_sql, | |
16 | ) | |
12 | 17 | from debug_toolbar.utils import render_stacktrace |
13 | from debug_toolbar.panels.sql.utils import reformat_sql, contrasting_color_generator | |
14 | from debug_toolbar.panels.sql.tracking import wrap_cursor, unwrap_cursor | |
15 | 18 | |
16 | 19 | |
17 | 20 | def get_isolation_level_display(vendor, level): |
119 | 122 | |
120 | 123 | @classmethod |
121 | 124 | def get_urls(cls): |
122 | return patterns('debug_toolbar.panels.sql.views', # noqa | |
123 | url(r'^sql_select/$', 'sql_select', name='sql_select'), | |
124 | url(r'^sql_explain/$', 'sql_explain', name='sql_explain'), | |
125 | url(r'^sql_profile/$', 'sql_profile', name='sql_profile'), | |
126 | ) | |
125 | return [ | |
126 | url(r'^sql_select/$', views.sql_select, name='sql_select'), | |
127 | url(r'^sql_explain/$', views.sql_explain, name='sql_explain'), | |
128 | url(r'^sql_profile/$', views.sql_profile, name='sql_profile'), | |
129 | ] | |
127 | 130 | |
128 | 131 | def enable_instrumentation(self): |
129 | 132 | # This is thread-safe because database connections are thread-local. |
134 | 137 | for connection in connections.all(): |
135 | 138 | unwrap_cursor(connection) |
136 | 139 | |
137 | def process_response(self, request, response): | |
140 | def generate_stats(self, request, response): | |
138 | 141 | colors = contrasting_color_generator() |
139 | 142 | trace_colors = defaultdict(lambda: next(colors)) |
143 | query_duplicates = defaultdict(lambda: defaultdict(int)) | |
140 | 144 | if self._queries: |
141 | 145 | width_ratio_tally = 0 |
142 | 146 | factor = int(256.0 / (len(self._databases) * 2.5)) |
159 | 163 | trans_id = None |
160 | 164 | i = 0 |
161 | 165 | for alias, query in self._queries: |
166 | query_duplicates[alias][query["raw_sql"]] += 1 | |
167 | ||
162 | 168 | trans_id = query.get('trans_id') |
163 | 169 | last_trans_id = trans_ids.get(alias) |
164 | 170 | |
202 | 208 | if trans_id: |
203 | 209 | self._queries[(i - 1)][1]['ends_trans'] = True |
204 | 210 | |
211 | # Queries are duplicates only if there's as least 2 of them. | |
212 | # Also, to hide queries, we need to give all the duplicate groups an id | |
213 | query_duplicates = dict( | |
214 | (alias, dict( | |
215 | (query, duplicate_count) | |
216 | for query, duplicate_count in queries.items() | |
217 | if duplicate_count >= 2 | |
218 | )) | |
219 | for alias, queries in query_duplicates.items() | |
220 | ) | |
221 | ||
222 | for alias, query in self._queries: | |
223 | try: | |
224 | duplicates_count = query_duplicates[alias][query["raw_sql"]] | |
225 | query["duplicate_count"] = duplicates_count | |
226 | except KeyError: | |
227 | pass | |
228 | ||
229 | for alias, alias_info in self._databases.items(): | |
230 | try: | |
231 | alias_info["duplicate_count"] = sum(e for e in query_duplicates[alias].values()) | |
232 | except KeyError: | |
233 | pass | |
234 | ||
205 | 235 | self.record_stats({ |
206 | 236 | 'databases': sorted(self._databases.items(), key=lambda x: -x[1]['time_spent']), |
207 | 237 | 'queries': [q for a, q in self._queries], |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | ||
2 | import sys | |
3 | 1 | |
4 | 2 | import json |
5 | 3 | from threading import local |
6 | 4 | from time import time |
7 | 5 | |
8 | from django.template import Node | |
6 | from django.utils import six | |
9 | 7 | from django.utils.encoding import force_text |
10 | from django.utils import six | |
11 | 8 | |
12 | from debug_toolbar.utils import tidy_stacktrace, get_template_info, get_stack | |
13 | 9 | from debug_toolbar import settings as dt_settings |
10 | from debug_toolbar.utils import get_stack, get_template_info, tidy_stacktrace | |
14 | 11 | |
15 | 12 | |
16 | 13 | class SQLQueryTriggered(Exception): |
114 | 111 | except Exception: |
115 | 112 | pass # object not JSON serializable |
116 | 113 | |
117 | template_info = None | |
118 | cur_frame = sys._getframe().f_back | |
119 | try: | |
120 | while cur_frame is not None: | |
121 | if cur_frame.f_code.co_name == 'render': | |
122 | node = cur_frame.f_locals['self'] | |
123 | if isinstance(node, Node): | |
124 | template_info = get_template_info(node.source) | |
125 | break | |
126 | cur_frame = cur_frame.f_back | |
127 | except Exception: | |
128 | pass | |
129 | del cur_frame | |
114 | template_info = get_template_info() | |
130 | 115 | |
131 | 116 | alias = getattr(self.db, 'alias', 'default') |
132 | 117 | conn = self.db.connection |
1 | 1 | |
2 | 2 | import re |
3 | 3 | |
4 | import sqlparse | |
4 | 5 | from django.utils.html import escape |
5 | ||
6 | import sqlparse | |
7 | 6 | from sqlparse import tokens as T |
8 | 7 | |
9 | 8 |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | from os.path import normpath, join | |
1 | ||
2 | from collections import OrderedDict | |
3 | from os.path import join, normpath | |
4 | ||
5 | from django.conf import settings | |
6 | from django.contrib.staticfiles import finders, storage | |
7 | from django.contrib.staticfiles.templatetags import staticfiles | |
8 | from django.core.files.storage import get_storage_class | |
9 | from django.utils.encoding import python_2_unicode_compatible | |
10 | from django.utils.functional import LazyObject | |
11 | from django.utils.translation import ugettext_lazy as _, ungettext | |
12 | ||
13 | from debug_toolbar import panels | |
14 | from debug_toolbar.utils import ThreadCollector | |
15 | ||
2 | 16 | try: |
3 | 17 | import threading |
4 | 18 | except ImportError: |
5 | 19 | threading = None |
6 | ||
7 | from django.conf import settings | |
8 | from django.core.files.storage import get_storage_class | |
9 | from django.contrib.staticfiles import finders, storage | |
10 | from django.contrib.staticfiles.templatetags import staticfiles | |
11 | ||
12 | from django.utils.encoding import python_2_unicode_compatible | |
13 | from django.utils.functional import LazyObject | |
14 | from django.utils.translation import ungettext, ugettext_lazy as _ | |
15 | try: | |
16 | from collections import OrderedDict | |
17 | except ImportError: | |
18 | from django.utils.datastructures import SortedDict as OrderedDict | |
19 | ||
20 | from debug_toolbar import panels | |
21 | from debug_toolbar.utils import ThreadCollector | |
22 | 20 | |
23 | 21 | |
24 | 22 | @python_2_unicode_compatible |
115 | 113 | def process_request(self, request): |
116 | 114 | collector.clear_collection() |
117 | 115 | |
118 | def process_response(self, request, response): | |
116 | def generate_stats(self, request, response): | |
119 | 117 | used_paths = collector.get_collection() |
120 | 118 | self._paths[threading.currentThread()] = used_paths |
121 | 119 |
0 | from debug_toolbar.panels.templates.panel import TemplatesPanel # noqa | |
0 | from debug_toolbar.panels.templates.panel import TemplatesPanel # noqa |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | try: | |
3 | from collections import OrderedDict | |
4 | except ImportError: | |
5 | from django.utils.datastructures import SortedDict as OrderedDict | |
2 | from collections import OrderedDict | |
3 | from contextlib import contextmanager | |
6 | 4 | from os.path import normpath |
7 | 5 | from pprint import pformat |
8 | 6 | |
9 | 7 | import django |
10 | 8 | from django import http |
11 | from django.conf import settings | |
12 | from django.conf.urls import patterns, url | |
9 | from django.conf.urls import url | |
13 | 10 | from django.db.models.query import QuerySet, RawQuerySet |
14 | 11 | from django.template import Context, RequestContext, Template |
15 | from django.template.context import get_standard_processors | |
16 | 12 | from django.test.signals import template_rendered |
17 | 13 | from django.test.utils import instrumented_test_render |
14 | from django.utils import six | |
18 | 15 | from django.utils.encoding import force_text |
19 | from django.utils import six | |
20 | 16 | from django.utils.translation import ugettext_lazy as _ |
21 | 17 | |
18 | from debug_toolbar.compat import ( | |
19 | get_template_context_processors, get_template_dirs, | |
20 | ) | |
22 | 21 | from debug_toolbar.panels import Panel |
23 | from debug_toolbar.panels.sql.tracking import recording, SQLQueryTriggered | |
24 | ||
22 | from debug_toolbar.panels.sql.tracking import SQLQueryTriggered, recording | |
23 | from debug_toolbar.panels.templates import views | |
25 | 24 | |
26 | 25 | # Monkey-patch to enable the template_rendered signal. The receiver returns |
27 | 26 | # immediately when the panel is disabled to keep the overhead small. |
37 | 36 | # Monkey-patch to store items added by template context processors. The |
38 | 37 | # overhead is sufficiently small to justify enabling it unconditionally. |
39 | 38 | |
40 | def _request_context__init__( | |
41 | self, request, dict_=None, processors=None, current_app=None, | |
42 | use_l10n=None, use_tz=None): | |
43 | Context.__init__( | |
44 | self, dict_, current_app=current_app, | |
45 | use_l10n=use_l10n, use_tz=use_tz) | |
46 | if processors is None: | |
47 | processors = () | |
48 | else: | |
49 | processors = tuple(processors) | |
50 | self.context_processors = OrderedDict() | |
51 | updates = dict() | |
52 | for processor in get_standard_processors() + processors: | |
53 | name = '%s.%s' % (processor.__module__, processor.__name__) | |
54 | context = processor(request) | |
55 | self.context_processors[name] = context | |
56 | updates.update(context) | |
57 | self.update(updates) | |
58 | ||
59 | RequestContext.__init__ = _request_context__init__ | |
60 | ||
61 | ||
62 | # Monkey-patch versions of Django where Template doesn't store origin. | |
63 | # See https://code.djangoproject.com/ticket/16096. | |
64 | ||
65 | if django.VERSION[:2] < (1, 7): | |
66 | ||
67 | old_template_init = Template.__init__ | |
68 | ||
69 | def new_template_init(self, template_string, origin=None, name='<Unknown Template>'): | |
70 | old_template_init(self, template_string, origin, name) | |
71 | self.origin = origin | |
72 | ||
73 | Template.__init__ = new_template_init | |
39 | if django.VERSION[:2] < (1, 8): | |
40 | ||
41 | def _request_context___init__( | |
42 | self, request, dict_=None, processors=None, current_app=None, | |
43 | use_l10n=None, use_tz=None): | |
44 | Context.__init__( | |
45 | self, dict_, current_app=current_app, | |
46 | use_l10n=use_l10n, use_tz=use_tz) | |
47 | if processors is None: | |
48 | processors = () | |
49 | else: | |
50 | processors = tuple(processors) | |
51 | self.context_processors = OrderedDict() | |
52 | updates = dict() | |
53 | std_processors = get_template_context_processors() | |
54 | for processor in std_processors + processors: | |
55 | name = '%s.%s' % (processor.__module__, processor.__name__) | |
56 | context = processor(request) | |
57 | self.context_processors[name] = context | |
58 | updates.update(context) | |
59 | self.update(updates) | |
60 | ||
61 | RequestContext.__init__ = _request_context___init__ | |
62 | ||
63 | else: | |
64 | ||
65 | @contextmanager | |
66 | def _request_context_bind_template(self, template): | |
67 | if self.template is not None: | |
68 | raise RuntimeError("Context is already bound to a template") | |
69 | ||
70 | self.template = template | |
71 | # Set context processors according to the template engine's settings. | |
72 | processors = (template.engine.template_context_processors + | |
73 | self._processors) | |
74 | self.context_processors = OrderedDict() | |
75 | updates = {} | |
76 | for processor in processors: | |
77 | name = '%s.%s' % (processor.__module__, processor.__name__) | |
78 | context = processor(self.request) | |
79 | self.context_processors[name] = context | |
80 | updates.update(context) | |
81 | self.dicts[self._processors_index] = updates | |
82 | ||
83 | try: | |
84 | yield | |
85 | finally: | |
86 | self.template = None | |
87 | # Unset context processors. | |
88 | self.dicts[self._processors_index] = {} | |
89 | ||
90 | RequestContext.bind_template = _request_context_bind_template | |
74 | 91 | |
75 | 92 | |
76 | 93 | class TemplatesPanel(Panel): |
154 | 171 | |
155 | 172 | @classmethod |
156 | 173 | def get_urls(cls): |
157 | return patterns('debug_toolbar.panels.templates.views', # noqa | |
158 | url(r'^template_source/$', 'template_source', name='template_source'), | |
159 | ) | |
174 | return [ | |
175 | url(r'^template_source/$', views.template_source, name='template_source'), | |
176 | ] | |
160 | 177 | |
161 | 178 | def enable_instrumentation(self): |
162 | 179 | template_rendered.connect(self._store_template_info) |
164 | 181 | def disable_instrumentation(self): |
165 | 182 | template_rendered.disconnect(self._store_template_info) |
166 | 183 | |
167 | def process_response(self, request, response): | |
184 | def generate_stats(self, request, response): | |
168 | 185 | template_context = [] |
169 | 186 | for template_data in self.templates: |
170 | 187 | info = {} |
171 | 188 | # Clean up some info about templates |
172 | 189 | template = template_data.get('template', None) |
173 | if not hasattr(template, 'origin'): | |
174 | continue | |
175 | if template.origin and template.origin.name: | |
190 | if hasattr(template, 'origin') and template.origin and template.origin.name: | |
176 | 191 | template.origin_name = template.origin.name |
177 | 192 | else: |
178 | template.origin_name = 'No origin' | |
193 | template.origin_name = _('No origin') | |
179 | 194 | info['template'] = template |
180 | 195 | # Clean up context for better readability |
181 | 196 | if self.toolbar.config['SHOW_TEMPLATE_CONTEXT']: |
189 | 204 | else: |
190 | 205 | context_processors = None |
191 | 206 | |
207 | template_dirs = get_template_dirs() | |
208 | ||
192 | 209 | self.record_stats({ |
193 | 210 | 'templates': template_context, |
194 | 'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], | |
211 | 'template_dirs': [normpath(x) for x in template_dirs], | |
195 | 212 | 'context_processors': context_processors, |
196 | 213 | }) |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | 2 | from django.http import HttpResponseBadRequest |
3 | from django.conf import settings | |
4 | 3 | from django.shortcuts import render_to_response |
5 | 4 | from django.template import TemplateDoesNotExist |
6 | from django.template.loader import find_template_loader | |
7 | 5 | from django.utils.safestring import mark_safe |
6 | ||
7 | from debug_toolbar.compat import get_template_loaders | |
8 | 8 | |
9 | 9 | |
10 | 10 | def template_source(request): |
16 | 16 | if template_name is None: |
17 | 17 | return HttpResponseBadRequest('"template" key is required') |
18 | 18 | |
19 | loaders = [] | |
20 | for loader_name in settings.TEMPLATE_LOADERS: | |
21 | loader = find_template_loader(loader_name) | |
19 | final_loaders = [] | |
20 | loaders = get_template_loaders() | |
21 | ||
22 | for loader in loaders: | |
22 | 23 | if loader is not None: |
23 | loaders.append(loader) | |
24 | for loader in loaders: | |
24 | # When the loader has loaders associated with it, | |
25 | # append those loaders to the list. This occurs with | |
26 | # django.template.loaders.cached.Loader | |
27 | if hasattr(loader, 'loaders'): | |
28 | final_loaders += loader.loaders | |
29 | else: | |
30 | final_loaders.append(loader) | |
31 | ||
32 | for loader in final_loaders: | |
25 | 33 | try: |
26 | 34 | source, display_name = loader.load_template_source(template_name) |
27 | 35 | break |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | ||
2 | import time | |
3 | ||
4 | from django.template.loader import render_to_string | |
5 | from django.utils.translation import ugettext_lazy as _ | |
6 | ||
7 | from debug_toolbar.panels import Panel | |
1 | 8 | |
2 | 9 | try: |
3 | 10 | import resource # Not available on Win32 systems |
4 | 11 | except ImportError: |
5 | 12 | resource = None |
6 | import time | |
7 | from django.template.loader import render_to_string | |
8 | from django.utils.translation import ugettext_lazy as _ | |
9 | from debug_toolbar.panels import Panel | |
10 | 13 | |
11 | 14 | |
12 | 15 | class TimerPanel(Panel): |
51 | 54 | if self.has_content: |
52 | 55 | self._start_rusage = resource.getrusage(resource.RUSAGE_SELF) |
53 | 56 | |
54 | def process_response(self, request, response): | |
57 | def generate_stats(self, request, response): | |
55 | 58 | stats = {} |
56 | 59 | if hasattr(self, '_start_time'): |
57 | 60 | stats['total_time'] = (time.time() - self._start_time) * 1000 |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | 2 | import sys |
3 | from collections import OrderedDict | |
3 | 4 | |
4 | 5 | import django |
5 | from django.conf import settings | |
6 | from django.utils.importlib import import_module | |
6 | from django.apps import apps | |
7 | 7 | from django.utils.translation import ugettext_lazy as _ |
8 | try: | |
9 | from collections import OrderedDict | |
10 | except ImportError: | |
11 | from django.utils.datastructures import SortedDict as OrderedDict | |
12 | 8 | |
13 | 9 | from debug_toolbar.panels import Panel |
14 | 10 | |
25 | 21 | |
26 | 22 | template = 'debug_toolbar/panels/versions.html' |
27 | 23 | |
28 | def process_response(self, request, response): | |
24 | def generate_stats(self, request, response): | |
29 | 25 | versions = [ |
30 | 26 | ('Python', '%d.%d.%d' % sys.version_info[:3]), |
31 | 27 | ('Django', self.get_app_version(django)), |
32 | 28 | ] |
33 | if django.VERSION[:2] >= (1, 7): | |
34 | versions += list(self.gen_app_versions_1_7()) | |
35 | else: | |
36 | versions += list(self.gen_app_versions_1_6()) | |
29 | versions += list(self.gen_app_versions()) | |
37 | 30 | self.record_stats({ |
38 | 31 | 'versions': OrderedDict(sorted(versions, key=lambda v: v[0])), |
39 | 32 | 'paths': sys.path, |
40 | 33 | }) |
41 | 34 | |
42 | def gen_app_versions_1_7(self): | |
43 | from django.apps import apps | |
35 | def gen_app_versions(self): | |
44 | 36 | for app_config in apps.get_app_configs(): |
45 | 37 | name = app_config.verbose_name |
46 | 38 | app = app_config.module |
47 | version = self.get_app_version(app) | |
48 | if version: | |
49 | yield name, version | |
50 | ||
51 | def gen_app_versions_1_6(self): | |
52 | for app in list(settings.INSTALLED_APPS): | |
53 | name = app.split('.')[-1].replace('_', ' ').capitalize() | |
54 | app = import_module(app) | |
55 | 39 | version = self.get_app_version(app) |
56 | 40 | if version: |
57 | 41 | yield name, version |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | 2 | import warnings |
3 | from importlib import import_module | |
3 | 4 | |
4 | 5 | from django.conf import settings |
5 | from django.utils.importlib import import_module | |
6 | 6 | from django.utils import six |
7 | ||
7 | from django.utils.module_loading import import_string | |
8 | 8 | |
9 | 9 | # Always import this module as follows: |
10 | 10 | # from debug_toolbar import settings [as dt_settings] |
17 | 17 | # Toolbar options |
18 | 18 | 'DISABLE_PANELS': set(['debug_toolbar.panels.redirects.RedirectsPanel']), |
19 | 19 | 'INSERT_BEFORE': '</body>', |
20 | 'JQUERY_URL': '//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js', | |
20 | 'JQUERY_URL': '//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', | |
21 | 21 | 'RENDER_PANELS': None, |
22 | 'RESULTS_STORE_SIZE': 10, | |
22 | 'RESULTS_CACHE_SIZE': 10, | |
23 | 23 | 'ROOT_TAG_EXTRA_ATTRS': '', |
24 | 24 | 'SHOW_COLLAPSED': False, |
25 | 25 | 'SHOW_TOOLBAR_CALLBACK': 'debug_toolbar.middleware.show_toolbar', |
64 | 64 | |
65 | 65 | CONFIG = CONFIG_DEFAULTS.copy() |
66 | 66 | CONFIG.update(USER_CONFIG) |
67 | if not isinstance(CONFIG['SHOW_TOOLBAR_CALLBACK'], six.string_types): | |
68 | warnings.warn( | |
69 | "SHOW_TOOLBAR_CALLBACK is now a dotted path. Update your " | |
70 | "DEBUG_TOOLBAR_CONFIG setting.", DeprecationWarning) | |
71 | 67 | |
72 | 68 | |
73 | 69 | PANELS_DEFAULTS = [ |
149 | 145 | 'debug_toolbar.panels.redirects.RedirectsPanel' |
150 | 146 | ) |
151 | 147 | |
152 | ||
153 | 148 | PATCH_SETTINGS = getattr(settings, 'DEBUG_TOOLBAR_PATCH_SETTINGS', settings.DEBUG) |
154 | 149 | |
155 | 150 | |
157 | 152 | # imports are placed inside functions to make it safe to import this module. |
158 | 153 | |
159 | 154 | |
160 | def is_toolbar_middleware(middleware_path): | |
155 | def check_middleware(): | |
156 | from django.middleware.gzip import GZipMiddleware | |
161 | 157 | from debug_toolbar.middleware import DebugToolbarMiddleware |
162 | # This could be replaced by import_by_path in Django >= 1.6. | |
158 | gzip_index = None | |
159 | debug_toolbar_index = None | |
160 | ||
161 | # Determine the indexes which gzip and/or the toolbar are installed at | |
162 | for i, middleware in enumerate(settings.MIDDLEWARE_CLASSES): | |
163 | if is_middleware_class(GZipMiddleware, middleware): | |
164 | gzip_index = i | |
165 | elif is_middleware_class(DebugToolbarMiddleware, middleware): | |
166 | debug_toolbar_index = i | |
167 | # If the toolbar appears before the gzip index, raise a warning | |
168 | if gzip_index is not None and debug_toolbar_index < gzip_index: | |
169 | warnings.warn( | |
170 | "Please use an explicit setup with the " | |
171 | "debug_toolbar.middleware.DebugToolbarMiddleware " | |
172 | "after django.middleware.gzip.GZipMiddlware " | |
173 | "in MIDDLEWARE_CLASSES.", Warning) | |
174 | ||
175 | ||
176 | def is_middleware_class(middleware_class, middleware_path): | |
163 | 177 | try: |
164 | mod_path, cls_name = middleware_path.rsplit('.', 1) | |
165 | mod = import_module(mod_path) | |
166 | middleware_cls = getattr(mod, cls_name) | |
167 | except (AttributeError, ImportError, ValueError): | |
178 | middleware_cls = import_string(middleware_path) | |
179 | except ImportError: | |
168 | 180 | return |
169 | return issubclass(middleware_cls, DebugToolbarMiddleware) | |
181 | return issubclass(middleware_cls, middleware_class) | |
170 | 182 | |
171 | 183 | |
172 | 184 | def is_toolbar_middleware_installed(): |
173 | return any(is_toolbar_middleware(middleware) | |
185 | from debug_toolbar.middleware import DebugToolbarMiddleware | |
186 | return any(is_middleware_class(DebugToolbarMiddleware, middleware) | |
174 | 187 | for middleware in settings.MIDDLEWARE_CLASSES) |
175 | 188 | |
176 | 189 | |
195 | 208 | |
196 | 209 | |
197 | 210 | def patch_root_urlconf(): |
198 | from django.conf.urls import include, patterns, url | |
211 | from django.conf.urls import include, url | |
199 | 212 | from django.core.urlresolvers import clear_url_caches, reverse, NoReverseMatch |
200 | 213 | import debug_toolbar |
201 | 214 | try: |
202 | 215 | reverse('djdt:render_panel') |
203 | 216 | except NoReverseMatch: |
204 | 217 | urlconf_module = import_module(settings.ROOT_URLCONF) |
205 | urlconf_module.urlpatterns = patterns('', # noqa | |
218 | urlconf_module.urlpatterns = [ | |
206 | 219 | url(r'^__debug__/', include(debug_toolbar.urls)), |
207 | ) + urlconf_module.urlpatterns | |
220 | ] + urlconf_module.urlpatterns | |
208 | 221 | clear_url_caches() |
209 | 222 | |
210 | 223 |
0 | 0 | /* http://www.positioniseverything.net/easyclearing.html */ |
1 | #djDebug .clearfix:after { | |
1 | #djDebug .djdt-clearfix:after { | |
2 | 2 | content: "."; |
3 | 3 | display: block; |
4 | 4 | height: 0; |
5 | 5 | clear: both; |
6 | 6 | visibility: hidden; |
7 | 7 | } |
8 | #djDebug .clearfix {display: inline-block;} | |
8 | #djDebug .djdt-clearfix {display: inline-block;} | |
9 | 9 | /* Hides from IE-mac \*/ |
10 | #djDebug .clearfix {display: block;} | |
11 | * html #djDebug .clearfix {height: 1%;} | |
10 | #djDebug .djdt-clearfix {display: block;} | |
11 | * html #djDebug .djdt-clearfix {height: 1%;} | |
12 | 12 | /* end hide from IE-mac */ |
13 | 13 | |
14 | 14 | /* Debug Toolbar CSS Reset, adapted from Eric Meyer's CSS Reset */ |
125 | 125 | } |
126 | 126 | |
127 | 127 | #djDebug #djDebugToolbar li>a, |
128 | #djDebug #djDebugToolbar li>div.contentless { | |
128 | #djDebug #djDebugToolbar li>div.djdt-contentless { | |
129 | 129 | font-weight:normal; |
130 | 130 | font-style:normal; |
131 | 131 | text-decoration:none; |
134 | 134 | padding:10px 10px 5px 25px; |
135 | 135 | color:#fff; |
136 | 136 | } |
137 | #djDebug #djDebugToolbar li>div.disabled { | |
137 | #djDebug #djDebugToolbar li>div.djdt-disabled { | |
138 | 138 | font-style: italic; |
139 | 139 | color: #999; |
140 | 140 | } |
144 | 144 | background-color:#ffc; |
145 | 145 | } |
146 | 146 | |
147 | #djDebug #djDebugToolbar li.active { | |
147 | #djDebug #djDebugToolbar li.djdt-active { | |
148 | 148 | background: #333 no-repeat left center; |
149 | 149 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAABe0lEQVR4AW2SO0tDQRCFz+bGJxEUrKzsBBsb/4DYiIWdECvB/6CCYGtp6QNRjJ1FihSCVpZGMIhYSBAfIGKRx70xyY3JbrKOk7DJLp7DXm7Il9nMmREYQgQeAI1W1/zZUhR9ZI9gjSZb0iHMRSPbE1QzhhF2jN4H6YdRCHaPvOTjdDb1jWECBhiJoC1tg6Kotbw9WkxBoIUGaqiiQs8fSCj+t9qAIL1nlg9fKgSGKKNEJ2RUMqh7QCDIr58k31AlrIiA0CqhDTQJtUFAqsTFxjV85FGAz1XrkDZodPewkih8IkdwCRWu2U6VerQ0O3OzuTSJ/k62JiIXJI2NL0wBjDiTseQHW8fnGY6myf3+Dz49x88+vjr9SoPaoG6lLteuCApMiu1otAWG/s7BXtYEzv3yZOyrc5nV3XTZjPAv7Jqp2AVf9+dOyx4EFCTqCAnimZB1z9X38fk05RblfVQE1LkR5a6vwCivruANV2ynjU5FHpIE+AsCnCuNfgGtjt1gZaIn2wAAAABJRU5ErkJggg=="); |
150 | 150 | padding-left:10px; |
151 | 151 | } |
152 | 152 | |
153 | #djDebug #djDebugToolbar li.active a:hover { | |
153 | #djDebug #djDebugToolbar li.djdt-active a:hover { | |
154 | 154 | color:#b36a60; |
155 | 155 | background-color:transparent; |
156 | 156 | } |
213 | 213 | background-color:#f5f5f5; |
214 | 214 | } |
215 | 215 | |
216 | #djDebug .panelContent { | |
216 | #djDebug .djdt-panelContent { | |
217 | 217 | display:none; |
218 | 218 | position:fixed; |
219 | 219 | margin:0; |
226 | 226 | z-index:100000000; |
227 | 227 | } |
228 | 228 | |
229 | #djDebug .panelContent > div { | |
229 | #djDebug .djdt-panelContent > div { | |
230 | 230 | border-bottom:1px solid #ddd; |
231 | 231 | } |
232 | 232 | |
256 | 256 | padding:5px 0 0 20px; |
257 | 257 | } |
258 | 258 | |
259 | #djDebug .djDebugPanelContent .loader { | |
259 | #djDebug .djDebugPanelContent .djdt-loader { | |
260 | 260 | display:block; |
261 | 261 | margin:80px auto; |
262 | 262 | } |
263 | 263 | |
264 | #djDebug .djDebugPanelContent .scroll { | |
264 | #djDebug .djDebugPanelContent .djdt-scroll { | |
265 | 265 | height:100%; |
266 | 266 | overflow:auto; |
267 | 267 | display:block; |
280 | 280 | margin-top:0.8em; |
281 | 281 | } |
282 | 282 | |
283 | #djDebug .panelContent table { | |
283 | #djDebug .djdt-panelContent table { | |
284 | 284 | border:1px solid #ccc; |
285 | 285 | border-collapse:collapse; |
286 | 286 | width:100%; |
289 | 289 | margin-top:0.8em; |
290 | 290 | overflow: auto; |
291 | 291 | } |
292 | #djDebug .panelContent tbody td, | |
293 | #djDebug .panelContent tbody th { | |
292 | #djDebug .djdt-panelContent tbody td, | |
293 | #djDebug .djdt-panelContent tbody th { | |
294 | 294 | vertical-align:top; |
295 | 295 | padding:2px 3px; |
296 | 296 | } |
297 | #djDebug .panelContent tbody td.time { | |
297 | #djDebug .djdt-panelContent tbody td.djdt-time { | |
298 | 298 | text-align: center; |
299 | 299 | } |
300 | 300 | |
301 | #djDebug .panelContent thead th { | |
301 | #djDebug .djdt-panelContent thead th { | |
302 | 302 | padding:1px 6px 1px 3px; |
303 | 303 | text-align:left; |
304 | 304 | font-weight:bold; |
305 | 305 | font-size:14px; |
306 | 306 | white-space: nowrap; |
307 | 307 | } |
308 | #djDebug .panelContent tbody th { | |
308 | #djDebug .djdt-panelContent tbody th { | |
309 | 309 | width:12em; |
310 | 310 | text-align:right; |
311 | 311 | color:#666; |
317 | 317 | } |
318 | 318 | |
319 | 319 | /* |
320 | #djDebug .panelContent p a:hover, #djDebug .panelContent dd a:hover { | |
320 | #djDebug .djdt-panelContent p a:hover, #djDebug .djdt-panelContent dd a:hover { | |
321 | 321 | color:#111; |
322 | 322 | background-color:#ffc; |
323 | 323 | } |
324 | 324 | |
325 | #djDebug .panelContent p { | |
325 | #djDebug .djdt-panelContent p { | |
326 | 326 | padding:0 5px; |
327 | 327 | } |
328 | 328 | |
329 | #djDebug .panelContent p, #djDebug .panelContent table, #djDebug .panelContent ol, #djDebug .panelContent ul, #djDebug .panelContent dl { | |
329 | #djDebug .djdt-panelContent p, #djDebug .djdt-panelContent table, #djDebug .djdt-panelContent ol, #djDebug .djdt-panelContent ul, #djDebug .djdt-panelContent dl { | |
330 | 330 | margin:5px 0 15px; |
331 | 331 | background-color:#fff; |
332 | 332 | } |
333 | #djDebug .panelContent table { | |
333 | #djDebug .djdt-panelContent table { | |
334 | 334 | clear:both; |
335 | 335 | border:0; |
336 | 336 | padding:0; |
339 | 339 | border-spacing:0; |
340 | 340 | } |
341 | 341 | |
342 | #djDebug .panelContent table a { | |
342 | #djDebug .djdt-panelContent table a { | |
343 | 343 | color:#000; |
344 | 344 | padding:2px 4px; |
345 | 345 | } |
346 | #djDebug .panelContent table a:hover { | |
346 | #djDebug .djdt-panelContent table a:hover { | |
347 | 347 | background-color:#ffc; |
348 | 348 | } |
349 | 349 | |
350 | #djDebug .panelContent table th { | |
350 | #djDebug .djdt-panelContent table th { | |
351 | 351 | background-color:#333; |
352 | 352 | font-weight:bold; |
353 | 353 | color:#fff; |
355 | 355 | text-align:left; |
356 | 356 | cursor:pointer; |
357 | 357 | } |
358 | #djDebug .panelContent table td { | |
358 | #djDebug .djdt-panelContent table td { | |
359 | 359 | padding:5px 10px; |
360 | 360 | font-size:14px; |
361 | 361 | background-color:#fff; |
363 | 363 | vertical-align:top; |
364 | 364 | border:0; |
365 | 365 | } |
366 | #djDebug .panelContent table tr.djDebugOdd td { | |
366 | #djDebug .djdt-panelContent table tr.djDebugOdd td { | |
367 | 367 | background-color:#eee; |
368 | 368 | } |
369 | 369 | */ |
370 | 370 | |
371 | #djDebug .panelContent .djDebugClose { | |
372 | text-indent:-9999999px; | |
371 | #djDebug .djdt-panelContent .djDebugClose { | |
373 | 372 | display:block; |
374 | 373 | position:absolute; |
375 | 374 | top:4px; |
380 | 379 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAQAAACQTsNJAAABuUlEQVR4Ab2WXeoaMRTFZxHz6pObcA0DeXUHhXktFl8LXYAPXYBLKhQGBOFqW3XS8YOW6vjHmnp6wWZyHU1maqHnQDAf/Ehu7twYhUQxdUixO/wr8ts3oeg9TQjCEx5R7SGvKCd4nPNsIyShD4QGf6QkBBkQWnrgg4zqS2fm01kbze3M3GFGjyBvCdLFqYRUySM1zLAOUXJ6dZAACcoPBOHkFpK5qY1BQBt5sExC0gAijEkriMuLfI9m8SqXNxbSd+QSbVSCnPtXyNjdCCoZ7PANF7C4/Y4tznASNzW+QqaP9lFcj4cLW4PY+vFepsSQ2Hbnp5vw2ShB231Cau72EkfUs53lC4R+YQkSXsBAiFdXc72IkupmDODDfKkhwKtdygUgHAuXw7gEIN7jCIQNsfc43sDuqlgsbC57A+u94q82nFVscv8Vy2Rbi72csGKQ+RPignvytGuZbPW0P6KNjvdpz97aIf3jLz/ArfuKXz9dCt7IojR9qihNAuUx33vL454grOqF+t2/F2rPk/H5pzaa2+Ynw3lIaOlh6BlVlDUCMlLND3pKhRdQUPof/1o4x9Qlxe6G/+T8BlqvQqkd4EyZAAAAAElFTkSuQmCC"); |
381 | 380 | } |
382 | 381 | |
383 | #djDebug .panelContent .djDebugClose:hover { | |
382 | #djDebug .djdt-panelContent .djDebugClose:hover { | |
384 | 383 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAACiUlEQVR4Ad2Yz2oTURSH+xCz7cqX6DMUZtudCxExigq2WhcKYtGFVLrwAboSKoVGxT+tJWpTa2lLTDsm6SSTjtZoIq0prf+2Hu8PcmHInJvJPYwuPPDBMDPn8DE5c+fcDEgjDENHMahwOwx2zg1IsE1wFXcVvoIM+J173L8hckrRUpAlLeSmITKsKNoKMHioJRUZU1DKjNmKTCUWDWpUL21RJbcAcIxz/chM9StyvVeh8rOHtH7nJi2dOc6Ba7gnSWY8ScQ1JVdfv6C1yQlegAH3+su5XjLDvUTKXNK77H0Ul4Bck0jZJJLhEry5GV1ULvNg1iSTiYlw64SfX0ShNEAtfp3pEhnhjJmekIJapqcyEhWZZt4OtuDqlXPUWHxKYXaGli+c1Ocpf/4E1Wfv0afcPK1dG+VyTW/TdFQk6Pdp7BfWSMdhtQIZSFDbe0s6DiqezVMJtIjTfXGnuv3b9Iib+RxF47C2Te3SJkUDsqZ81GZkHIgMdV+ora8YC62MnqbvH0Iyxa/WZ3pz+awxH7UZkSH9YaMolZfPdaJZZvd9XOJLU0sYQW1ucROJoCcOyh51x1G9ip4Ri1j9NLoxDaEbWPDTWDbrx/lHsZ740dilSKChBc1q+fp+3SyQjp/NBnoi1sB4KpLXl13QSo/n2GKFW1fpaKdG+8UNSEQbmPY2VulbWKfi5A0uFzUTFzR+ib/NFJSBWolLvKYd++i9WkhNxF9i35Y29/W9KB8DxDPJJU4EBP9wMPJFoyLmCetRUc8gPK5JRDOhIBOlJ9nE4Rn3yIdnwXYi2Crq7QSOU99OaGBNKTMu3XK63GQvADXcNDbhGcWeQAA5mf/ybwkOR3FM4XbAsSOt9wcLTn8FNtAKkQAAAABJRU5ErkJggg=="); |
385 | 384 | } |
386 | 385 | |
387 | #djDebug .panelContent .djDebugClose.djDebugBack { | |
386 | #djDebug .djdt-panelContent .djDebugClose.djDebugBack { | |
388 | 387 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAACBUlEQVR4Ad2Y0WriQBSGfYjc7tW+hM8g5HbfYMHbxcXbhT6AF30AH6cXvRCEttK0JgZi0qhQaRNtNWV2fuiADicZcxirdOCn45mccz4mMyczbXDbeDx2pH5IuRD6n7YGR3UdkPBSaiQlSoQxPOMeA+S3VIxENRXD1wZIS2rAANA1RCwuSAdBLKtTF6RnCur7fjGZTLZJkhQQ+rAdANM7FORfVaA0Td/zPBdlDWN4xgDTNYG4Zc7T6TQjASqA4jjOKmBaVSA3lNN8Pi8Es8GXBkEuGqTNh2DDtCkQvU5gal+FpYZYZJ3RQH5RxPqaWG82qmuw02uGyoHcuyB9anfowZ6el+I2jITellkOu9gWH5UwJbupvwvimWZDgVzdeWLohyqpAlF2zE7dWfEUiKMPBkGwNxt6QmjwEKik+Ltnz9ZvpTCITcA4AGnqA1EUreFkgrm+fwSEsuO3spfCIDYB0gRIi9gtlVsWSVRSAOzaMSOw4zVSDbGp4nZGIPxXg6TWXo39xarsjMV6LtuXLmiz2cx6QUNMuqAZSvxqtbJW4hHLVOKVFvpDSZK8HPmjt6C+vn9OcAz4S4FA3hcejEasoyKmlnFUVK+DklsFAl2c9vDMuE6EYbhR1wn0bV8nlEAtLKvLvXK62smeK8RwbVzC21IpAyCF77f8twQlR+onEkLow8aN9x+oYk85YNJqYQAAAABJRU5ErkJggg=="); |
389 | 388 | } |
390 | 389 | |
391 | #djDebug .panelContent .djDebugClose.djDebugBack:hover { | |
390 | #djDebug .djdt-panelContent .djDebugClose.djDebugBack:hover { | |
392 | 391 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAACLElEQVR4Ad2YT8sSURTG/RCzbdWX8DMIs23XMrBlTLiTSIKKIqgP4C6KQAuKsLCycqGLRHNsXqck2giStIiiRdDtPtABuz0zNw+XkAYe3vHce57zY/7cOfetaI/VahVZHbOKIZz/ilU02jcBBW9YZVaGC2OYg7nhQU5ZrVlhj9bIDQFSs5ooAFzN4KUFSWASWMm+INe8pvnSvJ1PzaLfg3COmB8G3n8Jcq7MKH14z4yvXjCD0yepMIY5HpiGDyQuSj56+cSMrrQ4ABHmZi/6ZTC1MpCUJb3u3oa5SsjlIKjFQeosYda5JaZ6mLt3OAxquiBsncieP4ZRCMGLrjMuyAlG7D4To2Yi5/44eWZYDdTeBWmzt8M1W95sm09Z+kd8dv0S4maY1EthCt6m9i5ITq8GAcHx+cN7KSogEqdXx3NVcgGJ3MF3R29+MCMpiOPbx40Uxd/f4q8uNgth4E1gIoBU3YHleOgYcJjvX78AQuL4LfFCGHgTkCpAau7A4umj0nuNIlIUALtxXBEcuI0kF950cTsoEO2tQdGAtyb8w4rfiof1cF5fvqDN73dCL2jwpAuaf4m/fD7UEg8v7xIv2rqTsme9cB+9AX1btuzre0bdBuh7krMMBMr/YWOUaVpF9BP7t4rSg3DFvua5xRJF8wddb/OMOfrmWbGdyKcT2U7gPPh2QgRqE1gN7ZYzZp29Qim8QmzC61YbBcAGuf/hvyW4IqvjKAjhHDGt309H4mp9BS17eAAAAABJRU5ErkJggg=="); |
393 | 392 | } |
394 | 393 | |
395 | #djDebug .panelContent dt, #djDebug .panelContent dd { | |
396 | display:block; | |
397 | } | |
398 | ||
399 | #djDebug .panelContent dt { | |
394 | #djDebug .djdt-panelContent dt, #djDebug .djdt-panelContent dd { | |
395 | display:block; | |
396 | } | |
397 | ||
398 | #djDebug .djdt-panelContent dt { | |
400 | 399 | margin-top:0.75em; |
401 | 400 | } |
402 | 401 | |
403 | #djDebug .panelContent dd { | |
402 | #djDebug .djdt-panelContent dd { | |
404 | 403 | margin-left:10px; |
405 | 404 | } |
406 | 405 | |
547 | 546 | } |
548 | 547 | |
549 | 548 | |
550 | #djDebug .panelContent ul.stats { | |
549 | #djDebug .djdt-panelContent ul.djdt-stats { | |
551 | 550 | position: relative; |
552 | 551 | list-style-type: none; |
553 | 552 | } |
554 | #djDebug .panelContent ul.stats li { | |
553 | #djDebug .djdt-panelContent ul.djdt-stats li { | |
555 | 554 | width: 30%; |
556 | 555 | float: left; |
557 | 556 | } |
558 | #djDebug .panelContent ul.stats li strong.label { | |
557 | #djDebug .djdt-panelContent ul.djdt-stats li strong.djdt-label { | |
559 | 558 | display: block; |
560 | 559 | } |
561 | #djDebug .panelContent ul.stats li span.color { | |
560 | #djDebug .djdt-panelContent ul.djdt-stats li span.djdt-color { | |
562 | 561 | height: 12px; |
563 | 562 | width: 3px; |
564 | 563 | display: inline-block; |
565 | 564 | } |
566 | #djDebug .panelContent ul.stats li span.info { | |
565 | #djDebug .djdt-panelContent ul.djdt-stats li span.djdt-info { | |
567 | 566 | display: block; |
568 | 567 | padding-left: 5px; |
569 | 568 | } |
570 | 569 | |
571 | #djDebug .panelcontent thead th { | |
570 | #djDebug .djdt-panelContent thead th { | |
572 | 571 | white-space: nowrap; |
573 | 572 | } |
574 | #djDebug .djDebugRowWarning .time { | |
573 | #djDebug .djDebugRowWarning .djdt-time { | |
575 | 574 | color: red; |
576 | 575 | } |
577 | #djdebug .panelcontent table .toggle { | |
576 | #djdebug .djdt-panelContent table .djdt-toggle { | |
578 | 577 | width: 14px; |
579 | 578 | padding-top: 3px; |
580 | 579 | } |
581 | #djDebug .panelContent table .actions { | |
580 | #djDebug .djdt-panelContent table .djdt-actions { | |
582 | 581 | min-width: 70px; |
583 | 582 | white-space: nowrap; |
584 | 583 | } |
585 | #djdebug .panelcontent table .color { | |
584 | #djdebug .djdt-panelContent table .djdt-color { | |
586 | 585 | width: 3px; |
587 | 586 | } |
588 | #djdebug .panelcontent table .color span { | |
587 | #djdebug .djdt-panelContent table .djdt-color span { | |
589 | 588 | width: 3px; |
590 | 589 | height: 12px; |
591 | 590 | overflow: hidden; |
629 | 628 | margin-bottom: 3px; |
630 | 629 | font-family:Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace; |
631 | 630 | } |
632 | #djDebug .stack span { | |
631 | #djDebug .djdt-stack span { | |
633 | 632 | color: #000; |
634 | 633 | font-weight: bold; |
635 | 634 | } |
636 | #djDebug .stack span.path { | |
635 | #djDebug .djdt-stack span.djdt-path { | |
637 | 636 | color: #777; |
638 | 637 | font-weight: normal; |
639 | 638 | } |
640 | #djDebug .stack span.code { | |
639 | #djDebug .djdt-stack span.djdt-code { | |
641 | 640 | font-weight: normal; |
642 | 641 | } |
643 | 642 | |
644 | 643 | @media print { |
645 | 644 | #djDebug { |
646 | display: none; | |
645 | display: none !important; | |
647 | 646 | } |
648 | 647 | } |
648 | ||
649 | #djDebug .djdt-width-20 { | |
650 | width: 20%; | |
651 | } | |
652 | #djDebug .djdt-width-60 { | |
653 | width: 60%; | |
654 | } | |
655 | #djDebug .djdt-highlighted { | |
656 | background-color: lightgrey; | |
657 | } |
0 | var djdt = {jQuery: jQuery}; |
0 | var djdt = {jQuery: jQuery.noConflict(true)}; window.define = _djdt_define_backup; |
0 | var _djdt_define_backup = window.define; window.define = undefined; |
0 | (function ($) { | |
0 | (function ($, publicAPI) { | |
1 | 1 | var djdt = { |
2 | 2 | handleDragged: false, |
3 | 3 | events: { |
7 | 7 | init: function() { |
8 | 8 | $('#djDebug').show(); |
9 | 9 | var current = null; |
10 | $(document).on('click', '#djDebugPanelList li a', function() { | |
10 | $('#djDebugPanelList').on('click', 'li a', function() { | |
11 | 11 | if (!this.className) { |
12 | 12 | return false; |
13 | 13 | } |
14 | 14 | current = $('#djDebug #' + this.className); |
15 | 15 | if (current.is(':visible')) { |
16 | 16 | $(document).trigger('close.djDebug'); |
17 | $(this).parent().removeClass('active'); | |
17 | $(this).parent().removeClass('djdt-active'); | |
18 | 18 | } else { |
19 | $('.panelContent').hide(); // Hide any that are already open | |
20 | var inner = current.find('.djDebugPanelContent .scroll'), | |
19 | $('.djdt-panelContent').hide(); // Hide any that are already open | |
20 | var inner = current.find('.djDebugPanelContent .djdt-scroll'), | |
21 | 21 | store_id = $('#djDebug').data('store-id'), |
22 | 22 | render_panel_url = $('#djDebug').data('render-panel-url'); |
23 | 23 | if (store_id !== '' && inner.children().length === 0) { |
33 | 33 | inner.prev().remove(); // Remove AJAX loader |
34 | 34 | inner.html(data); |
35 | 35 | }).fail(function(xhr){ |
36 | var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>'; | |
36 | var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href=""></a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>'; | |
37 | 37 | $('#djDebugWindow').html(message).show(); |
38 | 38 | }); |
39 | 39 | } |
40 | 40 | current.show(); |
41 | $('#djDebugToolbar li').removeClass('active'); | |
42 | $(this).parent().addClass('active'); | |
43 | } | |
44 | return false; | |
45 | }); | |
46 | $(document).on('click', '#djDebug a.djDebugClose', function() { | |
41 | $('#djDebugToolbar li').removeClass('djdt-active'); | |
42 | $(this).parent().addClass('djdt-active'); | |
43 | } | |
44 | return false; | |
45 | }); | |
46 | $('#djDebug').on('click', 'a.djDebugClose', function() { | |
47 | 47 | $(document).trigger('close.djDebug'); |
48 | $('#djDebugToolbar li').removeClass('active'); | |
49 | return false; | |
50 | }); | |
51 | $(document).on('click', '#djDebug .djDebugPanelButton input[type=checkbox]', function() { | |
48 | $('#djDebugToolbar li').removeClass('djdt-active'); | |
49 | return false; | |
50 | }); | |
51 | $('#djDebug').on('click', '.djDebugPanelButton input[type=checkbox]', function() { | |
52 | 52 | djdt.cookie.set($(this).attr('data-cookie'), $(this).prop('checked') ? 'on' : 'off', { |
53 | 53 | path: '/', |
54 | 54 | expires: 10 |
56 | 56 | }); |
57 | 57 | |
58 | 58 | // Used by the SQL and template panels |
59 | $(document).on('click', '#djDebug .remoteCall', function() { | |
59 | $('#djDebug').on('click', '.remoteCall', function() { | |
60 | 60 | var self = $(this); |
61 | 61 | var name = self[0].tagName.toLowerCase(); |
62 | 62 | var ajax_data = {}; |
78 | 78 | $.ajax(ajax_data).done(function(data){ |
79 | 79 | $('#djDebugWindow').html(data).show(); |
80 | 80 | }).fail(function(xhr){ |
81 | var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>'; | |
81 | var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href=""></a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>'; | |
82 | 82 | $('#djDebugWindow').html(message).show(); |
83 | 83 | }); |
84 | 84 | |
85 | $(document).on('click', '#djDebugWindow a.djDebugBack', function() { | |
85 | $('#djDebugWindow').on('click', 'a.djDebugBack', function() { | |
86 | 86 | $(this).parent().parent().hide(); |
87 | 87 | return false; |
88 | 88 | }); |
91 | 91 | }); |
92 | 92 | |
93 | 93 | // Used by the cache, profiling and SQL panels |
94 | $(document).on('click', '#djDebug a.djToggleSwitch', function(e) { | |
94 | $('#djDebug').on('click', 'a.djToggleSwitch', function(e) { | |
95 | 95 | e.preventDefault(); |
96 | 96 | var btn = $(this); |
97 | 97 | var id = btn.attr('data-toggle-id'); |
133 | 133 | $('#djShowToolBarButton').on('mousedown', function (event) { |
134 | 134 | var startPageY = event.pageY; |
135 | 135 | var baseY = handle.offset().top - startPageY; |
136 | var windowHeight = $(window).height(); | |
136 | 137 | $(document).on('mousemove.djDebug', function (event) { |
137 | 138 | // Chrome can send spurious mousemove events, so don't do anything unless the |
138 | 139 | // cursor really moved. Otherwise, it will be impossible to expand the toolbar |
139 | 140 | // due to djdt.handleDragged being set to true. |
140 | 141 | if (djdt.handleDragged || event.pageY != startPageY) { |
141 | var offset = handle.offset(); | |
142 | offset.top = baseY + event.pageY; | |
143 | handle.offset(offset); | |
142 | var top = baseY + event.clientY; | |
143 | ||
144 | if (top < 0) { | |
145 | top = 0; | |
146 | } else if (top + handle.height() > windowHeight) { | |
147 | top = windowHeight - handle.height(); | |
148 | } | |
149 | ||
150 | handle.css({top: top}); | |
144 | 151 | djdt.handleDragged = true; |
145 | 152 | } |
146 | 153 | }); |
149 | 156 | $(document).on('mouseup', function () { |
150 | 157 | $(document).off('mousemove.djDebug'); |
151 | 158 | if (djdt.handleDragged) { |
152 | var top = handle.offset().top; | |
159 | var top = handle.offset().top - window.pageYOffset; | |
153 | 160 | djdt.cookie.set('djdttop', top, { |
154 | 161 | path: '/', |
155 | 162 | expires: 10 |
167 | 174 | return; |
168 | 175 | } |
169 | 176 | // If a panel is open, close that |
170 | if ($('.panelContent').is(':visible')) { | |
171 | $('.panelContent').hide(); | |
172 | $('#djDebugToolbar li').removeClass('active'); | |
177 | if ($('.djdt-panelContent').is(':visible')) { | |
178 | $('.djdt-panelContent').hide(); | |
179 | $('#djDebugToolbar li').removeClass('djdt-active'); | |
173 | 180 | return; |
174 | 181 | } |
175 | 182 | // Otherwise, just minimize the toolbar |
201 | 208 | // close any sub panels |
202 | 209 | $('#djDebugWindow').hide(); |
203 | 210 | // close all panels |
204 | $('.panelContent').hide(); | |
205 | $('#djDebugToolbar li').removeClass('active'); | |
211 | $('.djdt-panelContent').hide(); | |
212 | $('#djDebugToolbar li').removeClass('djdt-active'); | |
206 | 213 | // finally close toolbar |
207 | 214 | $('#djDebugToolbar').hide('fast'); |
208 | 215 | $('#djDebugToolbarHandle').show(); |
277 | 284 | |
278 | 285 | return value; |
279 | 286 | } |
287 | }, | |
288 | applyStyle: function(name) { | |
289 | $('#djDebug [data-' + name + ']').each(function() { | |
290 | var css = {}; | |
291 | css[name] = $(this).data(name); | |
292 | $(this).css(css); | |
293 | }); | |
280 | 294 | } |
281 | 295 | }; |
296 | $.extend(publicAPI, { | |
297 | show_toolbar: djdt.show_toolbar, | |
298 | hide_toolbar: djdt.hide_toolbar, | |
299 | close: djdt.close, | |
300 | cookie: djdt.cookie, | |
301 | applyStyle: djdt.applyStyle | |
302 | }); | |
282 | 303 | $(document).ready(djdt.init); |
283 | })(djdt.jQuery); | |
304 | })(djdt.jQuery, djdt); |
3 | 3 | $(this).parent().find('.djDebugCollapsed').toggle(); |
4 | 4 | $(this).parent().find('.djDebugUncollapsed').toggle(); |
5 | 5 | }); |
6 | djdt.applyStyle('background-color'); | |
7 | djdt.applyStyle('left'); | |
8 | djdt.applyStyle('width'); | |
6 | 9 | })(djdt.jQuery); |
23 | 23 | if (endStat) { |
24 | 24 | // Render a start through end bar |
25 | 25 | $row.html('<td>' + stat.replace('Start', '') + '</td>' + |
26 | '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart" style="left:' + getLeft(stat) + '%;"><strong style="width:' + getCSSWidth(stat, endStat) + ';"> </strong></div></div></td>' + | |
26 | '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' + | |
27 | 27 | '<td>' + (perf.timing[stat] - timingOffset) + ' (+' + (perf.timing[endStat] - perf.timing[stat]) + ')</td>'); |
28 | $row.find('strong').css({width: getCSSWidth(stat, endStat)}); | |
28 | 29 | } else { |
29 | 30 | // Render a point in time |
30 | 31 | $row.html('<td>' + stat + '</td>' + |
31 | '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart" style="left:' + getLeft(stat) + '%;"><strong style="width:2px;"> </strong></div></div></td>' + | |
32 | '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' + | |
32 | 33 | '<td>' + (perf.timing[stat] - timingOffset) + '</td>'); |
34 | $row.find('strong').css({width: 2}); | |
33 | 35 | } |
36 | $row.find('djDebugLineChart').css({left: getLeft(stat) + '%'}); | |
34 | 37 | $('#djDebugBrowserTimingTableBody').append($row); |
35 | 38 | } |
36 | 39 |
0 | {% load i18n %}{% load static from staticfiles %}{% load url from future %} | |
1 | <style type="text/css"> | |
2 | @media print { #djDebug {display:none;}} | |
3 | </style> | |
0 | {% load i18n %}{% load static from staticfiles %} | |
1 | <link rel="stylesheet" href="{% static 'debug_toolbar/css/print.css' %}" type="text/css" media="print" /> | |
4 | 2 | <link rel="stylesheet" href="{% static 'debug_toolbar/css/toolbar.css' %}" type="text/css" /> |
5 | 3 | {% if toolbar.config.JQUERY_URL %} |
6 | 4 | <!-- Prevent our copy of jQuery from registering as an AMD module on sites that use RequireJS. --> |
7 | <script>var _djdt_define_backup = window.define; window.define = undefined;</script> | |
5 | <script src="{% static 'debug_toolbar/js/jquery_pre.js' %}"></script> | |
8 | 6 | <script src="{{ toolbar.config.JQUERY_URL }}"></script> |
9 | <script>var djdt = {jQuery: jQuery.noConflict(true)}; window.define = _djdt_define_backup;</script> | |
7 | <script src="{% static 'debug_toolbar/js/jquery_post.js' %}"></script> | |
10 | 8 | {% else %} |
11 | <script>var djdt = {jQuery: jQuery};</script> | |
9 | <script src="{% static 'debug_toolbar/js/jquery_existing.js' %}"></script> | |
12 | 10 | {% endif %} |
13 | 11 | <script src="{% static 'debug_toolbar/js/toolbar.js' %}"></script> |
14 | <div id="djDebug" style="display:none;" dir="ltr" | |
12 | <div id="djDebug" hidden="hidden" dir="ltr" | |
15 | 13 | data-store-id="{{ toolbar.store_id }}" data-render-panel-url="{% url 'djdt:render_panel' %}" |
16 | 14 | {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }}> |
17 | <div style="display:none;" id="djDebugToolbar"> | |
15 | <div hidden="hidden" id="djDebugToolbar"> | |
18 | 16 | <ul id="djDebugPanelList"> |
19 | 17 | {% if toolbar.panels %} |
20 | 18 | <li><a id="djHideToolBarButton" href="#" title="{% trans "Hide toolbar" %}">{% trans "Hide" %} »</a></li> |
27 | 25 | {% if panel.has_content and panel.enabled %} |
28 | 26 | <a href="#" title="{{ panel.title }}" class="{{ panel.panel_id }}"> |
29 | 27 | {% else %} |
30 | <div class="contentless{% if not panel.enabled %} disabled{% endif %}"> | |
28 | <div class="djdt-contentless{% if not panel.enabled %} djdt-disabled{% endif %}"> | |
31 | 29 | {% endif %} |
32 | 30 | {{ panel.nav_title }} |
33 | 31 | {% if panel.enabled %} |
44 | 42 | {% endfor %} |
45 | 43 | </ul> |
46 | 44 | </div> |
47 | <div style="display:none;" id="djDebugToolbarHandle"> | |
45 | <div hidden="hidden" id="djDebugToolbarHandle"> | |
48 | 46 | <span title="{% trans "Show toolbar" %}" id="djShowToolBarButton">«</span> |
49 | 47 | </div> |
50 | 48 | {% for panel in toolbar.panels %} |
51 | 49 | {% if panel.has_content and panel.enabled %} |
52 | <div id="{{ panel.panel_id }}" class="panelContent"> | |
50 | <div id="{{ panel.panel_id }}" class="djdt-panelContent"> | |
53 | 51 | <div class="djDebugPanelTitle"> |
54 | <a href="" class="djDebugClose">{% trans "Close" %}</a> | |
52 | <a href="" class="djDebugClose"></a> | |
55 | 53 | <h3>{{ panel.title|safe }}</h3> |
56 | 54 | </div> |
57 | 55 | <div class="djDebugPanelContent"> |
58 | 56 | {% if toolbar.store_id %} |
59 | <img src="{% static 'debug_toolbar/img/ajax-loader.gif' %}" alt="loading" class="loader" /> | |
60 | <div class="scroll"></div> | |
57 | <img src="{% static 'debug_toolbar/img/ajax-loader.gif' %}" alt="loading" class="djdt-loader" /> | |
58 | <div class="djdt-scroll"></div> | |
61 | 59 | {% else %} |
62 | <div class="scroll">{{ panel.content }}</div> | |
60 | <div class="djdt-scroll">{{ panel.content }}</div> | |
63 | 61 | {% endif %} |
64 | 62 | </div> |
65 | 63 | </div> |
66 | 64 | {% endif %} |
67 | 65 | {% endfor %} |
68 | <div id="djDebugWindow" class="panelContent"></div> | |
66 | <div id="djDebugWindow" class="djdt-panelContent"></div> | |
69 | 67 | </div> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | <h4>{% trans "Summary" %}</h4> |
2 | 2 | <table> |
3 | 3 | <thead> |
49 | 49 | <tbody> |
50 | 50 | {% for call in calls %} |
51 | 51 | <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}" id="cacheMain_{{ forloop.counter }}"> |
52 | <td class="toggle"> | |
53 | <a class="djToggleSwitch" data-toggle-name="cacheMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a> | |
52 | <td class="djdt-toggle"> | |
53 | <a class="djToggleSwitch" data-toggle-name="cacheMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href>+</a> | |
54 | 54 | </td> |
55 | 55 | <td>{{ call.time|floatformat:"4" }}</td> |
56 | 56 | <td>{{ call.name|escape }}</td> |
60 | 60 | </tr> |
61 | 61 | <tr class="djUnselected djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %} djToggleDetails_{{ forloop.counter }}" id="cacheDetails_{{ forloop.counter }}"> |
62 | 62 | <td colspan="1"></td> |
63 | <td colspan="5"><pre class="stack">{{ call.trace }}</pre></td> | |
63 | <td colspan="5"><pre class="djdt-stack">{{ call.trace }}</pre></td> | |
64 | 64 | </tr> |
65 | 65 | {% endfor %} |
66 | 66 | </tbody> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | |
2 | 2 | <h4>{% trans "Request headers" %}</h4> |
3 | 3 |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | {% if records %} |
2 | 2 | <table> |
3 | 3 | <thead> |
11 | 11 | </thead> |
12 | 12 | <tbody> |
13 | 13 | {% for call in func_list %} |
14 | <!-- style="background:{{ call.background }}" --> | |
15 | 14 | <tr class="djDebugProfileRow{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}" depth="{{ call.depth }}"> |
16 | 15 | <td> |
17 | <div style="padding-left: {{ call.indent }}px;"> | |
16 | <div data-padding-left="{{ call.indent }}px"> | |
18 | 17 | {% if call.has_subfuncs %} |
19 | <a class="djProfileToggleDetails djToggleSwitch" data-toggle-id="{{ call.id }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">-</a> | |
18 | <a class="djProfileToggleDetails djToggleSwitch" data-toggle-id="{{ call.id }}" data-toggle-open="+" data-toggle-close="-" href>-</a> | |
20 | 19 | {% else %} |
21 | 20 | <span class="djNoToggleSwitch"></span> |
22 | 21 | {% endif %} |
23 | <span class="stack">{{ call.func_std_string }}</span> | |
22 | <span class="djdt-stack">{{ call.func_std_string }}</span> | |
24 | 23 | </div> |
25 | 24 | </td> |
26 | 25 | <td>{{ call.cumtime|floatformat:3 }}</td> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | |
2 | 2 | <h4>{% trans "View information" %}</h4> |
3 | 3 | <table> |
23 | 23 | <h4>{% trans "Cookies" %}</h4> |
24 | 24 | <table> |
25 | 25 | <colgroup> |
26 | <col style="width:20%"/> | |
26 | <col class="djdt-width-20"/> | |
27 | 27 | <col/> |
28 | 28 | </colgroup> |
29 | 29 | <thead> |
49 | 49 | <h4>{% trans "Session data" %}</h4> |
50 | 50 | <table> |
51 | 51 | <colgroup> |
52 | <col style="width:20%"/> | |
52 | <col class="djdt-width-20"/> | |
53 | 53 | <col/> |
54 | 54 | </colgroup> |
55 | 55 | <thead> |
75 | 75 | <h4>{% trans "GET data" %}</h4> |
76 | 76 | <table> |
77 | 77 | <colgroup> |
78 | <col style="width:20%"/> | |
78 | <col class="djdt-width-20"/> | |
79 | 79 | <col/> |
80 | 80 | </colgroup> |
81 | 81 | <thead> |
101 | 101 | <h4>{% trans "POST data" %}</h4> |
102 | 102 | <table> |
103 | 103 | <colgroup> |
104 | <col style="width:20%"/> | |
104 | <col class="djdt-width-20"/> | |
105 | 105 | <col/> |
106 | </colgr | |
106 | </colgroup> | |
107 | 107 | <tr> |
108 | 108 | <th>{% trans "Variable" %}</th> |
109 | 109 | <th>{% trans "Value" %}</th> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | <table> |
2 | 2 | <thead> |
3 | 3 | <tr> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | <table> |
2 | 2 | <thead> |
3 | 3 | <tr> |
0 | {% load i18n l10n %}{% load static from staticfiles %}{% load url from future %} | |
1 | <div class="clearfix"> | |
2 | <ul class="stats"> | |
0 | {% load i18n l10n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles %} | |
1 | <div class="djdt-clearfix"> | |
2 | <ul class="djdt-stats"> | |
3 | 3 | {% for alias, info in databases %} |
4 | 4 | <li> |
5 | <strong class="label"><span style="background-color: rgb({{ info.rgb_color|join:", " }})" class="color"> </span> {{ alias }}</strong> | |
6 | <span class="info">{{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %})</span> | |
5 | <strong class="djdt-label"><span data-background-color="rgb({{ info.rgb_color|join:", " }})" class="djdt-color"> </span> {{ alias }}</strong> | |
6 | <span class="djdt-info">{{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %} | |
7 | {% if info.duplicate_count %} | |
8 | {% blocktrans with dupes=info.duplicate_count %}including {{ dupes }} duplicates{% endblocktrans %} | |
9 | {% endif %})</span> | |
7 | 10 | </li> |
8 | 11 | {% endfor %} |
9 | 12 | </ul> |
13 | 16 | <table> |
14 | 17 | <thead> |
15 | 18 | <tr> |
16 | <th class="color"> </th> | |
19 | <th class="djdt-color"> </th> | |
17 | 20 | <th class="query" colspan="2">{% trans "Query" %}</th> |
18 | 21 | <th class="timeline">{% trans "Timeline" %}</th> |
19 | <th class="time">{% trans "Time (ms)" %}</th> | |
20 | <th class="actions">{% trans "Action" %}</th> | |
22 | <th class="djdt-time">{% trans "Time (ms)" %}</th> | |
23 | <th class="djdt-actions">{% trans "Action" %}</th> | |
21 | 24 | </tr> |
22 | 25 | </thead> |
23 | 26 | <tbody> |
24 | 27 | {% for query in queries %} |
25 | 28 | <tr class="djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %}{% if query.is_slow %} djDebugRowWarning{% endif %}{% if query.starts_trans %} djDebugStartTransaction{% endif %}{% if query.ends_trans %} djDebugEndTransaction{% endif %}{% if query.in_trans %} djDebugInTransaction{% endif %}" id="sqlMain_{{ forloop.counter }}"> |
26 | <td class="color"><span style="background-color: rgb({{ query.rgb_color|join:", " }});"> </span></td> | |
27 | <td class="toggle"> | |
28 | <a class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a> | |
29 | <td class="djdt-color"><span data-background-color="rgb({{ query.rgb_color|join:", " }})"> </span></td> | |
30 | <td class="djdt-toggle"> | |
31 | <a class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href>+</a> | |
29 | 32 | </td> |
30 | 33 | <td class="query"> |
31 | 34 | <div class="djDebugSqlWrap"> |
32 | 35 | <div class="djDebugSql">{{ query.sql|safe }}</div> |
33 | 36 | </div> |
37 | {% if query.duplicate_count %} | |
38 | <strong>{% blocktrans with dupes=query.duplicate_count %}Duplicated {{ dupes }} times.{% endblocktrans %} | |
39 | </strong> | |
40 | {% endif %} | |
34 | 41 | </td> |
35 | 42 | <td class="timeline"> |
36 | <div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="left:{{ query.start_offset|unlocalize }}%;"><strong style="width:{{ query.width_ratio_relative|unlocalize }}%; background-color:{{ query.trace_color }};">{{ query.width_ratio }}%</strong></div></div> | |
43 | <div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" data-left="{{ query.start_offset|unlocalize }}%"><strong data-width="{{ query.width_ratio_relative|unlocalize }}%" data-background-color"{{ query.trace_color }}">{{ query.width_ratio }}%</strong></div></div> | |
37 | 44 | </td> |
38 | <td class="time"> | |
45 | <td class="djdt-time"> | |
39 | 46 | {{ query.duration|floatformat:"2" }} |
40 | 47 | </td> |
41 | <td class="actions"> | |
48 | <td class="djdt-actions"> | |
42 | 49 | |
43 | 50 | {% if query.params %} |
44 | 51 | {% if query.is_select %} |
68 | 75 | <p><strong>{% trans "Transaction status:" %}</strong> {{ query.trans_status }}</p> |
69 | 76 | {% endif %} |
70 | 77 | {% if query.stacktrace %} |
71 | <pre class="stack">{{ query.stacktrace }}</pre> | |
78 | <pre class="djdt-stack">{{ query.stacktrace }}</pre> | |
72 | 79 | {% endif %} |
73 | 80 | {% if query.template_info %} |
74 | 81 | <table> |
75 | 82 | {% for line in query.template_info.context %} |
76 | 83 | <tr> |
77 | 84 | <td>{{ line.num }}</td> |
78 | <td><code style="font-family: monospace;{% if line.highlight %}background-color: lightgrey{% endif %}">{{ line.content }}</code></td> | |
85 | <td><code {% if line.highlight %}class="djdt-highlighted"{% endif %}>{{ line.content }}</code></td> | |
79 | 86 | </tr> |
80 | 87 | {% endfor %} |
81 | 88 | </table> |
0 | {% load i18n %}{% load static from staticfiles %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles %} | |
1 | 1 | <div class="djDebugPanelTitle"> |
2 | <a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a> | |
2 | <a class="djDebugClose djDebugBack" href=""></a> | |
3 | 3 | <h3>{% trans "SQL explained" %}</h3> |
4 | 4 | </div> |
5 | 5 | <div class="djDebugPanelContent"> |
6 | <div class="scroll"> | |
6 | <div class="djdt-scroll"> | |
7 | 7 | <dl> |
8 | 8 | <dt>{% trans "Executed SQL" %}</dt> |
9 | 9 | <dd>{{ sql|safe }}</dd> |
0 | {% load i18n %}{% load static from staticfiles %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles %} | |
1 | 1 | <div class="djDebugPanelTitle"> |
2 | <a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a> | |
2 | <a class="djDebugClose djDebugBack" href=""></a> | |
3 | 3 | <h3>{% trans "SQL profiled" %}</h3> |
4 | 4 | </div> |
5 | 5 | <div class="djDebugPanelContent"> |
6 | <div class="scroll"> | |
6 | <div class="djdt-scroll"> | |
7 | 7 | {% if result %} |
8 | 8 | <dl> |
9 | 9 | <dt>{% trans "Executed SQL" %}</dt> |
0 | {% load i18n %}{% load static from staticfiles %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles %} | |
1 | 1 | <div class="djDebugPanelTitle"> |
2 | <a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a> | |
2 | <a class="djDebugClose djDebugBack" href=""></a> | |
3 | 3 | <h3>{% trans "SQL selected" %}</h3> |
4 | 4 | </div> |
5 | 5 | <div class="djDebugPanelContent"> |
6 | <div class="scroll"> | |
6 | <div class="djdt-scroll"> | |
7 | 7 | <dl> |
8 | 8 | <dt>{% trans "Executed SQL" %}</dt> |
9 | 9 | <dd>{{ sql|safe }}</dd> |
0 | {% load i18n %} | |
1 | {% load static from staticfiles%} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles%} | |
2 | 1 | |
3 | 2 | <h4>{% blocktrans count staticfiles_dirs|length as dirs_count %}Static file path{% plural %}Static file paths{% endblocktrans %}</h4> |
4 | 3 | {% if staticfiles_dirs %} |
0 | 0 | {% load i18n %} |
1 | 1 | <div class="djDebugPanelTitle"> |
2 | <a class="djDebugClose djDebugBack" href="">{% trans "Back" %}</a> | |
2 | <a class="djDebugClose djDebugBack" href=""></a> | |
3 | 3 | <h3>{% trans "Template source:" %} <code>{{ template_name }}</code></h3> |
4 | 4 | </div> |
5 | 5 | <div class="djDebugPanelContent"> |
6 | <div class="scroll"> | |
6 | <div class="djdt-scroll"> | |
7 | 7 | {% if not source.pygmentized %} |
8 | 8 | <code>{{ source }}</code> |
9 | 9 | {% else %} |
0 | {% load i18n %}{% load static from staticfiles %}{% load url from future %} | |
0 | {% load i18n %}{% load static from staticfiles %} | |
1 | 1 | <h4>{% blocktrans count template_dirs|length as template_count %}Template path{% plural %}Template paths{% endblocktrans %}</h4> |
2 | 2 | {% if template_dirs %} |
3 | 3 | <ol> |
18 | 18 | {% if template.context %} |
19 | 19 | <dd> |
20 | 20 | <div class="djTemplateShowContextDiv"><a class="djTemplateShowContext"><span class="toggleArrow">▶</span> {% trans "Toggle context" %}</a></div> |
21 | <div class="djTemplateHideContextDiv" style="display:none;"><code>{{ template.context }}</code></div> | |
21 | <div class="djTemplateHideContextDiv" hidden="hidden"><code>{{ template.context }}</code></div> | |
22 | 22 | </dd> |
23 | 23 | {% endif %} |
24 | 24 | {% endfor %} |
34 | 34 | <dt><strong>{{ key|escape }}</strong></dt> |
35 | 35 | <dd> |
36 | 36 | <div class="djTemplateShowContextDiv"><a class="djTemplateShowContext"><span class="toggleArrow">▶</span> {% trans "Toggle context" %}</a></div> |
37 | <div class="djTemplateHideContextDiv" style="display:none;"><code>{{ value|escape }}</code></div> | |
37 | <div class="djTemplateHideContextDiv" hidden="hidden"><code>{{ value|escape }}</code></div> | |
38 | 38 | </dd> |
39 | 39 | {% endfor %} |
40 | 40 | </dl> |
0 | {% load i18n %}{% load static from staticfiles %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %}{% load static from staticfiles %} | |
1 | 1 | <h4>{% trans "Resource usage" %}</h4> |
2 | 2 | <table> |
3 | 3 | <colgroup> |
4 | <col style="width:20%"/> | |
4 | <col class="djdt-width-20"/> | |
5 | 5 | <col/> |
6 | 6 | </colgroup> |
7 | 7 | <thead> |
21 | 21 | </table> |
22 | 22 | |
23 | 23 | <!-- This hidden div is populated and displayed by code in toolbar.timer.js --> |
24 | <div id="djDebugBrowserTiming" style="display:none"> | |
24 | <div id="djDebugBrowserTiming" hidden="hidden"> | |
25 | 25 | <h4>{% trans "Browser timing" %}</h4> |
26 | 26 | <table> |
27 | 27 | <colgroup> |
28 | <col style="width:20%"/> | |
29 | <col style="width:60%"/> | |
30 | <col style="width:20%"/> | |
28 | <col class="djdt-width-20"/> | |
29 | <col class="djdt-width-60"/> | |
30 | <col class="djdt-width-20"/> | |
31 | 31 | </colgroup> |
32 | 32 | <thead> |
33 | 33 | <tr> |
34 | 34 | <th>{% trans "Timing attribute" %}</th> |
35 | 35 | <th class="timeline">{% trans "Timeline" %}</th> |
36 | <th class="time">{% trans "Milliseconds since navigation start (+length)" %}</th> | |
36 | <th class="djdt-time">{% trans "Milliseconds since navigation start (+length)" %}</th> | |
37 | 37 | </tr> |
38 | 38 | </thead> |
39 | 39 | <tbody id="djDebugBrowserTimingTableBody"> |
0 | {% load i18n %} | |
0 | {% load i18n %}{% load cycle from debug_toolbar_compat %} | |
1 | 1 | <table> |
2 | 2 | <thead> |
3 | 3 | <tr> |
0 | import django | |
1 | from django.template import Library | |
2 | ||
3 | if django.VERSION >= (1, 8): | |
4 | from django.template.defaulttags import cycle | |
5 | else: | |
6 | from django.templatetags.future import cycle | |
7 | ||
8 | ||
9 | register = Library() | |
10 | ||
11 | ||
12 | cycle = register.tag(cycle) |
4 | 4 | from __future__ import absolute_import, unicode_literals |
5 | 5 | |
6 | 6 | import uuid |
7 | from collections import OrderedDict | |
8 | from importlib import import_module | |
7 | 9 | |
8 | import django | |
9 | from django.conf import settings | |
10 | from django.conf.urls import patterns, url | |
10 | from django.apps import apps | |
11 | from django.conf.urls import url | |
11 | 12 | from django.core.exceptions import ImproperlyConfigured |
12 | 13 | from django.template import TemplateSyntaxError |
13 | 14 | from django.template.loader import render_to_string |
14 | from django.utils.importlib import import_module | |
15 | try: | |
16 | from collections import OrderedDict | |
17 | except ImportError: | |
18 | from django.utils.datastructures import SortedDict as OrderedDict | |
19 | 15 | |
20 | 16 | from debug_toolbar import settings as dt_settings |
21 | 17 | |
66 | 62 | context = {'toolbar': self} |
67 | 63 | return render_to_string('debug_toolbar/base.html', context) |
68 | 64 | except TemplateSyntaxError: |
69 | if django.VERSION[:2] >= (1, 7): | |
70 | from django.apps import apps | |
71 | staticfiles_installed = apps.is_installed( | |
72 | 'django.contrib.staticfiles') | |
73 | else: | |
74 | staticfiles_installed = ('django.contrib.staticfiles' | |
75 | in settings.INSTALLED_APPS) | |
76 | if not staticfiles_installed: | |
65 | if not apps.is_installed('django.contrib.staticfiles'): | |
77 | 66 | raise ImproperlyConfigured( |
78 | 67 | "The debug toolbar requires the staticfiles contrib app. " |
79 | 68 | "Add 'django.contrib.staticfiles' to INSTALLED_APPS and " |
88 | 77 | def should_render_panels(self): |
89 | 78 | render_panels = self.config['RENDER_PANELS'] |
90 | 79 | if render_panels is None: |
91 | # Django 1.4 still supports mod_python :( Fall back to the safe | |
92 | # and inefficient default in that case. Revert when we drop 1.4. | |
93 | render_panels = self.request.META.get('wsgi.multiprocess', True) | |
80 | render_panels = self.request.META['wsgi.multiprocess'] | |
94 | 81 | return render_panels |
95 | 82 | |
96 | 83 | def store(self): |
97 | 84 | self.store_id = uuid.uuid4().hex |
98 | 85 | cls = type(self) |
99 | 86 | cls._store[self.store_id] = self |
100 | for _ in range(len(cls._store) - self.config['RESULTS_STORE_SIZE']): | |
87 | for _ in range(len(cls._store) - self.config['RESULTS_CACHE_SIZE']): | |
101 | 88 | try: |
102 | 89 | # collections.OrderedDict |
103 | 90 | cls._store.popitem(last=False) |
147 | 134 | @classmethod |
148 | 135 | def get_urls(cls): |
149 | 136 | if cls._urlpatterns is None: |
137 | from . import views | |
150 | 138 | # Load URLs in a temporary variable for thread safety. |
151 | 139 | # Global URLs |
152 | urlpatterns = patterns('debug_toolbar.views', # noqa | |
153 | url(r'^render_panel/$', 'render_panel', name='render_panel'), | |
154 | ) | |
140 | urlpatterns = [ | |
141 | url(r'^render_panel/$', views.render_panel, name='render_panel'), | |
142 | ] | |
155 | 143 | # Per-panel URLs |
156 | 144 | for panel_class in cls.get_panel_classes(): |
157 | 145 | urlpatterns += panel_class.get_urls() |
3 | 3 | import os.path |
4 | 4 | import re |
5 | 5 | import sys |
6 | from importlib import import_module | |
7 | ||
8 | import django | |
9 | from django.core.exceptions import ImproperlyConfigured | |
10 | from django.template import Node | |
11 | from django.utils import six | |
12 | from django.utils.encoding import force_text | |
13 | from django.utils.html import escape | |
14 | from django.utils.safestring import mark_safe | |
15 | ||
16 | from debug_toolbar.compat import linebreak_iter | |
17 | ||
18 | from .settings import CONFIG | |
19 | ||
6 | 20 | try: |
7 | 21 | import threading |
8 | 22 | except ImportError: |
9 | 23 | threading = None |
10 | 24 | |
11 | import django | |
12 | from django.core.exceptions import ImproperlyConfigured | |
13 | from django.utils.encoding import force_text | |
14 | from django.utils.html import escape | |
15 | from django.utils.importlib import import_module | |
16 | from django.utils.safestring import mark_safe | |
17 | from django.utils import six | |
18 | from django.views.debug import linebreak_iter | |
19 | ||
20 | from .settings import CONFIG | |
21 | 25 | |
22 | 26 | # Figure out some paths |
23 | 27 | django_path = os.path.realpath(os.path.dirname(django.__file__)) |
70 | 74 | params = map(escape, frame[0].rsplit(os.path.sep, 1) + list(frame[1:])) |
71 | 75 | params_dict = dict((six.text_type(idx), v) for idx, v in enumerate(params)) |
72 | 76 | try: |
73 | stacktrace.append('<span class="path">%(0)s/</span>' | |
74 | '<span class="file">%(1)s</span>' | |
75 | ' in <span class="func">%(3)s</span>' | |
76 | '(<span class="lineno">%(2)s</span>)\n' | |
77 | ' <span class="code">%(4)s</span>' | |
77 | stacktrace.append('<span class="djdt-path">%(0)s/</span>' | |
78 | '<span class="djdt-file">%(1)s</span>' | |
79 | ' in <span class="djdt-func">%(3)s</span>' | |
80 | '(<span class="djdt-lineno">%(2)s</span>)\n' | |
81 | ' <span class="djdt-code">%(4)s</span>' | |
78 | 82 | % params_dict) |
79 | 83 | except KeyError: |
80 | 84 | # This frame doesn't have the expected format, so skip it and move on to the next one |
82 | 86 | return mark_safe('\n'.join(stacktrace)) |
83 | 87 | |
84 | 88 | |
85 | def get_template_info(source, context_lines=3): | |
89 | def get_template_info(): | |
90 | template_info = None | |
91 | cur_frame = sys._getframe().f_back | |
92 | try: | |
93 | while cur_frame is not None: | |
94 | in_utils_module = cur_frame.f_code.co_filename.endswith( | |
95 | "/debug_toolbar/utils.py" | |
96 | ) | |
97 | is_get_template_context = ( | |
98 | cur_frame.f_code.co_name == get_template_context.__name__ | |
99 | ) | |
100 | if in_utils_module and is_get_template_context: | |
101 | # If the method in the stack trace is this one | |
102 | # then break from the loop as it's being check recursively. | |
103 | break | |
104 | elif cur_frame.f_code.co_name == 'render': | |
105 | node = cur_frame.f_locals['self'] | |
106 | if isinstance(node, Node): | |
107 | template_info = get_template_context(node.source) | |
108 | break | |
109 | cur_frame = cur_frame.f_back | |
110 | except Exception: | |
111 | pass | |
112 | del cur_frame | |
113 | return template_info | |
114 | ||
115 | ||
116 | def get_template_context(source, context_lines=3): | |
86 | 117 | line = 0 |
87 | 118 | upto = 0 |
88 | 119 | source_lines = [] |
0 | 0 | Change log |
1 | 1 | ========== |
2 | ||
3 | 1.4 | |
4 | --- | |
5 | ||
6 | This version is compatible with the upcoming Django 1.9 release. It requires | |
7 | Django 1.7 or later. | |
8 | ||
9 | New features | |
10 | ~~~~~~~~~~~~ | |
11 | ||
12 | * New panel method :meth:`debug_toolbar.panels.Panel.generate_stats` allows panels | |
13 | to only record stats when the toolbar is going to be inserted into the | |
14 | response. | |
15 | ||
16 | Bugfixes | |
17 | ~~~~~~~~ | |
18 | ||
19 | * Response time for requests of projects with numerous media files has | |
20 | been improved. | |
21 | ||
22 | 1.3 | |
23 | --- | |
24 | ||
25 | This is the first version compatible with Django 1.8. | |
26 | ||
27 | New features | |
28 | ~~~~~~~~~~~~ | |
29 | ||
30 | * A new panel is available: Template Profiler. | |
31 | * The ``SHOW_TOOLBAR_CALLBACK`` accepts a callable. | |
32 | * The toolbar now provides a :ref:`javascript-api`. | |
33 | ||
34 | Bugfixes | |
35 | ~~~~~~~~ | |
36 | ||
37 | * The toolbar handle cannot leave the visible area anymore when the toolbar is | |
38 | collapsed. | |
39 | * The root level logger is preserved. | |
40 | * The ``RESULTS_CACHE_SIZE`` setting is taken into account. | |
41 | * CSS classes are prefixed with ``djdt-`` to prevent name conflicts. | |
42 | * The private copy of jQuery no longer registers as an AMD module on sites | |
43 | that load RequireJS. | |
2 | 44 | |
3 | 45 | 1.2 |
4 | 46 | --- |
11 | 11 | # All configuration values have a default; values that are commented out |
12 | 12 | # serve to show the default. |
13 | 13 | |
14 | import datetime | |
14 | 15 | import sys |
15 | 16 | import os |
16 | 17 | |
49 | 50 | |
50 | 51 | # General information about the project. |
51 | 52 | project = u'Django Debug Toolbar' |
52 | copyright = u'2013, Django Debug Toolbar developers and contributors' | |
53 | copyright = u'{}, Django Debug Toolbar developers and contributors' | |
54 | copyright = copyright.format(datetime.date.today().year) | |
53 | 55 | |
54 | 56 | # The version info for the project you're documenting, acts as replacement for |
55 | 57 | # |version| and |release|, also used in various other places throughout the |
56 | 58 | # built documents. |
57 | 59 | # |
58 | 60 | # The short X.Y version. |
59 | version = '1.2' | |
61 | version = '1.4' | |
60 | 62 | # The full version, including alpha/beta/rc tags. |
61 | release = '1.2.1' | |
63 | release = '1.4' | |
62 | 64 | |
63 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation |
64 | 66 | # for a list of supported languages. |
72 | 72 | |
73 | 73 | * ``JQUERY_URL`` |
74 | 74 | |
75 | Default: ``'//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js'`` | |
75 | Default: ``'//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'`` | |
76 | 76 | |
77 | 77 | URL of the copy of jQuery that will be used by the toolbar. Set it to a |
78 | 78 | locally-hosted version of jQuery for offline development. Make it empty to |
92 | 92 | right thing depending on whether the WSGI container runs multiple processes. |
93 | 93 | This setting allows you to force a different behavior if needed. |
94 | 94 | |
95 | * ``RESULTS_STORE_SIZE`` | |
95 | * ``RESULTS_CACHE_SIZE`` | |
96 | 96 | |
97 | 97 | Default: ``10`` |
98 | 98 | |
120 | 120 | This is the dotted path to a function used for determining whether the |
121 | 121 | toolbar should show or not. The default checks are that ``DEBUG`` must be |
122 | 122 | set to ``True``, the IP of the request must be in ``INTERNAL_IPS``, and the |
123 | request must no be an AJAX request. You can provide your own function | |
123 | request must not be an AJAX request. You can provide your own function | |
124 | 124 | ``callback(request)`` which returns ``True`` or ``False``. |
125 | 125 | |
126 | 126 | Panel options |
190 | 190 | # This example is unlikely to be appropriate for your project. |
191 | 191 | CONFIG_DEFAULTS = { |
192 | 192 | # Toolbar options |
193 | 'RESULTS_STORE_SIZE': 3, | |
193 | 'RESULTS_CACHE_SIZE': 3, | |
194 | 194 | 'SHOW_COLLAPSED': True, |
195 | 195 | # Panel options |
196 | 196 | 'SQL_WARNING_THRESHOLD': 100, # milliseconds |
23 | 23 | |
24 | 24 | You can run now run the example application:: |
25 | 25 | |
26 | $ DJANGO_SETTINGS_MODULE=example.settings django-admin.py syncdb | |
27 | $ DJANGO_SETTINGS_MODULE=example.settings django-admin.py runserver | |
26 | $ DJANGO_SETTINGS_MODULE=example.settings django-admin migrate | |
27 | $ DJANGO_SETTINGS_MODULE=example.settings django-admin runserver | |
28 | 28 | |
29 | 29 | For convenience, there's an alias for the second command:: |
30 | 30 | |
75 | 75 | |
76 | 76 | $ make flake8 |
77 | 77 | |
78 | Import style is enforce by isort. You can sort import automatically with:: | |
79 | ||
80 | $ make isort | |
81 | ||
78 | 82 | Patches |
79 | 83 | ------- |
80 | 84 |
22 | 22 | |
23 | 23 | Make sure that ``'django.contrib.staticfiles'`` is `set up properly |
24 | 24 | <https://docs.djangoproject.com/en/stable/howto/static-files/>`_ and add |
25 | ``'debug_toolbar.apps.DebugToolbarConfig'`` (Django ≥ 1.7) or | |
26 | ``'debug_toolbar'`` (Django < 1.7) to your ``INSTALLED_APPS`` setting:: | |
25 | ``'debug_toolbar'`` to your ``INSTALLED_APPS`` setting:: | |
27 | 26 | |
28 | 27 | INSTALLED_APPS = ( |
29 | 28 | # ... |
30 | 29 | 'django.contrib.staticfiles', |
31 | 30 | # ... |
32 | # If you're using Django 1.7.x or later | |
33 | 'debug_toolbar.apps.DebugToolbarConfig', | |
34 | # If you're using Django 1.6.x or earlier | |
35 | 31 | 'debug_toolbar', |
36 | 32 | ) |
37 | 33 | |
63 | 59 | crashes with a long stack trace after hitting an :exc:`ImportError` or an |
64 | 60 | :exc:`~django.core.exceptions.ImproperlyConfigured` exception, follow the |
65 | 61 | explicit setup instructions. |
62 | ||
63 | When the automatic setup is used, the Debug Toolbar is not compatible with | |
64 | :class:`~django.middleware.gzip.GZipMiddleware`. Please disable that | |
65 | middleware during development or use the explicit setup to allow the | |
66 | toolbar to function properly. | |
66 | 67 | |
67 | 68 | Explicit setup |
68 | 69 | -------------- |
108 | 109 | The order of ``MIDDLEWARE_CLASSES`` is important. You should include the Debug |
109 | 110 | Toolbar middleware as early as possible in the list. However, it must come |
110 | 111 | after any other middleware that encodes the response's content, such as |
111 | ``GZipMiddleware``. | |
112 | :class:`~django.middleware.gzip.GZipMiddleware`. | |
112 | 113 | |
113 | 114 | If ``MIDDLEWARE_CLASSES`` doesn't contain the middleware, the Debug Toolbar |
114 | 115 | automatically adds it the beginning of the list. |
74 | 74 | |
75 | 75 | Path: ``debug_toolbar.panels.cache.CachePanel`` |
76 | 76 | |
77 | Cache queries. | |
77 | Cache queries. Is incompatible with Django's per-site caching. | |
78 | 78 | |
79 | 79 | Signal |
80 | 80 | ~~~~~~ |
104 | 104 | panel is included but inactive by default. You can activate it by default with |
105 | 105 | the ``DISABLE_PANELS`` configuration option. |
106 | 106 | |
107 | ||
108 | 107 | Non-default built-in panels |
109 | 108 | --------------------------- |
110 | 109 | |
111 | 110 | The following panels are disabled by default. You must add them to the |
112 | 111 | ``DEBUG_TOOLBAR_PANELS`` setting to enable them. |
113 | 112 | |
113 | .. _profiling-panel: | |
114 | ||
114 | 115 | Profiling |
115 | 116 | ~~~~~~~~~ |
116 | 117 | |
117 | 118 | Path: ``debug_toolbar.panels.profiling.ProfilingPanel`` |
118 | 119 | |
119 | Profiling information for the view function. | |
120 | Profiling information for the processing of the request. | |
121 | ||
122 | If the ``debug_toolbar.middleware.DebugToolbarMiddleware`` is first in | |
123 | ``MIDDLEWARE_CLASSES`` then the other middlewares' ``process_view`` methods | |
124 | will not be executed. This is because ``ProfilingPanel.process_view`` will | |
125 | return a ``HttpResponse`` which causes the other middlewares' | |
126 | ``process_view`` methods to be skipped. | |
127 | ||
128 | Note that the quick setup creates this situation, as it inserts | |
129 | ``DebugToolbarMiddleware`` first in ``MIDDLEWARE_CLASSES``. | |
130 | ||
131 | If you run into this issues, then you should either disable the | |
132 | ``ProfilingPanel`` or move ``DebugToolbarMiddleware`` to the end of | |
133 | ``MIDDLEWARE_CLASSES``. If you do the latter, then the debug toolbar won't | |
134 | track the execution of other middleware. | |
120 | 135 | |
121 | 136 | Third-party panels |
122 | 137 | ------------------ |
200 | 215 | Path: ``neo4j_panel.Neo4jPanel`` |
201 | 216 | |
202 | 217 | Trace neo4j rest API calls in your django application, this also works for neo4django and neo4jrestclient, support for py2neo is on its way. |
218 | ||
219 | Pympler | |
220 | ~~~~~~~ | |
221 | ||
222 | URL: https://pythonhosted.org/Pympler/django.html | |
223 | ||
224 | Path: ``pympler.panels.MemoryPanel`` | |
225 | ||
226 | Shows process memory information (virtual size, resident set size) and model instances for the current request. | |
227 | ||
228 | Request History | |
229 | ~~~~~~~~~~~~~~~ | |
230 | ||
231 | URL: https://github.com/djsutho/django-debug-toolbar-request-history | |
232 | ||
233 | Path: ``ddt_request_history.panels.request_history.RequestHistoryPanel`` | |
234 | ||
235 | Switch between requests to view their stats. Also adds support for viewing stats for ajax requests. | |
203 | 236 | |
204 | 237 | Sites |
205 | 238 | ~~~~~ |
213 | 246 | <https://bitbucket.org/uysrc/django-dynamicsites/src>`_ which sets SITE_ID |
214 | 247 | dynamically. |
215 | 248 | |
249 | Template Profiler | |
250 | ~~~~~~~~~~~~~~~~~ | |
251 | ||
252 | URL: https://github.com/node13h/django-debug-toolbar-template-profiler | |
253 | ||
254 | Path: ``template_profiler_panel.panels.template.TemplateProfilerPanel`` | |
255 | ||
256 | Shows template render call duration and distribution on the timeline. Lightweight. | |
257 | Compatible with WSGI servers which reuse threads for multiple requests (Werkzeug). | |
258 | ||
216 | 259 | Template Timings |
217 | 260 | ~~~~~~~~~~~~~~~~ |
218 | 261 | |
238 | 281 | according to the public API described below. Unless noted otherwise, all |
239 | 282 | methods are optional. |
240 | 283 | |
241 | Panels can ship their own templates, static files and views. They're no public | |
242 | CSS or JavaScript API at this time, but they can assume jQuery is available in | |
243 | ``djdt.jQuery``. | |
284 | Panels can ship their own templates, static files and views. There is no public | |
285 | CSS API at this time. | |
244 | 286 | |
245 | 287 | .. autoclass:: debug_toolbar.panels.Panel(*args, **kwargs) |
246 | 288 | |
271 | 313 | .. automethod:: debug_toolbar.panels.Panel.process_view |
272 | 314 | |
273 | 315 | .. automethod:: debug_toolbar.panels.Panel.process_response |
316 | ||
317 | .. automethod:: debug_toolbar.panels.Panel.generate_stats | |
318 | ||
319 | .. _javascript-api: | |
320 | ||
321 | JavaScript API | |
322 | ~~~~~~~~~~~~~~ | |
323 | ||
324 | Panel templates should include any JavaScript files they need. There are a few | |
325 | common methods available, as well as the toolbar's version of jQuery. | |
326 | ||
327 | .. js:function:: djdt.close | |
328 | ||
329 | Triggers the event to close any active panels. | |
330 | ||
331 | .. js:function:: djdt.cookie.get | |
332 | ||
333 | This is a helper function to fetch values stored in the cookies. | |
334 | ||
335 | :param string key: The key for the value to be fetched. | |
336 | ||
337 | .. js:function:: djdt.cookie.set | |
338 | ||
339 | This is a helper function to set a value stored in the cookies. | |
340 | ||
341 | :param string key: The key to be used. | |
342 | ||
343 | :param string value: The value to be set. | |
344 | ||
345 | :param Object options: The options for the value to be set. It should contain | |
346 | the properties ``expires`` and ``path``. | |
347 | ||
348 | .. js:function:: djdt.hide_toolbar | |
349 | ||
350 | Closes any panels and hides the toolbar. | |
351 | ||
352 | .. js:function:: djdt.jQuery | |
353 | ||
354 | This is the toolbar's version of jQuery. | |
355 | ||
356 | .. js:function:: djdt.show_toolbar | |
357 | ||
358 | Shows the toolbar. |
3 | 3 | The toolbar isn't displayed! |
4 | 4 | ---------------------------- |
5 | 5 | |
6 | The Debug Toolbar will only display itself if the mimetype of the response is | |
6 | The Debug Toolbar will only display when ``DEBUG = True`` in your project's | |
7 | settings. It will also only display if the mimetype of the response is | |
7 | 8 | either ``text/html`` or ``application/xhtml+xml`` and contains a closing |
8 | 9 | ``</body>`` tag. |
9 | 10 | |
11 | 12 | requests and return responses. Putting the debug toolbar middleware *after* |
12 | 13 | the Flatpage middleware, for example, means the toolbar will not show up on |
13 | 14 | flatpages. |
15 | ||
16 | Middleware isn't working correctly | |
17 | ---------------------------------- | |
18 | ||
19 | Using the Debug Toolbar in its default configuration and with the profiling | |
20 | panel will cause middlewares after | |
21 | ``debug_toolbar.middleware.DebugToolbarMiddleware`` to not execute their | |
22 | ``process_view`` functions. This can be resolved by disabling the profiling | |
23 | panel or moving the ``DebugToolbarMiddleware`` to the end of | |
24 | ``MIDDLEWARE_CLASSES``. Read more about it at | |
25 | :ref:`ProfilingPanel <profiling-panel>` | |
14 | 26 | |
15 | 27 | Using the toolbar offline |
16 | 28 | ------------------------- |
71 | 83 | By default, data gathered during the last 10 requests is kept in memory. This |
72 | 84 | allows you to use the toolbar on a page even if you have browsed to a few |
73 | 85 | other pages since you first loaded that page. You can reduce memory |
74 | consumption by setting the ``PANELS_CACHE_SIZE`` configuration option to a | |
86 | consumption by setting the ``RESULTS_CACHE_SIZE`` configuration option to a | |
75 | 87 | lower value. At worst, the toolbar will tell you that the data you're looking |
76 | 88 | for isn't available anymore. |
77 | 89 |
4 | 4 | ----- |
5 | 5 | |
6 | 6 | This sample project demonstrates how to use the debug toolbar. It is designed |
7 | to run under the latest stable version of Django, currently 1.5.x. | |
7 | to run under the latest stable version of Django. | |
8 | 8 | |
9 | 9 | It also provides a few test pages to ensure the debug toolbar doesn't |
10 | 10 | interfere with common JavaScript frameworks. |
21 | 21 | |
22 | 22 | Before running the example for the first time, you must create a database:: |
23 | 23 | |
24 | $ PYTHONPATH=. django-admin.py syncdb --settings=example.settings | |
24 | $ PYTHONPATH=. django-admin syncdb --settings=example.settings | |
25 | 25 | |
26 | 26 | Then you can use the following command to run the example:: |
27 | 27 | |
28 | $ PYTHONPATH=. django-admin.py runserver --settings=example.settings | |
28 | $ PYTHONPATH=. django-admin runserver --settings=example.settings |
Binary diff not shown
0 | 0 | """Django settings for example project.""" |
1 | 1 | |
2 | 2 | import os |
3 | ||
3 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) |
4 | 5 | |
5 | 6 | |
22 | 23 | 'django.contrib.messages', |
23 | 24 | 'django.contrib.staticfiles', |
24 | 25 | 'debug_toolbar', |
26 | ) | |
27 | ||
28 | MIDDLEWARE_CLASSES = ( | |
29 | 'django.middleware.common.CommonMiddleware', | |
30 | 'django.contrib.sessions.middleware.SessionMiddleware', | |
31 | 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
32 | 'django.contrib.messages.middleware.MessageMiddleware', | |
33 | 'django.middleware.csrf.CsrfViewMiddleware', | |
25 | 34 | ) |
26 | 35 | |
27 | 36 | ROOT_URLCONF = 'example.urls' |
0 | from django.conf.urls import patterns, include | |
0 | from django.conf.urls import include, url | |
1 | 1 | from django.contrib import admin |
2 | 2 | from django.views.generic import TemplateView |
3 | 3 | |
4 | admin.autodiscover() | |
5 | ||
6 | urlpatterns = patterns('', # noqa | |
7 | (r'^$', TemplateView.as_view(template_name='index.html')), | |
8 | (r'^jquery/$', TemplateView.as_view(template_name='jquery/index.html')), | |
9 | (r'^mootools/$', TemplateView.as_view(template_name='mootools/index.html')), | |
10 | (r'^prototype/$', TemplateView.as_view(template_name='prototype/index.html')), | |
11 | (r'^admin/', include(admin.site.urls)), | |
12 | ) | |
4 | urlpatterns = [ | |
5 | url(r'^$', TemplateView.as_view(template_name='index.html')), | |
6 | url(r'^jquery/$', TemplateView.as_view(template_name='jquery/index.html')), | |
7 | url(r'^mootools/$', TemplateView.as_view(template_name='mootools/index.html')), | |
8 | url(r'^prototype/$', TemplateView.as_view(template_name='prototype/index.html')), | |
9 | url(r'^admin/', include(admin.site.urls)), | |
10 | ] |
0 | 0 | """WSGI config for example project.""" |
1 | 1 | |
2 | 2 | import os |
3 | ||
4 | from django.core.wsgi import get_wsgi_application | |
5 | ||
3 | 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") |
4 | 7 | |
5 | from django.core.wsgi import get_wsgi_application | |
6 | 8 | application = get_wsgi_application() |
9 | 9 | # Testing |
10 | 10 | |
11 | 11 | coverage |
12 | django-discover-runner | |
12 | isort | |
13 | 13 | flake8 |
14 | 14 | selenium |
15 | 15 | tox |
4 | 4 | ignore = W601 ; # noqa doesn't silence this one |
5 | 5 | max-line-length = 100 |
6 | 6 | |
7 | [isort] | |
8 | combine_as_imports = true | |
9 | default_section = THIRDPARTY | |
10 | include_trailing_comma = true | |
11 | known_first_party = debug_toolbar | |
12 | multi_line_output = 5 | |
13 | not_skip = __init__.py | |
14 | ||
7 | 15 | [wheel] |
8 | 16 | universal = 1 |
0 | from setuptools import setup, find_packages | |
1 | 0 | from io import open |
1 | ||
2 | from setuptools import find_packages, setup | |
2 | 3 | |
3 | 4 | setup( |
4 | 5 | name='django-debug-toolbar', |
5 | version='1.2.1', | |
6 | version='1.4', | |
6 | 7 | description='A configurable set of panels that display various debug ' |
7 | 8 | 'information about the current request/response.', |
8 | 9 | long_description=open('README.rst', encoding='utf-8').read(), |
11 | 12 | url='https://github.com/django-debug-toolbar/django-debug-toolbar', |
12 | 13 | download_url='https://pypi.python.org/pypi/django-debug-toolbar', |
13 | 14 | license='BSD', |
14 | packages=find_packages(exclude=('tests', 'example')), | |
15 | packages=find_packages(exclude=('tests.*', 'tests', 'example')), | |
15 | 16 | install_requires=[ |
16 | 'django>=1.4.2', | |
17 | 'Django>=1.7', | |
17 | 18 | 'sqlparse', |
18 | 19 | ], |
19 | 20 | include_package_data=True, |
27 | 28 | 'Operating System :: OS Independent', |
28 | 29 | 'Programming Language :: Python', |
29 | 30 | 'Programming Language :: Python :: 2', |
30 | 'Programming Language :: Python :: 2.6', | |
31 | 31 | 'Programming Language :: Python :: 2.7', |
32 | 32 | 'Programming Language :: Python :: 3', |
33 | 33 | 'Programming Language :: Python :: 3.2', |
34 | 34 | 'Programming Language :: Python :: 3.3', |
35 | 35 | 'Programming Language :: Python :: 3.4', |
36 | 'Programming Language :: Python :: 3.5', | |
36 | 37 | 'Topic :: Software Development :: Libraries :: Python Modules', |
37 | 38 | ], |
38 | 39 | ) |
0 | 0 | # Refresh the debug toolbar's configuration when overriding settings. |
1 | ||
2 | from debug_toolbar.toolbar import DebugToolbar | |
3 | from debug_toolbar import settings as dt_settings | |
4 | 1 | |
5 | 2 | from django.dispatch import receiver |
6 | 3 | from django.test.signals import setting_changed |
4 | ||
5 | from debug_toolbar import settings as dt_settings | |
6 | from debug_toolbar.toolbar import DebugToolbar | |
7 | 7 | |
8 | 8 | |
9 | 9 | @receiver(setting_changed) |
2 | 2 | import threading |
3 | 3 | |
4 | 4 | from django.http import HttpResponse |
5 | from django.test import TestCase, RequestFactory | |
5 | from django.test import RequestFactory, TestCase | |
6 | 6 | |
7 | 7 | from debug_toolbar.middleware import DebugToolbarMiddleware |
8 | 8 | from debug_toolbar.toolbar import DebugToolbar |
3 | 3 | |
4 | 4 | from django.contrib.auth.models import User |
5 | 5 | from django.core import management |
6 | try: | |
7 | from django.db.backends import utils | |
8 | except ImportError: | |
9 | from django.db.backends import util as utils | |
6 | from django.db.backends import utils as db_backends_utils | |
10 | 7 | from django.test import TestCase |
11 | 8 | from django.test.utils import override_settings |
12 | 9 | from django.utils import six |
16 | 13 | class DebugSQLShellTestCase(TestCase): |
17 | 14 | |
18 | 15 | def setUp(self): |
19 | self.original_cursor_wrapper = utils.CursorDebugWrapper | |
16 | self.original_cursor_wrapper = db_backends_utils.CursorDebugWrapper | |
20 | 17 | # Since debugsqlshell monkey-patches django.db.backends.utils, we can |
21 | 18 | # test it simply by loading it, without executing it. But we have to |
22 | 19 | # undo the monkey-patch on exit. |
25 | 22 | management.load_command_class(app_name, command_name) |
26 | 23 | |
27 | 24 | def tearDown(self): |
28 | utils.CursorDebugWrapper = self.original_cursor_wrapper | |
25 | db_backends_utils.CursorDebugWrapper = self.original_cursor_wrapper | |
29 | 26 | |
30 | 27 | def test_command(self): |
31 | 28 | original_stdout, sys.stdout = sys.stdout, six.StringIO() |
32 | 29 | try: |
33 | 30 | User.objects.count() |
34 | self.assertIn("SELECT COUNT(*)", sys.stdout.getvalue()) | |
31 | self.assertIn("SELECT COUNT", sys.stdout.getvalue()) | |
35 | 32 | finally: |
36 | 33 | sys.stdout = original_stdout |
0 | import django | |
1 | from django.contrib.auth.models import User | |
2 | from django.template.loaders.app_directories import Loader | |
3 | ||
4 | ||
5 | class LoaderWithSQL(Loader): | |
6 | ||
7 | if django.VERSION[:2] >= (1, 9): | |
8 | def get_template(self, *args, **kwargs): | |
9 | # Force the template loader to run some SQL. Simulates a CMS. | |
10 | User.objects.all().count() | |
11 | return super(LoaderWithSQL, self).get_template(*args, **kwargs) | |
12 | else: | |
13 | def load_template(self, *args, **kwargs): | |
14 | # Force the template loader to run some SQL. Simulates a CMS. | |
15 | User.objects.all().count() | |
16 | return super(LoaderWithSQL, self).load_template(*args, **kwargs) |
22 | 22 | cache.cache.set('foo', 'bar') |
23 | 23 | cache.cache.get('foo') |
24 | 24 | cache.cache.delete('foo') |
25 | self.assertEqual(len(self.panel.calls), 3) | |
25 | # Verify that the cache has a valid clear method. | |
26 | cache.cache.clear() | |
27 | self.assertEqual(len(self.panel.calls), 4) | |
28 | ||
29 | def test_recording_caches(self): | |
30 | self.assertEqual(len(self.panel.calls), 0) | |
31 | default_cache = cache.caches[cache.DEFAULT_CACHE_ALIAS] | |
32 | second_cache = cache.caches['second'] | |
33 | default_cache.set('foo', 'bar') | |
34 | second_cache.get('foo') | |
35 | self.assertEqual(len(self.panel.calls), 2) | |
36 | ||
37 | def test_insert_content(self): | |
38 | """ | |
39 | Test that the panel only inserts content after generate_stats and | |
40 | not the process_response. | |
41 | """ | |
42 | cache.cache.get('café') | |
43 | self.panel.process_response(self.request, self.response) | |
44 | # ensure the panel does not have content yet. | |
45 | self.assertNotIn('café', self.panel.content) | |
46 | self.panel.generate_stats(self.request, self.response) | |
47 | # ensure the panel renders correctly. | |
48 | self.assertIn('café', self.panel.content) |
0 | # coding: utf-8 | |
1 | ||
0 | 2 | from __future__ import absolute_import, unicode_literals |
1 | 3 | |
2 | 4 | import logging |
3 | 5 | |
4 | 6 | from debug_toolbar.panels.logging import ( |
5 | collector, MESSAGE_IF_STRING_REPRESENTATION_INVALID) | |
7 | MESSAGE_IF_STRING_REPRESENTATION_INVALID, collector, | |
8 | ) | |
6 | 9 | |
7 | 10 | from ..base import BaseTestCase |
8 | 11 | |
15 | 18 | self.logger = logging.getLogger(__name__) |
16 | 19 | collector.clear_collection() |
17 | 20 | |
21 | # Assume the root logger has been configured with level=DEBUG. | |
22 | # Previously DDT forcefully set this itself to 0 (NOTSET). | |
23 | logging.root.setLevel(logging.DEBUG) | |
24 | ||
18 | 25 | def test_happy_case(self): |
19 | 26 | self.logger.info('Nothing to see here, move along!') |
20 | 27 | |
21 | 28 | self.panel.process_response(self.request, self.response) |
29 | self.panel.generate_stats(self.request, self.response) | |
22 | 30 | records = self.panel.get_stats()['records'] |
23 | 31 | |
24 | 32 | self.assertEqual(1, len(records)) |
29 | 37 | self.logger.info('There are %d %s', 5, 'apples') |
30 | 38 | |
31 | 39 | self.panel.process_response(self.request, self.response) |
40 | self.panel.generate_stats(self.request, self.response) | |
32 | 41 | records = self.panel.get_stats()['records'] |
33 | 42 | |
34 | 43 | self.assertEqual(1, len(records)) |
35 | 44 | self.assertEqual('There are 5 apples', |
36 | 45 | records[0]['message']) |
46 | ||
47 | def test_insert_content(self): | |
48 | """ | |
49 | Test that the panel only inserts content after generate_stats and | |
50 | not the process_response. | |
51 | """ | |
52 | self.logger.info('café') | |
53 | self.panel.process_response(self.request, self.response) | |
54 | # ensure the panel does not have content yet. | |
55 | self.assertNotIn('café', self.panel.content) | |
56 | self.panel.generate_stats(self.request, self.response) | |
57 | # ensure the panel renders correctly. | |
58 | self.assertIn('café', self.panel.content) | |
37 | 59 | |
38 | 60 | def test_failing_formatting(self): |
39 | 61 | class BadClass(object): |
44 | 66 | self.logger.debug('This class is misbehaving: %s', BadClass()) |
45 | 67 | |
46 | 68 | self.panel.process_response(self.request, self.response) |
69 | self.panel.generate_stats(self.request, self.response) | |
47 | 70 | records = self.panel.get_stats()['records'] |
48 | 71 | |
49 | 72 | self.assertEqual(1, len(records)) |
3 | 3 | from django.db import IntegrityError, transaction |
4 | 4 | from django.test import TestCase |
5 | 5 | from django.test.utils import override_settings |
6 | from django.utils import unittest | |
7 | 6 | |
8 | 7 | from ..base import BaseTestCase |
9 | 8 | from ..views import regular_view |
16 | 15 | super(ProfilingPanelTestCase, self).setUp() |
17 | 16 | self.panel = self.toolbar.get_panel_by_id('ProfilingPanel') |
18 | 17 | |
19 | # This test fails randomly for a reason I don't understand. | |
20 | ||
21 | @unittest.expectedFailure | |
22 | 18 | def test_regular_view(self): |
23 | 19 | self.panel.process_view(self.request, regular_view, ('profiling',), {}) |
24 | 20 | self.panel.process_response(self.request, self.response) |
21 | self.panel.generate_stats(self.request, self.response) | |
25 | 22 | self.assertIn('func_list', self.panel.get_stats()) |
23 | self.assertIn('regular_view', self.panel.content) | |
24 | ||
25 | def test_insert_content(self): | |
26 | """ | |
27 | Test that the panel only inserts content after generate_stats and | |
28 | not the process_response. | |
29 | """ | |
30 | self.panel.process_view(self.request, regular_view, ('profiling',), {}) | |
31 | self.panel.process_response(self.request, self.response) | |
32 | # ensure the panel does not have content yet. | |
33 | self.assertNotIn('regular_view', self.panel.content) | |
34 | self.panel.generate_stats(self.request, self.response) | |
35 | # ensure the panel renders correctly. | |
26 | 36 | self.assertIn('regular_view', self.panel.content) |
27 | 37 | |
28 | 38 | |
38 | 48 | self.assertEqual(User.objects.count(), 1) |
39 | 49 | |
40 | 50 | with self.assertRaises(IntegrityError): |
41 | if hasattr(transaction, 'atomic'): # Django >= 1.6 | |
42 | with transaction.atomic(): | |
43 | response = self.client.get('/new_user/') | |
44 | else: | |
51 | with transaction.atomic(): | |
45 | 52 | response = self.client.get('/new_user/') |
46 | 53 | self.assertEqual(User.objects.count(), 1) |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | import django | |
3 | 2 | from django.conf import settings |
4 | 3 | from django.http import HttpResponse |
5 | 4 | from django.test.utils import override_settings |
6 | from django.utils import unittest | |
7 | 5 | |
8 | 6 | from ..base import BaseTestCase |
9 | 7 | |
29 | 27 | redirect['Location'] = 'http://somewhere/else/' |
30 | 28 | response = self.panel.process_response(self.request, redirect) |
31 | 29 | self.assertFalse(response is redirect) |
32 | self.assertContains(response, '302 FOUND') | |
30 | try: | |
31 | self.assertContains(response, '302 Found') | |
32 | except AssertionError: # Django < 1.9 | |
33 | self.assertContains(response, '302 FOUND') | |
33 | 34 | self.assertContains(response, 'http://somewhere/else/') |
34 | 35 | |
35 | 36 | def test_redirect_with_broken_context_processor(self): |
36 | context_processors = settings.TEMPLATE_CONTEXT_PROCESSORS + ( | |
37 | context_processors = list(settings.TEMPLATE_CONTEXT_PROCESSORS) + [ | |
37 | 38 | 'tests.context_processors.broken', |
38 | ) | |
39 | ] | |
39 | 40 | |
40 | 41 | with self.settings(TEMPLATE_CONTEXT_PROCESSORS=context_processors): |
41 | 42 | redirect = HttpResponse(status=302) |
42 | 43 | redirect['Location'] = 'http://somewhere/else/' |
43 | 44 | response = self.panel.process_response(self.request, redirect) |
44 | 45 | self.assertFalse(response is redirect) |
45 | self.assertContains(response, '302 FOUND') | |
46 | try: | |
47 | self.assertContains(response, '302 Found') | |
48 | except AssertionError: # Django < 1.9 | |
49 | self.assertContains(response, '302 FOUND') | |
46 | 50 | self.assertContains(response, 'http://somewhere/else/') |
47 | 51 | |
48 | 52 | def test_unknown_status_code(self): |
49 | 53 | redirect = HttpResponse(status=369) |
50 | 54 | redirect['Location'] = 'http://somewhere/else/' |
51 | 55 | response = self.panel.process_response(self.request, redirect) |
52 | self.assertContains(response, '369 UNKNOWN STATUS CODE') | |
56 | try: | |
57 | self.assertContains(response, '369 Unknown Status Code') | |
58 | except AssertionError: # Django < 1.9 | |
59 | self.assertContains(response, '369 UNKNOWN STATUS CODE') | |
53 | 60 | |
54 | @unittest.skipIf(django.VERSION[:2] < (1, 6), "reason isn't supported") | |
55 | 61 | def test_unknown_status_code_with_reason(self): |
56 | 62 | redirect = HttpResponse(status=369, reason='Look Ma!') |
57 | 63 | redirect['Location'] = 'http://somewhere/else/' |
58 | 64 | response = self.panel.process_response(self.request, redirect) |
59 | 65 | self.assertContains(response, '369 Look Ma!') |
66 | ||
67 | def test_insert_content(self): | |
68 | """ | |
69 | Test that the panel only inserts content after generate_stats and | |
70 | not the process_response. | |
71 | """ | |
72 | redirect = HttpResponse(status=304) | |
73 | response = self.panel.process_response(self.request, redirect) | |
74 | self.assertIsNotNone(response) | |
75 | response = self.panel.generate_stats(self.request, redirect) | |
76 | self.assertIsNone(response) |
18 | 18 | self.request.session['là'.encode('utf-8')] = 'là'.encode('utf-8') |
19 | 19 | self.panel.process_request(self.request) |
20 | 20 | self.panel.process_response(self.request, self.response) |
21 | self.panel.generate_stats(self.request, self.response) | |
21 | 22 | content = self.panel.content |
22 | 23 | if six.PY3: |
23 | 24 | self.assertIn('où', content) |
29 | 30 | self.request.path = '/non_ascii_request/' |
30 | 31 | self.panel.process_request(self.request) |
31 | 32 | self.panel.process_response(self.request, self.response) |
33 | self.panel.generate_stats(self.request, self.response) | |
32 | 34 | self.assertIn('nôt åscíì', self.panel.content) |
35 | ||
36 | def test_insert_content(self): | |
37 | """ | |
38 | Test that the panel only inserts content after generate_stats and | |
39 | not the process_response. | |
40 | """ | |
41 | self.request.path = '/non_ascii_request/' | |
42 | self.panel.process_response(self.request, self.response) | |
43 | # ensure the panel does not have content yet. | |
44 | self.assertNotIn('nôt åscíì', self.panel.content) | |
45 | self.panel.generate_stats(self.request, self.response) | |
46 | # ensure the panel renders correctly. | |
47 | self.assertIn('nôt åscíì', self.panel.content) |
1 | 1 | |
2 | 2 | from __future__ import absolute_import, unicode_literals |
3 | 3 | |
4 | import unittest | |
5 | ||
4 | 6 | from django.contrib.auth.models import User |
5 | 7 | from django.db import connection |
6 | 8 | from django.db.utils import DatabaseError |
7 | from django.utils import unittest | |
9 | from django.shortcuts import render | |
10 | from django.test.utils import override_settings | |
8 | 11 | |
9 | 12 | from ..base import BaseTestCase |
10 | 13 | |
19 | 22 | def tearDown(self): |
20 | 23 | self.panel.disable_instrumentation() |
21 | 24 | super(SQLPanelTestCase, self).tearDown() |
25 | ||
26 | def test_disabled(self): | |
27 | config = { | |
28 | 'DISABLE_PANELS': set(['debug_toolbar.panels.sql.SQLPanel']) | |
29 | } | |
30 | self.assertTrue(self.panel.enabled) | |
31 | with self.settings(DEBUG_TOOLBAR_CONFIG=config): | |
32 | self.assertFalse(self.panel.enabled) | |
22 | 33 | |
23 | 34 | def test_recording(self): |
24 | 35 | self.assertEqual(len(self.panel._queries), 0) |
52 | 63 | self.assertEqual(len(self.panel._queries), 3) |
53 | 64 | |
54 | 65 | self.panel.process_response(self.request, self.response) |
66 | self.panel.generate_stats(self.request, self.response) | |
55 | 67 | |
56 | 68 | # ensure the panel renders correctly |
69 | self.assertIn('café', self.panel.content) | |
70 | ||
71 | def test_insert_content(self): | |
72 | """ | |
73 | Test that the panel only inserts content after generate_stats and | |
74 | not the process_response. | |
75 | """ | |
76 | list(User.objects.filter(username='café'.encode('utf-8'))) | |
77 | self.panel.process_response(self.request, self.response) | |
78 | # ensure the panel does not have content yet. | |
79 | self.assertNotIn('café', self.panel.content) | |
80 | self.panel.generate_stats(self.request, self.response) | |
81 | # ensure the panel renders correctly. | |
57 | 82 | self.assertIn('café', self.panel.content) |
58 | 83 | |
59 | 84 | @unittest.skipUnless(connection.vendor == 'postgresql', |
83 | 108 | |
84 | 109 | # ensure the stacktrace is empty |
85 | 110 | self.assertEqual([], query[1]['stacktrace']) |
111 | ||
112 | @override_settings(DEBUG=True, TEMPLATE_DEBUG=True, | |
113 | TEMPLATE_LOADERS=('tests.loaders.LoaderWithSQL',)) | |
114 | def test_regression_infinite_recursion(self): | |
115 | """ | |
116 | Test case for when the template loader runs a SQL query that causes | |
117 | an infinite recursion in the SQL panel. | |
118 | """ | |
119 | self.assertEqual(len(self.panel._queries), 0) | |
120 | ||
121 | render(self.request, "basic.html", {}) | |
122 | ||
123 | # Two queries are logged because the loader runs SQL every time a | |
124 | # template is loaded and basic.html extends base.html. | |
125 | self.assertEqual(len(self.panel._queries), 2) | |
126 | query = self.panel._queries[0] | |
127 | self.assertEqual(query[0], 'default') | |
128 | self.assertTrue('sql' in query[1]) | |
129 | self.assertTrue('duration' in query[1]) | |
130 | self.assertTrue('stacktrace' in query[1]) | |
131 | ||
132 | # ensure the stacktrace is populated | |
133 | self.assertTrue(len(query[1]['stacktrace']) > 0) |
15 | 15 | def test_default_case(self): |
16 | 16 | self.panel.process_request(self.request) |
17 | 17 | self.panel.process_response(self.request, self.response) |
18 | self.panel.generate_stats(self.request, self.response) | |
18 | 19 | self.assertIn('django.contrib.staticfiles.finders.' |
19 | 20 | 'AppDirectoriesFinder', self.panel.content) |
20 | 21 | self.assertIn('django.contrib.staticfiles.finders.' |
25 | 26 | ['django.contrib.admin', 'debug_toolbar']) |
26 | 27 | self.assertEqual(self.panel.get_staticfiles_dirs(), |
27 | 28 | finders.FileSystemFinder().locations) |
29 | ||
30 | def test_insert_content(self): | |
31 | """ | |
32 | Test that the panel only inserts content after generate_stats and | |
33 | not the process_response. | |
34 | """ | |
35 | self.panel.process_request(self.request) | |
36 | self.panel.process_response(self.request, self.response) | |
37 | # ensure the panel does not have content yet. | |
38 | self.assertNotIn('django.contrib.staticfiles.finders.' | |
39 | 'AppDirectoriesFinder', self.panel.content) | |
40 | self.panel.generate_stats(self.request, self.response) | |
41 | # ensure the panel renders correctly. | |
42 | self.assertIn('django.contrib.staticfiles.finders.' | |
43 | 'AppDirectoriesFinder', self.panel.content) |
1 | 1 | |
2 | 2 | from __future__ import absolute_import, unicode_literals |
3 | 3 | |
4 | import django | |
5 | 4 | from django.contrib.auth.models import User |
6 | 5 | from django.template import Context, RequestContext, Template |
7 | 6 | |
36 | 35 | # ensure the query was NOT logged |
37 | 36 | self.assertEqual(len(self.sql_panel._queries), 0) |
38 | 37 | |
39 | base_ctx_idx = 1 if django.VERSION[:2] >= (1, 5) else 0 | |
40 | ctx = self.panel.templates[0]['context'][base_ctx_idx] | |
38 | ctx = self.panel.templates[0]['context'][1] | |
41 | 39 | self.assertIn('<<queryset of auth.User>>', ctx) |
42 | 40 | self.assertIn('<<triggers database query>>', ctx) |
43 | 41 | |
47 | 45 | c = Context({'object': NonAsciiRepr()}) |
48 | 46 | t.render(c) |
49 | 47 | self.panel.process_response(self.request, self.response) |
48 | self.panel.generate_stats(self.request, self.response) | |
49 | self.assertIn('nôt åscíì', self.panel.content) | |
50 | ||
51 | def test_insert_content(self): | |
52 | """ | |
53 | Test that the panel only inserts content after generate_stats and | |
54 | not the process_response. | |
55 | """ | |
56 | t = Template("{{ object }}") | |
57 | c = Context({'object': NonAsciiRepr()}) | |
58 | t.render(c) | |
59 | self.panel.process_response(self.request, self.response) | |
60 | # ensure the panel does not have content yet. | |
61 | self.assertNotIn('nôt åscíì', self.panel.content) | |
62 | self.panel.generate_stats(self.request, self.response) | |
63 | # ensure the panel renders correctly. | |
50 | 64 | self.assertIn('nôt åscíì', self.panel.content) |
51 | 65 | |
52 | 66 | def test_custom_context_processor(self): |
55 | 69 | c = RequestContext(self.request, processors=[context_processor]) |
56 | 70 | t.render(c) |
57 | 71 | self.panel.process_response(self.request, self.response) |
72 | self.panel.generate_stats(self.request, self.response) | |
58 | 73 | self.assertIn('tests.panels.test_template.context_processor', self.panel.content) |
74 | ||
75 | def test_disabled(self): | |
76 | config = { | |
77 | 'DISABLE_PANELS': set([ | |
78 | 'debug_toolbar.panels.templates.TemplatesPanel']) | |
79 | } | |
80 | self.assertTrue(self.panel.enabled) | |
81 | with self.settings(DEBUG_TOOLBAR_CONFIG=config): | |
82 | self.assertFalse(self.panel.enabled) | |
59 | 83 | |
60 | 84 | |
61 | 85 | def context_processor(request): |
0 | 0 | """Django settings for tests.""" |
1 | 1 | |
2 | 2 | import os |
3 | import django | |
4 | 3 | |
5 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) |
6 | 5 | |
10 | 9 | SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' |
11 | 10 | |
12 | 11 | INTERNAL_IPS = ['127.0.0.1'] |
12 | ||
13 | LOGGING_CONFIG = None # avoids spurious output in tests | |
13 | 14 | |
14 | 15 | |
15 | 16 | # Application definition |
53 | 54 | CACHES = { |
54 | 55 | 'default': { |
55 | 56 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', |
56 | } | |
57 | }, | |
58 | 'second': { | |
59 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', | |
60 | }, | |
57 | 61 | } |
58 | 62 | |
59 | 63 | DATABASES = { |
69 | 73 | # Django's test client sets wsgi.multiprocess to True inappropriately |
70 | 74 | 'RENDER_PANELS': False, |
71 | 75 | } |
72 | ||
73 | if django.VERSION[:2] < (1, 6): | |
74 | TEST_RUNNER = 'discover_runner.DiscoverRunner' |
0 | <!DOCTYPE html> | |
1 | <html> | |
2 | <head> | |
3 | <title>{{ title }}</title> | |
4 | </head> | |
5 | <body> | |
6 | {% block content %}{% endblock %} | |
7 | </body> | |
8 | </html> |
0 | <!DOCTYPE html> | |
1 | <html> | |
2 | <head> | |
3 | <title>{{ title }}</title> | |
4 | </head> | |
5 | <body> | |
6 | </body> | |
7 | </html> | |
0 | {% extends "base.html" %} | |
1 | {% block content %}Test for {{ title }}{% endblock %} |
2 | 2 | from __future__ import absolute_import, unicode_literals |
3 | 3 | |
4 | 4 | import os |
5 | import unittest | |
5 | 6 | from xml.etree import ElementTree as ET |
7 | ||
8 | from django.contrib.staticfiles.testing import StaticLiveServerTestCase | |
9 | from django.test import RequestFactory, TestCase | |
10 | from django.test.utils import override_settings | |
11 | ||
12 | from debug_toolbar.middleware import DebugToolbarMiddleware, show_toolbar | |
13 | ||
14 | from .base import BaseTestCase | |
15 | from .views import regular_view | |
6 | 16 | |
7 | 17 | try: |
8 | 18 | from selenium import webdriver |
10 | 20 | from selenium.webdriver.support.wait import WebDriverWait |
11 | 21 | except ImportError: |
12 | 22 | webdriver = None |
13 | ||
14 | from django.test import LiveServerTestCase, RequestFactory, TestCase | |
15 | from django.test.utils import override_settings | |
16 | from django.utils.unittest import skipIf, skipUnless | |
17 | ||
18 | from debug_toolbar.middleware import DebugToolbarMiddleware, show_toolbar | |
19 | ||
20 | from .base import BaseTestCase | |
21 | from .views import regular_view | |
22 | 23 | |
23 | 24 | |
24 | 25 | rf = RequestFactory() |
44 | 45 | panel = self.toolbar.get_panel_by_id('RequestPanel') |
45 | 46 | panel.process_request(self.request) |
46 | 47 | panel.process_response(self.request, self.response) |
48 | panel.generate_stats(self.request, self.response) | |
47 | 49 | return panel.get_stats() |
48 | 50 | |
49 | 51 | def test_url_resolving_positional(self): |
85 | 87 | # check toolbar insertion before "</body>" |
86 | 88 | self.assertContains(resp, '</div>\n</body>') |
87 | 89 | |
90 | def test_cache_page(self): | |
91 | self.client.get('/cached_view/') | |
92 | self.assertEqual( | |
93 | len(self.toolbar.get_panel_by_id('CachePanel').calls), 3) | |
94 | self.client.get('/cached_view/') | |
95 | self.assertEqual( | |
96 | len(self.toolbar.get_panel_by_id('CachePanel').calls), 5) | |
97 | ||
88 | 98 | |
89 | 99 | @override_settings(DEBUG=True) |
90 | 100 | class DebugToolbarIntegrationTestCase(TestCase): |
108 | 118 | ET.fromstring(response.content) # shouldn't raise ParseError |
109 | 119 | |
110 | 120 | |
111 | @skipIf(webdriver is None, "selenium isn't installed") | |
112 | @skipUnless('DJANGO_SELENIUM_TESTS' in os.environ, "selenium tests not requested") | |
121 | @unittest.skipIf(webdriver is None, "selenium isn't installed") | |
122 | @unittest.skipUnless('DJANGO_SELENIUM_TESTS' in os.environ, "selenium tests not requested") | |
113 | 123 | @override_settings(DEBUG=True) |
114 | class DebugToolbarLiveTestCase(LiveServerTestCase): | |
124 | class DebugToolbarLiveTestCase(StaticLiveServerTestCase): | |
115 | 125 | |
116 | 126 | @classmethod |
117 | 127 | def setUpClass(cls): |
140 | 150 | self.assertIn("Name", table.text) |
141 | 151 | self.assertIn("Version", table.text) |
142 | 152 | |
143 | @override_settings(DEBUG_TOOLBAR_CONFIG={'RESULTS_STORE_SIZE': 0}) | |
153 | @override_settings(DEBUG_TOOLBAR_CONFIG={'RESULTS_CACHE_SIZE': 0}) | |
144 | 154 | def test_expired_store(self): |
145 | 155 | self.selenium.get(self.live_server_url + '/regular/basic/') |
146 | 156 | version_panel = self.selenium.find_element_by_id('VersionsPanel') |
152 | 162 | error = WebDriverWait(self.selenium, timeout=10).until( |
153 | 163 | lambda selenium: version_panel.find_element_by_tag_name('p')) |
154 | 164 | self.assertIn("Data for this panel isn't available anymore.", error.text) |
165 | ||
166 | @override_settings(TEMPLATE_LOADERS=[( | |
167 | 'django.template.loaders.cached.Loader', ( | |
168 | 'django.template.loaders.filesystem.Loader', | |
169 | 'django.template.loaders.app_directories.Loader', | |
170 | ), | |
171 | )]) | |
172 | def test_django_cached_template_loader(self): | |
173 | self.selenium.get(self.live_server_url + '/regular/basic/') | |
174 | version_panel = self.selenium.find_element_by_id('TemplatesPanel') | |
175 | ||
176 | # Click to show the versions panel | |
177 | self.selenium.find_element_by_class_name('TemplatesPanel').click() | |
178 | ||
179 | # Version panel loads | |
180 | trigger = WebDriverWait(self.selenium, timeout=10).until( | |
181 | lambda selenium: version_panel.find_element_by_css_selector( | |
182 | '.remoteCall')) | |
183 | trigger.click() | |
184 | ||
185 | # Verify the code is displayed | |
186 | WebDriverWait(self.selenium, timeout=10).until( | |
187 | lambda selenium: self.selenium.find_element_by_css_selector( | |
188 | '#djDebugWindow code')) |
0 | 0 | from __future__ import absolute_import, unicode_literals |
1 | 1 | |
2 | from django.utils.unittest import TestCase | |
2 | import unittest | |
3 | 3 | |
4 | 4 | from debug_toolbar.utils import get_name_from_obj |
5 | 5 | |
6 | 6 | |
7 | class GetNameFromObjTestCase(TestCase): | |
7 | class GetNameFromObjTestCase(unittest.TestCase): | |
8 | 8 | |
9 | 9 | def test_func(self): |
10 | 10 | def x(): |
1 | 1 | |
2 | 2 | from __future__ import absolute_import, unicode_literals |
3 | 3 | |
4 | from django.conf.urls import include, patterns, url | |
5 | from django.contrib import admin | |
4 | from django.conf.urls import include, url | |
6 | 5 | |
7 | 6 | import debug_toolbar |
8 | 7 | |
8 | from . import views | |
9 | 9 | from .models import NonAsciiRepr |
10 | 10 | |
11 | ||
12 | admin.autodiscover() | |
13 | ||
14 | urlpatterns = patterns('tests.views', # noqa | |
15 | url(r'^resolving1/(.+)/(.+)/$', 'resolving_view', name='positional-resolving'), | |
16 | url(r'^resolving2/(?P<arg1>.+)/(?P<arg2>.+)/$', 'resolving_view'), | |
17 | url(r'^resolving3/(.+)/$', 'resolving_view', {'arg2': 'default'}), | |
18 | url(r'^regular/(?P<title>.*)/$', 'regular_view'), | |
19 | url(r'^non_ascii_request/$', 'regular_view', {'title': NonAsciiRepr()}), | |
20 | url(r'^new_user/$', 'new_user'), | |
21 | url(r'^execute_sql/$', 'execute_sql'), | |
11 | urlpatterns = [ | |
12 | url(r'^resolving1/(.+)/(.+)/$', views.resolving_view, name='positional-resolving'), | |
13 | url(r'^resolving2/(?P<arg1>.+)/(?P<arg2>.+)/$', views.resolving_view), | |
14 | url(r'^resolving3/(.+)/$', views.resolving_view, {'arg2': 'default'}), | |
15 | url(r'^regular/(?P<title>.*)/$', views.regular_view), | |
16 | url(r'^non_ascii_request/$', views.regular_view, {'title': NonAsciiRepr()}), | |
17 | url(r'^new_user/$', views.new_user), | |
18 | url(r'^execute_sql/$', views.execute_sql), | |
19 | url(r'^cached_view/$', views.cached_view), | |
22 | 20 | url(r'^__debug__/', include(debug_toolbar.urls)), |
23 | ) | |
21 | ] |
4 | 4 | from django.contrib.auth.models import User |
5 | 5 | from django.http import HttpResponse |
6 | 6 | from django.shortcuts import render |
7 | from django.views.decorators.cache import cache_page | |
7 | 8 | |
8 | 9 | |
9 | 10 | def execute_sql(request): |
23 | 24 | def resolving_view(request, arg1, arg2): |
24 | 25 | # see test_url_resolving in tests.py |
25 | 26 | return HttpResponse() |
27 | ||
28 | ||
29 | @cache_page(60) | |
30 | def cached_view(request): | |
31 | return HttpResponse() |
0 | 0 | [tox] |
1 | 1 | envlist = |
2 | py26-django14, | |
3 | py27-django14, | |
4 | py26-django15, | |
5 | py27-django15, | |
6 | py32-django15, | |
7 | py33-django15, | |
8 | py26-django16, | |
9 | py27-django16, | |
10 | py32-django16, | |
11 | py33-django16, | |
12 | py27-django17, | |
13 | py32-django17, | |
14 | py33-django17, | |
15 | py34-django17, | |
16 | flake8 | |
2 | py{27,32,33,34}-django{17,18}, | |
3 | py{27,34,35}-django19, | |
4 | flake8, | |
5 | isort | |
17 | 6 | |
18 | 7 | [testenv] |
8 | basepython = | |
9 | py27: python2.7 | |
10 | py32: python3.2 | |
11 | py33: python3.3 | |
12 | py34: python3.4 | |
13 | py34: python3.5 | |
19 | 14 | commands = make test |
20 | 15 | deps = |
16 | django14: Django>=1.4,<1.5 | |
17 | django15: Django>=1.5,<1.6 | |
18 | django16: Django>=1.6,<1.7 | |
19 | django17: Django>=1.7,<1.8 | |
20 | django18: Django>=1.8,<1.9 | |
21 | django19: https://www.djangoproject.com/download/1.9a1/tarball/ | |
21 | 22 | django-discover-runner |
22 | 23 | selenium |
23 | 24 | sqlparse |
25 | 26 | PYTHONPATH = {toxinidir} |
26 | 27 | whitelist_externals = make |
27 | 28 | |
28 | [testenv:py26-django14] | |
29 | basepython = python2.6 | |
30 | deps = | |
31 | Django>=1.4,<1.5 | |
32 | {[testenv]deps} | |
33 | ||
34 | [testenv:py27-django14] | |
35 | basepython = python2.7 | |
36 | deps = | |
37 | Django>=1.4,<1.5 | |
38 | {[testenv]deps} | |
39 | ||
40 | [testenv:py26-django15] | |
41 | basepython = python2.6 | |
42 | deps = | |
43 | Django>=1.5,<1.6 | |
44 | {[testenv]deps} | |
45 | ||
46 | [testenv:py27-django15] | |
47 | basepython = python2.7 | |
48 | deps = | |
49 | Django>=1.5,<1.6 | |
50 | {[testenv]deps} | |
51 | ||
52 | [testenv:py32-django15] | |
53 | basepython = python3.2 | |
54 | deps = | |
55 | Django>=1.5,<1.6 | |
56 | {[testenv]deps} | |
57 | ||
58 | [testenv:py33-django15] | |
59 | basepython = python3.3 | |
60 | deps = | |
61 | Django>=1.5,<1.6 | |
62 | {[testenv]deps} | |
63 | ||
64 | [testenv:py26-django16] | |
65 | basepython = python2.6 | |
66 | deps = | |
67 | Django>=1.6,<1.7 | |
68 | {[testenv]deps} | |
69 | ||
70 | [testenv:py27-django16] | |
71 | basepython = python2.7 | |
72 | deps = | |
73 | Django>=1.6,<1.7 | |
74 | {[testenv]deps} | |
75 | ||
76 | [testenv:py32-django16] | |
77 | basepython = python3.2 | |
78 | deps = | |
79 | Django>=1.6,<1.7 | |
80 | {[testenv]deps} | |
81 | ||
82 | [testenv:py33-django16] | |
83 | basepython = python3.3 | |
84 | deps = | |
85 | Django>=1.6,<1.7 | |
86 | {[testenv]deps} | |
87 | ||
88 | [testenv:py27-django17] | |
89 | basepython = python2.7 | |
90 | deps = | |
91 | https://www.djangoproject.com/download/1.7b1/tarball/ | |
92 | {[testenv]deps} | |
93 | ||
94 | [testenv:py32-django17] | |
95 | basepython = python3.2 | |
96 | deps = | |
97 | https://www.djangoproject.com/download/1.7b1/tarball/ | |
98 | {[testenv]deps} | |
99 | ||
100 | [testenv:py33-django17] | |
101 | basepython = python3.3 | |
102 | deps = | |
103 | https://www.djangoproject.com/download/1.7b1/tarball/ | |
104 | {[testenv]deps} | |
105 | ||
106 | [testenv:py34-django17] | |
107 | basepython = python3.4 | |
108 | deps = | |
109 | https://www.djangoproject.com/download/1.7b1/tarball/ | |
110 | {[testenv]deps} | |
111 | ||
112 | 29 | [testenv:flake8] |
30 | basepython = | |
31 | python2.7 | |
113 | 32 | commands = make flake8 |
114 | 33 | deps = |
115 | 34 | flake8 |
35 | ||
36 | [testenv:isort] | |
37 | basepython = | |
38 | python2.7 | |
39 | commands = make isort_check_only | |
40 | deps = | |
41 | isort |