├── debug_toolbar ├── models.py ├── toolbar │ ├── __init__.py │ └── loader.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── debugsqlshell.py ├── tests │ ├── templates │ │ └── 404.html │ ├── __init__.py │ ├── views.py │ ├── urls.py │ └── tests.py ├── utils │ ├── compat │ │ ├── __init__.py │ │ └── db.py │ ├── sqlparse │ │ ├── __init__.py │ │ ├── engine │ │ │ ├── __init__.py │ │ │ ├── filter.py │ │ │ └── grouping.py │ │ ├── tokens.py │ │ └── formatter.py │ ├── __init__.py │ └── tracking │ │ ├── __init__.py │ │ └── db.py ├── __init__.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── he │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── ru │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── media │ └── debug_toolbar │ │ ├── img │ │ ├── back.png │ │ ├── close.png │ │ ├── dot.gif │ │ ├── back_hover.png │ │ ├── indicator.png │ │ ├── panel_bg.png │ │ ├── close_hover.png │ │ └── djdt_vertical.png │ │ ├── Makefile │ │ └── js │ │ ├── jquery.cookie.js │ │ └── toolbar.js ├── templates │ └── debug_toolbar │ │ ├── panels │ │ ├── headers.html │ │ ├── versions.html │ │ ├── settings_vars.html │ │ ├── template_source.html │ │ ├── timer.html │ │ ├── signals.html │ │ ├── logger.html │ │ ├── sql_explain.html │ │ ├── sql_select.html │ │ ├── sql_profile.html │ │ ├── profiling.html │ │ ├── cache.html │ │ ├── templates.html │ │ ├── request_vars.html │ │ └── sql.html │ │ ├── redirect.html │ │ └── base.html ├── urls.py ├── panels │ ├── settings_vars.py │ ├── __init__.py │ ├── headers.py │ ├── request_vars.py │ ├── version.py │ ├── signals.py │ ├── timer.py │ ├── cache.py │ ├── logger.py │ ├── template.py │ ├── profiling.py │ └── sql.py ├── runtests.py ├── middleware.py └── views.py ├── example ├── __init__.py ├── example.db ├── templates │ ├── index.html │ ├── mootools │ │ └── index.html │ ├── prototype │ │ └── index.html │ ├── jquery │ │ └── index.html │ └── admin │ │ └── login.html ├── manage.py ├── urls.py └── settings.py ├── setup.cfg ├── .gitignore ├── MANIFEST.in ├── AUTHORS ├── setup.py ├── NEWS ├── LICENSE └── README.rst /debug_toolbar/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/toolbar/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/tests/templates/404.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/utils/compat/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debug_toolbar/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from tests import * -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_svn_revision = false 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | *~ 4 | django_debug_toolbar.egg-info -------------------------------------------------------------------------------- /debug_toolbar/__init__.py: -------------------------------------------------------------------------------- 1 | VERSION = (0, 8, 5) 2 | __version__ = '.'.join(map(str, VERSION)) 3 | -------------------------------------------------------------------------------- /example/example.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/example/example.db -------------------------------------------------------------------------------- /debug_toolbar/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/locale/he/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/he/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/back.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/close.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/dot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/dot.gif -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include LICENSE 3 | include README.rst 4 | recursive-include debug_toolbar/media * 5 | recursive-include debug_toolbar/templates * 6 | -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/back_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/back_hover.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/indicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/indicator.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/panel_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/panel_bg.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/close_hover.png -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/img/djdt_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcramer/django-debug-toolbar/HEAD/debug_toolbar/media/debug_toolbar/img/djdt_vertical.png -------------------------------------------------------------------------------- /debug_toolbar/tests/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.http import HttpResponse 3 | 4 | def execute_sql(request): 5 | list(User.objects.all()) 6 | 7 | return HttpResponse() -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/Makefile: -------------------------------------------------------------------------------- 1 | # Make file to compress and join all JS files 2 | all: compress_js compress_css 3 | 4 | compress_js: 5 | java -jar ~/bin/yuicompressor.jar js/jquery.js > js/toolbar.min.js 6 | java -jar ~/bin/yuicompressor.jar js/toolbar.js >> js/toolbar.min.js 7 | 8 | compress_css: 9 | java -jar ~/bin/yuicompressor.jar --type css css/toolbar.css > css/toolbar.min.css 10 | -------------------------------------------------------------------------------- /debug_toolbar/utils/compat/db.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | try: 3 | from django.db import connections 4 | dbconf = settings.DATABASES 5 | except ImportError: 6 | # Compat with < Django 1.2 7 | from django.db import connection 8 | connections = {'default': connection} 9 | dbconf = { 10 | 'default': { 11 | 'ENGINE': settings.DATABASE_ENGINE, 12 | } 13 | } -------------------------------------------------------------------------------- /debug_toolbar/tests/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URLpatterns for the debug toolbar. 3 | 4 | These should not be loaded explicitly; the debug toolbar middleware will patch 5 | this into the urlconf for the request. 6 | """ 7 | from django.conf.urls.defaults import * 8 | from django.contrib import admin 9 | 10 | admin.autodiscover() 11 | 12 | urlpatterns = patterns('', 13 | url(r'^execute_sql/$', 'debug_toolbar.tests.views.execute_sql'), 14 | ) 15 | -------------------------------------------------------------------------------- /example/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Index of Tests 5 | 6 | 7 | 8 |

Index of Tests

9 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/headers.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for key, value in headers.iteritems %} 11 | 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 |
{% trans "Key" %}{% trans "Value" %}
{{ key|escape }}{{ value|escape }}
18 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/versions.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% for package, version in versions.iteritems %} 12 | 13 | 14 | 15 | 16 | {% endfor %} 17 | 18 |
{% trans "Package" %}{% trans "Version" %}
{{ package }}{{ version }}
19 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/settings_vars.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for var in settings.items|dictsort:"0" %} 11 | 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 |
{% trans "Setting" %}{% trans "Value" %}
{{ var.0 }}{{ var.1|pprint }}
18 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/template_source.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
3 | {% trans "Back" %} 4 |

{% trans 'Template Source' %}: {{ template_name }}

5 |
6 |
7 |
8 | {% if not source.pygmentized %} 9 | {{ source }} 10 | {% else %} 11 | {{ source }} 12 | {% endif %} 13 |
14 |
15 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/timer.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for key, value in rows %} 15 | 16 | 17 | 18 | 19 | {% endfor %} 20 | 21 |
{% trans "Resource" %}{% trans "Value" %}
{{ key|escape }}{{ value|escape }}
22 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/signals.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% for name, signal, receivers in signals %} 12 | 13 | 14 | 15 | 16 | 17 | {% endfor %} 18 | 19 |
{% trans "Signal" %}{% trans 'Providing Args' %}{% trans 'Receivers' %}
{{ name|escape }}{{ signal.providing_args|join:", " }}{{ receivers|join:", " }}
20 | -------------------------------------------------------------------------------- /example/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | try: 4 | import settings # Assumed to be in the same directory. 5 | except ImportError: 6 | import sys 7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 8 | sys.exit(1) 9 | 10 | if __name__ == "__main__": 11 | execute_manager(settings) 12 | -------------------------------------------------------------------------------- /example/templates/mootools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MooTools Test 5 | 8 | 9 | 14 | 15 | 16 |

MooTools Test

17 |

If you see this, MooTools is working.

18 | 19 | 20 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/redirect.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 |

HttpResponseRedirect

7 |

{% trans 'Location' %}: {{ redirect_to }}

8 |

9 | {% trans "The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes. You can click the above link to continue with the redirect as normal. If you'd like to disable this feature, set the DEBUG_TOOLBAR_CONFIG dictionary's key INTERCEPT_REDIRECTS to False." %} 10 |

11 | 12 | 13 | -------------------------------------------------------------------------------- /example/templates/prototype/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Prototype Test 5 | 8 | 9 | 14 | 15 | 16 |

Prototype Test

17 |

If you see this, Prototype is working.

18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/templates/jquery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Old jQuery Test 5 | 9 | 10 | 16 | 17 | 18 |

jQuery Test

19 |

If you see this, jQuery is working.

