aboutsummaryrefslogtreecommitdiffstats
path: root/debug_toolbar/utils
diff options
context:
space:
mode:
Diffstat (limited to 'debug_toolbar/utils')
-rw-r--r--debug_toolbar/utils/__init__.py10
-rw-r--r--debug_toolbar/utils/functional.py14
-rw-r--r--debug_toolbar/utils/sql.py15
-rw-r--r--debug_toolbar/utils/sqlparse/filters.py14
-rw-r--r--debug_toolbar/utils/tracking/db.py44
5 files changed, 81 insertions, 16 deletions
diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py
index bfb485c..2d2ff10 100644
--- a/debug_toolbar/utils/__init__.py
+++ b/debug_toolbar/utils/__init__.py
@@ -56,9 +56,15 @@ def render_stacktrace(trace):
stacktrace = []
for frame in trace:
params = map(escape, frame[0].rsplit(os.path.sep, 1) + list(frame[1:]))
+ params_dict = dict((unicode(idx), v) for idx, v in enumerate(params))
try:
- stacktrace.append(u'<span class="path">{0}/</span><span class="file">{1}</span> in <span class="func">{3}</span>(<span class="lineno">{2}</span>)\n <span class="code">{4}</span>'.format(*params))
- except IndexError:
+ stacktrace.append(u'<span class="path">%(0)s/</span>'
+ u'<span class="file">%(1)s</span>'
+ u' in <span class="func">%(3)s</span>'
+ u'(<span class="lineno">%(2)s</span>)\n'
+ u' <span class="code">%(4)s</span>'
+ % params_dict)
+ except KeyError:
# This frame doesn't have the expected format, so skip it and move on to the next one
continue
return mark_safe('\n'.join(stacktrace))
diff --git a/debug_toolbar/utils/functional.py b/debug_toolbar/utils/functional.py
new file mode 100644
index 0000000..1dbb734
--- /dev/null
+++ b/debug_toolbar/utils/functional.py
@@ -0,0 +1,14 @@
+try:
+ from django.utils.functional import cached_property
+except ImportError: # Django < 1.4
+ class cached_property(object):
+ """
+ Decorator that creates converts a method with a single
+ self argument into a property cached on the instance.
+ """
+ def __init__(self, func):
+ self.func = func
+
+ def __get__(self, instance, type):
+ res = instance.__dict__[self.func.__name__] = self.func(instance)
+ return res
diff --git a/debug_toolbar/utils/sql.py b/debug_toolbar/utils/sql.py
new file mode 100644
index 0000000..909edf7
--- /dev/null
+++ b/debug_toolbar/utils/sql.py
@@ -0,0 +1,15 @@
+import re
+from debug_toolbar.utils import sqlparse
+from debug_toolbar.utils.sqlparse.filters import BoldKeywordFilter
+
+
+def reformat_sql(sql):
+ stack = sqlparse.engine.FilterStack()
+ stack.preprocess.append(BoldKeywordFilter()) # add our custom filter
+ stack.postprocess.append(sqlparse.filters.SerializerUnicode()) # tokens -> strings
+ return swap_fields(''.join(stack.run(sql)))
+
+
+def swap_fields(sql):
+ return re.sub('SELECT</strong> (.*?) <strong>FROM', 'SELECT</strong> <a class="djDebugUncollapsed djDebugToggle" href="#">&bull;&bull;&bull;</a> ' +
+ '<a class="djDebugCollapsed djDebugToggle" href="#">\g<1></a> <strong>FROM', sql)
diff --git a/debug_toolbar/utils/sqlparse/filters.py b/debug_toolbar/utils/sqlparse/filters.py
index 897cc90..6443a3c 100644
--- a/debug_toolbar/utils/sqlparse/filters.py
+++ b/debug_toolbar/utils/sqlparse/filters.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import re
+from django.utils.html import escape
from debug_toolbar.utils.sqlparse import tokens as T
from debug_toolbar.utils.sqlparse import sql
@@ -423,3 +424,16 @@ class OutputPHPFilter(Filter):
varname = self.varname
stmt.tokens = tuple(self._process(stmt.tokens, varname))
return stmt
+
+
+class BoldKeywordFilter(Filter):
+ """sqlparse filter to bold SQL keywords"""
+ def process(self, stack, stream):
+ """Process the token stream"""
+ for token_type, value in stream:
+ is_keyword = token_type in T.Keyword
+ if is_keyword:
+ yield T.Text, '<strong>'
+ yield token_type, escape(value)
+ if is_keyword:
+ yield T.Text, '</strong>'
diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py
index 4d87090..d649984 100644
--- a/debug_toolbar/utils/tracking/db.py
+++ b/debug_toolbar/utils/tracking/db.py
@@ -5,13 +5,22 @@ from threading import local
from django.conf import settings
from django.template import Node
-from django.utils import simplejson
from django.utils.encoding import force_unicode, smart_str
-from django.utils.hashcompat import sha_constructor
from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \
get_template_info, get_stack
from debug_toolbar.utils.compat.db import connections
+
+try:
+ import json
+except ImportError: # python < 2.6
+ from django.utils import simplejson as json
+
+try:
+ from hashlib import sha1
+except ImportError: # python < 2.5
+ from django.utils.hashcompat import sha_constructor as sha1
+
# TODO:This should be set in the toolbar loader as a default and panels should
# get a copy of the toolbar object with access to its config dictionary
SQL_WARNING_THRESHOLD = getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}) \
@@ -82,8 +91,13 @@ class NormalCursorWrapper(object):
for key, value in params.iteritems())
return map(self._quote_expr, params)
+ def _decode(self, param):
+ try:
+ return force_unicode(param, strings_only=True)
+ except UnicodeDecodeError:
+ return '(encoded string)'
+
def execute(self, sql, params=()):
- __traceback_hide__ = True
start = datetime.now()
try:
return self.cursor.execute(sql, params)
@@ -98,10 +112,8 @@ class NormalCursorWrapper(object):
stacktrace = []
_params = ''
try:
- _params = simplejson.dumps(
- [force_unicode(x, strings_only=True) for x in params]
- )
- except TypeError:
+ _params = json.dumps(map(self._decode, params))
+ except Exception:
pass # object not JSON serializable
template_info = None
@@ -119,7 +131,7 @@ class NormalCursorWrapper(object):
del cur_frame
alias = getattr(self.db, 'alias', 'default')
- conn = connections[alias].connection
+ conn = self.db.connection
# HACK: avoid imports
if conn:
engine = conn.__class__.__module__.split('.', 1)[0]
@@ -129,14 +141,11 @@ class NormalCursorWrapper(object):
params = {
'engine': engine,
'alias': alias,
- 'sql': self.db.ops.last_executed_query(self.cursor, sql,
- self._quote_params(params)),
+ 'sql': self.db.ops.last_executed_query(
+ self.cursor, sql, self._quote_params(params)),
'duration': duration,
'raw_sql': sql,
'params': _params,
- 'hash': sha_constructor(settings.SECRET_KEY \
- + smart_str(sql) \
- + _params).hexdigest(),
'stacktrace': stacktrace,
'start_time': start,
'stop_time': stop,
@@ -146,10 +155,17 @@ class NormalCursorWrapper(object):
}
if engine == 'psycopg2':
+ # If an erroneous query was ran on the connection, it might
+ # be in a state where checking isolation_level raises an
+ # exception.
+ try:
+ iso_level = conn.isolation_level
+ except conn.InternalError:
+ iso_level = 'unknown'
params.update({
'trans_id': self.logger.get_transaction_id(alias),
'trans_status': conn.get_transaction_status(),
- 'iso_level': conn.isolation_level,
+ 'iso_level': iso_level,
'encoding': conn.encoding,
})