20 | 21 | 22 | -------------------------------------------------------------------------------- /example/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.defaults import * 3 | from django.contrib import admin 4 | from django.views.generic.simple import direct_to_template 5 | 6 | admin.autodiscover() 7 | 8 | urlpatterns = patterns('', 9 | (r'^$', direct_to_template, {'template': 'index.html'}), 10 | (r'^jquery/index/$', direct_to_template, {'template': 'jquery/index.html'}), 11 | (r'^mootools/index/$', direct_to_template, {'template': 'mootools/index.html'}), 12 | (r'^prototype/index/$', direct_to_template, {'template': 'prototype/index.html'}), 13 | (r'^admin/', include(admin.site.urls)), 14 | ) 15 | 16 | if settings.DEBUG: 17 | urlpatterns += patterns('', 18 | (r'^media/(.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}) 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /debug_toolbar/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URLpatterns for the debug toolbar. 3 | 4 | These should not be loaded explicitly; the debug toolbar middleware will patch 5 | this into the urlconf for the request. 6 | """ 7 | from django.conf.urls.defaults import * 8 | from django.conf import settings 9 | 10 | _PREFIX = '__debug__' 11 | 12 | urlpatterns = patterns('', 13 | url(r'^%s/m/(.*)$' % _PREFIX, 'debug_toolbar.views.debug_media'), 14 | url(r'^%s/sql_select/$' % _PREFIX, 'debug_toolbar.views.sql_select', name='sql_select'), 15 | url(r'^%s/sql_explain/$' % _PREFIX, 'debug_toolbar.views.sql_explain', name='sql_explain'), 16 | url(r'^%s/sql_profile/$' % _PREFIX, 'debug_toolbar.views.sql_profile', name='sql_profile'), 17 | url(r'^%s/template_source/$' % _PREFIX, 'debug_toolbar.views.template_source', name='template_source'), 18 | ) 19 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/logger.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if records %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for record in records %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | 24 |
{% trans "Level" %}{% trans "Time" %}{% trans "Channel" %}{% trans "Message" %}{% trans "Location" %}
{{ record.level }}{{ record.time|date:"h:i:s m/d/Y" }}{{ record.channel|default:"-" }}{{ record.message }}{{ record.file }}:{{ record.line }}
25 | {% else %} 26 |

{% trans "No messages logged" %}.

27 | {% endif %} 28 | 29 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/sql_explain.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
3 | {% trans "Back" %} 4 |

{% trans "SQL Explained" %}

5 |
6 |
7 |
8 |
9 |
{% trans "Executed SQL" %}
10 |
{{ sql|safe }}
11 |
{% trans "Time" %}
12 |
{{ duration }} ms
13 |
14 | 15 | 16 | 17 | {% for h in headers %} 18 | 19 | {% endfor %} 20 | 21 | 22 | 23 | {% for row in result %} 24 | 25 | {% for column in row %} 26 | 27 | {% endfor %} 28 | 29 | {% endfor %} 30 | 31 |
{{ h|upper }}
{{ column|escape }}
32 |
33 |
34 | -------------------------------------------------------------------------------- /debug_toolbar/panels/settings_vars.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.template.loader import render_to_string 3 | from django.views.debug import get_safe_settings 4 | from django.utils.translation import ugettext_lazy as _ 5 | from debug_toolbar.panels import DebugPanel 6 | 7 | 8 | class SettingsVarsDebugPanel(DebugPanel): 9 | """ 10 | A panel to display all variables in django.conf.settings 11 | """ 12 | name = 'SettingsVars' 13 | has_content = True 14 | 15 | def nav_title(self): 16 | return _('Settings') 17 | 18 | def title(self): 19 | return _('Settings from %s') % settings.SETTINGS_MODULE 20 | 21 | def url(self): 22 | return '' 23 | 24 | def content(self): 25 | context = self.context.copy() 26 | context.update({ 27 | 'settings': get_safe_settings(), 28 | }) 29 | return render_to_string('debug_toolbar/panels/settings_vars.html', context) 30 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/sql_select.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
3 | {% trans "Back" %} 4 |

{% trans "SQL Selected" %}

5 |
6 |
7 |
8 |
9 |
{% trans "Executed SQL" %}
10 |
{{ sql|safe }}
11 |
{% trans "Time" %}
12 |
{{ duration }} ms
13 |
14 | {% if result %} 15 | 16 | 17 | 18 | {% for h in headers %} 19 | 20 | {% endfor %} 21 | 22 | 23 | 24 | {% for row in result %} 25 | 26 | {% for column in row %} 27 | 28 | {% endfor %} 29 | 30 | {% endfor %} 31 | 32 |
{{ h|upper }}
{{ column|escape }}
33 | {% else %} 34 |

{% trans "Empty set" %}

35 | {% endif %} 36 |
37 |
38 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/sql_profile.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
3 | {% trans "Back" %} 4 |

{% trans "SQL Profiled" %}

5 |
6 |
7 |
8 | {% if result %} 9 |
10 |
{% trans "Executed SQL" %}
11 |
{{ sql|safe }}
12 |
{% trans "Time" %}
13 |
{{ duration }} ms
14 |
15 | 16 | 17 | 18 | {% for h in headers %} 19 | 20 | {% endfor %} 21 | 22 | 23 | 24 | {% for row in result %} 25 | 26 | {% for column in row %} 27 | 28 | {% endfor %} 29 | 30 | {% endfor %} 31 | 32 |
{{ h|upper }}
{{ column|escape }}
33 | {% else %} 34 |
35 |
{% trans 'Error' %}
36 |
{{ result_error }}
37 |
38 | {% endif %} 39 |
40 |
41 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The Django Debug Toolbar was original created by Rob Hudson in 2 | August 2008. 3 | 4 | The following is a list of much appreciated contributors: 5 | 6 | Reto Aebersold 7 | Andi Albrecht 8 | Chris Beaven 9 | Kenneth Belitzky 10 | Loic Bistuer 11 | Etienne Carrier 12 | David Cramer 13 | Michael Elsdoerfer 14 | Augie Fackler 15 | Dan Fairs 16 | Alex Gaynor 17 | Idan Gazit 18 | Matt George 19 | Adam Gomaa 20 | Daniel Hahler 21 | Jacob Kaplan-Moss 22 | Russell Keith-Magee 23 | Mikhail Korobov 24 | Arthur Koziel 25 | Jannis Leidel 26 | Martin Maney 27 | Percy Perez-Pinedo 28 | Nowell Strite 29 | Malcolm Tredinnick 30 | Bryan Veloso 31 | Simon Willison 32 | Diego Búrigo Zacarão 33 | Philip Zeyliger 34 | -------------------------------------------------------------------------------- /example/templates/admin/login.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n %} 3 | 4 | {% block extrastyle %}{% load adminmedia %}{{ block.super }}{% endblock %} 5 | 6 | {% block bodyclass %}login{% endblock %} 7 | 8 | {% block content_title %}{% endblock %} 9 | 10 | {% block breadcrumbs %}{% endblock %} 11 | 12 | {% block content %} 13 | {% if error_message %} 14 |

{{ error_message }}

15 | {% endif %} 16 |
17 |
18 |
19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 | 33 |
34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /debug_toolbar/panels/__init__.py: -------------------------------------------------------------------------------- 1 | """Base DebugPanel class""" 2 | 3 | class DebugPanel(object): 4 | """ 5 | Base class for debug panels. 6 | """ 7 | # name = Base 8 | has_content = False # If content returns something, set to true in subclass 9 | 10 | # We'll maintain a local context instance so we can expose our template 11 | # context variables to panels which need them: 12 | context = {} 13 | 14 | # Panel methods 15 | def __init__(self, context={}): 16 | self.context.update(context) 17 | 18 | def dom_id(self): 19 | return 'djDebug%sPanel' % (self.name.replace(' ', '')) 20 | 21 | def nav_title(self): 22 | """Title showing in toolbar""" 23 | raise NotImplementedError 24 | 25 | def nav_subtitle(self): 26 | """Subtitle showing until title in toolbar""" 27 | return '' 28 | 29 | def title(self): 30 | """Title showing in panel""" 31 | raise NotImplementedError 32 | 33 | def url(self): 34 | raise NotImplementedError 35 | 36 | def content(self): 37 | raise NotImplementedError 38 | 39 | # Standard middleware methods 40 | def process_request(self, request): 41 | pass 42 | 43 | def process_view(self, request, view_func, view_args, view_kwargs): 44 | pass 45 | 46 | def process_response(self, request, response): 47 | pass 48 | 49 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/profiling.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% for call in func_list %} 16 | 17 | 18 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% endfor %} 35 | 36 |
{% trans "Call" %}{% trans "TotTime" %}{% trans "Per" %}{% trans "CumTime" %}{% trans "Per" %}{% trans "Count" %}
19 |
20 | {% if call.has_subfuncs %} 21 | - 22 | {% else %} 23 | 24 | {% endif %} 25 | {{ call.func_std_string }} 26 |
27 |
{{ call.tottime|floatformat:3 }}{{ call.tottime_per_call|floatformat:3 }}{{ call.cumtime|floatformat:3 }}{{ call.cumtime_per_call|floatformat:3 }}{{ call.count }}
37 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='django-debug-toolbar', 5 | version=__import__('debug_toolbar').__version__, 6 | description='A configurable set of panels that display various debug information about the current request/response.', 7 | long_description=open('README.rst').read(), 8 | # Get more strings from http://www.python.org/pypi?:action=list_classifiers 9 | author='Rob Hudson', 10 | author_email='rob@cogit8.org', 11 | url='https://github.com/django-debug-toolbar/django-debug-toolbar', 12 | download_url='https://github.com/django-debug-toolbar/django-debug-toolbar/downloads', 13 | license='BSD', 14 | packages=find_packages(exclude=['ez_setup']), 15 | tests_require=[ 16 | 'django', 17 | 'dingus', 18 | ], 19 | test_suite='debug_toolbar.runtests.runtests', 20 | include_package_data=True, 21 | zip_safe=False, # because we're including media that Django needs 22 | classifiers=[ 23 | 'Development Status :: 4 - Beta', 24 | 'Environment :: Web Environment', 25 | 'Framework :: Django', 26 | 'Intended Audience :: Developers', 27 | 'License :: OSI Approved :: BSD License', 28 | 'Operating System :: OS Independent', 29 | 'Programming Language :: Python', 30 | 'Topic :: Software Development :: Libraries :: Python Modules', 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | News for django-debug-toolbar 2 | ============================= 3 | 4 | 0.8.5 (2011 Apr 25) 5 | ------------------- 6 | 7 | * Ensure if we're overriding the urlconf that we're resetting handler404/500. 8 | 9 | * Updated middleware logic to avoid work if content-type isn't right. 10 | 11 | * Change .load() calls to GET to avoid CSRF protection. 12 | 13 | * Updated SQL panel to match Django's which now includes logging. 14 | 15 | * Added basic multi-db support. 16 | 17 | * Some HTML validation fixes. 18 | 19 | * Added support for `executemany`. Thanks to postal2600. 20 | 21 | * Added support for LogBook. Thanks to Vincent Driessen. 22 | 23 | * Added clean_params method to DatabaseStatTracker to scrub non-unicode 24 | data for displaying on the sql panel. Thanks to Matthew J Morrison 25 | 26 | 0.8.4 (2010 Nov 8) 27 | ------------------ 28 | 29 | * Added print style to hide the toolbar (issue 90) 30 | 31 | * Fixed "Badly formatted SQL query plan" (issue 86) 32 | 33 | * Fixed "SQL not selectable due to line chart" (issue 85) 34 | 35 | * Fixed "Redirect page does not set cookie" (issue 6) 36 | 37 | * Fixed template block inheritance bug (issues 77 and 97). 38 | 39 | * Fixed flash of unstyled toolbar. 40 | 41 | * Updated to work with old TEMPLATE_LOADERS settings from < 1.2. 42 | 43 | * Updated to stop template loader iteration when template is found. 44 | 45 | 46 | (Note: NEWS was started after the 0.8.3 release and is not complete) 47 | 48 | -------------------------------------------------------------------------------- /debug_toolbar/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | from os.path import dirname, abspath 4 | 5 | from django.conf import settings 6 | 7 | if not settings.configured: 8 | settings.configure( 9 | DATABASE_ENGINE='sqlite3', 10 | # HACK: this fixes our threaded runserver remote tests 11 | # DATABASE_NAME='test_sentry', 12 | # TEST_DATABASE_NAME='test_sentry', 13 | INSTALLED_APPS=[ 14 | 'django.contrib.auth', 15 | 'django.contrib.admin', 16 | 'django.contrib.contenttypes', 17 | 'django.contrib.sessions', 18 | 'django.contrib.sites', 19 | 20 | 'debug_toolbar', 21 | 22 | 'debug_toolbar.tests', 23 | ], 24 | ROOT_URLCONF='', 25 | DEBUG=False, 26 | SITE_ID=1, 27 | ) 28 | import djcelery 29 | djcelery.setup_loader() 30 | 31 | from django.test.simple import run_tests 32 | 33 | def runtests(*test_args): 34 | if 'south' in settings.INSTALLED_APPS: 35 | from south.management.commands import patch_for_test_db_setup 36 | patch_for_test_db_setup() 37 | 38 | if not test_args: 39 | test_args = ['debug_toolbar'] 40 | parent = dirname(abspath(__file__)) 41 | sys.path.insert(0, parent) 42 | failures = run_tests(test_args, verbosity=1, interactive=True) 43 | sys.exit(failures) 44 | 45 | 46 | if __name__ == '__main__': 47 | runtests(*sys.argv[1:]) -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/cache.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
{% trans "Total Calls" %}{{ cache_calls }}{% trans "Total Time" %}{{ cache_time }}ms{% trans "Hits" %}{{ cache.hits }}{% trans "Misses" %}{{ cache.misses }}
gets{{ cache.gets }}sets{{ cache.sets }}deletes{{ cache.deletes }}get_many{{ cache.get_many }}
34 | {% if cache.calls %} 35 |

{% trans "Breakdown" %}

36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {% for query in cache.calls %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | {% endfor %} 54 | 55 |
{% trans "Time" %} (ms){% trans "Type" %}{% trans "Parameters" %}{% trans "Function" %}
{{ query.0|floatformat:"4" }}{{ query.1|escape }}{{ query.2|escape }}{{ query.3.2|escape }}: {{ query.3.3.0|escape }}
56 | {% endif %} 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Rob Hudson and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Django nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /debug_toolbar/panels/headers.py: -------------------------------------------------------------------------------- 1 | from django.template.loader import render_to_string 2 | from django.utils.translation import ugettext_lazy as _ 3 | from debug_toolbar.panels import DebugPanel 4 | 5 | class HeaderDebugPanel(DebugPanel): 6 | """ 7 | A panel to display HTTP headers. 8 | """ 9 | name = 'Header' 10 | has_content = True 11 | # List of headers we want to display 12 | header_filter = ( 13 | 'CONTENT_TYPE', 14 | 'HTTP_ACCEPT', 15 | 'HTTP_ACCEPT_CHARSET', 16 | 'HTTP_ACCEPT_ENCODING', 17 | 'HTTP_ACCEPT_LANGUAGE', 18 | 'HTTP_CACHE_CONTROL', 19 | 'HTTP_CONNECTION', 20 | 'HTTP_HOST', 21 | 'HTTP_KEEP_ALIVE', 22 | 'HTTP_REFERER', 23 | 'HTTP_USER_AGENT', 24 | 'QUERY_STRING', 25 | 'REMOTE_ADDR', 26 | 'REMOTE_HOST', 27 | 'REQUEST_METHOD', 28 | 'SCRIPT_NAME', 29 | 'SERVER_NAME', 30 | 'SERVER_PORT', 31 | 'SERVER_PROTOCOL', 32 | 'SERVER_SOFTWARE', 33 | ) 34 | 35 | def nav_title(self): 36 | return _('HTTP Headers') 37 | 38 | def title(self): 39 | return _('HTTP Headers') 40 | 41 | def url(self): 42 | return '' 43 | 44 | def process_request(self, request): 45 | self.headers = dict( 46 | [(k, request.META[k]) for k in self.header_filter if k in request.META] 47 | ) 48 | 49 | def content(self): 50 | context = self.context.copy() 51 | context.update({ 52 | 'headers': self.headers 53 | }) 54 | return render_to_string('debug_toolbar/panels/headers.html', context) 55 | -------------------------------------------------------------------------------- /debug_toolbar/panels/request_vars.py: -------------------------------------------------------------------------------- 1 | from django.template.loader import render_to_string 2 | from django.utils.translation import ugettext_lazy as _ 3 | from debug_toolbar.panels import DebugPanel 4 | 5 | class RequestVarsDebugPanel(DebugPanel): 6 | """ 7 | A panel to display request variables (POST/GET, session, cookies). 8 | """ 9 | name = 'RequestVars' 10 | has_content = True 11 | 12 | def nav_title(self): 13 | return _('Request Vars') 14 | 15 | def title(self): 16 | return _('Request Vars') 17 | 18 | def url(self): 19 | return '' 20 | 21 | def process_request(self, request): 22 | self.request = request 23 | 24 | def process_view(self, request, view_func, view_args, view_kwargs): 25 | self.view_func = view_func 26 | self.view_args = view_args 27 | self.view_kwargs = view_kwargs 28 | 29 | def content(self): 30 | context = self.context.copy() 31 | context.update({ 32 | 'get': [(k, self.request.GET.getlist(k)) for k in self.request.GET], 33 | 'post': [(k, self.request.POST.getlist(k)) for k in self.request.POST], 34 | 'cookies': [(k, self.request.COOKIES.get(k)) for k in self.request.COOKIES], 35 | 'view_func': '%s.%s' % (self.view_func.__module__, self.view_func.__name__), 36 | 'view_args': self.view_args, 37 | 'view_kwargs': self.view_kwargs 38 | }) 39 | if hasattr(self.request, 'session'): 40 | context.update({ 41 | 'session': [(k, self.request.session.get(k)) for k in self.request.session.iterkeys()] 42 | }) 43 | 44 | return render_to_string('debug_toolbar/panels/request_vars.html', context) 45 | -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com 2 | # 3 | # This module is part of python-sqlparse and is released under 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php. 5 | 6 | """Parse SQL statements.""" 7 | 8 | 9 | __version__ = '0.1.1' 10 | 11 | 12 | import os 13 | 14 | 15 | class SQLParseError(Exception): 16 | """Base class for exceptions in this module.""" 17 | 18 | 19 | # Setup namespace 20 | from debug_toolbar.utils.sqlparse import engine 21 | from debug_toolbar.utils.sqlparse import filters 22 | from debug_toolbar.utils.sqlparse import formatter 23 | 24 | 25 | def parse(sql): 26 | """Parse sql and return a list of statements. 27 | 28 | *sql* is a single string containting one or more SQL statements. 29 | 30 | Returns a tuple of :class:`~sqlparse.sql.Statement` instances. 31 | """ 32 | stack = engine.FilterStack() 33 | stack.full_analyze() 34 | return tuple(stack.run(sql)) 35 | 36 | 37 | def format(sql, **options): 38 | """Format *sql* according to *options*. 39 | 40 | Available options are documented in :ref:`formatting`. 41 | 42 | Returns the formatted SQL statement as string. 43 | """ 44 | stack = engine.FilterStack() 45 | options = formatter.validate_options(options) 46 | stack = formatter.build_filter_stack(stack, options) 47 | stack.postprocess.append(filters.SerializerUnicode()) 48 | return ''.join(stack.run(sql)) 49 | 50 | 51 | def split(sql): 52 | """Split *sql* into single statements. 53 | 54 | Returns a list of strings. 55 | """ 56 | stack = engine.FilterStack() 57 | stack.split_statements = True 58 | return [unicode(stmt) for stmt in stack.run(sql)] 59 | 60 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/templates.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |

{% trans 'Template path' %}{{ template_dirs|length|pluralize }}

3 | {% if template_dirs %} 4 |
    5 | {% for template in template_dirs %} 6 |
  1. {{ template }}
  2. 7 | {% endfor %} 8 |
9 | {% else %} 10 |

None

11 | {% endif %} 12 | 13 |

{% trans "Template" %}{{ templates|length|pluralize }}

14 | {% if templates %} 15 |
16 | {% for template in templates %} 17 |
{{ template.template.name|addslashes }}
18 |
{{ template.template.origin_name|addslashes }}
19 | {% if template.context %} 20 |
21 | 22 | 23 |
24 | {% endif %} 25 | {% endfor %} 26 |
27 | {% else %} 28 |

{% trans 'None' %}

29 | {% endif %} 30 | 31 |

{% trans 'Context processor' %}{{ context_processors|length|pluralize }}

32 | {% if context_processors %} 33 |
34 | {% for key, value in context_processors.iteritems %} 35 |
{{ key|escape }}
36 |
37 | 38 | 39 |
40 | {% endfor %} 41 |
42 | {% else %} 43 |

{% trans 'None' %}

44 | {% endif %} 45 | -------------------------------------------------------------------------------- /example/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | PROJECT_PATH = os.path.realpath(os.path.dirname(__file__)) 3 | 4 | ADMIN_MEDIA_PREFIX = '/admin_media/' 5 | DATABASE_ENGINE = 'sqlite3' 6 | DATABASE_NAME = 'example.db' 7 | DEBUG = True 8 | INSTALLED_APPS = ( 9 | 'django.contrib.auth', 10 | 'django.contrib.admin', 11 | 'django.contrib.contenttypes', 12 | 'django.contrib.sessions', 13 | 'django.contrib.sites', 14 | 'debug_toolbar', 15 | ) 16 | INTERNAL_IPS = ('127.0.0.1',) 17 | MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media') 18 | MEDIA_URL = '/media' 19 | MIDDLEWARE_CLASSES = ( 20 | 'django.middleware.common.CommonMiddleware', 21 | 'django.contrib.sessions.middleware.SessionMiddleware', 22 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 23 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 24 | ) 25 | ROOT_URLCONF = 'example.urls' 26 | SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcd' 27 | SITE_ID = 1 28 | TEMPLATE_CONTEXT_PROCESSORS = ( 29 | 'django.core.context_processors.auth', 30 | 'django.core.context_processors.media', 31 | 'django.core.context_processors.request', 32 | ) 33 | TEMPLATE_DEBUG = DEBUG 34 | TEMPLATE_DIRS = (os.path.join(PROJECT_PATH, 'templates')) 35 | DEBUG_TOOLBAR_PANELS = ( 36 | 'debug_toolbar.panels.version.VersionDebugPanel', 37 | 'debug_toolbar.panels.timer.TimerDebugPanel', 38 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 39 | 'debug_toolbar.panels.headers.HeaderDebugPanel', 40 | 'debug_toolbar.panels.profiling.ProfilingDebugPanel', 41 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 42 | 'debug_toolbar.panels.sql.SQLDebugPanel', 43 | 'debug_toolbar.panels.template.TemplateDebugPanel', 44 | #'debug_toolbar.panels.cache.CacheDebugPanel', 45 | 'debug_toolbar.panels.signals.SignalDebugPanel', 46 | 'debug_toolbar.panels.logger.LoggingPanel', 47 | ) -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/base.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 6 | 7 | 55 | -------------------------------------------------------------------------------- /debug_toolbar/panels/version.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import django 4 | from django.conf import settings 5 | from django.template.loader import render_to_string 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | import debug_toolbar 9 | from debug_toolbar.panels import DebugPanel 10 | 11 | 12 | class VersionDebugPanel(DebugPanel): 13 | """ 14 | Panel that displays the Django version. 15 | """ 16 | name = 'Version' 17 | has_content = True 18 | 19 | def nav_title(self): 20 | return _('Versions') 21 | 22 | def nav_subtitle(self): 23 | return 'Django %s' % django.get_version() 24 | 25 | def url(self): 26 | return '' 27 | 28 | def title(self): 29 | return _('Versions') 30 | 31 | def content(self): 32 | versions = {} 33 | for app in settings.INSTALLED_APPS + ['django']: 34 | name = app.split('.')[-1].replace('_', ' ').capitalize() 35 | __import__(app) 36 | app = sys.modules[app] 37 | if hasattr(app, 'get_version'): 38 | get_version = app.get_version 39 | if callable(get_version): 40 | version = get_version() 41 | else: 42 | version = get_version 43 | elif hasattr(app, 'VERSION'): 44 | version = app.VERSION 45 | elif hasattr(app, '__version__'): 46 | version = app.__version__ 47 | else: 48 | continue 49 | if isinstance(version, (list, tuple)): 50 | version = '.'.join(str(o) for o in version) 51 | versions[name] = version 52 | 53 | context = self.context.copy() 54 | context.update({ 55 | 'versions': versions, 56 | 'paths': sys.path, 57 | }) 58 | 59 | return render_to_string('debug_toolbar/panels/versions.html', context) 60 | -------------------------------------------------------------------------------- /debug_toolbar/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import django 3 | import SocketServer 4 | 5 | from django.conf import settings 6 | from django.views.debug import linebreak_iter 7 | 8 | # Figure out some paths 9 | django_path = os.path.realpath(os.path.dirname(django.__file__)) 10 | socketserver_path = os.path.realpath(os.path.dirname(SocketServer.__file__)) 11 | 12 | def ms_from_timedelta(td): 13 | """ 14 | Given a timedelta object, returns a float representing milliseconds 15 | """ 16 | return (td.seconds * 1000) + (td.microseconds / 1000.0) 17 | 18 | def tidy_stacktrace(strace): 19 | """ 20 | Clean up stacktrace and remove all entries that: 21 | 1. Are part of Django (except contrib apps) 22 | 2. Are part of SocketServer (used by Django's dev server) 23 | 3. Are the last entry (which is part of our stacktracing code) 24 | """ 25 | trace = [] 26 | for s in strace[:-1]: 27 | s_path = os.path.realpath(s[0]) 28 | if getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}).get('HIDE_DJANGO_SQL', True) \ 29 | and django_path in s_path and not 'django/contrib' in s_path: 30 | continue 31 | if socketserver_path in s_path: 32 | continue 33 | trace.append((s[0], s[1], s[2], s[3])) 34 | return trace 35 | 36 | def get_template_info(source, context_lines=3): 37 | line = 0 38 | upto = 0 39 | source_lines = [] 40 | before = during = after = "" 41 | 42 | origin, (start, end) = source 43 | template_source = origin.reload() 44 | 45 | for num, next in enumerate(linebreak_iter(template_source)): 46 | if start >= upto and end <= next: 47 | line = num 48 | before = template_source[upto:start] 49 | during = template_source[start:end] 50 | after = template_source[end:next] 51 | source_lines.append((num, template_source[upto:next])) 52 | upto = next 53 | 54 | top = max(1, line - context_lines) 55 | bottom = min(len(source_lines), line + 1 + context_lines) 56 | 57 | context = [] 58 | for num, content in source_lines[top:bottom]: 59 | context.append({ 60 | 'num': num, 61 | 'content': content, 62 | 'highlight': (num == line), 63 | }) 64 | 65 | return { 66 | 'name': origin.name, 67 | 'context': context, 68 | } -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/engine/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com 2 | # 3 | # This module is part of python-sqlparse and is released under 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php. 5 | 6 | """filter""" 7 | 8 | import re 9 | 10 | from debug_toolbar.utils.sqlparse import lexer, SQLParseError 11 | from debug_toolbar.utils.sqlparse.engine import grouping 12 | from debug_toolbar.utils.sqlparse.engine.filter import StatementFilter 13 | 14 | # XXX remove this when cleanup is complete 15 | Filter = object 16 | 17 | 18 | class FilterStack(object): 19 | 20 | def __init__(self): 21 | self.preprocess = [] 22 | self.stmtprocess = [] 23 | self.postprocess = [] 24 | self.split_statements = False 25 | self._grouping = False 26 | 27 | def _flatten(self, stream): 28 | for token in stream: 29 | if token.is_group(): 30 | for t in self._flatten(token.tokens): 31 | yield t 32 | else: 33 | yield token 34 | 35 | def enable_grouping(self): 36 | self._grouping = True 37 | 38 | def full_analyze(self): 39 | self.enable_grouping() 40 | 41 | def run(self, sql): 42 | stream = lexer.tokenize(sql) 43 | # Process token stream 44 | if self.preprocess: 45 | for filter_ in self.preprocess: 46 | stream = filter_.process(self, stream) 47 | 48 | if (self.stmtprocess or self.postprocess or self.split_statements 49 | or self._grouping): 50 | splitter = StatementFilter() 51 | stream = splitter.process(self, stream) 52 | 53 | if self._grouping: 54 | def _group(stream): 55 | for stmt in stream: 56 | grouping.group(stmt) 57 | yield stmt 58 | stream = _group(stream) 59 | 60 | if self.stmtprocess: 61 | def _run(stream): 62 | ret = [] 63 | for stmt in stream: 64 | for filter_ in self.stmtprocess: 65 | filter_.process(self, stmt) 66 | ret.append(stmt) 67 | return ret 68 | stream = _run(stream) 69 | 70 | if self.postprocess: 71 | def _run(stream): 72 | for stmt in stream: 73 | stmt.tokens = list(self._flatten(stmt.tokens)) 74 | for filter_ in self.postprocess: 75 | stmt = filter_.process(self, stmt) 76 | yield stmt 77 | stream = _run(stream) 78 | 79 | return stream 80 | 81 | -------------------------------------------------------------------------------- /debug_toolbar/utils/tracking/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | import types 4 | 5 | def post_dispatch(func): 6 | def wrapped(callback): 7 | register_hook(func, 'after', callback) 8 | return callback 9 | return wrapped 10 | 11 | def pre_dispatch(func): 12 | def wrapped(callback): 13 | register_hook(func, 'before', callback) 14 | return callback 15 | return wrapped 16 | 17 | def replace_call(func): 18 | def inner(callback): 19 | def wrapped(*args, **kwargs): 20 | return callback(func, *args, **kwargs) 21 | 22 | actual = getattr(func, '__wrapped__', func) 23 | wrapped.__wrapped__ = actual 24 | wrapped.__doc__ = getattr(actual, '__doc__', None) 25 | wrapped.__name__ = actual.__name__ 26 | 27 | _replace_function(func, wrapped) 28 | return wrapped 29 | return inner 30 | 31 | def fire_hook(hook, sender, **kwargs): 32 | try: 33 | for callback in callbacks[hook].get(id(sender), []): 34 | callback(sender=sender, **kwargs) 35 | except Exception, e: 36 | # Log the exception, dont mess w/ the underlying function 37 | logging.exception(e) 38 | 39 | def _replace_function(func, wrapped): 40 | if isinstance(func, types.FunctionType): 41 | if func.__module__ == '__builtin__': 42 | # oh shit 43 | __builtins__[func] = wrapped 44 | else: 45 | module = __import__(func.__module__, {}, {}, [func.__module__], 0) 46 | setattr(module, func.__name__, wrapped) 47 | elif getattr(func, 'im_self', None): 48 | # TODO: classmethods 49 | raise NotImplementedError 50 | elif hasattr(func, 'im_class'): 51 | # for unbound methods 52 | setattr(func.im_class, func.__name__, wrapped) 53 | else: 54 | raise NotImplementedError 55 | 56 | callbacks = { 57 | 'before': {}, 58 | 'after': {}, 59 | } 60 | 61 | def register_hook(func, hook, callback): 62 | """ 63 | def myhook(sender, args, kwargs): 64 | print func, "executed 65 | print "args:", args 66 | print "kwargs:", kwargs 67 | register_hook(BaseDatabaseWrapper.cursor, 'before', myhook) 68 | """ 69 | 70 | assert hook in ('before', 'after') 71 | 72 | def wrapped(*args, **kwargs): 73 | start = time.time() 74 | fire_hook('before', sender=wrapped.__wrapped__, args=args, kwargs=kwargs, 75 | start=start) 76 | result = wrapped.__wrapped__(*args, **kwargs) 77 | stop = time.time() 78 | fire_hook('after', sender=wrapped.__wrapped__, args=args, kwargs=kwargs, 79 | result=result, start=start, stop=stop) 80 | actual = getattr(func, '__wrapped__', func) 81 | wrapped.__wrapped__ = actual 82 | wrapped.__doc__ = getattr(actual, '__doc__', None) 83 | wrapped.__name__ = actual.__name__ 84 | 85 | id_ = id(actual) 86 | if id_ not in callbacks[hook]: 87 | callbacks[hook][id_] = [] 88 | callbacks[hook][id_].append(callback) 89 | 90 | _replace_function(func, wrapped) -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/request_vars.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 |

{% trans 'View information' %}

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 25 | 26 | 27 |
{% trans 'View Function' %}{% trans 'args' %}{% trans 'kwargs' %}
{{ view_func }}{{ view_args|default:"None" }} 17 | {% if view_kwargs.items %} 18 | {% for k, v in view_kwargs.items %} 19 | {{ k }}={{ v }}{% if not forloop.last %}, {% endif %} 20 | {% endfor %} 21 | {% else %} 22 | None 23 | {% endif %} 24 |
28 | 29 |

{% trans 'COOKIES Variables' %}

30 | {% if cookies %} 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% for key, value in cookies %} 44 | 45 | 46 | 47 | 48 | {% endfor %} 49 | 50 |
{% trans "Variable" %}{% trans "Value" %}
{{ key|escape }}{{ value|escape }}
51 | {% else %} 52 |

{% trans "No COOKIE data" %}

53 | {% endif %} 54 | 55 |

{% trans 'SESSION Variables' %}

56 | {% if session %} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {% for key, value in session %} 70 | 71 | 72 | 73 | 74 | {% endfor %} 75 | 76 |
{% trans "Variable" %}{% trans "Value" %}
{{ key|escape }}{{ value|escape }}
77 | {% else %} 78 |

{% trans "No SESSION data" %}

79 | {% endif %} 80 | 81 |

{% trans 'GET Variables' %}

82 | {% if get %} 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | {% for key, value in get %} 92 | 93 | 94 | 95 | 96 | {% endfor %} 97 | 98 |
{% trans "Variable" %}{% trans "Value" %}
{{ key|escape }}{{ value|join:", "|escape }}
99 | {% else %} 100 |

{% trans "No GET data" %}

101 | {% endif %} 102 | 103 |

{% trans 'POST Variables' %}

104 | {% if post %} 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | {% for key, value in post %} 114 | 115 | 116 | 117 | 118 | {% endfor %} 119 | 120 |
{% trans "Variable" %}{% trans "Value" %}
{{ key|escape }}{{ value|join:", "|escape }}
121 | {% else %} 122 |

{% trans "No POST data" %}

123 | {% endif %} 124 | -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/engine/filter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from debug_toolbar.utils.sqlparse import tokens as T 4 | from debug_toolbar.utils.sqlparse.engine.grouping import Statement, Token 5 | 6 | 7 | class TokenFilter(object): 8 | 9 | def __init__(self, **options): 10 | self.options = options 11 | 12 | def process(self, stack, stream): 13 | """Process token stream.""" 14 | raise NotImplementedError 15 | 16 | 17 | class StatementFilter(TokenFilter): 18 | 19 | def __init__(self): 20 | TokenFilter.__init__(self) 21 | self._in_declare = False 22 | self._in_dbldollar = False 23 | self._is_create = False 24 | 25 | def _reset(self): 26 | self._in_declare = False 27 | self._in_dbldollar = False 28 | self._is_create = False 29 | 30 | def _change_splitlevel(self, ttype, value): 31 | # PostgreSQL 32 | if (ttype == T.Name.Builtin 33 | and value.startswith('$') and value.endswith('$')): 34 | if self._in_dbldollar: 35 | self._in_dbldollar = False 36 | return -1 37 | else: 38 | self._in_dbldollar = True 39 | return 1 40 | elif self._in_dbldollar: 41 | return 0 42 | 43 | # ANSI 44 | if ttype is not T.Keyword: 45 | return 0 46 | 47 | unified = value.upper() 48 | 49 | if unified == 'DECLARE': 50 | self._in_declare = True 51 | return 1 52 | 53 | if unified == 'BEGIN': 54 | if self._in_declare: 55 | return 0 56 | return 0 57 | 58 | if unified == 'END': 59 | # Should this respect a preceeding BEGIN? 60 | # In CASE ... WHEN ... END this results in a split level -1. 61 | return -1 62 | 63 | if ttype is T.Keyword.DDL and unified.startswith('CREATE'): 64 | self._is_create = True 65 | 66 | if unified in ('IF', 'FOR') and self._is_create: 67 | return 1 68 | 69 | # Default 70 | return 0 71 | 72 | def process(self, stack, stream): 73 | splitlevel = 0 74 | stmt = None 75 | consume_ws = False 76 | stmt_tokens = [] 77 | for ttype, value in stream: 78 | # Before appending the token 79 | if (consume_ws and ttype is not T.Whitespace 80 | and ttype is not T.Comment.Single): 81 | consume_ws = False 82 | stmt.tokens = stmt_tokens 83 | yield stmt 84 | self._reset() 85 | stmt = None 86 | splitlevel = 0 87 | if stmt is None: 88 | stmt = Statement() 89 | stmt_tokens = [] 90 | splitlevel += self._change_splitlevel(ttype, value) 91 | # Append the token 92 | stmt_tokens.append(Token(ttype, value)) 93 | # After appending the token 94 | if (splitlevel <= 0 and ttype is T.Punctuation 95 | and value == ';'): 96 | consume_ws = True 97 | if stmt is not None: 98 | stmt.tokens = stmt_tokens 99 | yield stmt 100 | -------------------------------------------------------------------------------- /debug_toolbar/panels/signals.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from django.conf import settings 4 | from django.core.signals import request_started, request_finished, \ 5 | got_request_exception 6 | from django.db.models.signals import class_prepared, pre_init, post_init, \ 7 | pre_save, post_save, pre_delete, post_delete, post_syncdb 8 | from django.dispatch.dispatcher import WEAKREF_TYPES 9 | from django.template.loader import render_to_string 10 | from django.utils.translation import ugettext_lazy as _ 11 | 12 | try: 13 | from django.db.backends.signals import connection_created 14 | except ImportError: 15 | connection_created = None 16 | 17 | from debug_toolbar.panels import DebugPanel 18 | 19 | class SignalDebugPanel(DebugPanel): 20 | name = "Signals" 21 | has_content = True 22 | 23 | SIGNALS = { 24 | 'request_started': request_started, 25 | 'request_finished': request_finished, 26 | 'got_request_exception': got_request_exception, 27 | 'connection_created': connection_created, 28 | 'class_prepared': class_prepared, 29 | 'pre_init': pre_init, 30 | 'post_init': post_init, 31 | 'pre_save': pre_save, 32 | 'post_save': post_save, 33 | 'pre_delete': pre_delete, 34 | 'post_delete': post_delete, 35 | 'post_syncdb': post_syncdb, 36 | } 37 | 38 | def nav_title(self): 39 | return _("Signals") 40 | 41 | def title(self): 42 | return _("Signals") 43 | 44 | def url(self): 45 | return '' 46 | 47 | def signals(self): 48 | signals = self.SIGNALS.copy() 49 | if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): 50 | extra_signals = settings.DEBUG_TOOLBAR_CONFIG.get('EXTRA_SIGNALS', []) 51 | else: 52 | extra_signals = [] 53 | for signal in extra_signals: 54 | parts = signal.split('.') 55 | path = '.'.join(parts[:-1]) 56 | __import__(path) 57 | signals[parts[-1]] = getattr(sys.modules[path], parts[-1]) 58 | return signals 59 | signals = property(signals) 60 | 61 | def content(self): 62 | signals = [] 63 | keys = self.signals.keys() 64 | keys.sort() 65 | for name in keys: 66 | signal = self.signals[name] 67 | if signal is None: 68 | continue 69 | receivers = [] 70 | for (receiverkey, r_senderkey), receiver in signal.receivers: 71 | if isinstance(receiver, WEAKREF_TYPES): 72 | receiver = receiver() 73 | if receiver is None: 74 | continue 75 | if getattr(receiver, 'im_self', None) is not None: 76 | text = "method %s on %s object" % (receiver.__name__, receiver.im_self.__class__.__name__) 77 | elif getattr(receiver, 'im_class', None) is not None: 78 | text = "method %s on %s" % (receiver.__name__, receiver.im_class.__name__) 79 | else: 80 | text = "function %s" % receiver.__name__ 81 | receivers.append(text) 82 | signals.append((name, signal, receivers)) 83 | 84 | context = self.context.copy() 85 | context.update({'signals': signals}) 86 | 87 | return render_to_string('debug_toolbar/panels/signals.html', context) 88 | -------------------------------------------------------------------------------- /debug_toolbar/management/commands/debugsqlshell.py: -------------------------------------------------------------------------------- 1 | import os 2 | from optparse import make_option 3 | 4 | from django.core.management.base import NoArgsCommand 5 | from django.db.backends import util 6 | 7 | from debug_toolbar.utils import sqlparse 8 | 9 | class PrintQueryWrapper(util.CursorDebugWrapper): 10 | def execute(self, sql, params=()): 11 | try: 12 | return self.cursor.execute(sql, params) 13 | finally: 14 | raw_sql = self.db.ops.last_executed_query(self.cursor, sql, params) 15 | print sqlparse.format(raw_sql, reindent=True) 16 | print 17 | 18 | util.CursorDebugWrapper = PrintQueryWrapper 19 | 20 | # The rest is copy/paste from django/core/management/commands/shell.py 21 | 22 | class Command(NoArgsCommand): 23 | option_list = NoArgsCommand.option_list + ( 24 | make_option('--plain', action='store_true', dest='plain', 25 | help='Tells Django to use plain Python, not IPython.'), 26 | ) 27 | help = "Runs a Python interactive interpreter. Tries to use IPython, if it's available." 28 | 29 | requires_model_validation = False 30 | 31 | def handle_noargs(self, **options): 32 | # XXX: (Temporary) workaround for ticket #1796: force early loading of all 33 | # models from installed apps. 34 | from django.db.models.loading import get_models 35 | loaded_models = get_models() 36 | 37 | use_plain = options.get('plain', False) 38 | 39 | try: 40 | if use_plain: 41 | # Don't bother loading IPython, because the user wants plain Python. 42 | raise ImportError 43 | import IPython 44 | # Explicitly pass an empty list as arguments, because otherwise IPython 45 | # would use sys.argv from this script. 46 | shell = IPython.Shell.IPShell(argv=[]) 47 | shell.mainloop() 48 | except ImportError: 49 | import code 50 | # Set up a dictionary to serve as the environment for the shell, so 51 | # that tab completion works on objects that are imported at runtime. 52 | # See ticket 5082. 53 | imported_objects = {} 54 | try: # Try activating rlcompleter, because it's handy. 55 | import readline 56 | except ImportError: 57 | pass 58 | else: 59 | # We don't have to wrap the following import in a 'try', because 60 | # we already know 'readline' was imported successfully. 61 | import rlcompleter 62 | readline.set_completer(rlcompleter.Completer(imported_objects).complete) 63 | readline.parse_and_bind("tab:complete") 64 | 65 | # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system 66 | # conventions and get $PYTHONSTARTUP first then import user. 67 | if not use_plain: 68 | pythonrc = os.environ.get("PYTHONSTARTUP") 69 | if pythonrc and os.path.isfile(pythonrc): 70 | try: 71 | execfile(pythonrc) 72 | except NameError: 73 | pass 74 | # This will import .pythonrc.py as a side-effect 75 | import user 76 | code.interact(local=imported_objects) 77 | -------------------------------------------------------------------------------- /debug_toolbar/panels/timer.py: -------------------------------------------------------------------------------- 1 | try: 2 | import resource 3 | except ImportError: 4 | pass # Will fail on Win32 systems 5 | import time 6 | from django.template.loader import render_to_string 7 | from django.utils.translation import ugettext_lazy as _ 8 | from debug_toolbar.panels import DebugPanel 9 | 10 | class TimerDebugPanel(DebugPanel): 11 | """ 12 | Panel that displays the time a response took in milliseconds. 13 | """ 14 | name = 'Timer' 15 | try: # if resource module not available, don't show content panel 16 | resource 17 | except NameError: 18 | has_content = False 19 | has_resource = False 20 | else: 21 | has_content = True 22 | has_resource = True 23 | 24 | def process_request(self, request): 25 | self._start_time = time.time() 26 | if self.has_resource: 27 | self._start_rusage = resource.getrusage(resource.RUSAGE_SELF) 28 | 29 | def process_response(self, request, response): 30 | self.total_time = (time.time() - self._start_time) * 1000 31 | if self.has_resource: 32 | self._end_rusage = resource.getrusage(resource.RUSAGE_SELF) 33 | 34 | def nav_title(self): 35 | return _('Time') 36 | 37 | def nav_subtitle(self): 38 | # TODO l10n 39 | if self.has_resource: 40 | utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime 41 | stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime 42 | return 'CPU: %0.2fms (%0.2fms)' % ((utime + stime) * 1000.0, self.total_time) 43 | else: 44 | return 'TOTAL: %0.2fms' % (self.total_time) 45 | 46 | def title(self): 47 | return _('Resource Usage') 48 | 49 | def url(self): 50 | return '' 51 | 52 | def _elapsed_ru(self, name): 53 | return getattr(self._end_rusage, name) - getattr(self._start_rusage, name) 54 | 55 | def content(self): 56 | 57 | utime = 1000 * self._elapsed_ru('ru_utime') 58 | stime = 1000 * self._elapsed_ru('ru_stime') 59 | vcsw = self._elapsed_ru('ru_nvcsw') 60 | ivcsw = self._elapsed_ru('ru_nivcsw') 61 | minflt = self._elapsed_ru('ru_minflt') 62 | majflt = self._elapsed_ru('ru_majflt') 63 | 64 | # these are documented as not meaningful under Linux. If you're running BSD 65 | # feel free to enable them, and add any others that I hadn't gotten to before 66 | # I noticed that I was getting nothing but zeroes and that the docs agreed. :-( 67 | # 68 | # blkin = self._elapsed_ru('ru_inblock') 69 | # blkout = self._elapsed_ru('ru_oublock') 70 | # swap = self._elapsed_ru('ru_nswap') 71 | # rss = self._end_rusage.ru_maxrss 72 | # srss = self._end_rusage.ru_ixrss 73 | # urss = self._end_rusage.ru_idrss 74 | # usrss = self._end_rusage.ru_isrss 75 | 76 | # TODO l10n on values 77 | rows = ( 78 | (_('User CPU time'), '%0.3f msec' % utime), 79 | (_('System CPU time'), '%0.3f msec' % stime), 80 | (_('Total CPU time'), '%0.3f msec' % (utime + stime)), 81 | (_('Elapsed time'), '%0.3f msec' % self.total_time), 82 | (_('Context switches'), '%d voluntary, %d involuntary' % (vcsw, ivcsw)), 83 | # ('Memory use', '%d max RSS, %d shared, %d unshared' % (rss, srss, urss + usrss)), 84 | # ('Page faults', '%d no i/o, %d requiring i/o' % (minflt, majflt)), 85 | # ('Disk operations', '%d in, %d out, %d swapout' % (blkin, blkout, swap)), 86 | ) 87 | 88 | context = self.context.copy() 89 | context.update({ 90 | 'rows': rows, 91 | }) 92 | 93 | return render_to_string('debug_toolbar/panels/timer.html', context) 94 | -------------------------------------------------------------------------------- /debug_toolbar/panels/cache.py: -------------------------------------------------------------------------------- 1 | import time 2 | import inspect 3 | 4 | from django.core import cache 5 | from django.core.cache.backends.base import BaseCache 6 | from django.template.loader import render_to_string 7 | from django.utils.translation import ugettext_lazy as _ 8 | from debug_toolbar.panels import DebugPanel 9 | 10 | class CacheStatTracker(BaseCache): 11 | """A small class used to track cache calls.""" 12 | def __init__(self, cache): 13 | self.cache = cache 14 | self.reset() 15 | 16 | def reset(self): 17 | self.calls = [] 18 | self.hits = 0 19 | self.misses = 0 20 | self.sets = 0 21 | self.gets = 0 22 | self.get_many = 0 23 | self.deletes = 0 24 | self.total_time = 0 25 | 26 | def _get_func_info(self): 27 | stack = inspect.stack()[2] 28 | return (stack[1], stack[2], stack[3], stack[4]) 29 | 30 | def get(self, key, default=None): 31 | t = time.time() 32 | value = self.cache.get(key, default) 33 | this_time = time.time() - t 34 | self.total_time += this_time * 1000 35 | if value is None: 36 | self.misses += 1 37 | else: 38 | self.hits += 1 39 | self.gets += 1 40 | self.calls.append((this_time, 'get', (key,), self._get_func_info())) 41 | return value 42 | 43 | def set(self, key, value, timeout=None): 44 | t = time.time() 45 | self.cache.set(key, value, timeout) 46 | this_time = time.time() - t 47 | self.total_time += this_time * 1000 48 | self.sets += 1 49 | self.calls.append((this_time, 'set', (key, value, timeout), self._get_func_info())) 50 | 51 | def delete(self, key): 52 | t = time.time() 53 | self.cache.delete(key) 54 | this_time = time.time() - t 55 | self.total_time += this_time * 1000 56 | self.deletes += 1 57 | self.calls.append((this_time, 'delete', (key,), self._get_func_info())) 58 | 59 | def get_many(self, keys): 60 | t = time.time() 61 | results = self.cache.get_many(keys) 62 | this_time = time.time() - t 63 | self.total_time += this_time * 1000 64 | self.get_many += 1 65 | for key, value in results.iteritems(): 66 | if value is None: 67 | self.misses += 1 68 | else: 69 | self.hits += 1 70 | self.calls.append((this_time, 'get_many', (keys,), self._get_func_info())) 71 | 72 | class CacheDebugPanel(DebugPanel): 73 | """ 74 | Panel that displays the cache statistics. 75 | """ 76 | name = 'Cache' 77 | has_content = True 78 | 79 | def __init__(self, *args, **kwargs): 80 | super(self.__class__, self).__init__(*args, **kwargs) 81 | # This is hackish but to prevent threading issues is somewhat needed 82 | if isinstance(cache.cache, CacheStatTracker): 83 | cache.cache.reset() 84 | self.cache = cache.cache 85 | else: 86 | self.cache = CacheStatTracker(cache.cache) 87 | cache.cache = self.cache 88 | 89 | def nav_title(self): 90 | return _('Cache: %.2fms') % self.cache.total_time 91 | 92 | def title(self): 93 | return _('Cache Usage') 94 | 95 | def url(self): 96 | return '' 97 | 98 | def content(self): 99 | context = self.context.copy() 100 | context.update({ 101 | 'cache_calls': len(self.cache.calls), 102 | 'cache_time': self.cache.total_time, 103 | 'cache': self.cache, 104 | }) 105 | return render_to_string('debug_toolbar/panels/cache.html', context) 106 | -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/tokens.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com 2 | # 3 | # This module is part of python-sqlparse and is released under 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php. 5 | 6 | # The Token implementation is based on pygment's token system written 7 | # by Georg Brandl. 8 | # http://pygments.org/ 9 | 10 | """Tokens""" 11 | 12 | try: 13 | set 14 | except NameError: 15 | from sets import Set as set 16 | 17 | 18 | class _TokenType(tuple): 19 | parent = None 20 | 21 | def split(self): 22 | buf = [] 23 | node = self 24 | while node is not None: 25 | buf.append(node) 26 | node = node.parent 27 | buf.reverse() 28 | return buf 29 | 30 | def __init__(self, *args): 31 | # no need to call super.__init__ 32 | self.subtypes = set() 33 | 34 | def __contains__(self, val): 35 | return self is val or ( 36 | type(val) is self.__class__ and 37 | val[:len(self)] == self 38 | ) 39 | 40 | def __getattr__(self, val): 41 | if not val or not val[0].isupper(): 42 | return tuple.__getattribute__(self, val) 43 | new = _TokenType(self + (val,)) 44 | setattr(self, val, new) 45 | self.subtypes.add(new) 46 | new.parent = self 47 | return new 48 | 49 | def __hash__(self): 50 | return hash(tuple(self)) 51 | 52 | def __repr__(self): 53 | return 'Token' + (self and '.' or '') + '.'.join(self) 54 | 55 | 56 | Token = _TokenType() 57 | 58 | # Special token types 59 | Text = Token.Text 60 | Whitespace = Text.Whitespace 61 | Newline = Whitespace.Newline 62 | Error = Token.Error 63 | # Text that doesn't belong to this lexer (e.g. HTML in PHP) 64 | Other = Token.Other 65 | 66 | # Common token types for source code 67 | Keyword = Token.Keyword 68 | Name = Token.Name 69 | Literal = Token.Literal 70 | String = Literal.String 71 | Number = Literal.Number 72 | Punctuation = Token.Punctuation 73 | Operator = Token.Operator 74 | Wildcard = Token.Wildcard 75 | Comment = Token.Comment 76 | Assignment = Token.Assignement 77 | 78 | # Generic types for non-source code 79 | Generic = Token.Generic 80 | 81 | # String and some others are not direct childs of Token. 82 | # alias them: 83 | Token.Token = Token 84 | Token.String = String 85 | Token.Number = Number 86 | 87 | # SQL specific tokens 88 | DML = Keyword.DML 89 | DDL = Keyword.DDL 90 | Command = Keyword.Command 91 | 92 | Group = Token.Group 93 | Group.Parenthesis = Token.Group.Parenthesis 94 | Group.Comment = Token.Group.Comment 95 | Group.Where = Token.Group.Where 96 | 97 | 98 | def is_token_subtype(ttype, other): 99 | """ 100 | Return True if ``ttype`` is a subtype of ``other``. 101 | 102 | exists for backwards compatibility. use ``ttype in other`` now. 103 | """ 104 | return ttype in other 105 | 106 | 107 | def string_to_tokentype(s): 108 | """ 109 | Convert a string into a token type:: 110 | 111 | >>> string_to_token('String.Double') 112 | Token.Literal.String.Double 113 | >>> string_to_token('Token.Literal.Number') 114 | Token.Literal.Number 115 | >>> string_to_token('') 116 | Token 117 | 118 | Tokens that are already tokens are returned unchanged: 119 | 120 | >>> string_to_token(String) 121 | Token.Literal.String 122 | """ 123 | if isinstance(s, _TokenType): 124 | return s 125 | if not s: 126 | return Token 127 | node = Token 128 | for item in s.split('.'): 129 | node = getattr(node, item) 130 | return node 131 | 132 | -------------------------------------------------------------------------------- /debug_toolbar/toolbar/loader.py: -------------------------------------------------------------------------------- 1 | """ 2 | The main DebugToolbar class that loads and renders the Toolbar. 3 | """ 4 | import os.path, os 5 | 6 | from django.conf import settings 7 | from django.template.loader import render_to_string 8 | from django.utils.datastructures import SortedDict 9 | from django.utils.safestring import mark_safe 10 | 11 | class DebugToolbar(object): 12 | 13 | def __init__(self, request): 14 | self.request = request 15 | self._panels = SortedDict() 16 | base_url = self.request.META.get('SCRIPT_NAME', '') 17 | self.config = { 18 | 'INTERCEPT_REDIRECTS': True, 19 | 'MEDIA_URL': u'%s/__debug__/m/' % base_url 20 | } 21 | # Check if settings has a DEBUG_TOOLBAR_CONFIG and updated config 22 | self.config.update(getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {})) 23 | self.template_context = { 24 | 'BASE_URL': base_url, # for backwards compatibility 25 | 'DEBUG_TOOLBAR_MEDIA_URL': self.config.get('MEDIA_URL'), 26 | } 27 | # Override this tuple by copying to settings.py as `DEBUG_TOOLBAR_PANELS` 28 | self.default_panels = ( 29 | 'debug_toolbar.panels.version.VersionDebugPanel', 30 | 'debug_toolbar.panels.timer.TimerDebugPanel', 31 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 32 | 'debug_toolbar.panels.headers.HeaderDebugPanel', 33 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 34 | 'debug_toolbar.panels.sql.SQLDebugPanel', 35 | 'debug_toolbar.panels.template.TemplateDebugPanel', 36 | #'debug_toolbar.panels.cache.CacheDebugPanel', 37 | 'debug_toolbar.panels.signals.SignalDebugPanel', 38 | 'debug_toolbar.panels.logger.LoggingPanel', 39 | ) 40 | self.load_panels() 41 | 42 | def _get_panels(self): 43 | return self._panels.values() 44 | panels = property(_get_panels) 45 | 46 | def get_panel(self, cls): 47 | return self._panels[cls] 48 | 49 | def load_panels(self): 50 | """ 51 | Populate debug panels 52 | """ 53 | from django.conf import settings 54 | from django.core import exceptions 55 | 56 | # Check if settings has a DEBUG_TOOLBAR_PANELS, otherwise use default 57 | if hasattr(settings, 'DEBUG_TOOLBAR_PANELS'): 58 | self.default_panels = settings.DEBUG_TOOLBAR_PANELS 59 | 60 | for panel_path in self.default_panels: 61 | try: 62 | dot = panel_path.rindex('.') 63 | except ValueError: 64 | raise exceptions.ImproperlyConfigured, '%s isn\'t a debug panel module' % panel_path 65 | panel_module, panel_classname = panel_path[:dot], panel_path[dot+1:] 66 | try: 67 | mod = __import__(panel_module, {}, {}, ['']) 68 | except ImportError, e: 69 | raise exceptions.ImproperlyConfigured, 'Error importing debug panel %s: "%s"' % (panel_module, e) 70 | try: 71 | panel_class = getattr(mod, panel_classname) 72 | except AttributeError: 73 | raise exceptions.ImproperlyConfigured, 'Toolbar Panel module "%s" does not define a "%s" class' % (panel_module, panel_classname) 74 | 75 | try: 76 | panel_instance = panel_class(context=self.template_context) 77 | except: 78 | raise # Bubble up problem loading panel 79 | 80 | self._panels[panel_class] = panel_instance 81 | 82 | def render_toolbar(self): 83 | """ 84 | Renders the overall Toolbar with panels inside. 85 | """ 86 | media_path = os.path.join(os.path.dirname(__file__), os.pardir, 'media', 'debug_toolbar') 87 | 88 | context = self.template_context.copy() 89 | context.update({ 90 | 'panels': self.panels, 91 | 'js': mark_safe(open(os.path.join(media_path, 'js', 'toolbar.min.js'), 'r').read()), 92 | 'css': mark_safe(open(os.path.join(media_path, 'css', 'toolbar.min.css'), 'r').read()), 93 | }) 94 | 95 | return render_to_string('debug_toolbar/base.html', context) 96 | -------------------------------------------------------------------------------- /debug_toolbar/utils/tracking/db.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | from datetime import datetime 5 | 6 | from django.conf import settings 7 | from django.template import Node 8 | from django.utils import simplejson 9 | from django.utils.encoding import force_unicode 10 | from django.utils.hashcompat import sha_constructor 11 | 12 | from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, get_template_info 13 | from debug_toolbar.utils.compat.db import connections 14 | # TODO:This should be set in the toolbar loader as a default and panels should 15 | # get a copy of the toolbar object with access to its config dictionary 16 | SQL_WARNING_THRESHOLD = getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}) \ 17 | .get('SQL_WARNING_THRESHOLD', 500) 18 | 19 | class CursorWrapper(object): 20 | """ 21 | Wraps a cursor and logs queries. 22 | """ 23 | 24 | def __init__(self, cursor, db, logger): 25 | self.cursor = cursor 26 | # Instance of a BaseDatabaseWrapper subclass 27 | self.db = db 28 | # logger must implement a ``record`` method 29 | self.logger = logger 30 | 31 | def execute(self, sql, params=()): 32 | start = datetime.now() 33 | try: 34 | return self.cursor.execute(sql, params) 35 | finally: 36 | stop = datetime.now() 37 | duration = ms_from_timedelta(stop - start) 38 | stacktrace = tidy_stacktrace(traceback.extract_stack()) 39 | _params = '' 40 | try: 41 | _params = simplejson.dumps([force_unicode(x, strings_only=True) for x in params]) 42 | except TypeError: 43 | pass # object not JSON serializable 44 | 45 | template_info = None 46 | cur_frame = sys._getframe().f_back 47 | try: 48 | while cur_frame is not None: 49 | if cur_frame.f_code.co_name == 'render': 50 | node = cur_frame.f_locals['self'] 51 | if isinstance(node, Node): 52 | template_info = get_template_info(node.source) 53 | break 54 | cur_frame = cur_frame.f_back 55 | except: 56 | pass 57 | del cur_frame 58 | 59 | alias = getattr(self, 'alias', 'default') 60 | conn = connections[alias].connection 61 | # HACK: avoid imports 62 | if conn: 63 | engine = conn.__class__.__module__.split('.', 1)[0] 64 | else: 65 | engine = 'unknown' 66 | 67 | params = { 68 | 'engine': engine, 69 | 'alias': alias, 70 | 'sql': self.db.ops.last_executed_query(self.cursor, sql, params), 71 | 'duration': duration, 72 | 'raw_sql': sql, 73 | 'params': _params, 74 | 'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), 75 | 'stacktrace': stacktrace, 76 | 'start_time': start, 77 | 'stop_time': stop, 78 | 'is_slow': (duration > SQL_WARNING_THRESHOLD), 79 | 'is_select': sql.lower().strip().startswith('select'), 80 | 'template_info': template_info, 81 | } 82 | 83 | if engine == 'psycopg2': 84 | params.update({ 85 | 'trans_id': self.logger.get_transaction_id(alias), 86 | 'trans_status': conn.get_transaction_status(), 87 | 'iso_level': conn.isolation_level, 88 | 'encoding': conn.encoding, 89 | }) 90 | 91 | 92 | # We keep `sql` to maintain backwards compatibility 93 | self.logger.record(**params) 94 | 95 | def executemany(self, sql, param_list): 96 | return self.cursor.executemany(sql, param_list) 97 | 98 | def __getattr__(self, attr): 99 | if attr in self.__dict__: 100 | return self.__dict__[attr] 101 | else: 102 | return getattr(self.cursor, attr) 103 | 104 | def __iter__(self): 105 | return iter(self.cursor) -------------------------------------------------------------------------------- /debug_toolbar/panels/logger.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | try: 4 | import threading 5 | except ImportError: 6 | threading = None 7 | from django.template.loader import render_to_string 8 | from django.utils.translation import ugettext_lazy as _ 9 | from debug_toolbar.panels import DebugPanel 10 | 11 | 12 | class LogCollector(object): 13 | def __init__(self): 14 | if threading is None: 15 | raise NotImplementedError("threading module is not available, \ 16 | the logging panel cannot be used without it") 17 | self.records = {} # a dictionary that maps threads to log records 18 | 19 | def add_record(self, record, thread=None): 20 | # Avoid logging SQL queries since they are already in the SQL panel 21 | # TODO: Make this check whether SQL panel is enabled 22 | if record.get('channel', '') == 'django.db.backends': 23 | return 24 | 25 | self.get_records(thread).append(record) 26 | 27 | def get_records(self, thread=None): 28 | """ 29 | Returns a list of records for the provided thread, of if none is provided, 30 | returns a list for the current thread. 31 | """ 32 | if thread is None: 33 | thread = threading.currentThread() 34 | if thread not in self.records: 35 | self.records[thread] = [] 36 | return self.records[thread] 37 | 38 | def clear_records(self, thread=None): 39 | if thread is None: 40 | thread = threading.currentThread() 41 | if thread in self.records: 42 | del self.records[thread] 43 | 44 | 45 | class ThreadTrackingHandler(logging.Handler): 46 | def __init__(self, collector): 47 | logging.Handler.__init__(self) 48 | self.collector = collector 49 | 50 | def emit(self, record): 51 | record = { 52 | 'message': record.getMessage(), 53 | 'time': datetime.datetime.fromtimestamp(record.created), 54 | 'level': record.levelname, 55 | 'file': record.pathname, 56 | 'line': record.lineno, 57 | 'channel': record.name, 58 | } 59 | self.collector.add_record(record) 60 | 61 | 62 | collector = LogCollector() 63 | logging_handler = ThreadTrackingHandler(collector) 64 | logging.root.setLevel(logging.NOTSET) 65 | logging.root.addHandler(logging_handler) # register with logging 66 | 67 | try: 68 | import logbook 69 | logbook_supported = True 70 | except ImportError: 71 | # logbook support is optional, so fail silently 72 | logbook_supported = False 73 | 74 | if logbook_supported: 75 | class LogbookThreadTrackingHandler(logbook.handlers.Handler): 76 | def __init__(self, collector): 77 | logbook.handlers.Handler.__init__(self, bubble=True) 78 | self.collector = collector 79 | 80 | def emit(self, record): 81 | record = { 82 | 'message': record.message, 83 | 'time': record.time, 84 | 'level': record.level_name, 85 | 'file': record.filename, 86 | 'line': record.lineno, 87 | 'channel': record.channel, 88 | } 89 | self.collector.add_record(record) 90 | 91 | 92 | logbook_handler = LogbookThreadTrackingHandler(collector) 93 | logbook_handler.push_application() # register with logbook 94 | 95 | class LoggingPanel(DebugPanel): 96 | name = 'Logging' 97 | has_content = True 98 | 99 | def process_request(self, request): 100 | collector.clear_records() 101 | 102 | def get_and_delete(self): 103 | records = collector.get_records() 104 | collector.clear_records() 105 | return records 106 | 107 | def nav_title(self): 108 | return _("Logging") 109 | 110 | def nav_subtitle(self): 111 | # FIXME l10n: use ngettext 112 | return "%s message%s" % (len(collector.get_records()), (len(collector.get_records()) == 1) and '' or 's') 113 | 114 | def title(self): 115 | return _('Log Messages') 116 | 117 | def url(self): 118 | return '' 119 | 120 | def content(self): 121 | records = self.get_and_delete() 122 | context = self.context.copy() 123 | context.update({'records': records}) 124 | 125 | return render_to_string('debug_toolbar/panels/logger.html', context) 126 | 127 | -------------------------------------------------------------------------------- /debug_toolbar/templates/debug_toolbar/panels/sql.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
3 |
    4 | {% for alias, info in databases %} 5 |
  • 6 |   {{ alias }} 7 | {{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %}) 8 |
  • 9 | {% endfor %} 10 |
11 |
12 | 13 | {% if queries %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% for query in queries %} 26 | 27 | 28 | 31 | 36 | 39 | 42 | 53 | 54 | 55 | 56 | 81 | 82 | {% endfor %} 83 | 84 |
 {% trans 'Query' %}{% trans 'Timeline' %}{% trans 'Time (ms)' %}{% trans "Action" %}
  29 | + 30 | 32 |
33 |
{{ query.sql|safe }}
34 |
35 |
37 |
{{ query.width_ratio }}%
38 |
40 | {{ query.duration|floatformat:"2" }} 41 | 43 | {% if query.params %} 44 | {% if query.is_select %} 45 | Sel 46 | Expl 47 | {% ifequal query.engine 'mysql' %} 48 | Prof 49 | {% endifequal %} 50 | {% endif %} 51 | {% endif %} 52 |
57 |
58 |

Connection: {{ query.alias }}

59 | {% if query.iso_level %} 60 |

Isolation Level: {{ query.iso_level }}

61 | {% endif %} 62 | {% if query.trans_status %} 63 |

Transaction Status: {{ query.trans_status }}

64 | {% endif %} 65 | {% if query.stacktrace %} 66 |
{{ query.stacktrace }}
67 | {% endif %} 68 | {% if query.template_info %} 69 | 70 | {% for line in query.template_info.context %} 71 | 72 | 73 | 74 | 75 | {% endfor %} 76 |
{{ line.num }}{{ line.content }}
77 |

{{ query.template_info.name|default:"(unknown)" }}

78 | {% endif %} 79 |
80 |
85 | {% else %} 86 |

No SQL queries were recorded during this request.

87 | {% endif %} 88 | -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/js/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cookie plugin 3 | * 4 | * Copyright (c) 2006 Klaus Hartl (stilbuero.de) 5 | * Dual licensed under the MIT and GPL licenses: 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * http://www.gnu.org/licenses/gpl.html 8 | * 9 | */ 10 | 11 | /** 12 | * Create a cookie with the given name and value and other optional parameters. 13 | * 14 | * @example $.cookie('the_cookie', 'the_value'); 15 | * @desc Set the value of a cookie. 16 | * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); 17 | * @desc Create a cookie with all available options. 18 | * @example $.cookie('the_cookie', 'the_value'); 19 | * @desc Create a session cookie. 20 | * @example $.cookie('the_cookie', null); 21 | * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain 22 | * used when the cookie was set. 23 | * 24 | * @param String name The name of the cookie. 25 | * @param String value The value of the cookie. 26 | * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. 27 | * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. 28 | * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. 29 | * If set to null or omitted, the cookie will be a session cookie and will not be retained 30 | * when the the browser exits. 31 | * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). 32 | * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). 33 | * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will 34 | * require a secure protocol (like HTTPS). 35 | * @type undefined 36 | * 37 | * @name $.cookie 38 | * @cat Plugins/Cookie 39 | * @author Klaus Hartl/klaus.hartl@stilbuero.de 40 | */ 41 | 42 | /** 43 | * Get the value of a cookie with the given name. 44 | * 45 | * @example $.cookie('the_cookie'); 46 | * @desc Get the value of a cookie. 47 | * 48 | * @param String name The name of the cookie. 49 | * @return The value of the cookie. 50 | * @type String 51 | * 52 | * @name $.cookie 53 | * @cat Plugins/Cookie 54 | * @author Klaus Hartl/klaus.hartl@stilbuero.de 55 | */ 56 | jQuery.cookie = function(name, value, options) { 57 | if (typeof value != 'undefined') { // name and value given, set cookie 58 | options = options || {}; 59 | if (value === null) { 60 | value = ''; 61 | options.expires = -1; 62 | } 63 | var expires = ''; 64 | if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { 65 | var date; 66 | if (typeof options.expires == 'number') { 67 | date = new Date(); 68 | date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); 69 | } else { 70 | date = options.expires; 71 | } 72 | expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE 73 | } 74 | // CAUTION: Needed to parenthesize options.path and options.domain 75 | // in the following expressions, otherwise they evaluate to undefined 76 | // in the packed version for some reason... 77 | var path = options.path ? '; path=' + (options.path) : ''; 78 | var domain = options.domain ? '; domain=' + (options.domain) : ''; 79 | var secure = options.secure ? '; secure' : ''; 80 | document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); 81 | } else { // only name given, get cookie 82 | var cookieValue = null; 83 | if (document.cookie && document.cookie != '') { 84 | var cookies = document.cookie.split(';'); 85 | for (var i = 0; i < cookies.length; i++) { 86 | var cookie = jQuery.trim(cookies[i]); 87 | // Does this cookie string begin with the name we want? 88 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 89 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 90 | break; 91 | } 92 | } 93 | } 94 | return cookieValue; 95 | } 96 | }; -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/formatter.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com 2 | # 3 | # This module is part of python-sqlparse and is released under 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php. 5 | 6 | """SQL formatter""" 7 | 8 | from debug_toolbar.utils.sqlparse import SQLParseError 9 | from debug_toolbar.utils.sqlparse import filters 10 | 11 | 12 | def validate_options(options): 13 | """Validates options.""" 14 | kwcase = options.get('keyword_case', None) 15 | if kwcase not in [None, 'upper', 'lower', 'capitalize']: 16 | raise SQLParseError('Invalid value for keyword_case: %r' % kwcase) 17 | 18 | idcase = options.get('identifier_case', None) 19 | if idcase not in [None, 'upper', 'lower', 'capitalize']: 20 | raise SQLParseError('Invalid value for identifier_case: %r' % idcase) 21 | 22 | ofrmt = options.get('output_format', None) 23 | if ofrmt not in [None, 'sql', 'python', 'php']: 24 | raise SQLParseError('Unknown output format: %r' % ofrmt) 25 | 26 | strip_comments = options.get('strip_comments', False) 27 | if strip_comments not in [True, False]: 28 | raise SQLParseError('Invalid value for strip_comments: %r' 29 | % strip_comments) 30 | 31 | strip_ws = options.get('strip_whitespace', False) 32 | if strip_ws not in [True, False]: 33 | raise SQLParseError('Invalid value for strip_whitespace: %r' 34 | % strip_ws) 35 | 36 | reindent = options.get('reindent', False) 37 | if reindent not in [True, False]: 38 | raise SQLParseError('Invalid value for reindent: %r' 39 | % reindent) 40 | elif reindent: 41 | options['strip_whitespace'] = True 42 | indent_tabs = options.get('indent_tabs', False) 43 | if indent_tabs not in [True, False]: 44 | raise SQLParseError('Invalid value for indent_tabs: %r' % indent_tabs) 45 | elif indent_tabs: 46 | options['indent_char'] = '\t' 47 | else: 48 | options['indent_char'] = ' ' 49 | indent_width = options.get('indent_width', 2) 50 | try: 51 | indent_width = int(indent_width) 52 | except (TypeError, ValueError): 53 | raise SQLParseError('indent_width requires an integer') 54 | if indent_width < 1: 55 | raise SQLParseError('indent_width requires an positive integer') 56 | options['indent_width'] = indent_width 57 | 58 | right_margin = options.get('right_margin', None) 59 | if right_margin is not None: 60 | try: 61 | right_margin = int(right_margin) 62 | except (TypeError, ValueError): 63 | raise SQLParseError('right_margin requires an integer') 64 | if right_margin < 10: 65 | raise SQLParseError('right_margin requires an integer > 10') 66 | options['right_margin'] = right_margin 67 | 68 | return options 69 | 70 | 71 | def build_filter_stack(stack, options): 72 | """Setup and return a filter stack. 73 | 74 | Args: 75 | stack: :class:`~sqlparse.filters.FilterStack` instance 76 | options: Dictionary with options validated by validate_options. 77 | """ 78 | # Token filter 79 | if 'keyword_case' in options: 80 | stack.preprocess.append( 81 | filters.KeywordCaseFilter(options['keyword_case'])) 82 | 83 | if 'identifier_case' in options: 84 | stack.preprocess.append( 85 | filters.IdentifierCaseFilter(options['identifier_case'])) 86 | 87 | # After grouping 88 | if options.get('strip_comments', False): 89 | stack.enable_grouping() 90 | stack.stmtprocess.append(filters.StripCommentsFilter()) 91 | 92 | if (options.get('strip_whitespace', False) 93 | or options.get('reindent', False)): 94 | stack.enable_grouping() 95 | stack.stmtprocess.append(filters.StripWhitespaceFilter()) 96 | 97 | if options.get('reindent', False): 98 | stack.enable_grouping() 99 | stack.stmtprocess.append( 100 | filters.ReindentFilter(char=options['indent_char'], 101 | width=options['indent_width'])) 102 | 103 | if options.get('right_margin', False): 104 | stack.enable_grouping() 105 | stack.stmtprocess.append( 106 | filters.RightMarginFilter(width=options['right_margin'])) 107 | 108 | # Serializer 109 | if options.get('output_format'): 110 | frmt = options['output_format'] 111 | if frmt.lower() == 'php': 112 | fltr = filters.OutputPHPFilter() 113 | elif frmt.lower() == 'python': 114 | fltr = filters.OutputPythonFilter() 115 | else: 116 | fltr = None 117 | if fltr is not None: 118 | stack.postprocess.append(fltr) 119 | 120 | return stack 121 | 122 | 123 | -------------------------------------------------------------------------------- /debug_toolbar/middleware.py: -------------------------------------------------------------------------------- 1 | """ 2 | Debug Toolbar middleware 3 | """ 4 | import thread 5 | 6 | from django.conf import settings 7 | from django.http import HttpResponseRedirect 8 | from django.shortcuts import render_to_response 9 | from django.utils.encoding import smart_unicode 10 | from django.conf.urls.defaults import include, patterns 11 | 12 | import debug_toolbar.urls 13 | from debug_toolbar.toolbar.loader import DebugToolbar 14 | 15 | _HTML_TYPES = ('text/html', 'application/xhtml+xml') 16 | 17 | def replace_insensitive(string, target, replacement): 18 | """ 19 | Similar to string.replace() but is case insensitive 20 | Code borrowed from: http://forums.devshed.com/python-programming-11/case-insensitive-string-replace-490921.html 21 | """ 22 | no_case = string.lower() 23 | index = no_case.rfind(target.lower()) 24 | if index >= 0: 25 | return string[:index] + replacement + string[index + len(target):] 26 | else: # no results so return the original string 27 | return string 28 | 29 | class DebugToolbarMiddleware(object): 30 | """ 31 | Middleware to set up Debug Toolbar on incoming request and render toolbar 32 | on outgoing response. 33 | """ 34 | debug_toolbars = {} 35 | 36 | @classmethod 37 | def get_current(cls): 38 | return cls.debug_toolbars.get(thread.get_ident()) 39 | 40 | def __init__(self): 41 | self.override_url = True 42 | 43 | # Set method to use to decide to show toolbar 44 | self.show_toolbar = self._show_toolbar # default 45 | 46 | # The tag to attach the toolbar to 47 | self.tag= u'' 48 | 49 | if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): 50 | show_toolbar_callback = settings.DEBUG_TOOLBAR_CONFIG.get( 51 | 'SHOW_TOOLBAR_CALLBACK', None) 52 | if show_toolbar_callback: 53 | self.show_toolbar = show_toolbar_callback 54 | 55 | tag = settings.DEBUG_TOOLBAR_CONFIG.get('TAG', None) 56 | if tag: 57 | self.tag = u'' 58 | 59 | def _show_toolbar(self, request): 60 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', None) 61 | if x_forwarded_for: 62 | remote_addr = x_forwarded_for.split(',')[0].strip() 63 | else: 64 | remote_addr = request.META.get('REMOTE_ADDR', None) 65 | if not remote_addr in settings.INTERNAL_IPS \ 66 | or (request.is_ajax() and \ 67 | not debug_toolbar.urls._PREFIX in request.path) \ 68 | or not (settings.DEBUG or getattr(settings, 'TEST', False)): 69 | return False 70 | return True 71 | 72 | def process_request(self, request): 73 | if self.show_toolbar(request): 74 | if self.override_url: 75 | original_urlconf = __import__(getattr(request, 'urlconf', settings.ROOT_URLCONF), {}, {}, ['*']) 76 | debug_toolbar.urls.urlpatterns += patterns('', 77 | ('', include(original_urlconf)), 78 | ) 79 | if hasattr(original_urlconf, 'handler404'): 80 | debug_toolbar.urls.handler404 = original_urlconf.handler404 81 | if hasattr(original_urlconf, 'handler500'): 82 | debug_toolbar.urls.handler500 = original_urlconf.handler500 83 | self.override_url = False 84 | request.urlconf = 'debug_toolbar.urls' 85 | 86 | toolbar = DebugToolbar(request) 87 | for panel in toolbar.panels: 88 | panel.process_request(request) 89 | self.__class__.debug_toolbars[thread.get_ident()] = toolbar 90 | 91 | def process_view(self, request, view_func, view_args, view_kwargs): 92 | toolbar = self.__class__.debug_toolbars.get(thread.get_ident()) 93 | if not toolbar: 94 | return 95 | for panel in toolbar.panels: 96 | panel.process_view(request, view_func, view_args, view_kwargs) 97 | 98 | def process_response(self, request, response): 99 | ident = thread.get_ident() 100 | toolbar = self.__class__.debug_toolbars.get(ident) 101 | if not toolbar: 102 | return response 103 | if toolbar.config['INTERCEPT_REDIRECTS']: 104 | if isinstance(response, HttpResponseRedirect): 105 | redirect_to = response.get('Location', None) 106 | if redirect_to: 107 | cookies = response.cookies 108 | response = render_to_response( 109 | 'debug_toolbar/redirect.html', 110 | {'redirect_to': redirect_to} 111 | ) 112 | response.cookies = cookies 113 | if response.status_code == 200 and 'gzip' not in response.get('Content-Encoding', '') and \ 114 | response['Content-Type'].split(';')[0] in _HTML_TYPES: 115 | for panel in toolbar.panels: 116 | panel.process_response(request, response) 117 | response.content = replace_insensitive( 118 | smart_unicode(response.content), 119 | self.tag, 120 | smart_unicode(toolbar.render_toolbar() + self.tag)) 121 | if response.get('Content-Length', None): 122 | response['Content-Length'] = len(response.content) 123 | del self.__class__.debug_toolbars[ident] 124 | return response 125 | -------------------------------------------------------------------------------- /debug_toolbar/panels/template.py: -------------------------------------------------------------------------------- 1 | from os.path import normpath 2 | from pprint import pformat 3 | 4 | from django import http 5 | from django.conf import settings 6 | from django.template.context import get_standard_processors 7 | from django.template.loader import render_to_string 8 | from django.test.signals import template_rendered 9 | from django.utils.translation import ugettext_lazy as _ 10 | from debug_toolbar.panels import DebugPanel 11 | 12 | # Code taken and adapted from Simon Willison and Django Snippets: 13 | # http://www.djangosnippets.org/snippets/766/ 14 | 15 | # Monkeypatch instrumented test renderer from django.test.utils - we could use 16 | # django.test.utils.setup_test_environment for this but that would also set up 17 | # e-mail interception, which we don't want 18 | from django.test.utils import instrumented_test_render 19 | from django.template import Template 20 | 21 | if not hasattr(Template, '_render'): # Django < 1.2 22 | if Template.render != instrumented_test_render: 23 | Template.original_render = Template.render 24 | Template.render = instrumented_test_render 25 | else: 26 | if Template._render != instrumented_test_render: 27 | Template.original_render = Template._render 28 | Template._render = instrumented_test_render 29 | 30 | # MONSTER monkey-patch 31 | old_template_init = Template.__init__ 32 | def new_template_init(self, template_string, origin=None, name=''): 33 | old_template_init(self, template_string, origin, name) 34 | self.origin = origin 35 | Template.__init__ = new_template_init 36 | 37 | class TemplateDebugPanel(DebugPanel): 38 | """ 39 | A panel that lists all templates used during processing of a response. 40 | """ 41 | name = 'Template' 42 | has_content = True 43 | 44 | def __init__(self, *args, **kwargs): 45 | super(self.__class__, self).__init__(*args, **kwargs) 46 | self.templates = [] 47 | template_rendered.connect(self._store_template_info) 48 | 49 | def _store_template_info(self, sender, **kwargs): 50 | self.templates.append(kwargs) 51 | 52 | def nav_title(self): 53 | return _('Templates') 54 | 55 | def title(self): 56 | num_templates = len([t for t in self.templates 57 | if not (t['template'].name and t['template'].name.startswith('debug_toolbar/'))]) 58 | return _('Templates (%(num_templates)s rendered)') % {'num_templates': num_templates} 59 | 60 | def url(self): 61 | return '' 62 | 63 | def process_request(self, request): 64 | self.request = request 65 | 66 | def content(self): 67 | context_processors = dict( 68 | [ 69 | ("%s.%s" % (k.__module__, k.__name__), 70 | pformat(k(self.request))) for k in get_standard_processors() 71 | ] 72 | ) 73 | template_context = [] 74 | for template_data in self.templates: 75 | info = {} 76 | # Clean up some info about templates 77 | template = template_data.get('template', None) 78 | # Skip templates that we are generating through the debug toolbar. 79 | if template.name and template.name.startswith('debug_toolbar/'): 80 | continue 81 | if template.origin and template.origin.name: 82 | template.origin_name = template.origin.name 83 | else: 84 | template.origin_name = 'No origin' 85 | info['template'] = template 86 | # Clean up context for better readability 87 | if getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}).get('SHOW_TEMPLATE_CONTEXT', True): 88 | context_data = template_data.get('context', None) 89 | 90 | context_list = [] 91 | for context_layer in context_data.dicts: 92 | if hasattr(context_layer, 'items'): 93 | for key, value in context_layer.items(): 94 | # Replace any request elements - they have a large 95 | # unicode representation and the request data is 96 | # already made available from the Request Vars panel. 97 | if isinstance(value, http.HttpRequest): 98 | context_layer[key] = '<>' 99 | # Replace the debugging sql_queries element. The SQL 100 | # data is already made available from the SQL panel. 101 | elif key == 'sql_queries' and isinstance(value, list): 102 | context_layer[key] = '<>' 103 | # Replace LANGUAGES, which is available in i18n context processor 104 | elif key == 'LANGUAGES' and isinstance(value, tuple): 105 | context_layer[key] = '<>' 106 | try: 107 | context_list.append(pformat(context_layer)) 108 | except UnicodeEncodeError: 109 | pass 110 | info['context'] = '\n'.join(context_list) 111 | template_context.append(info) 112 | 113 | context = self.context.copy() 114 | context.update({ 115 | 'templates': template_context, 116 | 'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], 117 | 'context_processors': context_processors, 118 | }) 119 | 120 | return render_to_string('debug_toolbar/panels/templates.html', context) 121 | -------------------------------------------------------------------------------- /debug_toolbar/panels/profiling.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | from django.template.loader import render_to_string 4 | from django.utils.translation import ugettext_lazy as _ 5 | from django.utils.safestring import mark_safe 6 | from debug_toolbar.panels import DebugPanel 7 | 8 | import cProfile 9 | from pstats import Stats 10 | from colorsys import hsv_to_rgb 11 | 12 | class DjangoDebugToolbarStats(Stats): 13 | __root = None 14 | 15 | def get_root_func(self): 16 | if self.__root is None: 17 | for func, (cc, nc, tt, ct, callers) in self.stats.iteritems(): 18 | if len(callers) == 0: 19 | self.__root = func 20 | break 21 | return self.__root 22 | 23 | def print_call_tree_node(self, function, depth, max_depth, cum_filter=0.1): 24 | self.print_line(function, depth=depth) 25 | if depth < max_depth: 26 | for called in self.all_callees[function].keys(): 27 | if self.stats[called][3] >= cum_filter: 28 | self.print_call_tree_node(called, depth+1, max_depth, cum_filter=cum_filter) 29 | 30 | class FunctionCall(object): 31 | def __init__(self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=(0,0.5,1)): 32 | self.statobj = statobj 33 | self.func = func 34 | if stats: 35 | self.stats = stats 36 | else: 37 | self.stats = statobj.stats[func][:4] 38 | self.depth = depth 39 | self.id = id 40 | self.parent_ids = parent_ids 41 | self.hsv = hsv 42 | 43 | def parent_classes(self): 44 | return self.parent_classes 45 | 46 | def background(self): 47 | r,g,b = hsv_to_rgb(*self.hsv) 48 | return 'rgb(%f%%,%f%%,%f%%)' %(r*100, g*100, b*100) 49 | 50 | def func_std_string(self): # match what old profile produced 51 | func_name = self.func 52 | if func_name[:2] == ('~', 0): 53 | # special case for built-in functions 54 | name = func_name[2] 55 | if name.startswith('<') and name.endswith('>'): 56 | return '{%s}' % name[1:-1] 57 | else: 58 | return name 59 | else: 60 | file_name, line_num, method = self.func 61 | idx = file_name.find('/site-packages/') 62 | if idx > -1: 63 | file_name=file_name[idx+14:] 64 | 65 | file_path, file_name = file_name.rsplit('/', 1) 66 | 67 | return mark_safe('{0}/{1} in {3}({2})'.format( 68 | file_path, 69 | file_name, 70 | line_num, 71 | method, 72 | )) 73 | 74 | def subfuncs(self): 75 | i=0 76 | h,s,v = self.hsv 77 | count = len(self.statobj.all_callees[self.func]) 78 | for func, stats in self.statobj.all_callees[self.func].iteritems(): 79 | i += 1 80 | h1 = h + (i/count)/(self.depth+1) 81 | if stats[3] == 0: 82 | s1 = 0 83 | else: 84 | s1 = s*(stats[3]/self.stats[3]) 85 | yield FunctionCall(self.statobj, 86 | func, 87 | self.depth+1, 88 | stats=stats, 89 | id=str(self.id) + '_' + str(i), 90 | parent_ids=self.parent_ids + [self.id], 91 | hsv=(h1,s1,1)) 92 | 93 | def count(self): 94 | return self.stats[1] 95 | 96 | def tottime(self): 97 | return self.stats[2] 98 | 99 | def cumtime(self): 100 | cc, nc, tt, ct = self.stats 101 | return self.stats[3] 102 | 103 | def tottime_per_call(self): 104 | cc, nc, tt, ct = self.stats 105 | 106 | if nc == 0: 107 | return 0 108 | 109 | return tt/nc 110 | 111 | def cumtime_per_call(self): 112 | cc, nc, tt, ct = self.stats 113 | 114 | if cc == 0: 115 | return 0 116 | 117 | return ct/cc 118 | 119 | def indent(self): 120 | return 16 * self.depth 121 | 122 | class ProfilingDebugPanel(DebugPanel): 123 | """ 124 | Panel that displays the Django version. 125 | """ 126 | name = 'Profiling' 127 | has_content = True 128 | 129 | def nav_title(self): 130 | return _('Profiling') 131 | 132 | def url(self): 133 | return '' 134 | 135 | def title(self): 136 | return _('Profiling') 137 | 138 | def process_view(self, request, view_func, view_args, view_kwargs): 139 | self.profiler = cProfile.Profile() 140 | args = (request,) + view_args 141 | return self.profiler.runcall(view_func, *args, **view_kwargs) 142 | 143 | def process_response(self, request, response): 144 | self.profiler.create_stats() 145 | self.stats = DjangoDebugToolbarStats(self.profiler) 146 | return response 147 | 148 | def add_node(self, func_list, func, max_depth, cum_time=0.1): 149 | func_list.append(func) 150 | func.has_subfuncs = False 151 | if func.depth < max_depth: 152 | for subfunc in func.subfuncs(): 153 | if subfunc.stats[3] >= cum_time: 154 | func.has_subfuncs = True 155 | self.add_node(func_list, subfunc, max_depth, cum_time=cum_time) 156 | 157 | def content(self): 158 | 159 | self.stats.calc_callees() 160 | root = FunctionCall(self.stats, self.stats.get_root_func(), depth=0) 161 | 162 | func_list = [] 163 | self.add_node(func_list, root, 10, root.stats[3]/8) 164 | context = self.context.copy() 165 | context.update({ 166 | 'func_list': func_list, 167 | }) 168 | 169 | return render_to_string('debug_toolbar/panels/profiling.html', context) 170 | -------------------------------------------------------------------------------- /debug_toolbar/tests/tests.py: -------------------------------------------------------------------------------- 1 | from debug_toolbar.middleware import DebugToolbarMiddleware 2 | from debug_toolbar.panels.sql import SQLDebugPanel 3 | from debug_toolbar.toolbar.loader import DebugToolbar 4 | from debug_toolbar.utils.tracking import pre_dispatch, post_dispatch, callbacks 5 | 6 | from django.contrib.auth.models import User 7 | from django.test import TestCase 8 | 9 | from dingus import Dingus 10 | import thread 11 | 12 | class BaseTestCase(TestCase): 13 | def setUp(self): 14 | request = Dingus('request') 15 | toolbar = DebugToolbar(request) 16 | DebugToolbarMiddleware.debug_toolbars[thread.get_ident()] = toolbar 17 | self.toolbar = toolbar 18 | 19 | class DebugToolbarTestCase(BaseTestCase): 20 | urls = 'debug_toolbar.tests.urls' 21 | 22 | def test_middleware(self): 23 | resp = self.client.get('/execute_sql/') 24 | self.assertEquals(resp.status_code, 200) 25 | 26 | class SQLPanelTestCase(BaseTestCase): 27 | def test_recording(self): 28 | panel = self.toolbar.get_panel(SQLDebugPanel) 29 | self.assertEquals(len(panel._queries), 0) 30 | 31 | list(User.objects.all()) 32 | 33 | # ensure query was logged 34 | self.assertEquals(len(panel._queries), 1) 35 | query = panel._queries[0] 36 | self.assertEquals(query[0], 'default') 37 | self.assertTrue('sql' in query[1]) 38 | self.assertTrue('duration' in query[1]) 39 | self.assertTrue('stacktrace' in query[1]) 40 | 41 | def module_func(*args, **kwargs): 42 | """Used by dispatch tests""" 43 | return 'blah' 44 | 45 | class TrackingTestCase(BaseTestCase): 46 | @classmethod 47 | def class_method(cls, *args, **kwargs): 48 | return 'blah' 49 | 50 | def class_func(self, *args, **kwargs): 51 | """Used by dispatch tests""" 52 | return 'blah' 53 | 54 | def test_pre_hook(self): 55 | foo = {} 56 | 57 | @pre_dispatch(module_func) 58 | def test(**kwargs): 59 | foo.update(kwargs) 60 | 61 | self.assertTrue(hasattr(module_func, '__wrapped__')) 62 | self.assertEquals(len(callbacks['before']), 1) 63 | 64 | module_func('hi', foo='bar') 65 | 66 | self.assertTrue('sender' in foo, foo) 67 | # best we can do 68 | self.assertEquals(foo['sender'].__name__, 'module_func') 69 | self.assertTrue('start' in foo, foo) 70 | self.assertTrue(foo['start'] > 0) 71 | self.assertTrue('stop' not in foo, foo) 72 | self.assertTrue('args' in foo, foo) 73 | self.assertTrue(len(foo['args']), 1) 74 | self.assertEquals(foo['args'][0], 'hi') 75 | self.assertTrue('kwargs' in foo, foo) 76 | self.assertTrue(len(foo['kwargs']), 1) 77 | self.assertTrue('foo' in foo['kwargs']) 78 | self.assertEquals(foo['kwargs']['foo'], 'bar') 79 | 80 | callbacks['before'] = {} 81 | 82 | @pre_dispatch(TrackingTestCase.class_func) 83 | def test(**kwargs): 84 | foo.update(kwargs) 85 | 86 | self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) 87 | self.assertEquals(len(callbacks['before']), 1) 88 | 89 | self.class_func('hello', foo='bar') 90 | 91 | self.assertTrue('sender' in foo, foo) 92 | # best we can do 93 | self.assertEquals(foo['sender'].__name__, 'class_func') 94 | self.assertTrue('start' in foo, foo) 95 | self.assertTrue(foo['start'] > 0) 96 | self.assertTrue('stop' not in foo, foo) 97 | self.assertTrue('args' in foo, foo) 98 | self.assertTrue(len(foo['args']), 2) 99 | self.assertEquals(foo['args'][1], 'hello') 100 | self.assertTrue('kwargs' in foo, foo) 101 | self.assertTrue(len(foo['kwargs']), 1) 102 | self.assertTrue('foo' in foo['kwargs']) 103 | self.assertEquals(foo['kwargs']['foo'], 'bar') 104 | 105 | # callbacks['before'] = {} 106 | # 107 | # @pre_dispatch(TrackingTestCase.class_method) 108 | # def test(**kwargs): 109 | # foo.update(kwargs) 110 | # 111 | # self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) 112 | # self.assertEquals(len(callbacks['before']), 1) 113 | # 114 | # TrackingTestCase.class_method() 115 | # 116 | # self.assertTrue('sender' in foo, foo) 117 | # # best we can do 118 | # self.assertEquals(foo['sender'].__name__, 'class_method') 119 | # self.assertTrue('start' in foo, foo) 120 | # self.assertTrue('stop' not in foo, foo) 121 | # self.assertTrue('args' in foo, foo) 122 | 123 | def test_post_hook(self): 124 | foo = {} 125 | 126 | @post_dispatch(module_func) 127 | def test(**kwargs): 128 | foo.update(kwargs) 129 | 130 | self.assertTrue(hasattr(module_func, '__wrapped__')) 131 | self.assertEquals(len(callbacks['after']), 1) 132 | 133 | module_func('hi', foo='bar') 134 | 135 | self.assertTrue('sender' in foo, foo) 136 | # best we can do 137 | self.assertEquals(foo['sender'].__name__, 'module_func') 138 | self.assertTrue('start' in foo, foo) 139 | self.assertTrue(foo['start'] > 0) 140 | self.assertTrue('stop' in foo, foo) 141 | self.assertTrue(foo['stop'] > foo['start']) 142 | self.assertTrue('args' in foo, foo) 143 | self.assertTrue(len(foo['args']), 1) 144 | self.assertEquals(foo['args'][0], 'hi') 145 | self.assertTrue('kwargs' in foo, foo) 146 | self.assertTrue(len(foo['kwargs']), 1) 147 | self.assertTrue('foo' in foo['kwargs']) 148 | self.assertEquals(foo['kwargs']['foo'], 'bar') 149 | 150 | callbacks['after'] = {} 151 | 152 | @post_dispatch(TrackingTestCase.class_func) 153 | def test(**kwargs): 154 | foo.update(kwargs) 155 | 156 | self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) 157 | self.assertEquals(len(callbacks['after']), 1) 158 | 159 | self.class_func('hello', foo='bar') 160 | 161 | self.assertTrue('sender' in foo, foo) 162 | # best we can do 163 | self.assertEquals(foo['sender'].__name__, 'class_func') 164 | self.assertTrue('start' in foo, foo) 165 | self.assertTrue(foo['start'] > 0) 166 | self.assertTrue('stop' in foo, foo) 167 | self.assertTrue(foo['stop'] > foo['start']) 168 | self.assertTrue('args' in foo, foo) 169 | self.assertTrue(len(foo['args']), 2) 170 | self.assertEquals(foo['args'][1], 'hello') 171 | self.assertTrue('kwargs' in foo, foo) 172 | self.assertTrue(len(foo['kwargs']), 1) 173 | self.assertTrue('foo' in foo['kwargs']) 174 | self.assertEquals(foo['kwargs']['foo'], 'bar') -------------------------------------------------------------------------------- /debug_toolbar/media/debug_toolbar/js/toolbar.js: -------------------------------------------------------------------------------- 1 | window.djdt = (function(window, document, jQuery) { 2 | jQuery.cookie = function(name, value, options) { if (typeof value != 'undefined') { options = options || {}; if (value === null) { value = ''; options.expires = -1; } var expires = ''; if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { var date; if (typeof options.expires == 'number') { date = new Date(); date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); } else { date = options.expires; } expires = '; expires=' + date.toUTCString(); } var path = options.path ? '; path=' + (options.path) : ''; var domain = options.domain ? '; domain=' + (options.domain) : ''; var secure = options.secure ? '; secure' : ''; document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); } else { var cookieValue = null; if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = $.trim(cookies[i]); if (cookie.substring(0, name.length + 1) == (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } }; 3 | var $ = jQuery; 4 | var COOKIE_NAME = 'djdt'; 5 | var djdt = { 6 | jQuery: jQuery, 7 | events: { 8 | ready: [] 9 | }, 10 | isReady: false, 11 | init: function() { 12 | $('#djDebug').show(); 13 | var current = null; 14 | $('#djDebugPanelList li a').click(function() { 15 | if (!this.className) { 16 | return false; 17 | } 18 | current = $('#djDebug #' + this.className); 19 | if (current.is(':visible')) { 20 | $(document).trigger('close.djDebug'); 21 | $(this).parent().removeClass('active'); 22 | } else { 23 | $('.panelContent').hide(); // Hide any that are already open 24 | current.show(); 25 | $('#djDebugToolbar li').removeClass('active'); 26 | $(this).parent().addClass('active'); 27 | } 28 | return false; 29 | }); 30 | $('#djDebug a.djDebugClose').click(function() { 31 | $(document).trigger('close.djDebug'); 32 | $('#djDebugToolbar li').removeClass('active'); 33 | return false; 34 | }); 35 | $('#djDebug a.remoteCall').click(function() { 36 | $('#djDebugWindow').load(this.href, function(response, status, xhr) { 37 | if (status == "error") { 38 | var message = '
Back

'+xhr.status+': '+xhr.statusText+'

'; 39 | $('#djDebugWindow').html(message); 40 | } 41 | $('#djDebugWindow a.djDebugBack').click(function() { 42 | $(this).parent().parent().hide(); 43 | return false; 44 | }); 45 | }); 46 | $('#djDebugWindow').show(); 47 | return false; 48 | }); 49 | $('#djDebugTemplatePanel a.djTemplateShowContext').click(function() { 50 | djdt.toggle_arrow($(this).children('.toggleArrow')); 51 | djdt.toggle_content($(this).parent().next()); 52 | return false; 53 | }); 54 | $('#djDebug a.djToggleSwitch').click(function(e) { 55 | e.preventDefault(); 56 | var btn = $(this); 57 | var id = btn.attr('data-toggle-id'); 58 | var open_me = btn.text() == btn.attr('data-toggle-open'); 59 | if (id == '' || !id) { 60 | return; 61 | } 62 | 63 | $(this).parents('.djDebugPanelContent').find('.djToggleDetails_' + id).each(function(){ 64 | var $this = $(this); 65 | if (open_me) { 66 | $this.addClass('djSelected'); 67 | $this.removeClass('djUnselected'); 68 | btn.text(btn.attr('data-toggle-close')); 69 | $this.find('.djToggleSwitch').text(btn.text()); 70 | } else { 71 | $this.removeClass('djSelected'); 72 | $this.addClass('djUnselected'); 73 | btn.text(btn.attr('data-toggle-open')); 74 | $this.find('.djToggleSwitch').text(btn.text()); 75 | } 76 | }); 77 | return; 78 | }); 79 | function getSubcalls(row) { 80 | id = row.attr('id'); 81 | return $('.djDebugProfileRow[id^="'+id+'_"]'); 82 | } 83 | function getDirectSubcalls(row) { 84 | subcalls = getSubcalls(row); 85 | depth = parseInt(row.attr('depth')) + 1; 86 | return subcalls.filter('[depth='+depth+']'); 87 | } 88 | $('.djDebugProfileRow .djDebugProfileToggle').click(function(){ 89 | row = $(this).closest('.djDebugProfileRow') 90 | subcalls = getSubcalls(row); 91 | if (subcalls.css('display')=='none') { 92 | getDirectSubcalls(row).show(); 93 | } else { 94 | subcalls.hide(); 95 | } 96 | }); 97 | $('#djHideToolBarButton').click(function() { 98 | djdt.hide_toolbar(true); 99 | return false; 100 | }); 101 | $('#djShowToolBarButton').click(function() { 102 | djdt.show_toolbar(); 103 | return false; 104 | }); 105 | $(document).bind('close.djDebug', function() { 106 | // If a sub-panel is open, close that 107 | if ($('#djDebugWindow').is(':visible')) { 108 | $('#djDebugWindow').hide(); 109 | return; 110 | } 111 | // If a panel is open, close that 112 | if ($('.panelContent').is(':visible')) { 113 | $('.panelContent').hide(); 114 | return; 115 | } 116 | // Otherwise, just minimize the toolbar 117 | if ($('#djDebugToolbar').is(':visible')) { 118 | djdt.hide_toolbar(true); 119 | return; 120 | } 121 | }); 122 | if ($.cookie(COOKIE_NAME)) { 123 | djdt.hide_toolbar(false); 124 | } else { 125 | djdt.show_toolbar(false); 126 | } 127 | $('#djDebug .djDebugHoverable').hover(function(){ 128 | $(this).addClass('djDebugHover'); 129 | }, function(){ 130 | $(this).removeClass('djDebugHover'); 131 | }); 132 | djdt.isReady = true; 133 | $.each(djdt.events.ready, function(_, callback){ 134 | callback(djdt); 135 | }); 136 | }, 137 | toggle_content: function(elem) { 138 | if (elem.is(':visible')) { 139 | elem.hide(); 140 | } else { 141 | elem.show(); 142 | } 143 | }, 144 | close: function() { 145 | $(document).trigger('close.djDebug'); 146 | return false; 147 | }, 148 | hide_toolbar: function(setCookie) { 149 | // close any sub panels 150 | $('#djDebugWindow').hide(); 151 | // close all panels 152 | $('.panelContent').hide(); 153 | $('#djDebugToolbar li').removeClass('active'); 154 | // finally close toolbar 155 | $('#djDebugToolbar').hide('fast'); 156 | $('#djDebugToolbarHandle').show(); 157 | // Unbind keydown 158 | $(document).unbind('keydown.djDebug'); 159 | if (setCookie) { 160 | $.cookie(COOKIE_NAME, 'hide', { 161 | path: '/', 162 | expires: 10 163 | }); 164 | } 165 | }, 166 | show_toolbar: function(animate) { 167 | // Set up keybindings 168 | $(document).bind('keydown.djDebug', function(e) { 169 | if (e.keyCode == 27) { 170 | djdt.close(); 171 | } 172 | }); 173 | $('#djDebugToolbarHandle').hide(); 174 | if (animate) { 175 | $('#djDebugToolbar').show('fast'); 176 | } else { 177 | $('#djDebugToolbar').show(); 178 | } 179 | $.cookie(COOKIE_NAME, null, { 180 | path: '/', 181 | expires: -1 182 | }); 183 | }, 184 | toggle_arrow: function(elem) { 185 | var uarr = String.fromCharCode(0x25b6); 186 | var darr = String.fromCharCode(0x25bc); 187 | elem.html(elem.html() == uarr ? darr : uarr); 188 | }, 189 | ready: function(callback){ 190 | if (djdt.isReady) { 191 | callback(djdt); 192 | } else { 193 | djdt.events.ready.push(callback); 194 | } 195 | } 196 | }; 197 | $(document).ready(function() { 198 | djdt.init(); 199 | }); 200 | return djdt; 201 | }(window, document, jQuery.noConflict())); 202 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Django Debug Toolbar 3 | ==================== 4 | 5 | The Django Debug Toolbar is a configurable set of panels that display various 6 | debug information about the current request/response and when clicked, display 7 | more details about the panel's content. 8 | 9 | Currently, the following panels have been written and are working: 10 | 11 | - Django version 12 | - Request timer 13 | - A list of settings in settings.py 14 | - Common HTTP headers 15 | - GET/POST/cookie/session variable display 16 | - Templates and context used, and their template paths 17 | - SQL queries including time to execute and links to EXPLAIN each query 18 | - List of signals, their args and receivers 19 | - Logging output via Python's built-in logging, or via the `logbook `_ module 20 | 21 | There is also one Django management command currently: 22 | 23 | - `debugsqlshell`: Outputs the SQL that gets executed as you work in the Python 24 | interactive shell. (See example below) 25 | 26 | If you have ideas for other panels please let us know. 27 | 28 | Installation 29 | ============ 30 | 31 | #. Add the `debug_toolbar` directory to your Python path. 32 | 33 | #. Add the following middleware to your project's `settings.py` file: 34 | 35 | ``'debug_toolbar.middleware.DebugToolbarMiddleware',`` 36 | 37 | Tying into middleware allows each panel to be instantiated on request and 38 | rendering to happen on response. 39 | 40 | The order of MIDDLEWARE_CLASSES is important: the Debug Toolbar middleware 41 | must come after any other middleware that encodes the response's content 42 | (such as GZipMiddleware). 43 | 44 | Note: The debug toolbar will only display itself if the mimetype of the 45 | response is either `text/html` or `application/xhtml+xml` and contains a 46 | closing `` tag. 47 | 48 | Note: Be aware of middleware ordering and other middleware that may 49 | intercept requests and return responses. Putting the debug toolbar 50 | middleware *after* the Flatpage middleware, for example, means the 51 | toolbar will not show up on flatpages. 52 | 53 | #. Make sure your IP is listed in the `INTERNAL_IPS` setting. If you are 54 | working locally this will be: 55 | 56 | INTERNAL_IPS = ('127.0.0.1',) 57 | 58 | Note: This is required because of the built-in requirements of the 59 | `show_toolbar` method. See below for how to define a method to determine 60 | your own logic for displaying the toolbar. 61 | 62 | #. Add `debug_toolbar` to your `INSTALLED_APPS` setting so Django can find the 63 | template files associated with the Debug Toolbar. 64 | 65 | Alternatively, add the path to the debug toolbar templates 66 | (``'path/to/debug_toolbar/templates'`` to your ``TEMPLATE_DIRS`` setting.) 67 | 68 | Configuration 69 | ============= 70 | 71 | The debug toolbar has two settings that can be set in `settings.py`: 72 | 73 | #. Optional: Add a tuple called `DEBUG_TOOLBAR_PANELS` to your ``settings.py`` 74 | file that specifies the full Python path to the panel that you want included 75 | in the Toolbar. This setting looks very much like the `MIDDLEWARE_CLASSES` 76 | setting. For example:: 77 | 78 | DEBUG_TOOLBAR_PANELS = ( 79 | 'debug_toolbar.panels.version.VersionDebugPanel', 80 | 'debug_toolbar.panels.timer.TimerDebugPanel', 81 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 82 | 'debug_toolbar.panels.headers.HeaderDebugPanel', 83 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 84 | 'debug_toolbar.panels.template.TemplateDebugPanel', 85 | 'debug_toolbar.panels.sql.SQLDebugPanel', 86 | 'debug_toolbar.panels.signals.SignalDebugPanel', 87 | 'debug_toolbar.panels.logger.LoggingPanel', 88 | ) 89 | 90 | You can change the ordering of this tuple to customize the order of the 91 | panels you want to display, or add/remove panels. If you have custom panels 92 | you can include them in this way -- just provide the full Python path to 93 | your panel. 94 | 95 | #. Optional: There are a few configuration options to the debug toolbar that 96 | can be placed in a dictionary: 97 | 98 | * `INTERCEPT_REDIRECTS`: If set to True (default), the debug toolbar will 99 | show an intermediate page upon redirect so you can view any debug 100 | information prior to redirecting. This page will provide a link to the 101 | redirect destination you can follow when ready. If set to False, redirects 102 | will proceed as normal. 103 | 104 | * `SHOW_TOOLBAR_CALLBACK`: If not set or set to None, the debug_toolbar 105 | middleware will use its built-in show_toolbar method for determining whether 106 | the toolbar should show or not. The default checks are that DEBUG must be 107 | set to True and the IP of the request must be in INTERNAL_IPS. You can 108 | provide your own method for displaying the toolbar which contains your 109 | custom logic. This method should return True or False. 110 | 111 | * `EXTRA_SIGNALS`: An array of custom signals that might be in your project, 112 | defined as the python path to the signal. 113 | 114 | * `HIDE_DJANGO_SQL`: If set to True (the default) then code in Django itself 115 | won't be shown in SQL stacktraces. 116 | 117 | * `SHOW_TEMPLATE_CONTEXT`: If set to True (the default) then a template's 118 | context will be included with it in the Template debug panel. Turning this 119 | off is useful when you have large template contexts, or you have template 120 | contexts with lazy datastructures that you don't want to be evaluated. 121 | 122 | * `TAG`: If set, this will be the tag to which debug_toolbar will attach the 123 | debug toolbar. Defaults to 'body'. 124 | 125 | Example configuration:: 126 | 127 | def custom_show_toolbar(request): 128 | return True # Always show toolbar, for example purposes only. 129 | 130 | DEBUG_TOOLBAR_CONFIG = { 131 | 'INTERCEPT_REDIRECTS': False, 132 | 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, 133 | 'EXTRA_SIGNALS': ['myproject.signals.MySignal'], 134 | 'HIDE_DJANGO_SQL': False, 135 | 'TAG': 'div', 136 | } 137 | 138 | `debugsqlshell` 139 | =============== 140 | The following is sample output from running the `debugsqlshell` management 141 | command. Each ORM call that results in a database query will be beautifully 142 | output in the shell:: 143 | 144 | $ ./manage.py debugsqlshell 145 | Python 2.6.1 (r261:67515, Jul 7 2009, 23:51:51) 146 | [GCC 4.2.1 (Apple Inc. build 5646)] on darwin 147 | Type "help", "copyright", "credits" or "license" for more information. 148 | (InteractiveConsole) 149 | >>> from page.models import Page 150 | >>> ### Lookup and use resulting in an extra query... 151 | >>> p = Page.objects.get(pk=1) 152 | SELECT "page_page"."id", 153 | "page_page"."number", 154 | "page_page"."template_id", 155 | "page_page"."description" 156 | FROM "page_page" 157 | WHERE "page_page"."id" = 1 158 | 159 | >>> print p.template.name 160 | SELECT "page_template"."id", 161 | "page_template"."name", 162 | "page_template"."description" 163 | FROM "page_template" 164 | WHERE "page_template"."id" = 1 165 | 166 | Home 167 | >>> ### Using select_related to avoid 2nd database call... 168 | >>> p = Page.objects.select_related('template').get(pk=1) 169 | SELECT "page_page"."id", 170 | "page_page"."number", 171 | "page_page"."template_id", 172 | "page_page"."description", 173 | "page_template"."id", 174 | "page_template"."name", 175 | "page_template"."description" 176 | FROM "page_page" 177 | INNER JOIN "page_template" ON ("page_page"."template_id" = "page_template"."id") 178 | WHERE "page_page"."id" = 1 179 | 180 | >>> print p.template.name 181 | Home 182 | 183 | TODOs and BUGS 184 | ============== 185 | See: https://github.com/django-debug-toolbar/django-debug-toolbar/issues 186 | -------------------------------------------------------------------------------- /debug_toolbar/views.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper views for the debug toolbar. These are dynamically installed when the 3 | debug toolbar is displayed, and typically can do Bad Things, so hooking up these 4 | views in any other way is generally not advised. 5 | """ 6 | 7 | import os 8 | import django.views.static 9 | from django.conf import settings 10 | from django.db import connection 11 | from django.http import HttpResponseBadRequest 12 | from django.shortcuts import render_to_response 13 | from django.utils import simplejson 14 | from django.utils.hashcompat import sha_constructor 15 | 16 | class InvalidSQLError(Exception): 17 | def __init__(self, value): 18 | self.value = value 19 | def __str__(self): 20 | return repr(self.value) 21 | 22 | def debug_media(request, path): 23 | root = getattr(settings, 'DEBUG_TOOLBAR_MEDIA_ROOT', None) 24 | if root is None: 25 | parent = os.path.abspath(os.path.dirname(__file__)) 26 | root = os.path.join(parent, 'media', 'debug_toolbar') 27 | return django.views.static.serve(request, path, root) 28 | 29 | def sql_select(request): 30 | """ 31 | Returns the output of the SQL SELECT statement. 32 | 33 | Expected GET variables: 34 | sql: urlencoded sql with positional arguments 35 | params: JSON encoded parameter values 36 | duration: time for SQL to execute passed in from toolbar just for redisplay 37 | hash: the hash of (secret + sql + params) for tamper checking 38 | """ 39 | from debug_toolbar.panels.sql import reformat_sql 40 | sql = request.GET.get('sql', '') 41 | params = request.GET.get('params', '') 42 | hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() 43 | if hash != request.GET.get('hash', ''): 44 | return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert 45 | if sql.lower().strip().startswith('select'): 46 | params = simplejson.loads(params) 47 | cursor = connection.cursor() 48 | cursor.execute(sql, params) 49 | headers = [d[0] for d in cursor.description] 50 | result = cursor.fetchall() 51 | cursor.close() 52 | context = { 53 | 'result': result, 54 | 'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)), 55 | 'duration': request.GET.get('duration', 0.0), 56 | 'headers': headers, 57 | } 58 | return render_to_response('debug_toolbar/panels/sql_select.html', context) 59 | raise InvalidSQLError("Only 'select' queries are allowed.") 60 | 61 | def sql_explain(request): 62 | """ 63 | Returns the output of the SQL EXPLAIN on the given query. 64 | 65 | Expected GET variables: 66 | sql: urlencoded sql with positional arguments 67 | params: JSON encoded parameter values 68 | duration: time for SQL to execute passed in from toolbar just for redisplay 69 | hash: the hash of (secret + sql + params) for tamper checking 70 | """ 71 | from debug_toolbar.panels.sql import reformat_sql 72 | sql = request.GET.get('sql', '') 73 | params = request.GET.get('params', '') 74 | hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() 75 | if hash != request.GET.get('hash', ''): 76 | return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert 77 | if sql.lower().strip().startswith('select'): 78 | params = simplejson.loads(params) 79 | cursor = connection.cursor() 80 | 81 | if settings.DATABASE_ENGINE == "sqlite3": 82 | # SQLite's EXPLAIN dumps the low-level opcodes generated for a query; 83 | # EXPLAIN QUERY PLAN dumps a more human-readable summary 84 | # See http://www.sqlite.org/lang_explain.html for details 85 | cursor.execute("EXPLAIN QUERY PLAN %s" % (sql,), params) 86 | else: 87 | cursor.execute("EXPLAIN %s" % (sql,), params) 88 | 89 | headers = [d[0] for d in cursor.description] 90 | result = cursor.fetchall() 91 | cursor.close() 92 | context = { 93 | 'result': result, 94 | 'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)), 95 | 'duration': request.GET.get('duration', 0.0), 96 | 'headers': headers, 97 | } 98 | return render_to_response('debug_toolbar/panels/sql_explain.html', context) 99 | raise InvalidSQLError("Only 'select' queries are allowed.") 100 | 101 | def sql_profile(request): 102 | """ 103 | Returns the output of running the SQL and getting the profiling statistics. 104 | 105 | Expected GET variables: 106 | sql: urlencoded sql with positional arguments 107 | params: JSON encoded parameter values 108 | duration: time for SQL to execute passed in from toolbar just for redisplay 109 | hash: the hash of (secret + sql + params) for tamper checking 110 | """ 111 | from debug_toolbar.panels.sql import reformat_sql 112 | sql = request.GET.get('sql', '') 113 | params = request.GET.get('params', '') 114 | hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() 115 | if hash != request.GET.get('hash', ''): 116 | return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert 117 | if sql.lower().strip().startswith('select'): 118 | params = simplejson.loads(params) 119 | cursor = connection.cursor() 120 | result = None 121 | headers = None 122 | result_error = None 123 | try: 124 | cursor.execute("SET PROFILING=1") # Enable profiling 125 | cursor.execute(sql, params) # Execute SELECT 126 | cursor.execute("SET PROFILING=0") # Disable profiling 127 | # The Query ID should always be 1 here but I'll subselect to get the last one just in case... 128 | cursor.execute("SELECT * FROM information_schema.profiling WHERE query_id=(SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)") 129 | headers = [d[0] for d in cursor.description] 130 | result = cursor.fetchall() 131 | except: 132 | result_error = "Profiling is either not available or not supported by your database." 133 | cursor.close() 134 | context = { 135 | 'result': result, 136 | 'result_error': result_error, 137 | 'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)), 138 | 'duration': request.GET.get('duration', 0.0), 139 | 'headers': headers, 140 | } 141 | return render_to_response('debug_toolbar/panels/sql_profile.html', context) 142 | raise InvalidSQLError("Only 'select' queries are allowed.") 143 | 144 | def template_source(request): 145 | """ 146 | Return the source of a template, syntax-highlighted by Pygments if 147 | it's available. 148 | """ 149 | from django.template import TemplateDoesNotExist 150 | from django.utils.safestring import mark_safe 151 | from django.conf import settings 152 | 153 | template_name = request.GET.get('template', None) 154 | if template_name is None: 155 | return HttpResponseBadRequest('"template" key is required') 156 | 157 | try: # Django 1.2 ... 158 | from django.template.loader import find_template_loader, make_origin 159 | loaders = [] 160 | for loader_name in settings.TEMPLATE_LOADERS: 161 | loader = find_template_loader(loader_name) 162 | if loader is not None: 163 | loaders.append(loader) 164 | for loader in loaders: 165 | try: 166 | source, display_name = loader.load_template_source(template_name) 167 | origin = make_origin(display_name, loader, template_name, settings.TEMPLATE_DIRS) 168 | break 169 | except TemplateDoesNotExist: 170 | source = "Template Does Not Exist: %s" % (template_name,) 171 | except (ImportError, AttributeError): # Django 1.1 ... 172 | from django.template.loader import find_template_source 173 | source, origin = find_template_source(template_name) 174 | 175 | try: 176 | from pygments import highlight 177 | from pygments.lexers import HtmlDjangoLexer 178 | from pygments.formatters import HtmlFormatter 179 | 180 | source = highlight(source, HtmlDjangoLexer(), HtmlFormatter()) 181 | source = mark_safe(source) 182 | source.pygmentized = True 183 | except ImportError: 184 | pass 185 | 186 | return render_to_response('debug_toolbar/panels/template_source.html', { 187 | 'source': source, 188 | 'template_name': template_name 189 | }) 190 | -------------------------------------------------------------------------------- /debug_toolbar/panels/sql.py: -------------------------------------------------------------------------------- 1 | import re 2 | import uuid 3 | 4 | from django.db.backends import BaseDatabaseWrapper 5 | from django.template.loader import render_to_string 6 | from django.utils.html import escape 7 | from django.utils.safestring import mark_safe 8 | from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __ 9 | 10 | from debug_toolbar.utils.compat.db import connections 11 | from debug_toolbar.middleware import DebugToolbarMiddleware 12 | from debug_toolbar.panels import DebugPanel 13 | from debug_toolbar.utils import sqlparse 14 | from debug_toolbar.utils.tracking.db import CursorWrapper 15 | from debug_toolbar.utils.tracking import replace_call 16 | 17 | # Inject our tracking cursor 18 | @replace_call(BaseDatabaseWrapper.cursor) 19 | def cursor(func, self): 20 | result = func(self) 21 | 22 | djdt = DebugToolbarMiddleware.get_current() 23 | if not djdt: 24 | return result 25 | logger = djdt.get_panel(SQLDebugPanel) 26 | 27 | return CursorWrapper(result, self, logger=logger) 28 | 29 | def get_isolation_level_display(engine, level): 30 | if engine == 'psycopg2': 31 | import psycopg2.extensions 32 | choices = { 33 | psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT: 'Autocommit', 34 | psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: 'Read uncommitted', 35 | psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED: 'Read committed', 36 | psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: 'Repeatable read', 37 | psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE: 'Serializable', 38 | } 39 | else: 40 | raise ValueError(engine) 41 | 42 | return choices.get(level) 43 | 44 | def get_transaction_status_display(engine, level): 45 | if engine == 'psycopg2': 46 | import psycopg2.extensions 47 | choices = { 48 | psycopg2.extensions.TRANSACTION_STATUS_IDLE: 'Idle', 49 | psycopg2.extensions.TRANSACTION_STATUS_ACTIVE: 'Active', 50 | psycopg2.extensions.TRANSACTION_STATUS_INTRANS: 'In transaction', 51 | psycopg2.extensions.TRANSACTION_STATUS_INERROR: 'In error', 52 | psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN: 'Unknown', 53 | } 54 | else: 55 | raise ValueError(engine) 56 | 57 | return choices.get(level) 58 | 59 | class SQLDebugPanel(DebugPanel): 60 | """ 61 | Panel that displays information about the SQL queries run while processing 62 | the request. 63 | """ 64 | name = 'SQL' 65 | has_content = True 66 | 67 | def __init__(self, *args, **kwargs): 68 | super(self.__class__, self).__init__(*args, **kwargs) 69 | self._offset = dict((k, len(connections[k].queries)) for k in connections) 70 | self._sql_time = 0 71 | self._num_queries = 0 72 | self._queries = [] 73 | self._databases = {} 74 | self._transaction_status = {} 75 | self._transaction_ids = {} 76 | 77 | def get_transaction_id(self, alias): 78 | conn = connections[alias].connection 79 | if not conn: 80 | return None 81 | 82 | engine = conn.__class__.__module__.split('.', 1)[0] 83 | if engine == 'psycopg2': 84 | cur_status = conn.get_transaction_status() 85 | else: 86 | raise ValueError(engine) 87 | 88 | last_status = self._transaction_status.get(alias) 89 | self._transaction_status[alias] = cur_status 90 | 91 | if not cur_status: 92 | # No available state 93 | return None 94 | 95 | if cur_status != last_status: 96 | if cur_status: 97 | self._transaction_ids[alias] = uuid.uuid4().hex 98 | else: 99 | self._transaction_ids[alias] = None 100 | 101 | return self._transaction_ids[alias] 102 | 103 | def record(self, alias, **kwargs): 104 | self._queries.append((alias, kwargs)) 105 | if alias not in self._databases: 106 | self._databases[alias] = { 107 | 'time_spent': kwargs['duration'], 108 | 'num_queries': 1, 109 | } 110 | else: 111 | self._databases[alias]['time_spent'] += kwargs['duration'] 112 | self._databases[alias]['num_queries'] += 1 113 | self._sql_time += kwargs['duration'] 114 | self._num_queries += 1 115 | 116 | def nav_title(self): 117 | return _('SQL') 118 | 119 | def nav_subtitle(self): 120 | # TODO l10n: use ngettext 121 | return "%d %s in %.2fms" % ( 122 | self._num_queries, 123 | (self._num_queries == 1) and 'query' or 'queries', 124 | self._sql_time 125 | ) 126 | 127 | def title(self): 128 | count = len(self._databases) 129 | 130 | return __('SQL Queries from %(count)d connection', 'SQL Queries from %(count)d connections', count) % dict( 131 | count=count, 132 | ) 133 | 134 | def url(self): 135 | return '' 136 | 137 | def content(self): 138 | if self._queries: 139 | width_ratio_tally = 0 140 | colors = [ 141 | (256, 0, 0), # red 142 | (0, 256, 0), # blue 143 | (0, 0, 256), # green 144 | ] 145 | factor = int(256.0/(len(self._databases)*2.5)) 146 | for n, db in enumerate(self._databases.itervalues()): 147 | rgb = [0, 0, 0] 148 | color = n % 3 149 | rgb[color] = 256 - n/3*factor 150 | nn = color 151 | # XXX: pretty sure this is horrible after so many aliases 152 | while rgb[color] < factor: 153 | nc = min(256 - rgb[color], 256) 154 | rgb[color] += nc 155 | nn += 1 156 | if nn > 2: 157 | nn = 0 158 | rgb[nn] = nc 159 | db['rgb_color'] = rgb 160 | 161 | trans_ids = {} 162 | trans_id = None 163 | i = 0 164 | for alias, query in self._queries: 165 | trans_id = query.get('trans_id') 166 | last_trans_id = trans_ids.get(alias) 167 | 168 | if trans_id != last_trans_id: 169 | if last_trans_id: 170 | self._queries[i-1][1]['ends_trans'] = True 171 | trans_ids[alias] = trans_id 172 | if trans_id: 173 | query['starts_trans'] = True 174 | if trans_id: 175 | query['in_trans'] = True 176 | 177 | query['alias'] = alias 178 | if 'iso_level' in query: 179 | query['iso_level'] = get_isolation_level_display(query['engine'], query['iso_level']) 180 | if 'trans_status' in query: 181 | query['trans_status'] = get_transaction_status_display(query['engine'], query['trans_status']) 182 | query['sql'] = reformat_sql(query['sql']) 183 | query['rgb_color'] = self._databases[alias]['rgb_color'] 184 | try: 185 | query['width_ratio'] = (query['duration'] / self._sql_time) * 100 186 | except ZeroDivisionError: 187 | query['width_ratio'] = 0 188 | query['start_offset'] = width_ratio_tally 189 | query['end_offset'] = query['width_ratio'] + query['start_offset'] 190 | width_ratio_tally += query['width_ratio'] 191 | 192 | stacktrace = [] 193 | for frame in query['stacktrace']: 194 | params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:])) 195 | stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params)) 196 | query['stacktrace'] = mark_safe('\n'.join(stacktrace)) 197 | i += 1 198 | 199 | if trans_id: 200 | self._queries[i-1][1]['ends_trans'] = True 201 | 202 | context = self.context.copy() 203 | context.update({ 204 | 'databases': sorted(self._databases.items(), key=lambda x: -x[1]['time_spent']), 205 | 'queries': [q for a, q in self._queries], 206 | 'sql_time': self._sql_time, 207 | }) 208 | 209 | return render_to_string('debug_toolbar/panels/sql.html', context) 210 | 211 | class BoldKeywordFilter(sqlparse.filters.Filter): 212 | """sqlparse filter to bold SQL keywords""" 213 | def process(self, stack, stream): 214 | """Process the token stream""" 215 | for token_type, value in stream: 216 | is_keyword = token_type in sqlparse.tokens.Keyword 217 | if is_keyword: 218 | yield sqlparse.tokens.Text, '' 219 | yield token_type, escape(value) 220 | if is_keyword: 221 | yield sqlparse.tokens.Text, '' 222 | 223 | def swap_fields(sql): 224 | return re.sub('SELECT (.*) FROM', 'SELECT \g<1> FROM', sql) 225 | 226 | def reformat_sql(sql): 227 | stack = sqlparse.engine.FilterStack() 228 | stack.preprocess.append(BoldKeywordFilter()) # add our custom filter 229 | stack.postprocess.append(sqlparse.filters.SerializerUnicode()) # tokens -> strings 230 | return swap_fields(''.join(stack.run(sql))) 231 | -------------------------------------------------------------------------------- /debug_toolbar/locale/en/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # Percy Pérez-Pinedo, 2009. 4 | # 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-11-18 08:06-0800\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: panels/cache.py:92 19 | #, python-format 20 | msgid "Cache: %.2fms" 21 | msgstr "" 22 | 23 | #: panels/cache.py:95 24 | msgid "Cache Usage" 25 | msgstr "" 26 | 27 | #: panels/headers.py:36 panels/headers.py:39 28 | msgid "HTTP Headers" 29 | msgstr "" 30 | 31 | #: panels/logger.py:56 32 | msgid "Logging" 33 | msgstr "" 34 | 35 | #: panels/logger.py:63 36 | msgid "Log Messages" 37 | msgstr "" 38 | 39 | #: panels/request_vars.py:13 panels/request_vars.py:16 40 | msgid "Request Vars" 41 | msgstr "" 42 | 43 | #: panels/settings_vars.py:16 44 | msgid "Settings" 45 | msgstr "" 46 | 47 | #: panels/settings_vars.py:19 48 | #, python-format 49 | msgid "Settings from %s" 50 | msgstr "" 51 | 52 | #: panels/signals.py:39 panels/signals.py:42 53 | msgid "Signals" 54 | msgstr "" 55 | 56 | #: panels/sql.py:146 57 | msgid "SQL" 58 | msgstr "" 59 | 60 | #: panels/sql.py:160 61 | msgid "SQL Queries" 62 | msgstr "" 63 | 64 | #: panels/template.py:47 65 | msgid "Templates" 66 | msgstr "" 67 | 68 | #: panels/template.py:52 69 | #, python-format 70 | msgid "Templates (%(num_templates)s rendered)" 71 | msgstr "" 72 | 73 | #: panels/timer.py:35 templates/debug_toolbar/panels/cache.html:39 74 | #: templates/debug_toolbar/panels/logger.html:7 75 | #: templates/debug_toolbar/panels/sql.html:5 76 | #: templates/debug_toolbar/panels/sql_explain.html:11 77 | #: templates/debug_toolbar/panels/sql_profile.html:12 78 | #: templates/debug_toolbar/panels/sql_select.html:11 79 | msgid "Time" 80 | msgstr "" 81 | 82 | #: panels/timer.py:47 83 | msgid "Resource Usage" 84 | msgstr "" 85 | 86 | #: panels/timer.py:78 87 | msgid "User CPU time" 88 | msgstr "" 89 | 90 | #: panels/timer.py:79 91 | msgid "System CPU time" 92 | msgstr "" 93 | 94 | #: panels/timer.py:80 95 | msgid "Total CPU time" 96 | msgstr "" 97 | 98 | #: panels/timer.py:81 99 | msgid "Elapsed time" 100 | msgstr "" 101 | 102 | #: panels/timer.py:82 103 | msgid "Context switches" 104 | msgstr "" 105 | 106 | #: panels/version.py:20 panels/version.py:29 107 | msgid "Versions" 108 | msgstr "" 109 | 110 | #: templates/debug_toolbar/base.html:23 111 | msgid "Hide Toolbar" 112 | msgstr "" 113 | 114 | #: templates/debug_toolbar/base.html:23 115 | msgid "Hide" 116 | msgstr "" 117 | 118 | #: templates/debug_toolbar/base.html:48 119 | msgid "Show Toolbar" 120 | msgstr "" 121 | 122 | #: templates/debug_toolbar/base.html:54 123 | msgid "Close" 124 | msgstr "" 125 | 126 | #: templates/debug_toolbar/redirect.html:7 127 | #: templates/debug_toolbar/panels/logger.html:9 128 | msgid "Location" 129 | msgstr "" 130 | 131 | #: templates/debug_toolbar/redirect.html:9 132 | msgid "" 133 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " 134 | "debug viewing purposes. You can click the above link to continue with the " 135 | "redirect as normal. If you'd like to disable this feature, set the " 136 | "DEBUG_TOOLBAR_CONFIG dictionary's key " 137 | "INTERCEPT_REDIRECTS to False." 138 | msgstr "" 139 | 140 | #: templates/debug_toolbar/panels/cache.html:14 141 | msgid "Total Calls" 142 | msgstr "" 143 | 144 | #: templates/debug_toolbar/panels/cache.html:16 145 | msgid "Total Time" 146 | msgstr "" 147 | 148 | #: templates/debug_toolbar/panels/cache.html:18 149 | msgid "Hits" 150 | msgstr "" 151 | 152 | #: templates/debug_toolbar/panels/cache.html:20 153 | msgid "Misses" 154 | msgstr "" 155 | 156 | #: templates/debug_toolbar/panels/cache.html:35 157 | msgid "Breakdown" 158 | msgstr "" 159 | 160 | #: templates/debug_toolbar/panels/cache.html:40 161 | msgid "Type" 162 | msgstr "" 163 | 164 | #: templates/debug_toolbar/panels/cache.html:41 165 | msgid "Parameters" 166 | msgstr "" 167 | 168 | #: templates/debug_toolbar/panels/cache.html:42 169 | msgid "Function" 170 | msgstr "" 171 | 172 | #: templates/debug_toolbar/panels/headers.html:5 173 | msgid "Key" 174 | msgstr "" 175 | 176 | #: templates/debug_toolbar/panels/headers.html:6 177 | #: templates/debug_toolbar/panels/request_vars.html:37 178 | #: templates/debug_toolbar/panels/request_vars.html:63 179 | #: templates/debug_toolbar/panels/request_vars.html:85 180 | #: templates/debug_toolbar/panels/request_vars.html:107 181 | #: templates/debug_toolbar/panels/settings_vars.html:6 182 | #: templates/debug_toolbar/panels/timer.html:10 183 | msgid "Value" 184 | msgstr "" 185 | 186 | #: templates/debug_toolbar/panels/logger.html:6 187 | msgid "Level" 188 | msgstr "" 189 | 190 | #: templates/debug_toolbar/panels/logger.html:8 191 | msgid "Message" 192 | msgstr "" 193 | 194 | #: templates/debug_toolbar/panels/logger.html:24 195 | msgid "No messages logged" 196 | msgstr "" 197 | 198 | #: templates/debug_toolbar/panels/request_vars.html:3 199 | msgid "View information" 200 | msgstr "" 201 | 202 | #: templates/debug_toolbar/panels/request_vars.html:7 203 | msgid "View Function" 204 | msgstr "" 205 | 206 | #: templates/debug_toolbar/panels/request_vars.html:8 207 | msgid "args" 208 | msgstr "" 209 | 210 | #: templates/debug_toolbar/panels/request_vars.html:9 211 | msgid "kwargs" 212 | msgstr "" 213 | 214 | #: templates/debug_toolbar/panels/request_vars.html:27 215 | msgid "COOKIES Variables" 216 | msgstr "" 217 | 218 | #: templates/debug_toolbar/panels/request_vars.html:36 219 | #: templates/debug_toolbar/panels/request_vars.html:62 220 | #: templates/debug_toolbar/panels/request_vars.html:84 221 | #: templates/debug_toolbar/panels/request_vars.html:106 222 | msgid "Variable" 223 | msgstr "" 224 | 225 | #: templates/debug_toolbar/panels/request_vars.html:50 226 | msgid "No COOKIE data" 227 | msgstr "" 228 | 229 | #: templates/debug_toolbar/panels/request_vars.html:53 230 | msgid "SESSION Variables" 231 | msgstr "" 232 | 233 | #: templates/debug_toolbar/panels/request_vars.html:76 234 | msgid "No SESSION data" 235 | msgstr "" 236 | 237 | #: templates/debug_toolbar/panels/request_vars.html:79 238 | msgid "GET Variables" 239 | msgstr "" 240 | 241 | #: templates/debug_toolbar/panels/request_vars.html:98 242 | msgid "No GET data" 243 | msgstr "" 244 | 245 | #: templates/debug_toolbar/panels/request_vars.html:101 246 | msgid "POST Variables" 247 | msgstr "" 248 | 249 | #: templates/debug_toolbar/panels/request_vars.html:120 250 | msgid "No POST data" 251 | msgstr "" 252 | 253 | #: templates/debug_toolbar/panels/settings_vars.html:5 254 | msgid "Setting" 255 | msgstr "" 256 | 257 | #: templates/debug_toolbar/panels/signals.html:5 258 | msgid "Signal" 259 | msgstr "" 260 | 261 | #: templates/debug_toolbar/panels/signals.html:6 262 | msgid "Providing Args" 263 | msgstr "" 264 | 265 | #: templates/debug_toolbar/panels/signals.html:7 266 | msgid "Receivers" 267 | msgstr "" 268 | 269 | #: templates/debug_toolbar/panels/sql.html:6 270 | msgid "Action" 271 | msgstr "" 272 | 273 | #: templates/debug_toolbar/panels/sql.html:7 274 | msgid "Stacktrace" 275 | msgstr "" 276 | 277 | #: templates/debug_toolbar/panels/sql.html:8 278 | msgid "Query" 279 | msgstr "" 280 | 281 | #: templates/debug_toolbar/panels/sql.html:38 282 | msgid "Line" 283 | msgstr "" 284 | 285 | #: templates/debug_toolbar/panels/sql.html:39 286 | msgid "Method" 287 | msgstr "" 288 | 289 | #: templates/debug_toolbar/panels/sql.html:40 290 | msgid "File" 291 | msgstr "" 292 | 293 | #: templates/debug_toolbar/panels/sql_explain.html:3 294 | #: templates/debug_toolbar/panels/sql_profile.html:3 295 | #: templates/debug_toolbar/panels/sql_select.html:3 296 | #: templates/debug_toolbar/panels/template_source.html:3 297 | msgid "Back" 298 | msgstr "" 299 | 300 | #: templates/debug_toolbar/panels/sql_explain.html:4 301 | msgid "SQL Explained" 302 | msgstr "" 303 | 304 | #: templates/debug_toolbar/panels/sql_explain.html:9 305 | #: templates/debug_toolbar/panels/sql_profile.html:10 306 | #: templates/debug_toolbar/panels/sql_select.html:9 307 | msgid "Executed SQL" 308 | msgstr "" 309 | 310 | #: templates/debug_toolbar/panels/sql_profile.html:4 311 | msgid "SQL Profiled" 312 | msgstr "" 313 | 314 | #: templates/debug_toolbar/panels/sql_profile.html:35 315 | msgid "Error" 316 | msgstr "" 317 | 318 | #: templates/debug_toolbar/panels/sql_select.html:4 319 | msgid "SQL Selected" 320 | msgstr "" 321 | 322 | #: templates/debug_toolbar/panels/sql_select.html:34 323 | msgid "Empty set" 324 | msgstr "" 325 | 326 | #: templates/debug_toolbar/panels/template_source.html:4 327 | msgid "Template Source" 328 | msgstr "" 329 | 330 | #: templates/debug_toolbar/panels/templates.html:2 331 | msgid "Template path" 332 | msgstr "" 333 | 334 | #: templates/debug_toolbar/panels/templates.html:13 335 | msgid "Template" 336 | msgstr "" 337 | 338 | #: templates/debug_toolbar/panels/templates.html:21 339 | #: templates/debug_toolbar/panels/templates.html:37 340 | msgid "Toggle Context" 341 | msgstr "" 342 | 343 | #: templates/debug_toolbar/panels/templates.html:28 344 | #: templates/debug_toolbar/panels/templates.html:43 345 | msgid "None" 346 | msgstr "" 347 | 348 | #: templates/debug_toolbar/panels/templates.html:31 349 | msgid "Context processor" 350 | msgstr "" 351 | 352 | #: templates/debug_toolbar/panels/timer.html:9 353 | msgid "Resource" 354 | msgstr "" 355 | 356 | #: templates/debug_toolbar/panels/versions.html:6 357 | msgid "Package" 358 | msgstr "" 359 | 360 | #: templates/debug_toolbar/panels/versions.html:7 361 | msgid "Version" 362 | msgstr "" 363 | -------------------------------------------------------------------------------- /debug_toolbar/locale/he/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-11-18 08:06-0800\n" 11 | "PO-Revision-Date: 2009-08-24 23:08-0600\n" 12 | "Last-Translator: Alex \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: panels/cache.py:92 19 | #, python-format 20 | msgid "Cache: %.2fms" 21 | msgstr "" 22 | 23 | #: panels/cache.py:95 24 | msgid "Cache Usage" 25 | msgstr "" 26 | 27 | #: panels/headers.py:36 panels/headers.py:39 28 | msgid "HTTP Headers" 29 | msgstr "" 30 | 31 | #: panels/logger.py:56 32 | msgid "Logging" 33 | msgstr "רישום יומן" 34 | 35 | #: panels/logger.py:63 36 | #, fuzzy 37 | msgid "Log Messages" 38 | msgstr "הודעה" 39 | 40 | #: panels/request_vars.py:13 panels/request_vars.py:16 41 | msgid "Request Vars" 42 | msgstr "" 43 | 44 | #: panels/settings_vars.py:16 45 | msgid "Settings" 46 | msgstr "" 47 | 48 | #: panels/settings_vars.py:19 49 | #, python-format 50 | msgid "Settings from %s" 51 | msgstr "" 52 | 53 | #: panels/signals.py:39 panels/signals.py:42 54 | msgid "Signals" 55 | msgstr "סימנים" 56 | 57 | #: panels/sql.py:146 58 | msgid "SQL" 59 | msgstr "" 60 | 61 | #: panels/sql.py:160 62 | msgid "SQL Queries" 63 | msgstr "" 64 | 65 | #: panels/template.py:47 66 | msgid "Templates" 67 | msgstr "תבניות" 68 | 69 | #: panels/template.py:52 70 | #, python-format 71 | msgid "Templates (%(num_templates)s rendered)" 72 | msgstr "" 73 | 74 | #: panels/timer.py:35 templates/debug_toolbar/panels/cache.html:39 75 | #: templates/debug_toolbar/panels/logger.html:7 76 | #: templates/debug_toolbar/panels/sql.html:5 77 | #: templates/debug_toolbar/panels/sql_explain.html:11 78 | #: templates/debug_toolbar/panels/sql_profile.html:12 79 | #: templates/debug_toolbar/panels/sql_select.html:11 80 | msgid "Time" 81 | msgstr "זמן" 82 | 83 | #: panels/timer.py:47 84 | msgid "Resource Usage" 85 | msgstr "" 86 | 87 | #: panels/timer.py:78 88 | msgid "User CPU time" 89 | msgstr "" 90 | 91 | #: panels/timer.py:79 92 | msgid "System CPU time" 93 | msgstr "" 94 | 95 | #: panels/timer.py:80 96 | #, fuzzy 97 | msgid "Total CPU time" 98 | msgstr "זמן" 99 | 100 | #: panels/timer.py:81 101 | msgid "Elapsed time" 102 | msgstr "" 103 | 104 | #: panels/timer.py:82 105 | msgid "Context switches" 106 | msgstr "" 107 | 108 | #: panels/version.py:20 panels/version.py:29 109 | #, fuzzy 110 | msgid "Versions" 111 | msgstr "ג 'נגו גירסה" 112 | 113 | #: templates/debug_toolbar/base.html:23 114 | msgid "Hide Toolbar" 115 | msgstr "" 116 | 117 | #: templates/debug_toolbar/base.html:23 118 | msgid "Hide" 119 | msgstr "הסתיר" 120 | 121 | #: templates/debug_toolbar/base.html:48 122 | msgid "Show Toolbar" 123 | msgstr "" 124 | 125 | #: templates/debug_toolbar/base.html:54 126 | msgid "Close" 127 | msgstr "סגור" 128 | 129 | #: templates/debug_toolbar/redirect.html:7 130 | #: templates/debug_toolbar/panels/logger.html:9 131 | msgid "Location" 132 | msgstr "מקום" 133 | 134 | #: templates/debug_toolbar/redirect.html:9 135 | msgid "" 136 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " 137 | "debug viewing purposes. You can click the above link to continue with the " 138 | "redirect as normal. If you'd like to disable this feature, set the " 139 | "DEBUG_TOOLBAR_CONFIG dictionary's key " 140 | "INTERCEPT_REDIRECTS to False." 141 | msgstr "" 142 | 143 | #: templates/debug_toolbar/panels/cache.html:14 144 | msgid "Total Calls" 145 | msgstr "" 146 | 147 | #: templates/debug_toolbar/panels/cache.html:16 148 | msgid "Total Time" 149 | msgstr "זמן" 150 | 151 | #: templates/debug_toolbar/panels/cache.html:18 152 | msgid "Hits" 153 | msgstr "הצלחות" 154 | 155 | #: templates/debug_toolbar/panels/cache.html:20 156 | msgid "Misses" 157 | msgstr "" 158 | 159 | #: templates/debug_toolbar/panels/cache.html:35 160 | msgid "Breakdown" 161 | msgstr "" 162 | 163 | #: templates/debug_toolbar/panels/cache.html:40 164 | msgid "Type" 165 | msgstr "סוג" 166 | 167 | #: templates/debug_toolbar/panels/cache.html:41 168 | msgid "Parameters" 169 | msgstr "" 170 | 171 | #: templates/debug_toolbar/panels/cache.html:42 172 | msgid "Function" 173 | msgstr "" 174 | 175 | #: templates/debug_toolbar/panels/headers.html:5 176 | msgid "Key" 177 | msgstr "מפתח" 178 | 179 | #: templates/debug_toolbar/panels/headers.html:6 180 | #: templates/debug_toolbar/panels/request_vars.html:37 181 | #: templates/debug_toolbar/panels/request_vars.html:63 182 | #: templates/debug_toolbar/panels/request_vars.html:85 183 | #: templates/debug_toolbar/panels/request_vars.html:107 184 | #: templates/debug_toolbar/panels/settings_vars.html:6 185 | #: templates/debug_toolbar/panels/timer.html:10 186 | msgid "Value" 187 | msgstr "ערך" 188 | 189 | #: templates/debug_toolbar/panels/logger.html:6 190 | msgid "Level" 191 | msgstr "רמה" 192 | 193 | #: templates/debug_toolbar/panels/logger.html:8 194 | msgid "Message" 195 | msgstr "הודעה" 196 | 197 | #: templates/debug_toolbar/panels/logger.html:24 198 | msgid "No messages logged" 199 | msgstr "אין הודעות" 200 | 201 | #: templates/debug_toolbar/panels/request_vars.html:3 202 | msgid "View information" 203 | msgstr "" 204 | 205 | #: templates/debug_toolbar/panels/request_vars.html:7 206 | msgid "View Function" 207 | msgstr "" 208 | 209 | #: templates/debug_toolbar/panels/request_vars.html:8 210 | msgid "args" 211 | msgstr "" 212 | 213 | #: templates/debug_toolbar/panels/request_vars.html:9 214 | msgid "kwargs" 215 | msgstr "" 216 | 217 | #: templates/debug_toolbar/panels/request_vars.html:27 218 | #, fuzzy 219 | msgid "COOKIES Variables" 220 | msgstr "מזהה" 221 | 222 | #: templates/debug_toolbar/panels/request_vars.html:36 223 | #: templates/debug_toolbar/panels/request_vars.html:62 224 | #: templates/debug_toolbar/panels/request_vars.html:84 225 | #: templates/debug_toolbar/panels/request_vars.html:106 226 | msgid "Variable" 227 | msgstr "מזהה" 228 | 229 | #: templates/debug_toolbar/panels/request_vars.html:50 230 | msgid "No COOKIE data" 231 | msgstr "אין נתונים לעוגיות" 232 | 233 | #: templates/debug_toolbar/panels/request_vars.html:53 234 | #, fuzzy 235 | msgid "SESSION Variables" 236 | msgstr "מזהה" 237 | 238 | #: templates/debug_toolbar/panels/request_vars.html:76 239 | msgid "No SESSION data" 240 | msgstr "אין נתונים להתחברות" 241 | 242 | #: templates/debug_toolbar/panels/request_vars.html:79 243 | #, fuzzy 244 | msgid "GET Variables" 245 | msgstr "מזהה" 246 | 247 | #: templates/debug_toolbar/panels/request_vars.html:98 248 | msgid "No GET data" 249 | msgstr "שום דבר עבור GET" 250 | 251 | #: templates/debug_toolbar/panels/request_vars.html:101 252 | #, fuzzy 253 | msgid "POST Variables" 254 | msgstr "מזהה" 255 | 256 | #: templates/debug_toolbar/panels/request_vars.html:120 257 | msgid "No POST data" 258 | msgstr "שום דבר עבור POST" 259 | 260 | #: templates/debug_toolbar/panels/settings_vars.html:5 261 | msgid "Setting" 262 | msgstr "" 263 | 264 | #: templates/debug_toolbar/panels/signals.html:5 265 | msgid "Signal" 266 | msgstr "סימן" 267 | 268 | #: templates/debug_toolbar/panels/signals.html:6 269 | msgid "Providing Args" 270 | msgstr "" 271 | 272 | #: templates/debug_toolbar/panels/signals.html:7 273 | msgid "Receivers" 274 | msgstr "" 275 | 276 | #: templates/debug_toolbar/panels/sql.html:6 277 | msgid "Action" 278 | msgstr "פעילות" 279 | 280 | #: templates/debug_toolbar/panels/sql.html:7 281 | msgid "Stacktrace" 282 | msgstr "" 283 | 284 | #: templates/debug_toolbar/panels/sql.html:8 285 | msgid "Query" 286 | msgstr "" 287 | 288 | #: templates/debug_toolbar/panels/sql.html:38 289 | msgid "Line" 290 | msgstr "שורה" 291 | 292 | #: templates/debug_toolbar/panels/sql.html:39 293 | msgid "Method" 294 | msgstr "" 295 | 296 | #: templates/debug_toolbar/panels/sql.html:40 297 | msgid "File" 298 | msgstr "קובץ" 299 | 300 | #: templates/debug_toolbar/panels/sql_explain.html:3 301 | #: templates/debug_toolbar/panels/sql_profile.html:3 302 | #: templates/debug_toolbar/panels/sql_select.html:3 303 | #: templates/debug_toolbar/panels/template_source.html:3 304 | msgid "Back" 305 | msgstr "חזרה" 306 | 307 | #: templates/debug_toolbar/panels/sql_explain.html:4 308 | msgid "SQL Explained" 309 | msgstr "" 310 | 311 | #: templates/debug_toolbar/panels/sql_explain.html:9 312 | #: templates/debug_toolbar/panels/sql_profile.html:10 313 | #: templates/debug_toolbar/panels/sql_select.html:9 314 | msgid "Executed SQL" 315 | msgstr "הסבר עבור SQL" 316 | 317 | #: templates/debug_toolbar/panels/sql_profile.html:4 318 | msgid "SQL Profiled" 319 | msgstr "" 320 | 321 | #: templates/debug_toolbar/panels/sql_profile.html:35 322 | msgid "Error" 323 | msgstr "שגיאה" 324 | 325 | #: templates/debug_toolbar/panels/sql_select.html:4 326 | msgid "SQL Selected" 327 | msgstr "" 328 | 329 | #: templates/debug_toolbar/panels/sql_select.html:34 330 | msgid "Empty set" 331 | msgstr "תוצאות ריק" 332 | 333 | #: templates/debug_toolbar/panels/template_source.html:4 334 | #, fuzzy 335 | msgid "Template Source" 336 | msgstr "תבנית" 337 | 338 | #: templates/debug_toolbar/panels/templates.html:2 339 | #, fuzzy 340 | msgid "Template path" 341 | msgstr "תבנית" 342 | 343 | #: templates/debug_toolbar/panels/templates.html:13 344 | msgid "Template" 345 | msgstr "תבנית" 346 | 347 | #: templates/debug_toolbar/panels/templates.html:21 348 | #: templates/debug_toolbar/panels/templates.html:37 349 | msgid "Toggle Context" 350 | msgstr "" 351 | 352 | #: templates/debug_toolbar/panels/templates.html:28 353 | #: templates/debug_toolbar/panels/templates.html:43 354 | msgid "None" 355 | msgstr "" 356 | 357 | #: templates/debug_toolbar/panels/templates.html:31 358 | msgid "Context processor" 359 | msgstr "" 360 | 361 | #: templates/debug_toolbar/panels/timer.html:9 362 | msgid "Resource" 363 | msgstr "" 364 | 365 | #: templates/debug_toolbar/panels/versions.html:6 366 | msgid "Package" 367 | msgstr "" 368 | 369 | #: templates/debug_toolbar/panels/versions.html:7 370 | #, fuzzy 371 | msgid "Version" 372 | msgstr "ג 'נגו גירסה" 373 | -------------------------------------------------------------------------------- /debug_toolbar/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Debug Toolbar auf Deutsch. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Jannis Leidel, 2009. 5 | # 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2009-11-18 08:06-0800\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: panels/cache.py:92 20 | #, python-format 21 | msgid "Cache: %.2fms" 22 | msgstr "" 23 | 24 | #: panels/cache.py:95 25 | msgid "Cache Usage" 26 | msgstr "" 27 | 28 | #: panels/headers.py:36 panels/headers.py:39 29 | msgid "HTTP Headers" 30 | msgstr "" 31 | 32 | #: panels/logger.py:56 33 | msgid "Logging" 34 | msgstr "Logging" 35 | 36 | #: panels/logger.py:63 37 | #, fuzzy 38 | msgid "Log Messages" 39 | msgstr "Nachricht" 40 | 41 | #: panels/request_vars.py:13 panels/request_vars.py:16 42 | msgid "Request Vars" 43 | msgstr "" 44 | 45 | #: panels/settings_vars.py:16 46 | msgid "Settings" 47 | msgstr "Einstellungen" 48 | 49 | #: panels/settings_vars.py:19 50 | #, python-format 51 | msgid "Settings from %s" 52 | msgstr "" 53 | 54 | #: panels/signals.py:39 panels/signals.py:42 55 | msgid "Signals" 56 | msgstr "Signals" 57 | 58 | #: panels/sql.py:146 59 | msgid "SQL" 60 | msgstr "" 61 | 62 | #: panels/sql.py:160 63 | msgid "SQL Queries" 64 | msgstr "" 65 | 66 | #: panels/template.py:47 67 | msgid "Templates" 68 | msgstr "Templates" 69 | 70 | #: panels/template.py:52 71 | #, python-format 72 | msgid "Templates (%(num_templates)s rendered)" 73 | msgstr "" 74 | 75 | #: panels/timer.py:35 templates/debug_toolbar/panels/cache.html:39 76 | #: templates/debug_toolbar/panels/logger.html:7 77 | #: templates/debug_toolbar/panels/sql.html:5 78 | #: templates/debug_toolbar/panels/sql_explain.html:11 79 | #: templates/debug_toolbar/panels/sql_profile.html:12 80 | #: templates/debug_toolbar/panels/sql_select.html:11 81 | msgid "Time" 82 | msgstr "Zeit" 83 | 84 | #: panels/timer.py:47 85 | #, fuzzy 86 | msgid "Resource Usage" 87 | msgstr "Ressource" 88 | 89 | #: panels/timer.py:78 90 | msgid "User CPU time" 91 | msgstr "" 92 | 93 | #: panels/timer.py:79 94 | msgid "System CPU time" 95 | msgstr "" 96 | 97 | #: panels/timer.py:80 98 | #, fuzzy 99 | msgid "Total CPU time" 100 | msgstr "Zeit gesamt" 101 | 102 | #: panels/timer.py:81 103 | msgid "Elapsed time" 104 | msgstr "" 105 | 106 | #: panels/timer.py:82 107 | msgid "Context switches" 108 | msgstr "" 109 | 110 | #: panels/version.py:20 panels/version.py:29 111 | #, fuzzy 112 | msgid "Versions" 113 | msgstr "Django-Version" 114 | 115 | #: templates/debug_toolbar/base.html:23 116 | msgid "Hide Toolbar" 117 | msgstr "" 118 | 119 | #: templates/debug_toolbar/base.html:23 120 | msgid "Hide" 121 | msgstr "Verbergen" 122 | 123 | #: templates/debug_toolbar/base.html:48 124 | msgid "Show Toolbar" 125 | msgstr "" 126 | 127 | #: templates/debug_toolbar/base.html:54 128 | msgid "Close" 129 | msgstr "Schließen" 130 | 131 | #: templates/debug_toolbar/redirect.html:7 132 | #: templates/debug_toolbar/panels/logger.html:9 133 | msgid "Location" 134 | msgstr "Ort" 135 | 136 | #: templates/debug_toolbar/redirect.html:9 137 | msgid "" 138 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " 139 | "debug viewing purposes. You can click the above link to continue with the " 140 | "redirect as normal. If you'd like to disable this feature, set the " 141 | "DEBUG_TOOLBAR_CONFIG dictionary's key " 142 | "INTERCEPT_REDIRECTS to False." 143 | msgstr "" 144 | 145 | #: templates/debug_toolbar/panels/cache.html:14 146 | msgid "Total Calls" 147 | msgstr "Aufrufe gesamt" 148 | 149 | #: templates/debug_toolbar/panels/cache.html:16 150 | msgid "Total Time" 151 | msgstr "Zeit gesamt" 152 | 153 | #: templates/debug_toolbar/panels/cache.html:18 154 | msgid "Hits" 155 | msgstr "Aufrufe" 156 | 157 | #: templates/debug_toolbar/panels/cache.html:20 158 | msgid "Misses" 159 | msgstr "" 160 | 161 | #: templates/debug_toolbar/panels/cache.html:35 162 | msgid "Breakdown" 163 | msgstr "" 164 | 165 | #: templates/debug_toolbar/panels/cache.html:40 166 | msgid "Type" 167 | msgstr "Typ" 168 | 169 | #: templates/debug_toolbar/panels/cache.html:41 170 | msgid "Parameters" 171 | msgstr "Parameter" 172 | 173 | #: templates/debug_toolbar/panels/cache.html:42 174 | msgid "Function" 175 | msgstr "Funktion" 176 | 177 | #: templates/debug_toolbar/panels/headers.html:5 178 | msgid "Key" 179 | msgstr "Schlüssel" 180 | 181 | #: templates/debug_toolbar/panels/headers.html:6 182 | #: templates/debug_toolbar/panels/request_vars.html:37 183 | #: templates/debug_toolbar/panels/request_vars.html:63 184 | #: templates/debug_toolbar/panels/request_vars.html:85 185 | #: templates/debug_toolbar/panels/request_vars.html:107 186 | #: templates/debug_toolbar/panels/settings_vars.html:6 187 | #: templates/debug_toolbar/panels/timer.html:10 188 | msgid "Value" 189 | msgstr "Wert" 190 | 191 | #: templates/debug_toolbar/panels/logger.html:6 192 | msgid "Level" 193 | msgstr "Niveau" 194 | 195 | #: templates/debug_toolbar/panels/logger.html:8 196 | msgid "Message" 197 | msgstr "Nachricht" 198 | 199 | #: templates/debug_toolbar/panels/logger.html:24 200 | msgid "No messages logged" 201 | msgstr "Keine Nachricht gespeichert" 202 | 203 | #: templates/debug_toolbar/panels/request_vars.html:3 204 | msgid "View information" 205 | msgstr "" 206 | 207 | #: templates/debug_toolbar/panels/request_vars.html:7 208 | #, fuzzy 209 | msgid "View Function" 210 | msgstr "Funktion" 211 | 212 | #: templates/debug_toolbar/panels/request_vars.html:8 213 | msgid "args" 214 | msgstr "" 215 | 216 | #: templates/debug_toolbar/panels/request_vars.html:9 217 | msgid "kwargs" 218 | msgstr "" 219 | 220 | #: templates/debug_toolbar/panels/request_vars.html:27 221 | #, fuzzy 222 | msgid "COOKIES Variables" 223 | msgstr "Variable" 224 | 225 | #: templates/debug_toolbar/panels/request_vars.html:36 226 | #: templates/debug_toolbar/panels/request_vars.html:62 227 | #: templates/debug_toolbar/panels/request_vars.html:84 228 | #: templates/debug_toolbar/panels/request_vars.html:106 229 | msgid "Variable" 230 | msgstr "Variable" 231 | 232 | #: templates/debug_toolbar/panels/request_vars.html:50 233 | msgid "No COOKIE data" 234 | msgstr "Keine COOKIE-Daten" 235 | 236 | #: templates/debug_toolbar/panels/request_vars.html:53 237 | #, fuzzy 238 | msgid "SESSION Variables" 239 | msgstr "Variable" 240 | 241 | #: templates/debug_toolbar/panels/request_vars.html:76 242 | msgid "No SESSION data" 243 | msgstr "Keine SESSION-Daten" 244 | 245 | #: templates/debug_toolbar/panels/request_vars.html:79 246 | #, fuzzy 247 | msgid "GET Variables" 248 | msgstr "Variable" 249 | 250 | #: templates/debug_toolbar/panels/request_vars.html:98 251 | msgid "No GET data" 252 | msgstr "Keine GET-Daten" 253 | 254 | #: templates/debug_toolbar/panels/request_vars.html:101 255 | #, fuzzy 256 | msgid "POST Variables" 257 | msgstr "Variable" 258 | 259 | #: templates/debug_toolbar/panels/request_vars.html:120 260 | msgid "No POST data" 261 | msgstr "Keine POST-Daten" 262 | 263 | #: templates/debug_toolbar/panels/settings_vars.html:5 264 | msgid "Setting" 265 | msgstr "Einstellung" 266 | 267 | #: templates/debug_toolbar/panels/signals.html:5 268 | msgid "Signal" 269 | msgstr "Signal" 270 | 271 | #: templates/debug_toolbar/panels/signals.html:6 272 | msgid "Providing Args" 273 | msgstr "" 274 | 275 | #: templates/debug_toolbar/panels/signals.html:7 276 | msgid "Receivers" 277 | msgstr "" 278 | 279 | #: templates/debug_toolbar/panels/sql.html:6 280 | msgid "Action" 281 | msgstr "Aktion" 282 | 283 | #: templates/debug_toolbar/panels/sql.html:7 284 | msgid "Stacktrace" 285 | msgstr "" 286 | 287 | #: templates/debug_toolbar/panels/sql.html:8 288 | msgid "Query" 289 | msgstr "" 290 | 291 | #: templates/debug_toolbar/panels/sql.html:38 292 | msgid "Line" 293 | msgstr "Zeile" 294 | 295 | #: templates/debug_toolbar/panels/sql.html:39 296 | msgid "Method" 297 | msgstr "Methode" 298 | 299 | #: templates/debug_toolbar/panels/sql.html:40 300 | msgid "File" 301 | msgstr "Datei" 302 | 303 | #: templates/debug_toolbar/panels/sql_explain.html:3 304 | #: templates/debug_toolbar/panels/sql_profile.html:3 305 | #: templates/debug_toolbar/panels/sql_select.html:3 306 | #: templates/debug_toolbar/panels/template_source.html:3 307 | msgid "Back" 308 | msgstr "Zurück" 309 | 310 | #: templates/debug_toolbar/panels/sql_explain.html:4 311 | msgid "SQL Explained" 312 | msgstr "" 313 | 314 | #: templates/debug_toolbar/panels/sql_explain.html:9 315 | #: templates/debug_toolbar/panels/sql_profile.html:10 316 | #: templates/debug_toolbar/panels/sql_select.html:9 317 | msgid "Executed SQL" 318 | msgstr "Ausgeführtes SQL" 319 | 320 | #: templates/debug_toolbar/panels/sql_profile.html:4 321 | msgid "SQL Profiled" 322 | msgstr "" 323 | 324 | #: templates/debug_toolbar/panels/sql_profile.html:35 325 | msgid "Error" 326 | msgstr "Fehler" 327 | 328 | #: templates/debug_toolbar/panels/sql_select.html:4 329 | msgid "SQL Selected" 330 | msgstr "" 331 | 332 | #: templates/debug_toolbar/panels/sql_select.html:34 333 | msgid "Empty set" 334 | msgstr "Leeres Set" 335 | 336 | #: templates/debug_toolbar/panels/template_source.html:4 337 | #, fuzzy 338 | msgid "Template Source" 339 | msgstr "Template" 340 | 341 | #: templates/debug_toolbar/panels/templates.html:2 342 | #, fuzzy 343 | msgid "Template path" 344 | msgstr "Template" 345 | 346 | #: templates/debug_toolbar/panels/templates.html:13 347 | msgid "Template" 348 | msgstr "Template" 349 | 350 | #: templates/debug_toolbar/panels/templates.html:21 351 | #: templates/debug_toolbar/panels/templates.html:37 352 | msgid "Toggle Context" 353 | msgstr "" 354 | 355 | #: templates/debug_toolbar/panels/templates.html:28 356 | #: templates/debug_toolbar/panels/templates.html:43 357 | msgid "None" 358 | msgstr "Nichts" 359 | 360 | #: templates/debug_toolbar/panels/templates.html:31 361 | msgid "Context processor" 362 | msgstr "" 363 | 364 | #: templates/debug_toolbar/panels/timer.html:9 365 | msgid "Resource" 366 | msgstr "Ressource" 367 | 368 | #: templates/debug_toolbar/panels/versions.html:6 369 | msgid "Package" 370 | msgstr "" 371 | 372 | #: templates/debug_toolbar/panels/versions.html:7 373 | #, fuzzy 374 | msgid "Version" 375 | msgstr "Django-Version" 376 | -------------------------------------------------------------------------------- /debug_toolbar/locale/es/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Debug Toolbar en Español. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Percy Pérez-Pinedo , 2009. 5 | # 6 | # Caracteres especiales: á, é, í, ó, ú, ñ, 7 | # 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: PACKAGE VERSION\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2009-11-18 08:06-0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: panels/cache.py:92 21 | #, python-format 22 | msgid "Cache: %.2fms" 23 | msgstr "" 24 | 25 | #: panels/cache.py:95 26 | msgid "Cache Usage" 27 | msgstr "" 28 | 29 | #: panels/headers.py:36 panels/headers.py:39 30 | msgid "HTTP Headers" 31 | msgstr "" 32 | 33 | #: panels/logger.py:56 34 | msgid "Logging" 35 | msgstr "Registros" 36 | 37 | #: panels/logger.py:63 38 | #, fuzzy 39 | msgid "Log Messages" 40 | msgstr "Mensaje" 41 | 42 | #: panels/request_vars.py:13 panels/request_vars.py:16 43 | msgid "Request Vars" 44 | msgstr "" 45 | 46 | #: panels/settings_vars.py:16 47 | msgid "Settings" 48 | msgstr "Configuraciones" 49 | 50 | #: panels/settings_vars.py:19 51 | #, python-format 52 | msgid "Settings from %s" 53 | msgstr "" 54 | 55 | #: panels/signals.py:39 panels/signals.py:42 56 | msgid "Signals" 57 | msgstr "Señales" 58 | 59 | #: panels/sql.py:146 60 | msgid "SQL" 61 | msgstr "" 62 | 63 | #: panels/sql.py:160 64 | msgid "SQL Queries" 65 | msgstr "" 66 | 67 | #: panels/template.py:47 68 | msgid "Templates" 69 | msgstr "Plantillas" 70 | 71 | #: panels/template.py:52 72 | #, python-format 73 | msgid "Templates (%(num_templates)s rendered)" 74 | msgstr "" 75 | 76 | #: panels/timer.py:35 templates/debug_toolbar/panels/cache.html:39 77 | #: templates/debug_toolbar/panels/logger.html:7 78 | #: templates/debug_toolbar/panels/sql.html:5 79 | #: templates/debug_toolbar/panels/sql_explain.html:11 80 | #: templates/debug_toolbar/panels/sql_profile.html:12 81 | #: templates/debug_toolbar/panels/sql_select.html:11 82 | msgid "Time" 83 | msgstr "Tiempo" 84 | 85 | #: panels/timer.py:47 86 | #, fuzzy 87 | msgid "Resource Usage" 88 | msgstr "Recurso" 89 | 90 | #: panels/timer.py:78 91 | msgid "User CPU time" 92 | msgstr "" 93 | 94 | #: panels/timer.py:79 95 | msgid "System CPU time" 96 | msgstr "" 97 | 98 | #: panels/timer.py:80 99 | #, fuzzy 100 | msgid "Total CPU time" 101 | msgstr "Tiempo Total" 102 | 103 | #: panels/timer.py:81 104 | msgid "Elapsed time" 105 | msgstr "" 106 | 107 | #: panels/timer.py:82 108 | msgid "Context switches" 109 | msgstr "" 110 | 111 | #: panels/version.py:20 panels/version.py:29 112 | #, fuzzy 113 | msgid "Versions" 114 | msgstr "Versión Django" 115 | 116 | #: templates/debug_toolbar/base.html:23 117 | msgid "Hide Toolbar" 118 | msgstr "" 119 | 120 | #: templates/debug_toolbar/base.html:23 121 | msgid "Hide" 122 | msgstr "Ocultar" 123 | 124 | #: templates/debug_toolbar/base.html:48 125 | msgid "Show Toolbar" 126 | msgstr "" 127 | 128 | #: templates/debug_toolbar/base.html:54 129 | msgid "Close" 130 | msgstr "Cerrar" 131 | 132 | #: templates/debug_toolbar/redirect.html:7 133 | #: templates/debug_toolbar/panels/logger.html:9 134 | msgid "Location" 135 | msgstr "" 136 | 137 | #: templates/debug_toolbar/redirect.html:9 138 | msgid "" 139 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " 140 | "debug viewing purposes. You can click the above link to continue with the " 141 | "redirect as normal. If you'd like to disable this feature, set the " 142 | "DEBUG_TOOLBAR_CONFIG dictionary's key " 143 | "INTERCEPT_REDIRECTS to False." 144 | msgstr "" 145 | 146 | #: templates/debug_toolbar/panels/cache.html:14 147 | msgid "Total Calls" 148 | msgstr "Total Llamadas" 149 | 150 | #: templates/debug_toolbar/panels/cache.html:16 151 | msgid "Total Time" 152 | msgstr "Tiempo Total" 153 | 154 | #: templates/debug_toolbar/panels/cache.html:18 155 | msgid "Hits" 156 | msgstr "Visitas" 157 | 158 | #: templates/debug_toolbar/panels/cache.html:20 159 | msgid "Misses" 160 | msgstr "" 161 | 162 | #: templates/debug_toolbar/panels/cache.html:35 163 | msgid "Breakdown" 164 | msgstr "" 165 | 166 | #: templates/debug_toolbar/panels/cache.html:40 167 | msgid "Type" 168 | msgstr "Tipo" 169 | 170 | #: templates/debug_toolbar/panels/cache.html:41 171 | msgid "Parameters" 172 | msgstr "Parámetros" 173 | 174 | #: templates/debug_toolbar/panels/cache.html:42 175 | msgid "Function" 176 | msgstr "Función" 177 | 178 | #: templates/debug_toolbar/panels/headers.html:5 179 | msgid "Key" 180 | msgstr "Llave" 181 | 182 | #: templates/debug_toolbar/panels/headers.html:6 183 | #: templates/debug_toolbar/panels/request_vars.html:37 184 | #: templates/debug_toolbar/panels/request_vars.html:63 185 | #: templates/debug_toolbar/panels/request_vars.html:85 186 | #: templates/debug_toolbar/panels/request_vars.html:107 187 | #: templates/debug_toolbar/panels/settings_vars.html:6 188 | #: templates/debug_toolbar/panels/timer.html:10 189 | msgid "Value" 190 | msgstr "Valor" 191 | 192 | #: templates/debug_toolbar/panels/logger.html:6 193 | msgid "Level" 194 | msgstr "Nivel" 195 | 196 | #: templates/debug_toolbar/panels/logger.html:8 197 | msgid "Message" 198 | msgstr "Mensaje" 199 | 200 | #: templates/debug_toolbar/panels/logger.html:24 201 | msgid "No messages logged" 202 | msgstr "No hay mensajes registrados" 203 | 204 | #: templates/debug_toolbar/panels/request_vars.html:3 205 | msgid "View information" 206 | msgstr "" 207 | 208 | #: templates/debug_toolbar/panels/request_vars.html:7 209 | #, fuzzy 210 | msgid "View Function" 211 | msgstr "Función" 212 | 213 | #: templates/debug_toolbar/panels/request_vars.html:8 214 | msgid "args" 215 | msgstr "" 216 | 217 | #: templates/debug_toolbar/panels/request_vars.html:9 218 | msgid "kwargs" 219 | msgstr "" 220 | 221 | #: templates/debug_toolbar/panels/request_vars.html:27 222 | #, fuzzy 223 | msgid "COOKIES Variables" 224 | msgstr "Variable" 225 | 226 | #: templates/debug_toolbar/panels/request_vars.html:36 227 | #: templates/debug_toolbar/panels/request_vars.html:62 228 | #: templates/debug_toolbar/panels/request_vars.html:84 229 | #: templates/debug_toolbar/panels/request_vars.html:106 230 | #, fuzzy 231 | msgid "Variable" 232 | msgstr "Variable" 233 | 234 | #: templates/debug_toolbar/panels/request_vars.html:50 235 | #, fuzzy 236 | msgid "No COOKIE data" 237 | msgstr "No GET datos" 238 | 239 | #: templates/debug_toolbar/panels/request_vars.html:53 240 | #, fuzzy 241 | msgid "SESSION Variables" 242 | msgstr "Variable" 243 | 244 | #: templates/debug_toolbar/panels/request_vars.html:76 245 | msgid "No SESSION data" 246 | msgstr "No SESSION datos" 247 | 248 | #: templates/debug_toolbar/panels/request_vars.html:79 249 | #, fuzzy 250 | msgid "GET Variables" 251 | msgstr "Variable" 252 | 253 | #: templates/debug_toolbar/panels/request_vars.html:98 254 | msgid "No GET data" 255 | msgstr "No GET datos" 256 | 257 | #: templates/debug_toolbar/panels/request_vars.html:101 258 | #, fuzzy 259 | msgid "POST Variables" 260 | msgstr "Variable" 261 | 262 | #: templates/debug_toolbar/panels/request_vars.html:120 263 | msgid "No POST data" 264 | msgstr "No POST datos" 265 | 266 | #: templates/debug_toolbar/panels/settings_vars.html:5 267 | msgid "Setting" 268 | msgstr "Configuración" 269 | 270 | #: templates/debug_toolbar/panels/signals.html:5 271 | msgid "Signal" 272 | msgstr "Señal" 273 | 274 | #: templates/debug_toolbar/panels/signals.html:6 275 | msgid "Providing Args" 276 | msgstr "" 277 | 278 | #: templates/debug_toolbar/panels/signals.html:7 279 | msgid "Receivers" 280 | msgstr "" 281 | 282 | #: templates/debug_toolbar/panels/sql.html:6 283 | msgid "Action" 284 | msgstr "Acción" 285 | 286 | #: templates/debug_toolbar/panels/sql.html:7 287 | msgid "Stacktrace" 288 | msgstr "" 289 | 290 | #: templates/debug_toolbar/panels/sql.html:8 291 | msgid "Query" 292 | msgstr "" 293 | 294 | #: templates/debug_toolbar/panels/sql.html:38 295 | msgid "Line" 296 | msgstr "Línea" 297 | 298 | #: templates/debug_toolbar/panels/sql.html:39 299 | msgid "Method" 300 | msgstr "Método" 301 | 302 | #: templates/debug_toolbar/panels/sql.html:40 303 | msgid "File" 304 | msgstr "Archivo" 305 | 306 | #: templates/debug_toolbar/panels/sql_explain.html:3 307 | #: templates/debug_toolbar/panels/sql_profile.html:3 308 | #: templates/debug_toolbar/panels/sql_select.html:3 309 | #: templates/debug_toolbar/panels/template_source.html:3 310 | msgid "Back" 311 | msgstr "Regresar" 312 | 313 | #: templates/debug_toolbar/panels/sql_explain.html:4 314 | msgid "SQL Explained" 315 | msgstr "" 316 | 317 | #: templates/debug_toolbar/panels/sql_explain.html:9 318 | #: templates/debug_toolbar/panels/sql_profile.html:10 319 | #: templates/debug_toolbar/panels/sql_select.html:9 320 | msgid "Executed SQL" 321 | msgstr "SQL Ejecutado" 322 | 323 | #: templates/debug_toolbar/panels/sql_profile.html:4 324 | msgid "SQL Profiled" 325 | msgstr "" 326 | 327 | #: templates/debug_toolbar/panels/sql_profile.html:35 328 | msgid "Error" 329 | msgstr "" 330 | 331 | #: templates/debug_toolbar/panels/sql_select.html:4 332 | msgid "SQL Selected" 333 | msgstr "" 334 | 335 | #: templates/debug_toolbar/panels/sql_select.html:34 336 | msgid "Empty set" 337 | msgstr "Set Vacío" 338 | 339 | #: templates/debug_toolbar/panels/template_source.html:4 340 | #, fuzzy 341 | msgid "Template Source" 342 | msgstr "Plantilla" 343 | 344 | #: templates/debug_toolbar/panels/templates.html:2 345 | #, fuzzy 346 | msgid "Template path" 347 | msgstr "Plantilla" 348 | 349 | #: templates/debug_toolbar/panels/templates.html:13 350 | msgid "Template" 351 | msgstr "Plantilla" 352 | 353 | #: templates/debug_toolbar/panels/templates.html:21 354 | #: templates/debug_toolbar/panels/templates.html:37 355 | msgid "Toggle Context" 356 | msgstr "" 357 | 358 | #: templates/debug_toolbar/panels/templates.html:28 359 | #: templates/debug_toolbar/panels/templates.html:43 360 | msgid "None" 361 | msgstr "Ninguno" 362 | 363 | #: templates/debug_toolbar/panels/templates.html:31 364 | msgid "Context processor" 365 | msgstr "" 366 | 367 | #: templates/debug_toolbar/panels/timer.html:9 368 | msgid "Resource" 369 | msgstr "Recurso" 370 | 371 | #: templates/debug_toolbar/panels/versions.html:6 372 | msgid "Package" 373 | msgstr "" 374 | 375 | #: templates/debug_toolbar/panels/versions.html:7 376 | #, fuzzy 377 | msgid "Version" 378 | msgstr "Versión Django" 379 | -------------------------------------------------------------------------------- /debug_toolbar/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Debug Toolbar in Russian. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Mikhail Korobov, 2009. 5 | # 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: \n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2009-11-18 08:06-0800\n" 12 | "PO-Revision-Date: \n" 13 | "Last-Translator: Mikhail Korobov \n" 14 | "Language-Team: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: panels/cache.py:92 20 | #, python-format 21 | msgid "Cache: %.2fms" 22 | msgstr "" 23 | 24 | #: panels/cache.py:95 25 | msgid "Cache Usage" 26 | msgstr "" 27 | 28 | #: panels/headers.py:36 panels/headers.py:39 29 | msgid "HTTP Headers" 30 | msgstr "Заголовки HTTP" 31 | 32 | #: panels/logger.py:56 33 | msgid "Logging" 34 | msgstr "Журналирование" 35 | 36 | #: panels/logger.py:63 37 | #, fuzzy 38 | msgid "Log Messages" 39 | msgstr "Сообщение" 40 | 41 | #: panels/request_vars.py:13 panels/request_vars.py:16 42 | msgid "Request Vars" 43 | msgstr "Запрос" 44 | 45 | #: panels/settings_vars.py:16 46 | msgid "Settings" 47 | msgstr "Настройки" 48 | 49 | #: panels/settings_vars.py:19 50 | #, python-format 51 | msgid "Settings from %s" 52 | msgstr "" 53 | 54 | #: panels/signals.py:39 panels/signals.py:42 55 | msgid "Signals" 56 | msgstr "Сигналы" 57 | 58 | #: panels/sql.py:146 59 | msgid "SQL" 60 | msgstr "" 61 | 62 | #: panels/sql.py:160 63 | msgid "SQL Queries" 64 | msgstr "" 65 | 66 | #: panels/template.py:47 67 | msgid "Templates" 68 | msgstr "Шаблоны" 69 | 70 | #: panels/template.py:52 71 | #, python-format 72 | msgid "Templates (%(num_templates)s rendered)" 73 | msgstr "" 74 | 75 | #: panels/timer.py:35 templates/debug_toolbar/panels/cache.html:39 76 | #: templates/debug_toolbar/panels/logger.html:7 77 | #: templates/debug_toolbar/panels/sql.html:5 78 | #: templates/debug_toolbar/panels/sql_explain.html:11 79 | #: templates/debug_toolbar/panels/sql_profile.html:12 80 | #: templates/debug_toolbar/panels/sql_select.html:11 81 | msgid "Time" 82 | msgstr "Время" 83 | 84 | #: panels/timer.py:47 85 | #, fuzzy 86 | msgid "Resource Usage" 87 | msgstr "Ресурс" 88 | 89 | #: panels/timer.py:78 90 | msgid "User CPU time" 91 | msgstr "" 92 | 93 | #: panels/timer.py:79 94 | msgid "System CPU time" 95 | msgstr "" 96 | 97 | #: panels/timer.py:80 98 | #, fuzzy 99 | msgid "Total CPU time" 100 | msgstr "Общее время" 101 | 102 | #: panels/timer.py:81 103 | msgid "Elapsed time" 104 | msgstr "" 105 | 106 | #: panels/timer.py:82 107 | msgid "Context switches" 108 | msgstr "" 109 | 110 | #: panels/version.py:20 panels/version.py:29 111 | #, fuzzy 112 | msgid "Versions" 113 | msgstr "Версия Django" 114 | 115 | #: templates/debug_toolbar/base.html:23 116 | msgid "Hide Toolbar" 117 | msgstr "" 118 | 119 | #: templates/debug_toolbar/base.html:23 120 | msgid "Hide" 121 | msgstr "Скрыть" 122 | 123 | #: templates/debug_toolbar/base.html:48 124 | msgid "Show Toolbar" 125 | msgstr "" 126 | 127 | #: templates/debug_toolbar/base.html:54 128 | msgid "Close" 129 | msgstr "Закрыть" 130 | 131 | #: templates/debug_toolbar/redirect.html:7 132 | #: templates/debug_toolbar/panels/logger.html:9 133 | msgid "Location" 134 | msgstr "Место" 135 | 136 | #: templates/debug_toolbar/redirect.html:9 137 | msgid "" 138 | "The Django Debug Toolbar has intercepted a redirect to the above URL for " 139 | "debug viewing purposes. You can click the above link to continue with the " 140 | "redirect as normal. If you'd like to disable this feature, set the " 141 | "DEBUG_TOOLBAR_CONFIG dictionary's key " 142 | "INTERCEPT_REDIRECTS to False." 143 | msgstr "" 144 | "Django Debug Toolbar в отладочных целях перехватила редирект на адрес, " 145 | "указанный выше. Вы можете нажать на ссылку, чтобы продолжить обычное " 146 | "выполнение. Если хотите отключить это поведение, установите в словаре " 147 | "DEBUG_TOOLBAR_CONFIG ключ INTERCEPT_REDIRECTS " 148 | "равным False." 149 | 150 | #: templates/debug_toolbar/panels/cache.html:14 151 | msgid "Total Calls" 152 | msgstr "Всего вызовов" 153 | 154 | #: templates/debug_toolbar/panels/cache.html:16 155 | msgid "Total Time" 156 | msgstr "Общее время" 157 | 158 | #: templates/debug_toolbar/panels/cache.html:18 159 | msgid "Hits" 160 | msgstr "" 161 | 162 | #: templates/debug_toolbar/panels/cache.html:20 163 | msgid "Misses" 164 | msgstr "" 165 | 166 | #: templates/debug_toolbar/panels/cache.html:35 167 | msgid "Breakdown" 168 | msgstr "" 169 | 170 | #: templates/debug_toolbar/panels/cache.html:40 171 | msgid "Type" 172 | msgstr "" 173 | 174 | #: templates/debug_toolbar/panels/cache.html:41 175 | msgid "Parameters" 176 | msgstr "" 177 | 178 | #: templates/debug_toolbar/panels/cache.html:42 179 | msgid "Function" 180 | msgstr "" 181 | 182 | #: templates/debug_toolbar/panels/headers.html:5 183 | msgid "Key" 184 | msgstr "Заголовок" 185 | 186 | #: templates/debug_toolbar/panels/headers.html:6 187 | #: templates/debug_toolbar/panels/request_vars.html:37 188 | #: templates/debug_toolbar/panels/request_vars.html:63 189 | #: templates/debug_toolbar/panels/request_vars.html:85 190 | #: templates/debug_toolbar/panels/request_vars.html:107 191 | #: templates/debug_toolbar/panels/settings_vars.html:6 192 | #: templates/debug_toolbar/panels/timer.html:10 193 | msgid "Value" 194 | msgstr "Значение" 195 | 196 | #: templates/debug_toolbar/panels/logger.html:6 197 | msgid "Level" 198 | msgstr "Уровень" 199 | 200 | #: templates/debug_toolbar/panels/logger.html:8 201 | msgid "Message" 202 | msgstr "Сообщение" 203 | 204 | #: templates/debug_toolbar/panels/logger.html:24 205 | msgid "No messages logged" 206 | msgstr "Сообщений нет" 207 | 208 | #: templates/debug_toolbar/panels/request_vars.html:3 209 | msgid "View information" 210 | msgstr "" 211 | 212 | #: templates/debug_toolbar/panels/request_vars.html:7 213 | msgid "View Function" 214 | msgstr "" 215 | 216 | #: templates/debug_toolbar/panels/request_vars.html:8 217 | msgid "args" 218 | msgstr "" 219 | 220 | #: templates/debug_toolbar/panels/request_vars.html:9 221 | msgid "kwargs" 222 | msgstr "" 223 | 224 | #: templates/debug_toolbar/panels/request_vars.html:27 225 | #, fuzzy 226 | msgid "COOKIES Variables" 227 | msgstr "Переменная" 228 | 229 | #: templates/debug_toolbar/panels/request_vars.html:36 230 | #: templates/debug_toolbar/panels/request_vars.html:62 231 | #: templates/debug_toolbar/panels/request_vars.html:84 232 | #: templates/debug_toolbar/panels/request_vars.html:106 233 | msgid "Variable" 234 | msgstr "Переменная" 235 | 236 | #: templates/debug_toolbar/panels/request_vars.html:50 237 | msgid "No COOKIE data" 238 | msgstr "" 239 | 240 | #: templates/debug_toolbar/panels/request_vars.html:53 241 | #, fuzzy 242 | msgid "SESSION Variables" 243 | msgstr "Переменная" 244 | 245 | #: templates/debug_toolbar/panels/request_vars.html:76 246 | msgid "No SESSION data" 247 | msgstr "" 248 | 249 | #: templates/debug_toolbar/panels/request_vars.html:79 250 | #, fuzzy 251 | msgid "GET Variables" 252 | msgstr "Переменная" 253 | 254 | #: templates/debug_toolbar/panels/request_vars.html:98 255 | msgid "No GET data" 256 | msgstr "" 257 | 258 | #: templates/debug_toolbar/panels/request_vars.html:101 259 | #, fuzzy 260 | msgid "POST Variables" 261 | msgstr "Переменная" 262 | 263 | #: templates/debug_toolbar/panels/request_vars.html:120 264 | msgid "No POST data" 265 | msgstr "" 266 | 267 | #: templates/debug_toolbar/panels/settings_vars.html:5 268 | msgid "Setting" 269 | msgstr "Параметр" 270 | 271 | #: templates/debug_toolbar/panels/signals.html:5 272 | msgid "Signal" 273 | msgstr "Сигналы" 274 | 275 | #: templates/debug_toolbar/panels/signals.html:6 276 | msgid "Providing Args" 277 | msgstr "" 278 | 279 | #: templates/debug_toolbar/panels/signals.html:7 280 | msgid "Receivers" 281 | msgstr "" 282 | 283 | #: templates/debug_toolbar/panels/sql.html:6 284 | msgid "Action" 285 | msgstr "Действие" 286 | 287 | #: templates/debug_toolbar/panels/sql.html:7 288 | msgid "Stacktrace" 289 | msgstr "" 290 | 291 | #: templates/debug_toolbar/panels/sql.html:8 292 | msgid "Query" 293 | msgstr "Запрос" 294 | 295 | #: templates/debug_toolbar/panels/sql.html:38 296 | msgid "Line" 297 | msgstr "Строка" 298 | 299 | #: templates/debug_toolbar/panels/sql.html:39 300 | msgid "Method" 301 | msgstr "Метод" 302 | 303 | #: templates/debug_toolbar/panels/sql.html:40 304 | msgid "File" 305 | msgstr "Файл" 306 | 307 | #: templates/debug_toolbar/panels/sql_explain.html:3 308 | #: templates/debug_toolbar/panels/sql_profile.html:3 309 | #: templates/debug_toolbar/panels/sql_select.html:3 310 | #: templates/debug_toolbar/panels/template_source.html:3 311 | msgid "Back" 312 | msgstr "Назад" 313 | 314 | #: templates/debug_toolbar/panels/sql_explain.html:4 315 | msgid "SQL Explained" 316 | msgstr "" 317 | 318 | #: templates/debug_toolbar/panels/sql_explain.html:9 319 | #: templates/debug_toolbar/panels/sql_profile.html:10 320 | #: templates/debug_toolbar/panels/sql_select.html:9 321 | msgid "Executed SQL" 322 | msgstr "Запрос" 323 | 324 | #: templates/debug_toolbar/panels/sql_profile.html:4 325 | msgid "SQL Profiled" 326 | msgstr "" 327 | 328 | #: templates/debug_toolbar/panels/sql_profile.html:35 329 | msgid "Error" 330 | msgstr "Ошибка" 331 | 332 | #: templates/debug_toolbar/panels/sql_select.html:4 333 | msgid "SQL Selected" 334 | msgstr "" 335 | 336 | #: templates/debug_toolbar/panels/sql_select.html:34 337 | msgid "Empty set" 338 | msgstr "Ничего" 339 | 340 | #: templates/debug_toolbar/panels/template_source.html:4 341 | #, fuzzy 342 | msgid "Template Source" 343 | msgstr "Шаблоны" 344 | 345 | #: templates/debug_toolbar/panels/templates.html:2 346 | #, fuzzy 347 | msgid "Template path" 348 | msgstr "Шаблоны" 349 | 350 | #: templates/debug_toolbar/panels/templates.html:13 351 | msgid "Template" 352 | msgstr "" 353 | 354 | #: templates/debug_toolbar/panels/templates.html:21 355 | #: templates/debug_toolbar/panels/templates.html:37 356 | msgid "Toggle Context" 357 | msgstr "" 358 | 359 | #: templates/debug_toolbar/panels/templates.html:28 360 | #: templates/debug_toolbar/panels/templates.html:43 361 | msgid "None" 362 | msgstr "" 363 | 364 | #: templates/debug_toolbar/panels/templates.html:31 365 | msgid "Context processor" 366 | msgstr "" 367 | 368 | #: templates/debug_toolbar/panels/timer.html:9 369 | msgid "Resource" 370 | msgstr "Ресурс" 371 | 372 | #: templates/debug_toolbar/panels/versions.html:6 373 | msgid "Package" 374 | msgstr "" 375 | 376 | #: templates/debug_toolbar/panels/versions.html:7 377 | #, fuzzy 378 | msgid "Version" 379 | msgstr "Версия Django" 380 | -------------------------------------------------------------------------------- /debug_toolbar/utils/sqlparse/engine/grouping.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import itertools 4 | import re 5 | import types 6 | 7 | from debug_toolbar.utils.sqlparse import tokens as T 8 | from debug_toolbar.utils.sqlparse.sql import * 9 | 10 | 11 | 12 | def _group_left_right(tlist, ttype, value, cls, 13 | check_right=lambda t: True, 14 | include_semicolon=False): 15 | [_group_left_right(sgroup, ttype, value, cls, check_right, 16 | include_semicolon) for sgroup in tlist.get_sublists() 17 | if not isinstance(sgroup, cls)] 18 | idx = 0 19 | token = tlist.token_next_match(idx, ttype, value) 20 | while token: 21 | right = tlist.token_next(tlist.token_index(token)) 22 | left = tlist.token_prev(tlist.token_index(token)) 23 | if (right is None or not check_right(right) 24 | or left is None): 25 | token = tlist.token_next_match(tlist.token_index(token)+1, 26 | ttype, value) 27 | else: 28 | if include_semicolon: 29 | right = tlist.token_next_match(tlist.token_index(right), 30 | T.Punctuation, ';') 31 | tokens = tlist.tokens_between(left, right)[1:] 32 | if not isinstance(left, cls): 33 | new = cls([left]) 34 | new_idx = tlist.token_index(left) 35 | tlist.tokens.remove(left) 36 | tlist.tokens.insert(new_idx, new) 37 | left = new 38 | left.tokens.extend(tokens) 39 | for t in tokens: 40 | tlist.tokens.remove(t) 41 | token = tlist.token_next_match(tlist.token_index(left)+1, 42 | ttype, value) 43 | 44 | def _group_matching(tlist, start_ttype, start_value, end_ttype, end_value, 45 | cls, include_semicolon=False, recurse=False): 46 | def _find_matching(i, tl, stt, sva, ett, eva): 47 | depth = 1 48 | for t in tl.tokens[i:]: 49 | if t.match(stt, sva): 50 | depth += 1 51 | elif t.match(ett, eva): 52 | depth -= 1 53 | if depth == 1: 54 | return t 55 | return None 56 | [_group_matching(sgroup, start_ttype, start_value, end_ttype, end_value, 57 | cls, include_semicolon) for sgroup in tlist.get_sublists() 58 | if recurse] 59 | if isinstance(tlist, cls): 60 | idx = 1 61 | else: 62 | idx = 0 63 | token = tlist.token_next_match(idx, start_ttype, start_value) 64 | while token: 65 | tidx = tlist.token_index(token) 66 | end = _find_matching(tidx, tlist, start_ttype, start_value, 67 | end_ttype, end_value) 68 | if end is None: 69 | idx = tidx+1 70 | else: 71 | if include_semicolon: 72 | next_ = tlist.token_next(tlist.token_index(end)) 73 | if next_ and next_.match(T.Punctuation, ';'): 74 | end = next_ 75 | group = tlist.group_tokens(cls, tlist.tokens_between(token, end)) 76 | _group_matching(group, start_ttype, start_value, 77 | end_ttype, end_value, cls, include_semicolon) 78 | idx = tlist.token_index(group)+1 79 | token = tlist.token_next_match(idx, start_ttype, start_value) 80 | 81 | def group_if(tlist): 82 | _group_matching(tlist, T.Keyword, 'IF', T.Keyword, 'END IF', If, True) 83 | 84 | def group_for(tlist): 85 | _group_matching(tlist, T.Keyword, 'FOR', T.Keyword, 'END LOOP', For, True) 86 | 87 | def group_as(tlist): 88 | _group_left_right(tlist, T.Keyword, 'AS', Identifier) 89 | 90 | def group_assignment(tlist): 91 | _group_left_right(tlist, T.Assignment, ':=', Assignment, 92 | include_semicolon=True) 93 | 94 | def group_comparsion(tlist): 95 | _group_left_right(tlist, T.Operator, None, Comparsion) 96 | 97 | 98 | def group_case(tlist): 99 | _group_matching(tlist, T.Keyword, 'CASE', T.Keyword, 'END', Case, 100 | include_semicolon=True, recurse=True) 101 | 102 | 103 | def group_identifier(tlist): 104 | def _consume_cycle(tl, i): 105 | x = itertools.cycle((lambda y: y.match(T.Punctuation, '.'), 106 | lambda y: y.ttype in (T.String.Symbol, 107 | T.Name, 108 | T.Wildcard))) 109 | for t in tl.tokens[i:]: 110 | if x.next()(t): 111 | yield t 112 | else: 113 | raise StopIteration 114 | 115 | # bottom up approach: group subgroups first 116 | [group_identifier(sgroup) for sgroup in tlist.get_sublists() 117 | if not isinstance(sgroup, Identifier)] 118 | 119 | # real processing 120 | idx = 0 121 | token = tlist.token_next_by_type(idx, (T.String.Symbol, T.Name)) 122 | while token: 123 | identifier_tokens = [token]+list( 124 | _consume_cycle(tlist, 125 | tlist.token_index(token)+1)) 126 | group = tlist.group_tokens(Identifier, identifier_tokens) 127 | idx = tlist.token_index(group)+1 128 | token = tlist.token_next_by_type(idx, (T.String.Symbol, T.Name)) 129 | 130 | 131 | def group_identifier_list(tlist): 132 | [group_identifier_list(sgroup) for sgroup in tlist.get_sublists() 133 | if not isinstance(sgroup, (Identifier, IdentifierList))] 134 | idx = 0 135 | # Allowed list items 136 | fend1_funcs = [lambda t: isinstance(t, Identifier), 137 | lambda t: t.is_whitespace(), 138 | lambda t: t.ttype == T.Wildcard, 139 | lambda t: t.match(T.Keyword, 'null'), 140 | lambda t: t.ttype == T.Number.Integer, 141 | lambda t: t.ttype == T.String.Single, 142 | lambda t: isinstance(t, Comparsion), 143 | ] 144 | tcomma = tlist.token_next_match(idx, T.Punctuation, ',') 145 | start = None 146 | while tcomma is not None: 147 | before = tlist.token_prev(tcomma) 148 | after = tlist.token_next(tcomma) 149 | # Check if the tokens around tcomma belong to a list 150 | bpassed = apassed = False 151 | for func in fend1_funcs: 152 | if before is not None and func(before): 153 | bpassed = True 154 | if after is not None and func(after): 155 | apassed = True 156 | if not bpassed or not apassed: 157 | # Something's wrong here, skip ahead to next "," 158 | start = None 159 | tcomma = tlist.token_next_match(tlist.token_index(tcomma)+1, 160 | T.Punctuation, ',') 161 | else: 162 | if start is None: 163 | start = before 164 | next_ = tlist.token_next(after) 165 | if next_ is None or not next_.match(T.Punctuation, ','): 166 | # Reached the end of the list 167 | tokens = tlist.tokens_between(start, after) 168 | group = tlist.group_tokens(IdentifierList, tokens) 169 | start = None 170 | tcomma = tlist.token_next_match(tlist.token_index(group)+1, 171 | T.Punctuation, ',') 172 | else: 173 | tcomma = next_ 174 | 175 | 176 | def group_parenthesis(tlist): 177 | _group_matching(tlist, T.Punctuation, '(', T.Punctuation, ')', Parenthesis) 178 | 179 | def group_comments(tlist): 180 | [group_comments(sgroup) for sgroup in tlist.get_sublists() 181 | if not isinstance(sgroup, Comment)] 182 | idx = 0 183 | token = tlist.token_next_by_type(idx, T.Comment) 184 | while token: 185 | tidx = tlist.token_index(token) 186 | end = tlist.token_not_matching(tidx+1, 187 | [lambda t: t.ttype in T.Comment, 188 | lambda t: t.is_whitespace()]) 189 | if end is None: 190 | idx = tidx + 1 191 | else: 192 | eidx = tlist.token_index(end) 193 | grp_tokens = tlist.tokens_between(token, 194 | tlist.token_prev(eidx, False)) 195 | group = tlist.group_tokens(Comment, grp_tokens) 196 | idx = tlist.token_index(group) 197 | token = tlist.token_next_by_type(idx, T.Comment) 198 | 199 | def group_where(tlist): 200 | [group_where(sgroup) for sgroup in tlist.get_sublists() 201 | if not isinstance(sgroup, Where)] 202 | idx = 0 203 | token = tlist.token_next_match(idx, T.Keyword, 'WHERE') 204 | stopwords = ('ORDER', 'GROUP', 'LIMIT', 'UNION') 205 | while token: 206 | tidx = tlist.token_index(token) 207 | end = tlist.token_next_match(tidx+1, T.Keyword, stopwords) 208 | if end is None: 209 | end = tlist.tokens[-1] 210 | else: 211 | end = tlist.tokens[tlist.token_index(end)-1] 212 | group = tlist.group_tokens(Where, tlist.tokens_between(token, end)) 213 | idx = tlist.token_index(group) 214 | token = tlist.token_next_match(idx, T.Keyword, 'WHERE') 215 | 216 | def group_aliased(tlist): 217 | [group_aliased(sgroup) for sgroup in tlist.get_sublists() 218 | if not isinstance(sgroup, Identifier)] 219 | idx = 0 220 | token = tlist.token_next_by_instance(idx, Identifier) 221 | while token: 222 | next_ = tlist.token_next(tlist.token_index(token)) 223 | if next_ is not None and isinstance(next_, Identifier): 224 | grp = tlist.tokens_between(token, next_)[1:] 225 | token.tokens.extend(grp) 226 | for t in grp: 227 | tlist.tokens.remove(t) 228 | idx = tlist.token_index(token)+1 229 | token = tlist.token_next_by_instance(idx, Identifier) 230 | 231 | 232 | def group_typecasts(tlist): 233 | _group_left_right(tlist, T.Punctuation, '::', Identifier) 234 | 235 | 236 | def group(tlist): 237 | for func in [group_parenthesis, 238 | group_comments, 239 | group_where, 240 | group_case, 241 | group_identifier, 242 | group_typecasts, 243 | group_as, 244 | group_aliased, 245 | group_assignment, 246 | group_comparsion, 247 | group_identifier_list, 248 | group_if, 249 | group_for,]: 250 | func(tlist) 251 | --------------------------------------------------------------------------------