From eb0d02d3cc147e6097e02d21151220b8a6d6cece Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:00:49 +0200 Subject: [PATCH 01/34] Add python 3.3 and 3.2 as allow_failures --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6e0da0acc..dd40eb81d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,15 @@ env: - DJANGO_VERSION=1.3.7 - DJANGO_VERSION=1.4.5 - DJANGO_VERSION=1.5 +matrix: + include: + - python: 3.2 + env: DJANGO_VERSION=1.5 + - python: 3.3 + env: DJANGO_VERSION=1.5 + allow_failures: + - python: 3.2 + - python: 3.3 install: - pip install Django==$DJANGO_VERSION - python setup.py install From eb53791e650dc9ac7ca1271e0d6652718cb3341a Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 20:09:13 +0100 Subject: [PATCH 02/34] add <1.6 to tests_require --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ea402391a..06cd86f30 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ license='BSD', packages=find_packages(exclude=('tests', 'example')), tests_require=[ - 'django>=1.3,<1.5', + 'django>=1.3,<1.6', ], test_suite='runtests.runtests', include_package_data=True, From 005ca0b8d604f923a23cff5a56fd4b8634fb51da Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:18:22 +0100 Subject: [PATCH 03/34] use sqlparse from pypi --- .../management/commands/debugsqlshell.py | 3 +- debug_toolbar/panels/sql.py | 3 +- debug_toolbar/utils/sqlparse/__init__.py | 55 -- .../utils/sqlparse/engine/__init__.py | 80 --- debug_toolbar/utils/sqlparse/engine/filter.py | 104 ---- .../utils/sqlparse/engine/grouping.py | 314 ---------- debug_toolbar/utils/sqlparse/filters.py | 425 ------------- debug_toolbar/utils/sqlparse/formatter.py | 120 ---- debug_toolbar/utils/sqlparse/keywords.py | 565 ------------------ debug_toolbar/utils/sqlparse/lexer.py | 331 ---------- debug_toolbar/utils/sqlparse/sql.py | 529 ---------------- debug_toolbar/utils/sqlparse/tokens.py | 83 --- setup.py | 3 + 13 files changed, 7 insertions(+), 2608 deletions(-) delete mode 100644 debug_toolbar/utils/sqlparse/__init__.py delete mode 100644 debug_toolbar/utils/sqlparse/engine/__init__.py delete mode 100644 debug_toolbar/utils/sqlparse/engine/filter.py delete mode 100644 debug_toolbar/utils/sqlparse/engine/grouping.py delete mode 100644 debug_toolbar/utils/sqlparse/filters.py delete mode 100644 debug_toolbar/utils/sqlparse/formatter.py delete mode 100644 debug_toolbar/utils/sqlparse/keywords.py delete mode 100644 debug_toolbar/utils/sqlparse/lexer.py delete mode 100644 debug_toolbar/utils/sqlparse/sql.py delete mode 100644 debug_toolbar/utils/sqlparse/tokens.py diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index 1774a76d2..68ad3d9d9 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -3,7 +3,8 @@ from django.db.backends import util from django.core.management.commands.shell import Command -from debug_toolbar.utils import ms_from_timedelta, sqlparse +from debug_toolbar.utils import ms_from_timedelta +import sqlparse class PrintQueryWrapper(util.CursorDebugWrapper): diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index a49288863..3bdc1a4b3 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,5 +1,6 @@ import re import uuid +import sqlparse from django.db.backends import BaseDatabaseWrapper from django.utils.html import escape @@ -8,7 +9,7 @@ from debug_toolbar.utils.compat.db import connections from debug_toolbar.middleware import DebugToolbarMiddleware from debug_toolbar.panels import DebugPanel -from debug_toolbar.utils import sqlparse, render_stacktrace +from debug_toolbar.utils import render_stacktrace from debug_toolbar.utils.tracking.db import CursorWrapper from debug_toolbar.utils.tracking import replace_call diff --git a/debug_toolbar/utils/sqlparse/__init__.py b/debug_toolbar/utils/sqlparse/__init__.py deleted file mode 100644 index 99db30ece..000000000 --- a/debug_toolbar/utils/sqlparse/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com -# -# This module is part of python-sqlparse and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php. - -"""Parse SQL statements.""" - - -__version__ = '0.1.3' - - -class SQLParseError(Exception): - """Base class for exceptions in this module.""" - - -# Setup namespace -from debug_toolbar.utils.sqlparse import engine -from debug_toolbar.utils.sqlparse import filters -from debug_toolbar.utils.sqlparse import formatter - - -def parse(sql): - """Parse sql and return a list of statements. - - *sql* is a single string containting one or more SQL statements. - - Returns a tuple of :class:`~sqlparse.sql.Statement` instances. - """ - stack = engine.FilterStack() - stack.full_analyze() - return tuple(stack.run(sql)) - - -def format(sql, **options): - """Format *sql* according to *options*. - - Available options are documented in :ref:`formatting`. - - Returns the formatted SQL statement as string. - """ - stack = engine.FilterStack() - options = formatter.validate_options(options) - stack = formatter.build_filter_stack(stack, options) - stack.postprocess.append(filters.SerializerUnicode()) - return ''.join(stack.run(sql)) - - -def split(sql): - """Split *sql* into single statements. - - Returns a list of strings. - """ - stack = engine.FilterStack() - stack.split_statements = True - return [unicode(stmt) for stmt in stack.run(sql)] diff --git a/debug_toolbar/utils/sqlparse/engine/__init__.py b/debug_toolbar/utils/sqlparse/engine/__init__.py deleted file mode 100644 index e838a3ede..000000000 --- a/debug_toolbar/utils/sqlparse/engine/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com -# -# This module is part of python-sqlparse and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php. - -"""filter""" - -from debug_toolbar.utils.sqlparse import lexer -from debug_toolbar.utils.sqlparse.engine import grouping -from debug_toolbar.utils.sqlparse.engine.filter import StatementFilter - -# XXX remove this when cleanup is complete -Filter = object - - -class FilterStack(object): - - def __init__(self): - self.preprocess = [] - self.stmtprocess = [] - self.postprocess = [] - self.split_statements = False - self._grouping = False - - def _flatten(self, stream): - for token in stream: - if token.is_group(): - for t in self._flatten(token.tokens): - yield t - else: - yield token - - def enable_grouping(self): - self._grouping = True - - def full_analyze(self): - self.enable_grouping() - - def run(self, sql): - stream = lexer.tokenize(sql) - # Process token stream - if self.preprocess: - for filter_ in self.preprocess: - stream = filter_.process(self, stream) - - if (self.stmtprocess or self.postprocess or self.split_statements - or self._grouping): - splitter = StatementFilter() - stream = splitter.process(self, stream) - - if self._grouping: - - def _group(stream): - for stmt in stream: - grouping.group(stmt) - yield stmt - stream = _group(stream) - - if self.stmtprocess: - - def _run1(stream): - ret = [] - for stmt in stream: - for filter_ in self.stmtprocess: - filter_.process(self, stmt) - ret.append(stmt) - return ret - stream = _run1(stream) - - if self.postprocess: - - def _run2(stream): - for stmt in stream: - stmt.tokens = list(self._flatten(stmt.tokens)) - for filter_ in self.postprocess: - stmt = filter_.process(self, stmt) - yield stmt - stream = _run2(stream) - - return stream diff --git a/debug_toolbar/utils/sqlparse/engine/filter.py b/debug_toolbar/utils/sqlparse/engine/filter.py deleted file mode 100644 index a31c5dea9..000000000 --- a/debug_toolbar/utils/sqlparse/engine/filter.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- - -from debug_toolbar.utils.sqlparse.sql import Statement, Token -from debug_toolbar.utils.sqlparse import tokens as T - - -class TokenFilter(object): - - def __init__(self, **options): - self.options = options - - def process(self, stack, stream): - """Process token stream.""" - raise NotImplementedError - - -class StatementFilter(TokenFilter): - - def __init__(self): - TokenFilter.__init__(self) - self._in_declare = False - self._in_dbldollar = False - self._is_create = False - self._begin_depth = 0 - - def _reset(self): - self._in_declare = False - self._in_dbldollar = False - self._is_create = False - self._begin_depth = 0 - - def _change_splitlevel(self, ttype, value): - # PostgreSQL - if (ttype == T.Name.Builtin - and value.startswith('$') and value.endswith('$')): - if self._in_dbldollar: - self._in_dbldollar = False - return -1 - else: - self._in_dbldollar = True - return 1 - elif self._in_dbldollar: - return 0 - - # ANSI - if ttype not in T.Keyword: - return 0 - - unified = value.upper() - - if unified == 'DECLARE' and self._is_create: - self._in_declare = True - return 1 - - if unified == 'BEGIN': - self._begin_depth += 1 - if self._in_declare: # FIXME(andi): This makes no sense. - return 0 - return 0 - - if unified == 'END': - # Should this respect a preceeding BEGIN? - # In CASE ... WHEN ... END this results in a split level -1. - self._begin_depth = max(0, self._begin_depth - 1) - return -1 - - if ttype is T.Keyword.DDL and unified.startswith('CREATE'): - self._is_create = True - return 0 - - if unified in ('IF', 'FOR') and self._is_create and self._begin_depth > 0: - return 1 - - # Default - return 0 - - def process(self, stack, stream): - splitlevel = 0 - stmt = None - consume_ws = False - stmt_tokens = [] - for ttype, value in stream: - # Before appending the token - if (consume_ws and ttype is not T.Whitespace - and ttype is not T.Comment.Single): - consume_ws = False - stmt.tokens = stmt_tokens - yield stmt - self._reset() - stmt = None - splitlevel = 0 - if stmt is None: - stmt = Statement() - stmt_tokens = [] - splitlevel += self._change_splitlevel(ttype, value) - # Append the token - stmt_tokens.append(Token(ttype, value)) - # After appending the token - if (splitlevel <= 0 and ttype is T.Punctuation - and value == ';'): - consume_ws = True - if stmt is not None: - stmt.tokens = stmt_tokens - yield stmt diff --git a/debug_toolbar/utils/sqlparse/engine/grouping.py b/debug_toolbar/utils/sqlparse/engine/grouping.py deleted file mode 100644 index 4e50c7b19..000000000 --- a/debug_toolbar/utils/sqlparse/engine/grouping.py +++ /dev/null @@ -1,314 +0,0 @@ -# -*- coding: utf-8 -*- - -import itertools - -from debug_toolbar.utils.sqlparse import sql -from debug_toolbar.utils.sqlparse import tokens as T - -try: - next -except NameError: # Python < 2.6 - next = lambda i: i.next() - - -def _group_left_right(tlist, ttype, value, cls, - check_right=lambda t: True, - check_left=lambda t: True, - include_semicolon=False): - [_group_left_right(sgroup, ttype, value, cls, check_right, - include_semicolon) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, cls)] - idx = 0 - token = tlist.token_next_match(idx, ttype, value) - while token: - right = tlist.token_next(tlist.token_index(token)) - left = tlist.token_prev(tlist.token_index(token)) - if right is None or not check_right(right): - token = tlist.token_next_match(tlist.token_index(token) + 1, - ttype, value) - elif left is None or not check_right(left): - token = tlist.token_next_match(tlist.token_index(token) + 1, - ttype, value) - else: - if include_semicolon: - sright = tlist.token_next_match(tlist.token_index(right), - T.Punctuation, ';') - if sright is not None: - # only overwrite "right" if a semicolon is actually - # present. - right = sright - tokens = tlist.tokens_between(left, right)[1:] - if not isinstance(left, cls): - new = cls([left]) - new_idx = tlist.token_index(left) - tlist.tokens.remove(left) - tlist.tokens.insert(new_idx, new) - left = new - left.tokens.extend(tokens) - for t in tokens: - tlist.tokens.remove(t) - token = tlist.token_next_match(tlist.token_index(left) + 1, - ttype, value) - - -def _group_matching(tlist, start_ttype, start_value, end_ttype, end_value, - cls, include_semicolon=False, recurse=False): - def _find_matching(i, tl, stt, sva, ett, eva): - depth = 1 - for t in tl.tokens[i:]: - if t.match(stt, sva): - depth += 1 - elif t.match(ett, eva): - depth -= 1 - if depth == 1: - return t - return None - [_group_matching(sgroup, start_ttype, start_value, end_ttype, end_value, - cls, include_semicolon) for sgroup in tlist.get_sublists() - if recurse] - if isinstance(tlist, cls): - idx = 1 - else: - idx = 0 - token = tlist.token_next_match(idx, start_ttype, start_value) - while token: - tidx = tlist.token_index(token) - end = _find_matching(tidx, tlist, start_ttype, start_value, - end_ttype, end_value) - if end is None: - idx = tidx + 1 - else: - if include_semicolon: - next_ = tlist.token_next(tlist.token_index(end)) - if next_ and next_.match(T.Punctuation, ';'): - end = next_ - group = tlist.group_tokens(cls, tlist.tokens_between(token, end)) - _group_matching(group, start_ttype, start_value, - end_ttype, end_value, cls, include_semicolon) - idx = tlist.token_index(group) + 1 - token = tlist.token_next_match(idx, start_ttype, start_value) - - -def group_if(tlist): - _group_matching(tlist, T.Keyword, 'IF', T.Keyword, 'END IF', sql.If, True) - - -def group_for(tlist): - _group_matching(tlist, T.Keyword, 'FOR', T.Keyword, 'END LOOP', - sql.For, True) - - -def group_as(tlist): - - def _right_valid(token): - # Currently limited to DML/DDL. Maybe additional more non SQL reserved - # keywords should appear here (see issue8). - return not token.ttype in (T.DML, T.DDL) - _group_left_right(tlist, T.Keyword, 'AS', sql.Identifier, - check_right=_right_valid) - - -def group_assignment(tlist): - _group_left_right(tlist, T.Assignment, ':=', sql.Assignment, - include_semicolon=True) - - -def group_comparison(tlist): - - def _parts_valid(token): - return (token.ttype in (T.String.Symbol, T.Name, T.Number, - T.Number.Integer, T.Literal, - T.Literal.Number.Integer) - or isinstance(token, (sql.Identifier,))) - _group_left_right(tlist, T.Operator.Comparison, None, sql.Comparison, - check_left=_parts_valid, check_right=_parts_valid) - - -def group_case(tlist): - _group_matching(tlist, T.Keyword, 'CASE', T.Keyword, 'END', sql.Case, - include_semicolon=True, recurse=True) - - -def group_identifier(tlist): - def _consume_cycle(tl, i): - x = itertools.cycle(( - lambda y: (y.match(T.Punctuation, '.') - or y.ttype is T.Operator), - lambda y: (y.ttype in (T.String.Symbol, - T.Name, - T.Wildcard, - T.Literal.Number.Integer)))) - for t in tl.tokens[i:]: - if next(x)(t): - yield t - else: - raise StopIteration - - # bottom up approach: group subgroups first - [group_identifier(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, sql.Identifier)] - - # real processing - idx = 0 - token = tlist.token_next_by_instance(idx, sql.Function) - if token is None: - token = tlist.token_next_by_type(idx, (T.String.Symbol, T.Name)) - while token: - identifier_tokens = [token] + list( - _consume_cycle(tlist, - tlist.token_index(token) + 1)) - if not (len(identifier_tokens) == 1 - and isinstance(identifier_tokens[0], sql.Function)): - group = tlist.group_tokens(sql.Identifier, identifier_tokens) - idx = tlist.token_index(group) + 1 - else: - idx += 1 - token = tlist.token_next_by_instance(idx, sql.Function) - if token is None: - token = tlist.token_next_by_type(idx, (T.String.Symbol, T.Name)) - - -def group_identifier_list(tlist): - [group_identifier_list(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, sql.IdentifierList)] - idx = 0 - # Allowed list items - fend1_funcs = [lambda t: isinstance(t, (sql.Identifier, sql.Function)), - lambda t: t.is_whitespace(), - lambda t: t.ttype == T.Name, - lambda t: t.ttype == T.Wildcard, - lambda t: t.match(T.Keyword, 'null'), - lambda t: t.ttype == T.Number.Integer, - lambda t: t.ttype == T.String.Single, - lambda t: isinstance(t, sql.Comparison), - ] - tcomma = tlist.token_next_match(idx, T.Punctuation, ',') - start = None - while tcomma is not None: - before = tlist.token_prev(tcomma) - after = tlist.token_next(tcomma) - # Check if the tokens around tcomma belong to a list - bpassed = apassed = False - for func in fend1_funcs: - if before is not None and func(before): - bpassed = True - if after is not None and func(after): - apassed = True - if not bpassed or not apassed: - # Something's wrong here, skip ahead to next "," - start = None - tcomma = tlist.token_next_match(tlist.token_index(tcomma) + 1, - T.Punctuation, ',') - else: - if start is None: - start = before - next_ = tlist.token_next(after) - if next_ is None or not next_.match(T.Punctuation, ','): - # Reached the end of the list - tokens = tlist.tokens_between(start, after) - group = tlist.group_tokens(sql.IdentifierList, tokens) - start = None - tcomma = tlist.token_next_match(tlist.token_index(group) + 1, - T.Punctuation, ',') - else: - tcomma = next_ - - -def group_parenthesis(tlist): - _group_matching(tlist, T.Punctuation, '(', T.Punctuation, ')', - sql.Parenthesis) - - -def group_comments(tlist): - [group_comments(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, sql.Comment)] - idx = 0 - token = tlist.token_next_by_type(idx, T.Comment) - while token: - tidx = tlist.token_index(token) - end = tlist.token_not_matching(tidx + 1, - [lambda t: t.ttype in T.Comment, - lambda t: t.is_whitespace()]) - if end is None: - idx = tidx + 1 - else: - eidx = tlist.token_index(end) - grp_tokens = tlist.tokens_between(token, - tlist.token_prev(eidx, False)) - group = tlist.group_tokens(sql.Comment, grp_tokens) - idx = tlist.token_index(group) - token = tlist.token_next_by_type(idx, T.Comment) - - -def group_where(tlist): - [group_where(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, sql.Where)] - idx = 0 - token = tlist.token_next_match(idx, T.Keyword, 'WHERE') - stopwords = ('ORDER', 'GROUP', 'LIMIT', 'UNION') - while token: - tidx = tlist.token_index(token) - end = tlist.token_next_match(tidx + 1, T.Keyword, stopwords) - if end is None: - end = tlist._groupable_tokens[-1] - else: - end = tlist.tokens[tlist.token_index(end) - 1] - group = tlist.group_tokens(sql.Where, - tlist.tokens_between(token, end), - ignore_ws=True) - idx = tlist.token_index(group) - token = tlist.token_next_match(idx, T.Keyword, 'WHERE') - - -def group_aliased(tlist): - [group_aliased(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, (sql.Identifier, sql.Function))] - idx = 0 - token = tlist.token_next_by_instance(idx, (sql.Identifier, sql.Function)) - while token: - next_ = tlist.token_next(tlist.token_index(token)) - if next_ is not None and isinstance(next_, (sql.Identifier, sql.Function)): - grp = tlist.tokens_between(token, next_)[1:] - token.tokens.extend(grp) - for t in grp: - tlist.tokens.remove(t) - idx = tlist.token_index(token) + 1 - token = tlist.token_next_by_instance(idx, (sql.Identifier, sql.Function)) - - -def group_typecasts(tlist): - _group_left_right(tlist, T.Punctuation, '::', sql.Identifier) - - -def group_functions(tlist): - [group_functions(sgroup) for sgroup in tlist.get_sublists() - if not isinstance(sgroup, sql.Function)] - idx = 0 - token = tlist.token_next_by_type(idx, T.Name) - while token: - next_ = tlist.token_next(token) - if not isinstance(next_, sql.Parenthesis): - idx = tlist.token_index(token) + 1 - else: - func = tlist.group_tokens(sql.Function, - tlist.tokens_between(token, next_)) - idx = tlist.token_index(func) + 1 - token = tlist.token_next_by_type(idx, T.Name) - - -def group(tlist): - for func in [group_parenthesis, - group_functions, - group_comments, - group_where, - group_case, - group_identifier, - group_typecasts, - group_as, - group_aliased, - group_assignment, - group_comparison, - group_identifier_list, - group_if, - group_for]: - func(tlist) diff --git a/debug_toolbar/utils/sqlparse/filters.py b/debug_toolbar/utils/sqlparse/filters.py deleted file mode 100644 index 897cc9097..000000000 --- a/debug_toolbar/utils/sqlparse/filters.py +++ /dev/null @@ -1,425 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - -from debug_toolbar.utils.sqlparse import tokens as T -from debug_toolbar.utils.sqlparse import sql - - -class Filter(object): - - def process(self, *args): - raise NotImplementedError - - -class TokenFilter(Filter): - - def process(self, stack, stream): - raise NotImplementedError - - -# -------------------------- -# token process - -class _CaseFilter(TokenFilter): - - ttype = None - - def __init__(self, case=None): - if case is None: - case = 'upper' - assert case in ['lower', 'upper', 'capitalize'] - self.convert = getattr(unicode, case) - - def process(self, stack, stream): - for ttype, value in stream: - if ttype in self.ttype: - value = self.convert(value) - yield ttype, value - - -class KeywordCaseFilter(_CaseFilter): - ttype = T.Keyword - - -class IdentifierCaseFilter(_CaseFilter): - ttype = (T.Name, T.String.Symbol) - - def process(self, stack, stream): - for ttype, value in stream: - if ttype in self.ttype and not value.strip()[0] == '"': - value = self.convert(value) - yield ttype, value - - -# ---------------------- -# statement process - -class StripCommentsFilter(Filter): - - def _get_next_comment(self, tlist): - # TODO(andi) Comment types should be unified, see related issue38 - token = tlist.token_next_by_instance(0, sql.Comment) - if token is None: - token = tlist.token_next_by_type(0, T.Comment) - return token - - def _process(self, tlist): - token = self._get_next_comment(tlist) - while token: - tidx = tlist.token_index(token) - prev = tlist.token_prev(tidx, False) - next_ = tlist.token_next(tidx, False) - # Replace by whitespace if prev and next exist and if they're not - # whitespaces. This doesn't apply if prev or next is a paranthesis. - if (prev is not None and next_ is not None - and not prev.is_whitespace() and not next_.is_whitespace() - and not (prev.match(T.Punctuation, '(') - or next_.match(T.Punctuation, ')'))): - tlist.tokens[tidx] = sql.Token(T.Whitespace, ' ') - else: - tlist.tokens.pop(tidx) - token = self._get_next_comment(tlist) - - def process(self, stack, stmt): - [self.process(stack, sgroup) for sgroup in stmt.get_sublists()] - self._process(stmt) - - -class StripWhitespaceFilter(Filter): - - def _stripws(self, tlist): - func_name = '_stripws_%s' % tlist.__class__.__name__.lower() - func = getattr(self, func_name, self._stripws_default) - func(tlist) - - def _stripws_default(self, tlist): - last_was_ws = False - for token in tlist.tokens: - if token.is_whitespace(): - if last_was_ws: - token.value = '' - else: - token.value = ' ' - last_was_ws = token.is_whitespace() - - def _stripws_parenthesis(self, tlist): - if tlist.tokens[1].is_whitespace(): - tlist.tokens.pop(1) - if tlist.tokens[-2].is_whitespace(): - tlist.tokens.pop(-2) - self._stripws_default(tlist) - - def process(self, stack, stmt): - [self.process(stack, sgroup) for sgroup in stmt.get_sublists()] - self._stripws(stmt) - if stmt.tokens[-1].is_whitespace(): - stmt.tokens.pop(-1) - - -class ReindentFilter(Filter): - - def __init__(self, width=2, char=' ', line_width=None): - self.width = width - self.char = char - self.indent = 0 - self.offset = 0 - self.line_width = line_width - self._curr_stmt = None - self._last_stmt = None - - def _get_offset(self, token): - all_ = list(self._curr_stmt.flatten()) - idx = all_.index(token) - raw = ''.join(unicode(x) for x in all_[:idx + 1]) - line = raw.splitlines()[-1] - # Now take current offset into account and return relative offset. - full_offset = len(line) - len(self.char * (self.width * self.indent)) - return full_offset - self.offset - - def nl(self): - # TODO: newline character should be configurable - ws = '\n' + (self.char * ((self.indent * self.width) + self.offset)) - return sql.Token(T.Whitespace, ws) - - def _split_kwds(self, tlist): - split_words = ('FROM', 'JOIN$', 'AND', 'OR', - 'GROUP', 'ORDER', 'UNION', 'VALUES', - 'SET', 'BETWEEN') - - def _next_token(i): - t = tlist.token_next_match(i, T.Keyword, split_words, - regex=True) - if t and t.value.upper() == 'BETWEEN': - t = _next_token(tlist.token_index(t) + 1) - if t and t.value.upper() == 'AND': - t = _next_token(tlist.token_index(t) + 1) - return t - - idx = 0 - token = _next_token(idx) - while token: - prev = tlist.token_prev(tlist.token_index(token), False) - offset = 1 - if prev and prev.is_whitespace(): - tlist.tokens.pop(tlist.token_index(prev)) - offset += 1 - if (prev - and isinstance(prev, sql.Comment) - and (str(prev).endswith('\n') - or str(prev).endswith('\r'))): - nl = tlist.token_next(token) - else: - nl = self.nl() - tlist.insert_before(token, nl) - token = _next_token(tlist.token_index(nl) + offset) - - def _split_statements(self, tlist): - idx = 0 - token = tlist.token_next_by_type(idx, (T.Keyword.DDL, T.Keyword.DML)) - while token: - prev = tlist.token_prev(tlist.token_index(token), False) - if prev and prev.is_whitespace(): - tlist.tokens.pop(tlist.token_index(prev)) - # only break if it's not the first token - if prev: - nl = self.nl() - tlist.insert_before(token, nl) - token = tlist.token_next_by_type(tlist.token_index(token) + 1, - (T.Keyword.DDL, T.Keyword.DML)) - - def _process(self, tlist): - func_name = '_process_%s' % tlist.__class__.__name__.lower() - func = getattr(self, func_name, self._process_default) - func(tlist) - - def _process_where(self, tlist): - token = tlist.token_next_match(0, T.Keyword, 'WHERE') - tlist.insert_before(token, self.nl()) - self.indent += 1 - self._process_default(tlist) - self.indent -= 1 - - def _process_parenthesis(self, tlist): - first = tlist.token_next(0) - indented = False - if first and first.ttype in (T.Keyword.DML, T.Keyword.DDL): - self.indent += 1 - tlist.tokens.insert(0, self.nl()) - indented = True - num_offset = self._get_offset(tlist.token_next_match(0, - T.Punctuation, '(')) - self.offset += num_offset - self._process_default(tlist, stmts=not indented) - if indented: - self.indent -= 1 - self.offset -= num_offset - - def _process_identifierlist(self, tlist): - identifiers = tlist.get_identifiers() - if len(identifiers) > 1 and not tlist.within(sql.Function): - first = list(identifiers[0].flatten())[0] - num_offset = self._get_offset(first) - len(first.value) - self.offset += num_offset - for token in identifiers[1:]: - tlist.insert_before(token, self.nl()) - self.offset -= num_offset - self._process_default(tlist) - - def _process_case(self, tlist): - is_first = True - num_offset = None - case = tlist.tokens[0] - outer_offset = self._get_offset(case) - len(case.value) - self.offset += outer_offset - for cond, value in tlist.get_cases(): - if is_first: - tcond = list(cond[0].flatten())[0] - is_first = False - num_offset = self._get_offset(tcond) - len(tcond.value) - self.offset += num_offset - continue - if cond is None: - token = value[0] - else: - token = cond[0] - tlist.insert_before(token, self.nl()) - # Line breaks on group level are done. Now let's add an offset of - # 5 (=length of "when", "then", "else") and process subgroups. - self.offset += 5 - self._process_default(tlist) - self.offset -= 5 - if num_offset is not None: - self.offset -= num_offset - end = tlist.token_next_match(0, T.Keyword, 'END') - tlist.insert_before(end, self.nl()) - self.offset -= outer_offset - - def _process_default(self, tlist, stmts=True, kwds=True): - if stmts: - self._split_statements(tlist) - if kwds: - self._split_kwds(tlist) - [self._process(sgroup) for sgroup in tlist.get_sublists()] - - def process(self, stack, stmt): - if isinstance(stmt, sql.Statement): - self._curr_stmt = stmt - self._process(stmt) - if isinstance(stmt, sql.Statement): - if self._last_stmt is not None: - if self._last_stmt.to_unicode().endswith('\n'): - nl = '\n' - else: - nl = '\n\n' - stmt.tokens.insert(0, - sql.Token(T.Whitespace, nl)) - if self._last_stmt != stmt: - self._last_stmt = stmt - - -# FIXME: Doesn't work ;) -class RightMarginFilter(Filter): - - keep_together = ( -# sql.TypeCast, sql.Identifier, sql.Alias, - ) - - def __init__(self, width=79): - self.width = width - self.line = '' - - def _process(self, stack, group, stream): - for token in stream: - if token.is_whitespace() and '\n' in token.value: - if token.value.endswith('\n'): - self.line = '' - else: - self.line = token.value.splitlines()[-1] - elif (token.is_group() - and not token.__class__ in self.keep_together): - token.tokens = self._process(stack, token, token.tokens) - else: - val = token.to_unicode() - if len(self.line) + len(val) > self.width: - match = re.search('^ +', self.line) - if match is not None: - indent = match.group() - else: - indent = '' - yield sql.Token(T.Whitespace, '\n%s' % indent) - self.line = indent - self.line += val - yield token - - def process(self, stack, group): - return - group.tokens = self._process(stack, group, group.tokens) - - -# --------------------------- -# postprocess - -class SerializerUnicode(Filter): - - def process(self, stack, stmt): - raw = stmt.to_unicode() - add_nl = raw.endswith('\n') - res = '\n'.join(line.rstrip() for line in raw.splitlines()) - if add_nl: - res += '\n' - return res - - -class OutputPythonFilter(Filter): - - def __init__(self, varname='sql'): - self.varname = varname - self.cnt = 0 - - def _process(self, stream, varname, count, has_nl): - if count > 1: - yield sql.Token(T.Whitespace, '\n') - yield sql.Token(T.Name, varname) - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Operator, '=') - yield sql.Token(T.Whitespace, ' ') - if has_nl: - yield sql.Token(T.Operator, '(') - yield sql.Token(T.Text, "'") - cnt = 0 - for token in stream: - cnt += 1 - if token.is_whitespace() and '\n' in token.value: - if cnt == 1: - continue - after_lb = token.value.split('\n', 1)[1] - yield sql.Token(T.Text, " '") - yield sql.Token(T.Whitespace, '\n') - for i in range(len(varname) + 4): - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Text, "'") - if after_lb: # it's the indendation - yield sql.Token(T.Whitespace, after_lb) - continue - elif token.value and "'" in token.value: - token.value = token.value.replace("'", "\\'") - yield sql.Token(T.Text, token.value or '') - yield sql.Token(T.Text, "'") - if has_nl: - yield sql.Token(T.Operator, ')') - - def process(self, stack, stmt): - self.cnt += 1 - if self.cnt > 1: - varname = '%s%d' % (self.varname, self.cnt) - else: - varname = self.varname - has_nl = len(stmt.to_unicode().strip().splitlines()) > 1 - stmt.tokens = self._process(stmt.tokens, varname, self.cnt, has_nl) - return stmt - - -class OutputPHPFilter(Filter): - - def __init__(self, varname='sql'): - self.varname = '$%s' % varname - self.count = 0 - - def _process(self, stream, varname): - if self.count > 1: - yield sql.Token(T.Whitespace, '\n') - yield sql.Token(T.Name, varname) - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Operator, '=') - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Text, '"') - for token in stream: - if token.is_whitespace() and '\n' in token.value: - after_lb = token.value.split('\n', 1)[1] - yield sql.Token(T.Text, ' "') - yield sql.Token(T.Operator, ';') - yield sql.Token(T.Whitespace, '\n') - yield sql.Token(T.Name, varname) - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Punctuation, '.') - yield sql.Token(T.Operator, '=') - yield sql.Token(T.Whitespace, ' ') - yield sql.Token(T.Text, '"') - if after_lb: - yield sql.Token(T.Text, after_lb) - continue - elif '"' in token.value: - token.value = token.value.replace('"', '\\"') - yield sql.Token(T.Text, token.value) - yield sql.Token(T.Text, '"') - yield sql.Token(T.Punctuation, ';') - - def process(self, stack, stmt): - self.count += 1 - if self.count > 1: - varname = '%s%d' % (self.varname, self.count) - else: - varname = self.varname - stmt.tokens = tuple(self._process(stmt.tokens, varname)) - return stmt diff --git a/debug_toolbar/utils/sqlparse/formatter.py b/debug_toolbar/utils/sqlparse/formatter.py deleted file mode 100644 index 3acece971..000000000 --- a/debug_toolbar/utils/sqlparse/formatter.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com -# -# This module is part of python-sqlparse and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php. - -"""SQL formatter""" - -from debug_toolbar.utils.sqlparse import SQLParseError -from debug_toolbar.utils.sqlparse import filters - - -def validate_options(options): - """Validates options.""" - kwcase = options.get('keyword_case', None) - if kwcase not in [None, 'upper', 'lower', 'capitalize']: - raise SQLParseError('Invalid value for keyword_case: %r' % kwcase) - - idcase = options.get('identifier_case', None) - if idcase not in [None, 'upper', 'lower', 'capitalize']: - raise SQLParseError('Invalid value for identifier_case: %r' % idcase) - - ofrmt = options.get('output_format', None) - if ofrmt not in [None, 'sql', 'python', 'php']: - raise SQLParseError('Unknown output format: %r' % ofrmt) - - strip_comments = options.get('strip_comments', False) - if strip_comments not in [True, False]: - raise SQLParseError('Invalid value for strip_comments: %r' - % strip_comments) - - strip_ws = options.get('strip_whitespace', False) - if strip_ws not in [True, False]: - raise SQLParseError('Invalid value for strip_whitespace: %r' - % strip_ws) - - reindent = options.get('reindent', False) - if reindent not in [True, False]: - raise SQLParseError('Invalid value for reindent: %r' - % reindent) - elif reindent: - options['strip_whitespace'] = True - indent_tabs = options.get('indent_tabs', False) - if indent_tabs not in [True, False]: - raise SQLParseError('Invalid value for indent_tabs: %r' % indent_tabs) - elif indent_tabs: - options['indent_char'] = '\t' - else: - options['indent_char'] = ' ' - indent_width = options.get('indent_width', 2) - try: - indent_width = int(indent_width) - except (TypeError, ValueError): - raise SQLParseError('indent_width requires an integer') - if indent_width < 1: - raise SQLParseError('indent_width requires an positive integer') - options['indent_width'] = indent_width - - right_margin = options.get('right_margin', None) - if right_margin is not None: - try: - right_margin = int(right_margin) - except (TypeError, ValueError): - raise SQLParseError('right_margin requires an integer') - if right_margin < 10: - raise SQLParseError('right_margin requires an integer > 10') - options['right_margin'] = right_margin - - return options - - -def build_filter_stack(stack, options): - """Setup and return a filter stack. - - Args: - stack: :class:`~sqlparse.filters.FilterStack` instance - options: Dictionary with options validated by validate_options. - """ - # Token filter - if options.get('keyword_case', None): - stack.preprocess.append( - filters.KeywordCaseFilter(options['keyword_case'])) - - if options.get('identifier_case', None): - stack.preprocess.append( - filters.IdentifierCaseFilter(options['identifier_case'])) - - # After grouping - if options.get('strip_comments', False): - stack.enable_grouping() - stack.stmtprocess.append(filters.StripCommentsFilter()) - - if (options.get('strip_whitespace', False) - or options.get('reindent', False)): - stack.enable_grouping() - stack.stmtprocess.append(filters.StripWhitespaceFilter()) - - if options.get('reindent', False): - stack.enable_grouping() - stack.stmtprocess.append( - filters.ReindentFilter(char=options['indent_char'], - width=options['indent_width'])) - - if options.get('right_margin', False): - stack.enable_grouping() - stack.stmtprocess.append( - filters.RightMarginFilter(width=options['right_margin'])) - - # Serializer - if options.get('output_format'): - frmt = options['output_format'] - if frmt.lower() == 'php': - fltr = filters.OutputPHPFilter() - elif frmt.lower() == 'python': - fltr = filters.OutputPythonFilter() - else: - fltr = None - if fltr is not None: - stack.postprocess.append(fltr) - - return stack diff --git a/debug_toolbar/utils/sqlparse/keywords.py b/debug_toolbar/utils/sqlparse/keywords.py deleted file mode 100644 index 4782cfe4f..000000000 --- a/debug_toolbar/utils/sqlparse/keywords.py +++ /dev/null @@ -1,565 +0,0 @@ -from debug_toolbar.utils.sqlparse import tokens - -KEYWORDS = { - 'ABORT': tokens.Keyword, - 'ABS': tokens.Keyword, - 'ABSOLUTE': tokens.Keyword, - 'ACCESS': tokens.Keyword, - 'ADA': tokens.Keyword, - 'ADD': tokens.Keyword, - 'ADMIN': tokens.Keyword, - 'AFTER': tokens.Keyword, - 'AGGREGATE': tokens.Keyword, - 'ALIAS': tokens.Keyword, - 'ALL': tokens.Keyword, - 'ALLOCATE': tokens.Keyword, - 'ANALYSE': tokens.Keyword, - 'ANALYZE': tokens.Keyword, - 'ANY': tokens.Keyword, - 'ARE': tokens.Keyword, - 'ASC': tokens.Keyword, - 'ASENSITIVE': tokens.Keyword, - 'ASSERTION': tokens.Keyword, - 'ASSIGNMENT': tokens.Keyword, - 'ASYMMETRIC': tokens.Keyword, - 'AT': tokens.Keyword, - 'ATOMIC': tokens.Keyword, - 'AUTHORIZATION': tokens.Keyword, - 'AVG': tokens.Keyword, - - 'BACKWARD': tokens.Keyword, - 'BEFORE': tokens.Keyword, - 'BEGIN': tokens.Keyword, - 'BETWEEN': tokens.Keyword, - 'BITVAR': tokens.Keyword, - 'BIT_LENGTH': tokens.Keyword, - 'BOTH': tokens.Keyword, - 'BREADTH': tokens.Keyword, - -# 'C': tokens.Keyword, # most likely this is an alias - 'CACHE': tokens.Keyword, - 'CALL': tokens.Keyword, - 'CALLED': tokens.Keyword, - 'CARDINALITY': tokens.Keyword, - 'CASCADE': tokens.Keyword, - 'CASCADED': tokens.Keyword, - 'CAST': tokens.Keyword, - 'CATALOG': tokens.Keyword, - 'CATALOG_NAME': tokens.Keyword, - 'CHAIN': tokens.Keyword, - 'CHARACTERISTICS': tokens.Keyword, - 'CHARACTER_LENGTH': tokens.Keyword, - 'CHARACTER_SET_CATALOG': tokens.Keyword, - 'CHARACTER_SET_NAME': tokens.Keyword, - 'CHARACTER_SET_SCHEMA': tokens.Keyword, - 'CHAR_LENGTH': tokens.Keyword, - 'CHECK': tokens.Keyword, - 'CHECKED': tokens.Keyword, - 'CHECKPOINT': tokens.Keyword, - 'CLASS': tokens.Keyword, - 'CLASS_ORIGIN': tokens.Keyword, - 'CLOB': tokens.Keyword, - 'CLOSE': tokens.Keyword, - 'CLUSTER': tokens.Keyword, - 'COALSECE': tokens.Keyword, - 'COBOL': tokens.Keyword, - 'COLLATE': tokens.Keyword, - 'COLLATION': tokens.Keyword, - 'COLLATION_CATALOG': tokens.Keyword, - 'COLLATION_NAME': tokens.Keyword, - 'COLLATION_SCHEMA': tokens.Keyword, - 'COLUMN': tokens.Keyword, - 'COLUMN_NAME': tokens.Keyword, - 'COMMAND_FUNCTION': tokens.Keyword, - 'COMMAND_FUNCTION_CODE': tokens.Keyword, - 'COMMENT': tokens.Keyword, - 'COMMIT': tokens.Keyword, - 'COMMITTED': tokens.Keyword, - 'COMPLETION': tokens.Keyword, - 'CONDITION_NUMBER': tokens.Keyword, - 'CONNECT': tokens.Keyword, - 'CONNECTION': tokens.Keyword, - 'CONNECTION_NAME': tokens.Keyword, - 'CONSTRAINT': tokens.Keyword, - 'CONSTRAINTS': tokens.Keyword, - 'CONSTRAINT_CATALOG': tokens.Keyword, - 'CONSTRAINT_NAME': tokens.Keyword, - 'CONSTRAINT_SCHEMA': tokens.Keyword, - 'CONSTRUCTOR': tokens.Keyword, - 'CONTAINS': tokens.Keyword, - 'CONTINUE': tokens.Keyword, - 'CONVERSION': tokens.Keyword, - 'CONVERT': tokens.Keyword, - 'COPY': tokens.Keyword, - 'CORRESPONTING': tokens.Keyword, - 'COUNT': tokens.Keyword, - 'CREATEDB': tokens.Keyword, - 'CREATEUSER': tokens.Keyword, - 'CROSS': tokens.Keyword, - 'CUBE': tokens.Keyword, - 'CURRENT': tokens.Keyword, - 'CURRENT_DATE': tokens.Keyword, - 'CURRENT_PATH': tokens.Keyword, - 'CURRENT_ROLE': tokens.Keyword, - 'CURRENT_TIME': tokens.Keyword, - 'CURRENT_TIMESTAMP': tokens.Keyword, - 'CURRENT_USER': tokens.Keyword, - 'CURSOR': tokens.Keyword, - 'CURSOR_NAME': tokens.Keyword, - 'CYCLE': tokens.Keyword, - - 'DATA': tokens.Keyword, - 'DATABASE': tokens.Keyword, - 'DATETIME_INTERVAL_CODE': tokens.Keyword, - 'DATETIME_INTERVAL_PRECISION': tokens.Keyword, - 'DAY': tokens.Keyword, - 'DEALLOCATE': tokens.Keyword, - 'DECLARE': tokens.Keyword, - 'DEFAULT': tokens.Keyword, - 'DEFAULTS': tokens.Keyword, - 'DEFERRABLE': tokens.Keyword, - 'DEFERRED': tokens.Keyword, - 'DEFINED': tokens.Keyword, - 'DEFINER': tokens.Keyword, - 'DELIMITER': tokens.Keyword, - 'DELIMITERS': tokens.Keyword, - 'DEREF': tokens.Keyword, - 'DESC': tokens.Keyword, - 'DESCRIBE': tokens.Keyword, - 'DESCRIPTOR': tokens.Keyword, - 'DESTROY': tokens.Keyword, - 'DESTRUCTOR': tokens.Keyword, - 'DETERMINISTIC': tokens.Keyword, - 'DIAGNOSTICS': tokens.Keyword, - 'DICTIONARY': tokens.Keyword, - 'DISCONNECT': tokens.Keyword, - 'DISPATCH': tokens.Keyword, - 'DO': tokens.Keyword, - 'DOMAIN': tokens.Keyword, - 'DYNAMIC': tokens.Keyword, - 'DYNAMIC_FUNCTION': tokens.Keyword, - 'DYNAMIC_FUNCTION_CODE': tokens.Keyword, - - 'EACH': tokens.Keyword, - 'ENCODING': tokens.Keyword, - 'ENCRYPTED': tokens.Keyword, - 'END-EXEC': tokens.Keyword, - 'EQUALS': tokens.Keyword, - 'ESCAPE': tokens.Keyword, - 'EVERY': tokens.Keyword, - 'EXCEPT': tokens.Keyword, - 'ESCEPTION': tokens.Keyword, - 'EXCLUDING': tokens.Keyword, - 'EXCLUSIVE': tokens.Keyword, - 'EXEC': tokens.Keyword, - 'EXECUTE': tokens.Keyword, - 'EXISTING': tokens.Keyword, - 'EXISTS': tokens.Keyword, - 'EXTERNAL': tokens.Keyword, - 'EXTRACT': tokens.Keyword, - - 'FALSE': tokens.Keyword, - 'FETCH': tokens.Keyword, - 'FINAL': tokens.Keyword, - 'FIRST': tokens.Keyword, - 'FORCE': tokens.Keyword, - 'FOREIGN': tokens.Keyword, - 'FORTRAN': tokens.Keyword, - 'FORWARD': tokens.Keyword, - 'FOUND': tokens.Keyword, - 'FREE': tokens.Keyword, - 'FREEZE': tokens.Keyword, - 'FULL': tokens.Keyword, - 'FUNCTION': tokens.Keyword, - -# 'G': tokens.Keyword, - 'GENERAL': tokens.Keyword, - 'GENERATED': tokens.Keyword, - 'GET': tokens.Keyword, - 'GLOBAL': tokens.Keyword, - 'GO': tokens.Keyword, - 'GOTO': tokens.Keyword, - 'GRANT': tokens.Keyword, - 'GRANTED': tokens.Keyword, - 'GROUPING': tokens.Keyword, - - 'HANDLER': tokens.Keyword, - 'HAVING': tokens.Keyword, - 'HIERARCHY': tokens.Keyword, - 'HOLD': tokens.Keyword, - 'HOST': tokens.Keyword, - - 'IDENTITY': tokens.Keyword, - 'IGNORE': tokens.Keyword, - 'ILIKE': tokens.Keyword, - 'IMMEDIATE': tokens.Keyword, - 'IMMUTABLE': tokens.Keyword, - - 'IMPLEMENTATION': tokens.Keyword, - 'IMPLICIT': tokens.Keyword, - 'INCLUDING': tokens.Keyword, - 'INCREMENT': tokens.Keyword, - 'INDEX': tokens.Keyword, - - 'INDITCATOR': tokens.Keyword, - 'INFIX': tokens.Keyword, - 'INHERITS': tokens.Keyword, - 'INITIALIZE': tokens.Keyword, - 'INITIALLY': tokens.Keyword, - 'INOUT': tokens.Keyword, - 'INPUT': tokens.Keyword, - 'INSENSITIVE': tokens.Keyword, - 'INSTANTIABLE': tokens.Keyword, - 'INSTEAD': tokens.Keyword, - 'INTERSECT': tokens.Keyword, - 'INTO': tokens.Keyword, - 'INVOKER': tokens.Keyword, - 'IS': tokens.Keyword, - 'ISNULL': tokens.Keyword, - 'ISOLATION': tokens.Keyword, - 'ITERATE': tokens.Keyword, - -# 'K': tokens.Keyword, - 'KEY': tokens.Keyword, - 'KEY_MEMBER': tokens.Keyword, - 'KEY_TYPE': tokens.Keyword, - - 'LANCOMPILER': tokens.Keyword, - 'LANGUAGE': tokens.Keyword, - 'LARGE': tokens.Keyword, - 'LAST': tokens.Keyword, - 'LATERAL': tokens.Keyword, - 'LEADING': tokens.Keyword, - 'LENGTH': tokens.Keyword, - 'LESS': tokens.Keyword, - 'LEVEL': tokens.Keyword, - 'LIMIT': tokens.Keyword, - 'LISTEN': tokens.Keyword, - 'LOAD': tokens.Keyword, - 'LOCAL': tokens.Keyword, - 'LOCALTIME': tokens.Keyword, - 'LOCALTIMESTAMP': tokens.Keyword, - 'LOCATION': tokens.Keyword, - 'LOCATOR': tokens.Keyword, - 'LOCK': tokens.Keyword, - 'LOWER': tokens.Keyword, - -# 'M': tokens.Keyword, - 'MAP': tokens.Keyword, - 'MATCH': tokens.Keyword, - 'MAXVALUE': tokens.Keyword, - 'MESSAGE_LENGTH': tokens.Keyword, - 'MESSAGE_OCTET_LENGTH': tokens.Keyword, - 'MESSAGE_TEXT': tokens.Keyword, - 'METHOD': tokens.Keyword, - 'MINUTE': tokens.Keyword, - 'MINVALUE': tokens.Keyword, - 'MOD': tokens.Keyword, - 'MODE': tokens.Keyword, - 'MODIFIES': tokens.Keyword, - 'MODIFY': tokens.Keyword, - 'MONTH': tokens.Keyword, - 'MORE': tokens.Keyword, - 'MOVE': tokens.Keyword, - 'MUMPS': tokens.Keyword, - - 'NAMES': tokens.Keyword, - 'NATIONAL': tokens.Keyword, - 'NATURAL': tokens.Keyword, - 'NCHAR': tokens.Keyword, - 'NCLOB': tokens.Keyword, - 'NEW': tokens.Keyword, - 'NEXT': tokens.Keyword, - 'NO': tokens.Keyword, - 'NOCREATEDB': tokens.Keyword, - 'NOCREATEUSER': tokens.Keyword, - 'NONE': tokens.Keyword, - 'NOT': tokens.Keyword, - 'NOTHING': tokens.Keyword, - 'NOTIFY': tokens.Keyword, - 'NOTNULL': tokens.Keyword, - 'NULL': tokens.Keyword, - 'NULLABLE': tokens.Keyword, - 'NULLIF': tokens.Keyword, - - 'OBJECT': tokens.Keyword, - 'OCTET_LENGTH': tokens.Keyword, - 'OF': tokens.Keyword, - 'OFF': tokens.Keyword, - 'OFFSET': tokens.Keyword, - 'OIDS': tokens.Keyword, - 'OLD': tokens.Keyword, - 'ONLY': tokens.Keyword, - 'OPEN': tokens.Keyword, - 'OPERATION': tokens.Keyword, - 'OPERATOR': tokens.Keyword, - 'OPTION': tokens.Keyword, - 'OPTIONS': tokens.Keyword, - 'ORDINALITY': tokens.Keyword, - 'OUT': tokens.Keyword, - 'OUTPUT': tokens.Keyword, - 'OVERLAPS': tokens.Keyword, - 'OVERLAY': tokens.Keyword, - 'OVERRIDING': tokens.Keyword, - 'OWNER': tokens.Keyword, - - 'PAD': tokens.Keyword, - 'PARAMETER': tokens.Keyword, - 'PARAMETERS': tokens.Keyword, - 'PARAMETER_MODE': tokens.Keyword, - 'PARAMATER_NAME': tokens.Keyword, - 'PARAMATER_ORDINAL_POSITION': tokens.Keyword, - 'PARAMETER_SPECIFIC_CATALOG': tokens.Keyword, - 'PARAMETER_SPECIFIC_NAME': tokens.Keyword, - 'PARAMATER_SPECIFIC_SCHEMA': tokens.Keyword, - 'PARTIAL': tokens.Keyword, - 'PASCAL': tokens.Keyword, - 'PENDANT': tokens.Keyword, - 'PLACING': tokens.Keyword, - 'PLI': tokens.Keyword, - 'POSITION': tokens.Keyword, - 'POSTFIX': tokens.Keyword, - 'PRECISION': tokens.Keyword, - 'PREFIX': tokens.Keyword, - 'PREORDER': tokens.Keyword, - 'PREPARE': tokens.Keyword, - 'PRESERVE': tokens.Keyword, - 'PRIMARY': tokens.Keyword, - 'PRIOR': tokens.Keyword, - 'PRIVILEGES': tokens.Keyword, - 'PROCEDURAL': tokens.Keyword, - 'PROCEDURE': tokens.Keyword, - 'PUBLIC': tokens.Keyword, - - 'RAISE': tokens.Keyword, - 'READ': tokens.Keyword, - 'READS': tokens.Keyword, - 'RECHECK': tokens.Keyword, - 'RECURSIVE': tokens.Keyword, - 'REF': tokens.Keyword, - 'REFERENCES': tokens.Keyword, - 'REFERENCING': tokens.Keyword, - 'REINDEX': tokens.Keyword, - 'RELATIVE': tokens.Keyword, - 'RENAME': tokens.Keyword, - 'REPEATABLE': tokens.Keyword, - 'RESET': tokens.Keyword, - 'RESTART': tokens.Keyword, - 'RESTRICT': tokens.Keyword, - 'RESULT': tokens.Keyword, - 'RETURN': tokens.Keyword, - 'RETURNED_LENGTH': tokens.Keyword, - 'RETURNED_OCTET_LENGTH': tokens.Keyword, - 'RETURNED_SQLSTATE': tokens.Keyword, - 'RETURNS': tokens.Keyword, - 'REVOKE': tokens.Keyword, - 'RIGHT': tokens.Keyword, - 'ROLE': tokens.Keyword, - 'ROLLBACK': tokens.Keyword, - 'ROLLUP': tokens.Keyword, - 'ROUTINE': tokens.Keyword, - 'ROUTINE_CATALOG': tokens.Keyword, - 'ROUTINE_NAME': tokens.Keyword, - 'ROUTINE_SCHEMA': tokens.Keyword, - 'ROW': tokens.Keyword, - 'ROWS': tokens.Keyword, - 'ROW_COUNT': tokens.Keyword, - 'RULE': tokens.Keyword, - - 'SAVE_POINT': tokens.Keyword, - 'SCALE': tokens.Keyword, - 'SCHEMA': tokens.Keyword, - 'SCHEMA_NAME': tokens.Keyword, - 'SCOPE': tokens.Keyword, - 'SCROLL': tokens.Keyword, - 'SEARCH': tokens.Keyword, - 'SECOND': tokens.Keyword, - 'SECURITY': tokens.Keyword, - 'SELF': tokens.Keyword, - 'SENSITIVE': tokens.Keyword, - 'SERIALIZABLE': tokens.Keyword, - 'SERVER_NAME': tokens.Keyword, - 'SESSION': tokens.Keyword, - 'SESSION_USER': tokens.Keyword, - 'SETOF': tokens.Keyword, - 'SETS': tokens.Keyword, - 'SHARE': tokens.Keyword, - 'SHOW': tokens.Keyword, - 'SIMILAR': tokens.Keyword, - 'SIMPLE': tokens.Keyword, - 'SIZE': tokens.Keyword, - 'SOME': tokens.Keyword, - 'SOURCE': tokens.Keyword, - 'SPACE': tokens.Keyword, - 'SPECIFIC': tokens.Keyword, - 'SPECIFICTYPE': tokens.Keyword, - 'SPECIFIC_NAME': tokens.Keyword, - 'SQL': tokens.Keyword, - 'SQLCODE': tokens.Keyword, - 'SQLERROR': tokens.Keyword, - 'SQLEXCEPTION': tokens.Keyword, - 'SQLSTATE': tokens.Keyword, - 'SQLWARNING': tokens.Keyword, - 'STABLE': tokens.Keyword, - 'START': tokens.Keyword, - 'STATE': tokens.Keyword, - 'STATEMENT': tokens.Keyword, - 'STATIC': tokens.Keyword, - 'STATISTICS': tokens.Keyword, - 'STDIN': tokens.Keyword, - 'STDOUT': tokens.Keyword, - 'STORAGE': tokens.Keyword, - 'STRICT': tokens.Keyword, - 'STRUCTURE': tokens.Keyword, - 'STYPE': tokens.Keyword, - 'SUBCLASS_ORIGIN': tokens.Keyword, - 'SUBLIST': tokens.Keyword, - 'SUBSTRING': tokens.Keyword, - 'SUM': tokens.Keyword, - 'SYMMETRIC': tokens.Keyword, - 'SYSID': tokens.Keyword, - 'SYSTEM': tokens.Keyword, - 'SYSTEM_USER': tokens.Keyword, - - 'TABLE': tokens.Keyword, - 'TABLE_NAME': tokens.Keyword, - ' TEMP': tokens.Keyword, - 'TEMPLATE': tokens.Keyword, - 'TEMPORARY': tokens.Keyword, - 'TERMINATE': tokens.Keyword, - 'THAN': tokens.Keyword, - 'TIMESTAMP': tokens.Keyword, - 'TIMEZONE_HOUR': tokens.Keyword, - 'TIMEZONE_MINUTE': tokens.Keyword, - 'TO': tokens.Keyword, - 'TOAST': tokens.Keyword, - 'TRAILING': tokens.Keyword, - 'TRANSATION': tokens.Keyword, - 'TRANSACTIONS_COMMITTED': tokens.Keyword, - 'TRANSACTIONS_ROLLED_BACK': tokens.Keyword, - 'TRANSATION_ACTIVE': tokens.Keyword, - 'TRANSFORM': tokens.Keyword, - 'TRANSFORMS': tokens.Keyword, - 'TRANSLATE': tokens.Keyword, - 'TRANSLATION': tokens.Keyword, - 'TREAT': tokens.Keyword, - 'TRIGGER': tokens.Keyword, - 'TRIGGER_CATALOG': tokens.Keyword, - 'TRIGGER_NAME': tokens.Keyword, - 'TRIGGER_SCHEMA': tokens.Keyword, - 'TRIM': tokens.Keyword, - 'TRUE': tokens.Keyword, - 'TRUNCATE': tokens.Keyword, - 'TRUSTED': tokens.Keyword, - 'TYPE': tokens.Keyword, - - 'UNCOMMITTED': tokens.Keyword, - 'UNDER': tokens.Keyword, - 'UNENCRYPTED': tokens.Keyword, - 'UNION': tokens.Keyword, - 'UNIQUE': tokens.Keyword, - 'UNKNOWN': tokens.Keyword, - 'UNLISTEN': tokens.Keyword, - 'UNNAMED': tokens.Keyword, - 'UNNEST': tokens.Keyword, - 'UNTIL': tokens.Keyword, - 'UPPER': tokens.Keyword, - 'USAGE': tokens.Keyword, - 'USER': tokens.Keyword, - 'USER_DEFINED_TYPE_CATALOG': tokens.Keyword, - 'USER_DEFINED_TYPE_NAME': tokens.Keyword, - 'USER_DEFINED_TYPE_SCHEMA': tokens.Keyword, - 'USING': tokens.Keyword, - - 'VACUUM': tokens.Keyword, - 'VALID': tokens.Keyword, - 'VALIDATOR': tokens.Keyword, - 'VALUES': tokens.Keyword, - 'VARIABLE': tokens.Keyword, - 'VERBOSE': tokens.Keyword, - 'VERSION': tokens.Keyword, - 'VIEW': tokens.Keyword, - 'VOLATILE': tokens.Keyword, - - 'WHENEVER': tokens.Keyword, - 'WITH': tokens.Keyword, - 'WITHOUT': tokens.Keyword, - 'WORK': tokens.Keyword, - 'WRITE': tokens.Keyword, - - 'YEAR': tokens.Keyword, - - 'ZONE': tokens.Keyword, - - - 'ARRAY': tokens.Name.Builtin, - 'BIGINT': tokens.Name.Builtin, - 'BINARY': tokens.Name.Builtin, - 'BIT': tokens.Name.Builtin, - 'BLOB': tokens.Name.Builtin, - 'BOOLEAN': tokens.Name.Builtin, - 'CHAR': tokens.Name.Builtin, - 'CHARACTER': tokens.Name.Builtin, - 'DATE': tokens.Name.Builtin, - 'DEC': tokens.Name.Builtin, - 'DECIMAL': tokens.Name.Builtin, - 'FLOAT': tokens.Name.Builtin, - 'INT': tokens.Name.Builtin, - 'INTEGER': tokens.Name.Builtin, - 'INTERVAL': tokens.Name.Builtin, - 'LONG': tokens.Name.Builtin, - 'NUMBER': tokens.Name.Builtin, - 'NUMERIC': tokens.Name.Builtin, - 'REAL': tokens.Name.Builtin, - 'SERIAL': tokens.Name.Builtin, - 'SMALLINT': tokens.Name.Builtin, - 'VARCHAR': tokens.Name.Builtin, - 'VARCHAR2': tokens.Name.Builtin, - 'VARYING': tokens.Name.Builtin, - 'INT8': tokens.Name.Builtin, - 'SERIAL8': tokens.Name.Builtin, - 'TEXT': tokens.Name.Builtin, - } - - -KEYWORDS_COMMON = { - 'SELECT': tokens.Keyword.DML, - 'INSERT': tokens.Keyword.DML, - 'DELETE': tokens.Keyword.DML, - 'UPDATE': tokens.Keyword.DML, - 'REPLACE': tokens.Keyword.DML, - 'DROP': tokens.Keyword.DDL, - 'CREATE': tokens.Keyword.DDL, - 'ALTER': tokens.Keyword.DDL, - - 'WHERE': tokens.Keyword, - 'FROM': tokens.Keyword, - 'INNER': tokens.Keyword, - 'JOIN': tokens.Keyword, - 'AND': tokens.Keyword, - 'OR': tokens.Keyword, - 'LIKE': tokens.Keyword, - 'ON': tokens.Keyword, - 'IN': tokens.Keyword, - 'SET': tokens.Keyword, - - 'BY': tokens.Keyword, - 'GROUP': tokens.Keyword, - 'ORDER': tokens.Keyword, - 'LEFT': tokens.Keyword, - 'OUTER': tokens.Keyword, - - 'IF': tokens.Keyword, - 'END': tokens.Keyword, - 'THEN': tokens.Keyword, - 'LOOP': tokens.Keyword, - 'AS': tokens.Keyword, - 'ELSE': tokens.Keyword, - 'FOR': tokens.Keyword, - - 'CASE': tokens.Keyword, - 'WHEN': tokens.Keyword, - 'MIN': tokens.Keyword, - 'MAX': tokens.Keyword, - 'DISTINCT': tokens.Keyword, - } diff --git a/debug_toolbar/utils/sqlparse/lexer.py b/debug_toolbar/utils/sqlparse/lexer.py deleted file mode 100644 index ae3fc2e95..000000000 --- a/debug_toolbar/utils/sqlparse/lexer.py +++ /dev/null @@ -1,331 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com -# -# This module is part of python-sqlparse and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php. - -"""SQL Lexer""" - -# This code is based on the SqlLexer in pygments. -# http://pygments.org/ -# It's separated from the rest of pygments to increase performance -# and to allow some customizations. - -import re - -from debug_toolbar.utils.sqlparse import tokens -from debug_toolbar.utils.sqlparse.keywords import KEYWORDS, KEYWORDS_COMMON - - -class include(str): - pass - - -class combined(tuple): - """Indicates a state combined from multiple states.""" - - def __new__(cls, *args): - return tuple.__new__(cls, args) - - def __init__(self, *args): - # tuple.__init__ doesn't do anything - pass - - -def is_keyword(value): - test = value.upper() - return KEYWORDS_COMMON.get(test, KEYWORDS.get(test, tokens.Name)), value - - -def apply_filters(stream, filters, lexer=None): - """ - Use this method to apply an iterable of filters to - a stream. If lexer is given it's forwarded to the - filter, otherwise the filter receives `None`. - """ - - def _apply(filter_, stream): - for token in filter_.filter(lexer, stream): - yield token - - for filter_ in filters: - stream = _apply(filter_, stream) - return stream - - -class LexerMeta(type): - """ - Metaclass for Lexer, creates the self._tokens attribute from - self.tokens on the first instantiation. - """ - - def _process_state(cls, unprocessed, processed, state): - assert type(state) is str, "wrong state name %r" % state - assert state[0] != '#', "invalid state name %r" % state - if state in processed: - return processed[state] - tokenlist = processed[state] = [] - rflags = cls.flags - for tdef in unprocessed[state]: - if isinstance(tdef, include): - # it's a state reference - assert tdef != state, "circular state reference %r" % state - tokenlist.extend(cls._process_state( - unprocessed, processed, str(tdef))) - continue - - assert type(tdef) is tuple, "wrong rule def %r" % tdef - - try: - rex = re.compile(tdef[0], rflags).match - except Exception, err: - raise ValueError(("uncompilable regex %r in state" - " %r of %r: %s" - % (tdef[0], state, cls, err))) - - assert type(tdef[1]) is tokens._TokenType or callable(tdef[1]), \ - ('token type must be simple type or callable, not %r' - % (tdef[1],)) - - if len(tdef) == 2: - new_state = None - else: - tdef2 = tdef[2] - if isinstance(tdef2, str): - # an existing state - if tdef2 == '#pop': - new_state = -1 - elif tdef2 in unprocessed: - new_state = (tdef2,) - elif tdef2 == '#push': - new_state = tdef2 - elif tdef2[:5] == '#pop:': - new_state = -int(tdef2[5:]) - else: - assert False, 'unknown new state %r' % tdef2 - elif isinstance(tdef2, combined): - # combine a new state from existing ones - new_state = '_tmp_%d' % cls._tmpname - cls._tmpname += 1 - itokens = [] - for istate in tdef2: - assert istate != state, \ - 'circular state ref %r' % istate - itokens.extend(cls._process_state(unprocessed, - processed, istate)) - processed[new_state] = itokens - new_state = (new_state,) - elif isinstance(tdef2, tuple): - # push more than one state - for state in tdef2: - assert (state in unprocessed or - state in ('#pop', '#push')), \ - 'unknown new state ' + state - new_state = tdef2 - else: - assert False, 'unknown new state def %r' % tdef2 - tokenlist.append((rex, tdef[1], new_state)) - return tokenlist - - def process_tokendef(cls): - cls._all_tokens = {} - cls._tmpname = 0 - processed = cls._all_tokens[cls.__name__] = {} - #tokendefs = tokendefs or cls.tokens[name] - for state in cls.tokens.keys(): - cls._process_state(cls.tokens, processed, state) - return processed - - def __call__(cls, *args, **kwds): - if not hasattr(cls, '_tokens'): - cls._all_tokens = {} - cls._tmpname = 0 - if hasattr(cls, 'token_variants') and cls.token_variants: - # don't process yet - pass - else: - cls._tokens = cls.process_tokendef() - - return type.__call__(cls, *args, **kwds) - - -class Lexer(object): - - __metaclass__ = LexerMeta - - encoding = 'utf-8' - stripall = False - stripnl = False - tabsize = 0 - flags = re.IGNORECASE - - tokens = { - 'root': [ - (r'--.*?(\r\n|\r|\n)', tokens.Comment.Single), - # $ matches *before* newline, therefore we have two patterns - # to match Comment.Single - (r'--.*?$', tokens.Comment.Single), - (r'(\r|\n|\r\n)', tokens.Newline), - (r'\s+', tokens.Whitespace), - (r'/\*', tokens.Comment.Multiline, 'multiline-comments'), - (r':=', tokens.Assignment), - (r'::', tokens.Punctuation), - (r'[*]', tokens.Wildcard), - (r'CASE\b', tokens.Keyword), # extended CASE(foo) - (r"`(``|[^`])*`", tokens.Name), - (r"´(´´|[^´])*´", tokens.Name), - (r'\$([a-zA-Z_][a-zA-Z0-9_]*)?\$', tokens.Name.Builtin), - (r'\?{1}', tokens.Name.Placeholder), - (r'[$:?%][a-zA-Z0-9_]+[^$:?%]?', tokens.Name.Placeholder), - (r'@[a-zA-Z_][a-zA-Z0-9_]+', tokens.Name), - (r'[a-zA-Z_][a-zA-Z0-9_]*(?=[.(])', tokens.Name), # see issue39 - (r'[<>=~!]+', tokens.Operator.Comparison), - (r'[+/@#%^&|`?^-]+', tokens.Operator), - (r'0x[0-9a-fA-F]+', tokens.Number.Hexadecimal), - (r'[0-9]*\.[0-9]+', tokens.Number.Float), - (r'[0-9]+', tokens.Number.Integer), - # TODO: Backslash escapes? - (r"(''|'.*?[^\\]')", tokens.String.Single), - # not a real string literal in ANSI SQL: - (r'(""|".*?[^\\]")', tokens.String.Symbol), - (r'(\[.*[^\]]\])', tokens.Name), - (r'(LEFT |RIGHT )?(INNER |OUTER )?JOIN\b', tokens.Keyword), - (r'END( IF| LOOP)?\b', tokens.Keyword), - (r'NOT NULL\b', tokens.Keyword), - (r'CREATE( OR REPLACE)?\b', tokens.Keyword.DDL), - (r'[a-zA-Z_][a-zA-Z0-9_]*', is_keyword), - (r'[;:()\[\],\.]', tokens.Punctuation), - ], - 'multiline-comments': [ - (r'/\*', tokens.Comment.Multiline, 'multiline-comments'), - (r'\*/', tokens.Comment.Multiline, '#pop'), - (r'[^/\*]+', tokens.Comment.Multiline), - (r'[/*]', tokens.Comment.Multiline) - ]} - - def __init__(self): - self.filters = [] - - def add_filter(self, filter_, **options): - from debug_toolbar.utils.sqlparse.filters import Filter - if not isinstance(filter_, Filter): - filter_ = filter_(**options) - self.filters.append(filter_) - - def get_tokens(self, text, unfiltered=False): - """ - Return an iterable of (tokentype, value) pairs generated from - `text`. If `unfiltered` is set to `True`, the filtering mechanism - is bypassed even if filters are defined. - - Also preprocess the text, i.e. expand tabs and strip it if - wanted and applies registered filters. - """ - if not isinstance(text, unicode): - if self.encoding == 'guess': - try: - text = text.decode('utf-8') - if text.startswith(u'\ufeff'): - text = text[len(u'\ufeff'):] - except UnicodeDecodeError: - text = text.decode('latin1') - elif self.encoding == 'chardet': - try: - import chardet - except ImportError: - raise ImportError('To enable chardet encoding guessing, ' - 'please install the chardet library ' - 'from http://chardet.feedparser.org/') - enc = chardet.detect(text) - text = text.decode(enc['encoding']) - else: - text = text.decode(self.encoding) - if self.stripall: - text = text.strip() - elif self.stripnl: - text = text.strip('\n') - if self.tabsize > 0: - text = text.expandtabs(self.tabsize) -# if not text.endswith('\n'): -# text += '\n' - - def streamer(): - for i, t, v in self.get_tokens_unprocessed(text): - yield t, v - stream = streamer() - if not unfiltered: - stream = apply_filters(stream, self.filters, self) - return stream - - def get_tokens_unprocessed(self, text, stack=('root',)): - """ - Split ``text`` into (tokentype, text) pairs. - - ``stack`` is the inital stack (default: ``['root']``) - """ - pos = 0 - tokendefs = self._tokens - statestack = list(stack) - statetokens = tokendefs[statestack[-1]] - known_names = {} - while 1: - for rexmatch, action, new_state in statetokens: - m = rexmatch(text, pos) - if m: - # print rex.pattern - value = m.group() - if value in known_names: - yield pos, known_names[value], value - elif type(action) is tokens._TokenType: - yield pos, action, value - elif hasattr(action, '__call__'): - ttype, value = action(value) - known_names[value] = ttype - yield pos, ttype, value - else: - for item in action(self, m): - yield item - pos = m.end() - if new_state is not None: - # state transition - if isinstance(new_state, tuple): - for state in new_state: - if state == '#pop': - statestack.pop() - elif state == '#push': - statestack.append(statestack[-1]) - else: - statestack.append(state) - elif isinstance(new_state, int): - # pop - del statestack[new_state:] - elif new_state == '#push': - statestack.append(statestack[-1]) - else: - assert False, "wrong state def: %r" % new_state - statetokens = tokendefs[statestack[-1]] - break - else: - try: - if text[pos] == '\n': - # at EOL, reset state to "root" - pos += 1 - statestack = ['root'] - statetokens = tokendefs['root'] - yield pos, tokens.Text, u'\n' - continue - yield pos, tokens.Error, text[pos] - pos += 1 - except IndexError: - break - - -def tokenize(sql): - """Tokenize sql. - - Tokenize *sql* using the :class:`Lexer` and return a 2-tuple stream - of ``(token type, value)`` items. - """ - lexer = Lexer() - return lexer.get_tokens(sql) diff --git a/debug_toolbar/utils/sqlparse/sql.py b/debug_toolbar/utils/sqlparse/sql.py deleted file mode 100644 index 2f9f538a6..000000000 --- a/debug_toolbar/utils/sqlparse/sql.py +++ /dev/null @@ -1,529 +0,0 @@ -# -*- coding: utf-8 -*- - -"""This module contains classes representing syntactical elements of SQL.""" - -import re - -from debug_toolbar.utils.sqlparse import tokens as T - - -class Token(object): - """Base class for all other classes in this module. - - It represents a single token and has two instance attributes: - ``value`` is the unchange value of the token and ``ttype`` is - the type of the token. - """ - - __slots__ = ('value', 'ttype', 'parent') - - def __init__(self, ttype, value): - self.value = value - self.ttype = ttype - self.parent = None - - def __str__(self): - return unicode(self).encode('utf-8') - - def __repr__(self): - short = self._get_repr_value() - return '<%s \'%s\' at 0x%07x>' % (self._get_repr_name(), - short, id(self)) - - def __unicode__(self): - return self.value or '' - - def to_unicode(self): - """Returns a unicode representation of this object.""" - return unicode(self) - - def _get_repr_name(self): - return str(self.ttype).split('.')[-1] - - def _get_repr_value(self): - raw = unicode(self) - if len(raw) > 7: - short = raw[:6] + u'...' - else: - short = raw - return re.sub('\s+', ' ', short) - - def flatten(self): - """Resolve subgroups.""" - yield self - - def match(self, ttype, values, regex=False): - """Checks whether the token matches the given arguments. - - *ttype* is a token type. If this token doesn't match the given token - type. - *values* is a list of possible values for this token. The values - are OR'ed together so if only one of the values matches ``True`` - is returned. Except for keyword tokens the comparison is - case-sensitive. For convenience it's ok to pass in a single string. - If *regex* is ``True`` (default is ``False``) the given values are - treated as regular expressions. - """ - type_matched = self.ttype is ttype - if not type_matched or values is None: - return type_matched - if isinstance(values, basestring): - values = set([values]) - if regex: - if self.ttype is T.Keyword: - values = set([re.compile(v, re.IGNORECASE) for v in values]) - else: - values = set([re.compile(v) for v in values]) - for pattern in values: - if pattern.search(self.value): - return True - return False - else: - if self.ttype in T.Keyword: - values = set([v.upper() for v in values]) - return self.value.upper() in values - else: - return self.value in values - - def is_group(self): - """Returns ``True`` if this object has children.""" - return False - - def is_whitespace(self): - """Return ``True`` if this token is a whitespace token.""" - return self.ttype and self.ttype in T.Whitespace - - def within(self, group_cls): - """Returns ``True`` if this token is within *group_cls*. - - Use this method for example to check if an identifier is within - a function: ``t.within(sql.Function)``. - """ - parent = self.parent - while parent: - if isinstance(parent, group_cls): - return True - parent = parent.parent - return False - - def is_child_of(self, other): - """Returns ``True`` if this token is a direct child of *other*.""" - return self.parent == other - - def has_ancestor(self, other): - """Returns ``True`` if *other* is in this tokens ancestry.""" - parent = self.parent - while parent: - if parent == other: - return True - parent = parent.parent - return False - - -class TokenList(Token): - """A group of tokens. - - It has an additional instance attribute ``tokens`` which holds a - list of child-tokens. - """ - - __slots__ = ('value', 'ttype', 'tokens') - - def __init__(self, tokens=None): - if tokens is None: - tokens = [] - self.tokens = tokens - Token.__init__(self, None, None) - - def __unicode__(self): - return ''.join(unicode(x) for x in self.flatten()) - - def __str__(self): - return unicode(self).encode('utf-8') - - def _get_repr_name(self): - return self.__class__.__name__ - - def _pprint_tree(self, max_depth=None, depth=0): - """Pretty-print the object tree.""" - indent = ' ' * (depth * 2) - for idx, token in enumerate(self.tokens): - if token.is_group(): - pre = ' +-' - else: - pre = ' | ' - print '%s%s%d %s \'%s\'' % (indent, pre, idx, - token._get_repr_name(), - token._get_repr_value()) - if (token.is_group() and (max_depth is None or depth < max_depth)): - token._pprint_tree(max_depth, depth + 1) - - def flatten(self): - """Generator yielding ungrouped tokens. - - This method is recursively called for all child tokens. - """ - for token in self.tokens: - if isinstance(token, TokenList): - for item in token.flatten(): - yield item - else: - yield token - - def is_group(self): - return True - - def get_sublists(self): - return [x for x in self.tokens if isinstance(x, TokenList)] - - @property - def _groupable_tokens(self): - return self.tokens - - def token_first(self, ignore_whitespace=True): - """Returns the first child token. - - If *ignore_whitespace* is ``True`` (the default), whitespace - tokens are ignored. - """ - for token in self.tokens: - if ignore_whitespace and token.is_whitespace(): - continue - return token - return None - - def token_next_by_instance(self, idx, clss): - """Returns the next token matching a class. - - *idx* is where to start searching in the list of child tokens. - *clss* is a list of classes the token should be an instance of. - - If no matching token can be found ``None`` is returned. - """ - if isinstance(clss, (list, tuple)): - clss = (clss,) - if isinstance(clss, tuple): - clss = tuple(clss) - for token in self.tokens[idx:]: - if isinstance(token, clss): - return token - return None - - def token_next_by_type(self, idx, ttypes): - """Returns next matching token by it's token type.""" - if not isinstance(ttypes, (list, tuple)): - ttypes = [ttypes] - for token in self.tokens[idx:]: - if token.ttype in ttypes: - return token - return None - - def token_next_match(self, idx, ttype, value, regex=False): - """Returns next token where it's ``match`` method returns ``True``.""" - if not isinstance(idx, int): - idx = self.token_index(idx) - for token in self.tokens[idx:]: - if token.match(ttype, value, regex): - return token - return None - - def token_not_matching(self, idx, funcs): - for token in self.tokens[idx:]: - passed = False - for func in funcs: - if func(token): - passed = True - break - if not passed: - return token - return None - - def token_matching(self, idx, funcs): - for token in self.tokens[idx:]: - for i, func in enumerate(funcs): - if func(token): - return token - return None - - def token_prev(self, idx, skip_ws=True): - """Returns the previous token relative to *idx*. - - If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. - ``None`` is returned if there's no previous token. - """ - if idx is None: - return None - if not isinstance(idx, int): - idx = self.token_index(idx) - while idx != 0: - idx -= 1 - if self.tokens[idx].is_whitespace() and skip_ws: - continue - return self.tokens[idx] - - def token_next(self, idx, skip_ws=True): - """Returns the next token relative to *idx*. - - If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. - ``None`` is returned if there's no next token. - """ - if idx is None: - return None - if not isinstance(idx, int): - idx = self.token_index(idx) - while idx < len(self.tokens) - 1: - idx += 1 - if self.tokens[idx].is_whitespace() and skip_ws: - continue - return self.tokens[idx] - - def token_index(self, token): - """Return list index of token.""" - return self.tokens.index(token) - - def tokens_between(self, start, end, exclude_end=False): - """Return all tokens between (and including) start and end. - - If *exclude_end* is ``True`` (default is ``False``) the end token - is included too. - """ - # FIXME(andi): rename exclude_end to inlcude_end - if exclude_end: - offset = 0 - else: - offset = 1 - end_idx = self.token_index(end) + offset - start_idx = self.token_index(start) - return self.tokens[start_idx:end_idx] - - def group_tokens(self, grp_cls, tokens, ignore_ws=False): - """Replace tokens by an instance of *grp_cls*.""" - idx = self.token_index(tokens[0]) - if ignore_ws: - while tokens and tokens[-1].is_whitespace(): - tokens = tokens[:-1] - for t in tokens: - self.tokens.remove(t) - grp = grp_cls(tokens) - for token in tokens: - token.parent = grp - grp.parent = self - self.tokens.insert(idx, grp) - return grp - - def insert_before(self, where, token): - """Inserts *token* before *where*.""" - self.tokens.insert(self.token_index(where), token) - - -class Statement(TokenList): - """Represents a SQL statement.""" - - __slots__ = ('value', 'ttype', 'tokens') - - def get_type(self): - """Returns the type of a statement. - - The returned value is a string holding an upper-cased reprint of - the first DML or DDL keyword. If the first token in this group - isn't a DML or DDL keyword "UNKNOWN" is returned. - """ - first_token = self.token_first() - if first_token is None: - # An "empty" statement that either has not tokens at all - # or only whitespace tokens. - return 'UNKNOWN' - elif first_token.ttype in (T.Keyword.DML, T.Keyword.DDL): - return first_token.value.upper() - else: - return 'UNKNOWN' - - -class Identifier(TokenList): - """Represents an identifier. - - Identifiers may have aliases or typecasts. - """ - - __slots__ = ('value', 'ttype', 'tokens') - - def has_alias(self): - """Returns ``True`` if an alias is present.""" - return self.get_alias() is not None - - def get_alias(self): - """Returns the alias for this identifier or ``None``.""" - kw = self.token_next_match(0, T.Keyword, 'AS') - if kw is not None: - alias = self.token_next(self.token_index(kw)) - if alias is None: - return None - else: - next_ = self.token_next(0) - if next_ is None or not isinstance(next_, Identifier): - return None - alias = next_ - if isinstance(alias, Identifier): - return alias.get_name() - else: - return alias.to_unicode() - - def get_name(self): - """Returns the name of this identifier. - - This is either it's alias or it's real name. The returned valued can - be considered as the name under which the object corresponding to - this identifier is known within the current statement. - """ - alias = self.get_alias() - if alias is not None: - return alias - return self.get_real_name() - - def get_real_name(self): - """Returns the real name (object name) of this identifier.""" - # a.b - dot = self.token_next_match(0, T.Punctuation, '.') - if dot is None: - return self.token_next_by_type(0, T.Name).value - else: - next_ = self.token_next_by_type(self.token_index(dot), - (T.Name, T.Wildcard)) - if next_ is None: # invalid identifier, e.g. "a." - return None - return next_.value - - def get_parent_name(self): - """Return name of the parent object if any. - - A parent object is identified by the first occuring dot. - """ - dot = self.token_next_match(0, T.Punctuation, '.') - if dot is None: - return None - prev_ = self.token_prev(self.token_index(dot)) - if prev_ is None: # something must be verry wrong here.. - return None - return prev_.value - - def is_wildcard(self): - """Return ``True`` if this identifier contains a wildcard.""" - token = self.token_next_by_type(0, T.Wildcard) - return token is not None - - def get_typecast(self): - """Returns the typecast or ``None`` of this object as a string.""" - marker = self.token_next_match(0, T.Punctuation, '::') - if marker is None: - return None - next_ = self.token_next(self.token_index(marker), False) - if next_ is None: - return None - return next_.to_unicode() - - -class IdentifierList(TokenList): - """A list of :class:`~sqlparse.sql.Identifier`\'s.""" - - __slots__ = ('value', 'ttype', 'tokens') - - def get_identifiers(self): - """Returns the identifiers. - - Whitespaces and punctuations are not included in this list. - """ - return [x for x in self.tokens - if not x.is_whitespace() and not x.match(T.Punctuation, ',')] - - -class Parenthesis(TokenList): - """Tokens between parenthesis.""" - __slots__ = ('value', 'ttype', 'tokens') - - @property - def _groupable_tokens(self): - return self.tokens[1:-1] - - -class Assignment(TokenList): - """An assignment like 'var := val;'""" - __slots__ = ('value', 'ttype', 'tokens') - - -class If(TokenList): - """An 'if' clause with possible 'else if' or 'else' parts.""" - __slots__ = ('value', 'ttype', 'tokens') - - -class For(TokenList): - """A 'FOR' loop.""" - __slots__ = ('value', 'ttype', 'tokens') - - -class Comparison(TokenList): - """A comparison used for example in WHERE clauses.""" - __slots__ = ('value', 'ttype', 'tokens') - - -class Comment(TokenList): - """A comment.""" - __slots__ = ('value', 'ttype', 'tokens') - - -class Where(TokenList): - """A WHERE clause.""" - __slots__ = ('value', 'ttype', 'tokens') - - -class Case(TokenList): - """A CASE statement with one or more WHEN and possibly an ELSE part.""" - - __slots__ = ('value', 'ttype', 'tokens') - - def get_cases(self): - """Returns a list of 2-tuples (condition, value). - - If an ELSE exists condition is None. - """ - ret = [] - in_value = False - in_condition = True - for token in self.tokens: - if token.match(T.Keyword, 'CASE'): - continue - elif token.match(T.Keyword, 'WHEN'): - ret.append(([], [])) - in_condition = True - in_value = False - elif token.match(T.Keyword, 'ELSE'): - ret.append((None, [])) - in_condition = False - in_value = True - elif token.match(T.Keyword, 'THEN'): - in_condition = False - in_value = True - elif token.match(T.Keyword, 'END'): - in_condition = False - in_value = False - if (in_condition or in_value) and not ret: - # First condition withou preceding WHEN - ret.append(([], [])) - if in_condition: - ret[-1][0].append(token) - elif in_value: - ret[-1][1].append(token) - return ret - - -class Function(TokenList): - """A function or procedure call.""" - - __slots__ = ('value', 'ttype', 'tokens') - - def get_parameters(self): - """Return a list of parameters.""" - parenthesis = self.tokens[-1] - for t in parenthesis.tokens: - if isinstance(t, IdentifierList): - return t.get_identifiers() - return [] diff --git a/debug_toolbar/utils/sqlparse/tokens.py b/debug_toolbar/utils/sqlparse/tokens.py deleted file mode 100644 index 01a9b896e..000000000 --- a/debug_toolbar/utils/sqlparse/tokens.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com -# -# This module is part of python-sqlparse and is released under -# the BSD License: http://www.opensource.org/licenses/bsd-license.php. - -# The Token implementation is based on pygment's token system written -# by Georg Brandl. -# http://pygments.org/ - -"""Tokens""" - - -class _TokenType(tuple): - parent = None - - def split(self): - buf = [] - node = self - while node is not None: - buf.append(node) - node = node.parent - buf.reverse() - return buf - - def __contains__(self, val): - return val is not None and (self is val or val[:len(self)] == self) - - def __getattr__(self, val): - if not val or not val[0].isupper(): - return tuple.__getattribute__(self, val) - new = _TokenType(self + (val,)) - setattr(self, val, new) - new.parent = self - return new - - def __hash__(self): - return hash(tuple(self)) - - def __repr__(self): - return 'Token' + (self and '.' or '') + '.'.join(self) - - -Token = _TokenType() - -# Special token types -Text = Token.Text -Whitespace = Text.Whitespace -Newline = Whitespace.Newline -Error = Token.Error -# Text that doesn't belong to this lexer (e.g. HTML in PHP) -Other = Token.Other - -# Common token types for source code -Keyword = Token.Keyword -Name = Token.Name -Literal = Token.Literal -String = Literal.String -Number = Literal.Number -Punctuation = Token.Punctuation -Operator = Token.Operator -Comparison = Operator.Comparison -Wildcard = Token.Wildcard -Comment = Token.Comment -Assignment = Token.Assignement - -# Generic types for non-source code -Generic = Token.Generic - -# String and some others are not direct childs of Token. -# alias them: -Token.Token = Token -Token.String = String -Token.Number = Number - -# SQL specific tokens -DML = Keyword.DML -DDL = Keyword.DDL -Command = Keyword.Command - -Group = Token.Group -Group.Parenthesis = Token.Group.Parenthesis -Group.Comment = Token.Group.Comment -Group.Where = Token.Group.Where diff --git a/setup.py b/setup.py index 06cd86f30..bbd5a477c 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,9 @@ tests_require=[ 'django>=1.3,<1.6', ], + install_requires=[ + "sqlparse", + ], test_suite='runtests.runtests', include_package_data=True, zip_safe=False, # because we're including media that Django needs From c73c77a047c21200358ecb98d9336d86b43bd360 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:20:11 +0100 Subject: [PATCH 04/34] remove support for django <1.4.2 --- .travis.yml | 1 - setup.py | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd40eb81d..0c4b4ddc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ python: - "2.6" - "2.7" env: - - DJANGO_VERSION=1.3.7 - DJANGO_VERSION=1.4.5 - DJANGO_VERSION=1.5 matrix: diff --git a/setup.py b/setup.py index bbd5a477c..6b3208fd2 100644 --- a/setup.py +++ b/setup.py @@ -12,11 +12,9 @@ download_url='https://github.com/django-debug-toolbar/django-debug-toolbar/downloads', license='BSD', packages=find_packages(exclude=('tests', 'example')), - tests_require=[ - 'django>=1.3,<1.6', - ], install_requires=[ - "sqlparse", + 'sqlparse', + 'django>=1.4.2,<1.6', ], test_suite='runtests.runtests', include_package_data=True, From c58094b337ed2a40b7d8ec0e091a237ea72a0813 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:29:52 +0100 Subject: [PATCH 05/34] use new style exception in __init__.py --- debug_toolbar/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index 48da4a621..feb6925a2 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -3,5 +3,5 @@ try: VERSION = __import__('pkg_resources') \ .get_distribution('django-debug-toolbar').version -except Exception, e: +except Exception as e: VERSION = 'unknown' From e749ab176c011352d22a6dc11f0e83e428b11b66 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:38:49 +0100 Subject: [PATCH 06/34] port imports to py3k in middleware.py --- debug_toolbar/middleware.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 2c36a19f3..6654bc307 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -2,12 +2,12 @@ Debug Toolbar middleware """ import imp -import thread +from django.utils.six.moves import _thread from django.conf import settings from django.http import HttpResponseRedirect from django.shortcuts import render_to_response -from django.utils.encoding import smart_unicode +from django.utils.encoding import smart_text from django.utils.importlib import import_module import debug_toolbar.urls @@ -38,7 +38,7 @@ class DebugToolbarMiddleware(object): @classmethod def get_current(cls): - return cls.debug_toolbars.get(thread.get_ident()) + return cls.debug_toolbars.get(_thread.get_ident()) def __init__(self): self._urlconfs = {} @@ -82,7 +82,7 @@ def process_request(self, request): if urlconf not in self._urlconfs: new_urlconf = imp.new_module('urlconf') new_urlconf.urlpatterns = debug_toolbar.urls.urlpatterns + \ - list(urlconf.urlpatterns) + list(urlconf.urlpatterns) if hasattr(urlconf, 'handler403'): new_urlconf.handler403 = urlconf.handler403 @@ -98,11 +98,11 @@ def process_request(self, request): toolbar = DebugToolbar(request) for panel in toolbar.panels: panel.process_request(request) - self.__class__.debug_toolbars[thread.get_ident()] = toolbar + self.__class__.debug_toolbars[_thread.get_ident()] = toolbar def process_view(self, request, view_func, view_args, view_kwargs): __traceback_hide__ = True - toolbar = self.__class__.debug_toolbars.get(thread.get_ident()) + toolbar = self.__class__.debug_toolbars.get(_thread.get_ident()) if not toolbar: return result = None @@ -114,7 +114,7 @@ def process_view(self, request, view_func, view_args, view_kwargs): def process_response(self, request, response): __traceback_hide__ = True - ident = thread.get_ident() + ident = _thread.get_ident() toolbar = self.__class__.debug_toolbars.get(ident) if not toolbar or request.is_ajax(): return response @@ -134,9 +134,9 @@ def process_response(self, request, response): for panel in toolbar.panels: panel.process_response(request, response) response.content = replace_insensitive( - smart_unicode(response.content), + smart_text(response.content), self.tag, - smart_unicode(toolbar.render_toolbar() + self.tag)) + smart_text(toolbar.render_toolbar() + self.tag)) if response.get('Content-Length', None): response['Content-Length'] = len(response.content) del self.__class__.debug_toolbars[ident] From f6aad29ec8ae18701b64d3854b322e01f1d63739 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:39:23 +0100 Subject: [PATCH 07/34] support new style exceptions in loader.py --- debug_toolbar/toolbar/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index 570111848..d5e21624e 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -93,7 +93,7 @@ def load_panel_classes(): panel_module, panel_classname = panel_path[:dot], panel_path[dot + 1:] try: mod = import_module(panel_module) - except ImportError, e: + except ImportError as e: raise ImproperlyConfigured( 'Error importing debug panel %s: "%s"' % (panel_module, e)) From f1d5de5c609707efc6e2ecf0b86a51e823cada0d Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:40:08 +0100 Subject: [PATCH 08/34] port imports to py3k in utils/__init__.py --- debug_toolbar/utils/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index 2d2ff101f..292f51d15 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -1,17 +1,17 @@ import inspect import os.path import django -import SocketServer import sys from django.conf import settings from django.views.debug import linebreak_iter +from django.utils.six.moves import socketserver from django.utils.html import escape from django.utils.safestring import mark_safe # Figure out some paths django_path = os.path.realpath(os.path.dirname(django.__file__)) -socketserver_path = os.path.realpath(os.path.dirname(SocketServer.__file__)) +socketserver_path = os.path.realpath(os.path.dirname(socketserver.__file__)) def ms_from_timedelta(td): From 61d12864d8513166ada17b6d12de9d3be2d6d07a Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:41:03 +0100 Subject: [PATCH 09/34] support new style exceptions in utils/tracking/__init__.py --- debug_toolbar/utils/tracking/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index 766c24891..9a5927394 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -37,7 +37,7 @@ def fire_hook(hook, sender, **kwargs): try: for callback in callbacks[hook].get(id(sender), []): callback(sender=sender, **kwargs) - except Exception, e: + except Exception as e: # Log the exception, dont mess w/ the underlying function logging.exception(e) From e4e8388be0a58db3e7a14e9a1ee7663248406075 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 21:44:09 +0100 Subject: [PATCH 10/34] use force_text instead of force_unicode --- debug_toolbar/utils/tracking/db.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 0ff33591b..685aa8ab2 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -5,7 +5,7 @@ from django.conf import settings from django.template import Node -from django.utils.encoding import force_unicode, smart_str +from django.utils.encoding import force_text, smart_str from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -13,12 +13,12 @@ try: import json -except ImportError: # python < 2.6 +except ImportError: # python < 2.6 from django.utils import simplejson as json try: from hashlib import sha1 -except ImportError: # python < 2.5 +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 @@ -93,7 +93,7 @@ def _quote_params(self, params): def _decode(self, param): try: - return force_unicode(param, strings_only=True) + return force_text(param, strings_only=True) except UnicodeDecodeError: return '(encoded string)' From e3723e3dea066ad4c883a9269f8279e591497f7f Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:13:06 +0100 Subject: [PATCH 11/34] remove use of deprecated sqlparse.filters.Filters --- debug_toolbar/panels/sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 3bdc1a4b3..c9eb8f7a5 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -196,7 +196,7 @@ def process_response(self, request, response): }) -class BoldKeywordFilter(sqlparse.filters.Filter): +class BoldKeywordFilter(): """sqlparse filter to bold SQL keywords""" def process(self, stack, stream): """Process the token stream""" From bab0aee735982fb33b4cad4acf634a714a5ba43e Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:15:45 +0100 Subject: [PATCH 12/34] use thread from six.moves in tests/tests.py --- tests/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index f76f1ab0d..b33b3dd11 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,5 @@ from __future__ import with_statement -import thread +from django.utils.six.moves import _thread from django.conf import settings from django.contrib.auth.models import User @@ -48,7 +48,7 @@ def setUp(self): response = HttpResponse() toolbar = DebugToolbar(request) - DebugToolbarMiddleware.debug_toolbars[thread.get_ident()] = toolbar + DebugToolbarMiddleware.debug_toolbars[_thread.get_ident()] = toolbar self.request = request self.response = response From 417c852896151f5f875045e7847f46bfab274aec Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:21:58 +0100 Subject: [PATCH 13/34] use assertEqual instead of assertEquals in tests --- tests/tests.py | 90 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index b33b3dd11..b5108db7d 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -62,7 +62,7 @@ class DebugToolbarTestCase(BaseTestCase): def test_middleware(self): with Settings(INTERNAL_IPS=['127.0.0.1'], DEBUG=True): resp = self.client.get('/execute_sql/') - self.assertEquals(resp.status_code, 200) + self.assertEqual(resp.status_code, 200) def test_show_toolbar_DEBUG(self): request = rf.get('/') @@ -107,7 +107,7 @@ def test_request_urlconf_string(self): self.assertFalse(isinstance(request.urlconf, basestring)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) - self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') + self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') def test_request_urlconf_string_per_request(self): request = rf.get('/') @@ -122,7 +122,7 @@ def test_request_urlconf_string_per_request(self): self.assertFalse(isinstance(request.urlconf, basestring)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) - self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') + self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') def test_request_urlconf_module(self): request = rf.get('/') @@ -135,7 +135,7 @@ def test_request_urlconf_module(self): self.assertFalse(isinstance(request.urlconf, basestring)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) - self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') + self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') def test_tuple_urlconf(self): request = rf.get('/') @@ -158,27 +158,27 @@ def _resolve_stats(self, path): def test_url_resolving_positional(self): stats = self._resolve_stats('/resolving1/a/b/') - self.assertEquals(stats['view_urlname'], 'positional-resolving') # Django >= 1.3 - self.assertEquals(stats['view_func'], 'tests.views.resolving_view') - self.assertEquals(stats['view_args'], ('a', 'b')) - self.assertEquals(stats['view_kwargs'], {}) + self.assertEqual(stats['view_urlname'], 'positional-resolving') # Django >= 1.3 + self.assertEqual(stats['view_func'], 'tests.views.resolving_view') + self.assertEqual(stats['view_args'], ('a', 'b')) + self.assertEqual(stats['view_kwargs'], {}) def test_url_resolving_named(self): stats = self._resolve_stats('/resolving2/a/b/') - self.assertEquals(stats['view_args'], ()) - self.assertEquals(stats['view_kwargs'], {'arg1': 'a', 'arg2': 'b'}) + self.assertEqual(stats['view_args'], ()) + self.assertEqual(stats['view_kwargs'], {'arg1': 'a', 'arg2': 'b'}) def test_url_resolving_mixed(self): stats = self._resolve_stats('/resolving3/a/') - self.assertEquals(stats['view_args'], ('a',)) - self.assertEquals(stats['view_kwargs'], {'arg2': 'default'}) + self.assertEqual(stats['view_args'], ('a',)) + self.assertEqual(stats['view_kwargs'], {'arg2': 'default'}) def test_url_resolving_bad(self): stats = self._resolve_stats('/non-existing-url/') - self.assertEquals(stats['view_urlname'], 'None') - self.assertEquals(stats['view_args'], 'None') - self.assertEquals(stats['view_kwargs'], 'None') - self.assertEquals(stats['view_func'], '') + self.assertEqual(stats['view_urlname'], 'None') + self.assertEqual(stats['view_args'], 'None') + self.assertEqual(stats['view_kwargs'], 'None') + self.assertEqual(stats['view_func'], '') class DebugToolbarNameFromObjectTest(BaseTestCase): @@ -186,30 +186,30 @@ def test_func(self): def x(): return 1 res = get_name_from_obj(x) - self.assertEquals(res, 'tests.tests.x') + self.assertEqual(res, 'tests.tests.x') def test_lambda(self): res = get_name_from_obj(lambda: 1) - self.assertEquals(res, 'tests.tests.') + self.assertEqual(res, 'tests.tests.') def test_class(self): class A: pass res = get_name_from_obj(A) - self.assertEquals(res, 'tests.tests.A') + self.assertEqual(res, 'tests.tests.A') class SQLPanelTestCase(BaseTestCase): def test_recording(self): panel = self.toolbar.get_panel(SQLDebugPanel) - self.assertEquals(len(panel._queries), 0) + self.assertEqual(len(panel._queries), 0) list(User.objects.all()) # ensure query was logged - self.assertEquals(len(panel._queries), 1) + self.assertEqual(len(panel._queries), 1) query = panel._queries[0] - self.assertEquals(query[0], 'default') + self.assertEqual(query[0], 'default') self.assertTrue('sql' in query[1]) self.assertTrue('duration' in query[1]) self.assertTrue('stacktrace' in query[1]) @@ -232,21 +232,21 @@ def test_erroneous_query(self): def test_disable_stacktraces(self): panel = self.toolbar.get_panel(SQLDebugPanel) - self.assertEquals(len(panel._queries), 0) + self.assertEqual(len(panel._queries), 0) with Settings(DEBUG_TOOLBAR_CONFIG={'ENABLE_STACKTRACES': False}): list(User.objects.all()) # ensure query was logged - self.assertEquals(len(panel._queries), 1) + self.assertEqual(len(panel._queries), 1) query = panel._queries[0] - self.assertEquals(query[0], 'default') + self.assertEqual(query[0], 'default') self.assertTrue('sql' in query[1]) self.assertTrue('duration' in query[1]) self.assertTrue('stacktrace' in query[1]) # ensure the stacktrace is empty - self.assertEquals([], query[1]['stacktrace']) + self.assertEqual([], query[1]['stacktrace']) class TemplatePanelTestCase(BaseTestCase): @@ -262,7 +262,7 @@ def test_queryset_hook(self): }) t.render(c) # ensure the query was NOT logged - self.assertEquals(len(sql_panel._queries), 0) + self.assertEqual(len(sql_panel._queries), 0) ctx = template_panel.templates[0]['context'][0] self.assertIn('<>', ctx) self.assertIn('<>', ctx) @@ -290,23 +290,23 @@ def test(**kwargs): foo.update(kwargs) self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEquals(len(callbacks['before']), 1) + self.assertEqual(len(callbacks['before']), 1) module_func('hi', foo='bar') self.assertTrue('sender' in foo, foo) # best we can do - self.assertEquals(foo['sender'].__name__, 'module_func') + self.assertEqual(foo['sender'].__name__, 'module_func') self.assertTrue('start' in foo, foo) self.assertTrue(foo['start'] > 0) self.assertTrue('stop' not in foo, foo) self.assertTrue('args' in foo, foo) self.assertTrue(len(foo['args']), 1) - self.assertEquals(foo['args'][0], 'hi') + self.assertEqual(foo['args'][0], 'hi') self.assertTrue('kwargs' in foo, foo) self.assertTrue(len(foo['kwargs']), 1) self.assertTrue('foo' in foo['kwargs']) - self.assertEquals(foo['kwargs']['foo'], 'bar') + self.assertEqual(foo['kwargs']['foo'], 'bar') callbacks['before'] = {} @@ -315,23 +315,23 @@ def test(**kwargs): foo.update(kwargs) self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEquals(len(callbacks['before']), 1) + self.assertEqual(len(callbacks['before']), 1) self.class_func('hello', foo='bar') self.assertTrue('sender' in foo, foo) # best we can do - self.assertEquals(foo['sender'].__name__, 'class_func') + self.assertEqual(foo['sender'].__name__, 'class_func') self.assertTrue('start' in foo, foo) self.assertTrue(foo['start'] > 0) self.assertTrue('stop' not in foo, foo) self.assertTrue('args' in foo, foo) self.assertTrue(len(foo['args']), 2) - self.assertEquals(foo['args'][1], 'hello') + self.assertEqual(foo['args'][1], 'hello') self.assertTrue('kwargs' in foo, foo) self.assertTrue(len(foo['kwargs']), 1) self.assertTrue('foo' in foo['kwargs']) - self.assertEquals(foo['kwargs']['foo'], 'bar') + self.assertEqual(foo['kwargs']['foo'], 'bar') callbacks['before'] = {} @@ -340,13 +340,13 @@ def test(**kwargs): foo.update(kwargs) self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) - self.assertEquals(len(callbacks['before']), 1) + self.assertEqual(len(callbacks['before']), 1) TrackingTestCase.class_method() self.assertTrue('sender' in foo, foo) # best we can do - self.assertEquals(foo['sender'].__name__, 'class_method') + self.assertEqual(foo['sender'].__name__, 'class_method') self.assertTrue('start' in foo, foo) self.assertTrue('stop' not in foo, foo) self.assertTrue('args' in foo, foo) @@ -359,24 +359,24 @@ def test(**kwargs): foo.update(kwargs) self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEquals(len(callbacks['after']), 1) + self.assertEqual(len(callbacks['after']), 1) module_func('hi', foo='bar') self.assertTrue('sender' in foo, foo) # best we can do - self.assertEquals(foo['sender'].__name__, 'module_func') + self.assertEqual(foo['sender'].__name__, 'module_func') self.assertTrue('start' in foo, foo) self.assertTrue(foo['start'] > 0) self.assertTrue('stop' in foo, foo) self.assertTrue(foo['stop'] > foo['start']) self.assertTrue('args' in foo, foo) self.assertTrue(len(foo['args']), 1) - self.assertEquals(foo['args'][0], 'hi') + self.assertEqual(foo['args'][0], 'hi') self.assertTrue('kwargs' in foo, foo) self.assertTrue(len(foo['kwargs']), 1) self.assertTrue('foo' in foo['kwargs']) - self.assertEquals(foo['kwargs']['foo'], 'bar') + self.assertEqual(foo['kwargs']['foo'], 'bar') callbacks['after'] = {} @@ -385,21 +385,21 @@ def test(**kwargs): foo.update(kwargs) self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEquals(len(callbacks['after']), 1) + self.assertEqual(len(callbacks['after']), 1) self.class_func('hello', foo='bar') self.assertTrue('sender' in foo, foo) # best we can do - self.assertEquals(foo['sender'].__name__, 'class_func') + self.assertEqual(foo['sender'].__name__, 'class_func') self.assertTrue('start' in foo, foo) self.assertTrue(foo['start'] > 0) self.assertTrue('stop' in foo, foo) self.assertTrue(foo['stop'] > foo['start']) self.assertTrue('args' in foo, foo) self.assertTrue(len(foo['args']), 2) - self.assertEquals(foo['args'][1], 'hello') + self.assertEqual(foo['args'][1], 'hello') self.assertTrue('kwargs' in foo, foo) self.assertTrue(len(foo['kwargs']), 1) self.assertTrue('foo' in foo['kwargs']) - self.assertEquals(foo['kwargs']['foo'], 'bar') + self.assertEqual(foo['kwargs']['foo'], 'bar') From 3929d60c81431e4781027aa573ee6e3d40489a2e Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:38:06 +0100 Subject: [PATCH 14/34] use iteritems from six, or items in templates --- debug_toolbar/panels/cache.py | 3 ++- debug_toolbar/panels/profiling.py | 5 +++-- debug_toolbar/templates/debug_toolbar/panels/headers.html | 2 +- debug_toolbar/templates/debug_toolbar/panels/templates.html | 2 +- debug_toolbar/templates/debug_toolbar/panels/versions.html | 2 +- debug_toolbar/utils/tracking/db.py | 3 ++- tests/tests.py | 5 +++-- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index e25f08b8e..e21b50229 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -10,6 +10,7 @@ from django.template import Node from django.utils.datastructures import SortedDict from django.utils.translation import ugettext_lazy as _, ungettext +from django.utils.six import iteritems from debug_toolbar.panels import DebugPanel from debug_toolbar.utils import (tidy_stacktrace, render_stacktrace, @@ -166,7 +167,7 @@ def _store_call_info(self, sender, name=None, time_taken=0, else: self.hits += 1 elif name == 'get_many': - for key, value in return_value.iteritems(): + for key, value in iteritems(return_value): if value is None: self.misses += 1 else: diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index f21c34555..1658ee298 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,5 +1,6 @@ from __future__ import division +from django.utils.six import iteritems from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe from debug_toolbar.panels import DebugPanel @@ -23,7 +24,7 @@ class DjangoDebugToolbarStats(Stats): def get_root_func(self): if self.__root is None: - for func, (cc, nc, tt, ct, callers) in self.stats.iteritems(): + for func, (cc, nc, tt, ct, callers) in iteritems(self.stats): if len(callers) == 0: self.__root = func break @@ -80,7 +81,7 @@ def subfuncs(self): i = 0 h, s, v = self.hsv count = len(self.statobj.all_callees[self.func]) - for func, stats in self.statobj.all_callees[self.func].iteritems(): + for func, stats in iteritems(self.statobj.all_callees[self.func]): i += 1 h1 = h + (i / count) / (self.depth + 1) if stats[3] == 0: diff --git a/debug_toolbar/templates/debug_toolbar/panels/headers.html b/debug_toolbar/templates/debug_toolbar/panels/headers.html index f25105602..981b8479b 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/headers.html +++ b/debug_toolbar/templates/debug_toolbar/panels/headers.html @@ -7,7 +7,7 @@ - {% for key, value in headers.iteritems %} + {% for key, value in headers.items %} {{ key|escape }} {{ value|escape }} diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html index bde09a951..7e44a460e 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/templates.html +++ b/debug_toolbar/templates/debug_toolbar/panels/templates.html @@ -31,7 +31,7 @@

{% blocktrans count templates|length as template_count %}Template{% plural %

{% blocktrans count context_processors|length as context_processors_count %}Context processor{% plural %}Context processors{% endblocktrans %}

{% if context_processors %}
-{% for key, value in context_processors.iteritems %} +{% for key, value in context_processors.items %}
{{ key|escape }}
diff --git a/debug_toolbar/templates/debug_toolbar/panels/versions.html b/debug_toolbar/templates/debug_toolbar/panels/versions.html index 283d0523f..2c614f11c 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/versions.html +++ b/debug_toolbar/templates/debug_toolbar/panels/versions.html @@ -7,7 +7,7 @@ - {% for package, version in versions.iteritems %} + {% for package, version in versions.items %} {{ package }} {{ version }} diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 685aa8ab2..a6b4f587b 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -6,6 +6,7 @@ from django.conf import settings from django.template import Node from django.utils.encoding import force_text, smart_str +from django.utils.six import iteritems from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -88,7 +89,7 @@ def _quote_expr(self, element): def _quote_params(self, params): if isinstance(params, dict): return dict((key, self._quote_expr(value)) - for key, value in params.iteritems()) + for key, value in iteritems(params)) return map(self._quote_expr, params) def _decode(self, param): diff --git a/tests/tests.py b/tests/tests.py index b5108db7d..3e298cf15 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,6 @@ from __future__ import with_statement from django.utils.six.moves import _thread +from django.utils.six import iteritems from django.conf import settings from django.contrib.auth.models import User @@ -30,12 +31,12 @@ def __init__(self, **overrides): self._orig = {} def __enter__(self): - for k, v in self.overrides.iteritems(): + for k, v in iteritems(self.overrides): self._orig[k] = getattr(settings, k, self.NotDefined) setattr(settings, k, v) def __exit__(self, exc_type, exc_value, traceback): - for k, v in self._orig.iteritems(): + for k, v in iteritems(self._orig): if v is self.NotDefined: delattr(settings, k) else: From 068a6c1df1eefbf3b7a20069c5013abfd00a4400 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:44:35 +0100 Subject: [PATCH 15/34] use string_types from six instead of basestring --- debug_toolbar/middleware.py | 3 ++- debug_toolbar/utils/tracking/db.py | 4 ++-- tests/tests.py | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 6654bc307..f60684e83 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -9,6 +9,7 @@ from django.shortcuts import render_to_response from django.utils.encoding import smart_text from django.utils.importlib import import_module +from django.utils.six import string_types import debug_toolbar.urls from debug_toolbar.toolbar.loader import DebugToolbar @@ -76,7 +77,7 @@ def process_request(self, request): __traceback_hide__ = True if self.show_toolbar(request): urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) - if isinstance(urlconf, basestring): + if isinstance(urlconf, string_types): urlconf = import_module(getattr(request, 'urlconf', settings.ROOT_URLCONF)) if urlconf not in self._urlconfs: diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index a6b4f587b..5f718fdc3 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -6,7 +6,7 @@ from django.conf import settings from django.template import Node from django.utils.encoding import force_text, smart_str -from django.utils.six import iteritems +from django.utils.six import iteritems, string_types from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -80,7 +80,7 @@ def __init__(self, cursor, db, logger): self.logger = logger def _quote_expr(self, element): - if isinstance(element, basestring): + if isinstance(element, string_types): element = element.replace("'", "''") return "'%s'" % element else: diff --git a/tests/tests.py b/tests/tests.py index 3e298cf15..105a78955 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,6 +1,6 @@ from __future__ import with_statement from django.utils.six.moves import _thread -from django.utils.six import iteritems +from django.utils.six import iteritems, string_types from django.conf import settings from django.contrib.auth.models import User @@ -105,7 +105,7 @@ def test_request_urlconf_string(self): with Settings(INTERNAL_IPS=['127.0.0.1'], DEBUG=True): middleware.process_request(request) - self.assertFalse(isinstance(request.urlconf, basestring)) + self.assertFalse(isinstance(request.urlconf, string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -120,7 +120,7 @@ def test_request_urlconf_string_per_request(self): request.urlconf = 'tests.urls' middleware.process_request(request) - self.assertFalse(isinstance(request.urlconf, basestring)) + self.assertFalse(isinstance(request.urlconf, string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -133,7 +133,7 @@ def test_request_urlconf_module(self): with Settings(INTERNAL_IPS=['127.0.0.1'], DEBUG=True): middleware.process_request(request) - self.assertFalse(isinstance(request.urlconf, basestring)) + self.assertFalse(isinstance(request.urlconf, string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEqual(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -146,7 +146,7 @@ def test_tuple_urlconf(self): middleware = DebugToolbarMiddleware() with Settings(INTERNAL_IPS=['127.0.0.1'], DEBUG=True): middleware.process_request(request) - self.assertFalse(isinstance(request.urlconf, basestring)) + self.assertFalse(isinstance(request.urlconf, string_types)) def _resolve_stats(self, path): # takes stats from RequestVars panel From 7cb3cb0c43cee025c96226d7025311d0edf07989 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Apr 2013 22:48:50 +0100 Subject: [PATCH 16/34] use iterkeys and itervalues from six, or .keys and .values in templates --- debug_toolbar/panels/request_vars.py | 3 ++- debug_toolbar/panels/sql.py | 3 ++- debug_toolbar/templates/debug_toolbar/panels/cache.html | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/panels/request_vars.py b/debug_toolbar/panels/request_vars.py index ade23f342..d9be3ce2b 100644 --- a/debug_toolbar/panels/request_vars.py +++ b/debug_toolbar/panels/request_vars.py @@ -1,6 +1,7 @@ from django.core.urlresolvers import resolve from django.http import Http404 from django.utils.translation import ugettext_lazy as _ +from django.utils.six import iterkeys from debug_toolbar.panels import DebugPanel from debug_toolbar.utils import get_name_from_obj @@ -53,5 +54,5 @@ def process_response(self, request, response): if hasattr(self.request, 'session'): self.record_stats({ 'session': [(k, self.request.session.get(k)) - for k in self.request.session.iterkeys()] + for k in iterkeys(self.request.session)] }) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index c9eb8f7a5..44c007244 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -5,6 +5,7 @@ from django.db.backends import BaseDatabaseWrapper from django.utils.html import escape from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __ +from django.utils.six import itervalues from debug_toolbar.utils.compat.db import connections from debug_toolbar.middleware import DebugToolbarMiddleware @@ -135,7 +136,7 @@ def process_response(self, request, response): if self._queries: width_ratio_tally = 0 factor = int(256.0 / (len(self._databases) * 2.5)) - for n, db in enumerate(self._databases.itervalues()): + for n, db in enumerate(itervalues(self._databases)): rgb = [0, 0, 0] color = n % 3 rgb[color] = 256 - n / 3 * factor diff --git a/debug_toolbar/templates/debug_toolbar/panels/cache.html b/debug_toolbar/templates/debug_toolbar/panels/cache.html index 889177a33..f9cebca64 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/cache.html +++ b/debug_toolbar/templates/debug_toolbar/panels/cache.html @@ -22,14 +22,14 @@

{% trans "Commands" %}

- {% for name in counts.iterkeys %} + {% for name in counts.keys %} {% endfor %} - {% for value in counts.itervalues %} + {% for value in counts.values %} {% endfor %} From c2485f5f99969fcfe9fc1f2b56f5862d3ab247e5 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 15:17:50 +0100 Subject: [PATCH 17/34] deprecate functions unusable on Python3 --- debug_toolbar/utils/__init__.py | 35 +++++++++++++++++++++++- debug_toolbar/utils/tracking/__init__.py | 7 +++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index 292f51d15..9bc3d6968 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -2,9 +2,12 @@ import os.path import django import sys +import warnings +from functools import wraps from django.conf import settings from django.views.debug import linebreak_iter +from django.utils import six from django.utils.six.moves import socketserver from django.utils.html import escape from django.utils.safestring import mark_safe @@ -40,7 +43,7 @@ def tidy_stacktrace(stack): # inspection. if '__traceback_hide__' in frame.f_locals: continue - if hide_django_sql and django_path in s_path and not 'django/contrib' in s_path: + if hide_django_sql and django_path in s_path and not 'django/contrib' in s_path: continue if socketserver_path in s_path: continue @@ -177,3 +180,33 @@ def get_stack(context=1): framelist.append((frame,) + getframeinfo(frame, context)) frame = frame.f_back return framelist + + +class deprecated(object): + """This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emmitted + when the function is used.""" + + def __init__(self, reason="Call to deprecated function: {name}"): + self.reason = reason + + def __call__(self, func): + @wraps(func) + def wrapper(*args, **kwargs): + warnings.warn( + self.reason.format( + name=getattr(func, '__qualname__', func.__name__) + ), + category=DeprecationWarning) + return func(*args, **kwargs) + return wrapper + + +if six.PY3: + def not_on_py3(func): + @wraps(func) + def wrapper(*args, **kwargs): + raise NotImplementedError() + return wrapper +else: + not_on_py3 = deprecated(reason="{name} will not be supported on Python 3") diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index 9a5927394..b72fbc7aa 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -3,7 +3,10 @@ import types from django.utils.importlib import import_module +from debug_toolbar.utils import not_on_py3 + +@not_on_py3 def post_dispatch(func): def wrapped(callback): register_hook(func, 'after', callback) @@ -11,6 +14,7 @@ def wrapped(callback): return wrapped +@not_on_py3 def pre_dispatch(func): def wrapped(callback): register_hook(func, 'before', callback) @@ -18,6 +22,7 @@ def wrapped(callback): return wrapped +@not_on_py3 def replace_call(func): def inner(callback): def wrapped(*args, **kwargs): @@ -42,6 +47,7 @@ def fire_hook(hook, sender, **kwargs): logging.exception(e) +@not_on_py3 def _replace_function(func, wrapped): if isinstance(func, types.FunctionType): if func.__module__ == '__builtin__': @@ -64,6 +70,7 @@ def _replace_function(func, wrapped): } +@not_on_py3 def register_hook(func, hook, callback): """ def myhook(sender, args, kwargs): From 61bbd7f335014476ac640d04f313d212fd7cd6c0 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 22:22:13 +0100 Subject: [PATCH 18/34] use more simpler, more portable monkey_patch_call func in tracking --- debug_toolbar/panels/sql.py | 6 +- debug_toolbar/utils/tracking/__init__.py | 17 ++ tests/tests.py | 264 ++++++++++++----------- 3 files changed, 154 insertions(+), 133 deletions(-) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 44c007244..2dff03db5 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -12,11 +12,11 @@ from debug_toolbar.panels import DebugPanel from debug_toolbar.utils import render_stacktrace from debug_toolbar.utils.tracking.db import CursorWrapper -from debug_toolbar.utils.tracking import replace_call +from debug_toolbar.utils.tracking import monkey_patch_call # Inject our tracking cursor -@replace_call(BaseDatabaseWrapper.cursor) +@monkey_patch_call(BaseDatabaseWrapper, 'cursor') def cursor(func, self): result = func(self) @@ -27,6 +27,8 @@ def cursor(func, self): return CursorWrapper(result, self, logger=logger) +BaseDatabaseWrapper.cursor = cursor + def get_isolation_level_display(engine, level): if engine == 'psycopg2': diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index b72fbc7aa..c0ddf1ca5 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -38,6 +38,23 @@ def wrapped(*args, **kwargs): return inner +def monkey_patch_call(obj, attr): + func = getattr(obj, attr) + + def inner(callback): + def wrapped(*args, **kwargs): + return callback(func, *args, **kwargs) + actual = getattr(func, '__wrapped__', func) + wrapped.__wrapped__ = actual + wrapped.__doc__ = getattr(actual, '__doc__', None) + wrapped.__name__ = actual.__name__ + if hasattr(actual, '__self__'): + setattr(wrapped, '__self__', actual.__self__) + setattr(obj, attr, wrapped) + return wrapped + return inner + + def fire_hook(hook, sender, **kwargs): try: for callback in callbacks[hook].get(id(sender), []): diff --git a/tests/tests.py b/tests/tests.py index 105a78955..a7d414085 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,4 +1,6 @@ from __future__ import with_statement + +from django.utils import six from django.utils.six.moves import _thread from django.utils.six import iteritems, string_types @@ -273,134 +275,134 @@ def module_func(*args, **kwargs): """Used by dispatch tests""" return 'blah' - -class TrackingTestCase(BaseTestCase): - @classmethod - def class_method(cls, *args, **kwargs): - return 'blah' - - def class_func(self, *args, **kwargs): - """Used by dispatch tests""" - return 'blah' - - def test_pre_hook(self): - foo = {} - - @pre_dispatch(module_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - module_func('hi', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'module_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 1) - self.assertEqual(foo['args'][0], 'hi') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['before'] = {} - - @pre_dispatch(TrackingTestCase.class_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - self.class_func('hello', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 2) - self.assertEqual(foo['args'][1], 'hello') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['before'] = {} - - @pre_dispatch(TrackingTestCase.class_method) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - TrackingTestCase.class_method() - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_method') - self.assertTrue('start' in foo, foo) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - - def test_post_hook(self): - foo = {} - - @post_dispatch(module_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEqual(len(callbacks['after']), 1) - - module_func('hi', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'module_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' in foo, foo) - self.assertTrue(foo['stop'] > foo['start']) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 1) - self.assertEqual(foo['args'][0], 'hi') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['after'] = {} - - @post_dispatch(TrackingTestCase.class_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEqual(len(callbacks['after']), 1) - - self.class_func('hello', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' in foo, foo) - self.assertTrue(foo['stop'] > foo['start']) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 2) - self.assertEqual(foo['args'][1], 'hello') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') +if not six.PY3: + class TrackingTestCase(BaseTestCase): + @classmethod + def class_method(cls, *args, **kwargs): + return 'blah' + + def class_func(self, *args, **kwargs): + """Used by dispatch tests""" + return 'blah' + + def test_pre_hook(self): + foo = {} + + @pre_dispatch(module_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(module_func, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + module_func('hi', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'module_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 1) + self.assertEqual(foo['args'][0], 'hi') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['before'] = {} + + @pre_dispatch(TrackingTestCase.class_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + self.class_func('hello', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 2) + self.assertEqual(foo['args'][1], 'hello') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['before'] = {} + + @pre_dispatch(TrackingTestCase.class_method) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + TrackingTestCase.class_method() + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_method') + self.assertTrue('start' in foo, foo) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + + def test_post_hook(self): + foo = {} + + @post_dispatch(module_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(module_func, '__wrapped__')) + self.assertEqual(len(callbacks['after']), 1) + + module_func('hi', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'module_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' in foo, foo) + self.assertTrue(foo['stop'] > foo['start']) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 1) + self.assertEqual(foo['args'][0], 'hi') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['after'] = {} + + @post_dispatch(TrackingTestCase.class_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) + self.assertEqual(len(callbacks['after']), 1) + + self.class_func('hello', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' in foo, foo) + self.assertTrue(foo['stop'] > foo['start']) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 2) + self.assertEqual(foo['args'][1], 'hello') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') From cdfa80341d412907c74738507a6897f193a9cf51 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 22:33:50 +0100 Subject: [PATCH 19/34] use map from six --- debug_toolbar/utils/__init__.py | 2 +- debug_toolbar/utils/tracking/db.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index 9bc3d6968..f566a5226 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -8,7 +8,7 @@ from django.conf import settings from django.views.debug import linebreak_iter from django.utils import six -from django.utils.six.moves import socketserver +from django.utils.six.moves import socketserver, map from django.utils.html import escape from django.utils.safestring import mark_safe diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 5f718fdc3..342e80271 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -7,6 +7,7 @@ from django.template import Node from django.utils.encoding import force_text, smart_str from django.utils.six import iteritems, string_types +from django.utils.six.moves import map from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -90,7 +91,7 @@ def _quote_params(self, params): if isinstance(params, dict): return dict((key, self._quote_expr(value)) for key, value in iteritems(params)) - return map(self._quote_expr, params) + return list(map(self._quote_expr, params)) def _decode(self, param): try: @@ -121,7 +122,7 @@ def execute(self, sql, params=()): stacktrace = [] _params = '' try: - _params = json.dumps(map(self._decode, params)) + _params = json.dumps(list(map(self._decode, params))) except Exception: pass # object not JSON serializable From 494f4215acb3e100a917e05c4a19bbfbbe374d85 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 23:00:06 +0100 Subject: [PATCH 20/34] force more things to bytes before hashing --- debug_toolbar/utils/tracking/db.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 342e80271..c31024881 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -5,7 +5,7 @@ from django.conf import settings from django.template import Node -from django.utils.encoding import force_text, smart_str +from django.utils.encoding import force_text, smart_bytes from django.utils.six import iteritems, string_types from django.utils.six.moves import map @@ -156,9 +156,9 @@ def execute(self, sql, params=()): 'duration': duration, 'raw_sql': sql, 'params': _params, - 'hash': sha1(settings.SECRET_KEY \ - + smart_str(sql) \ - + _params).hexdigest(), + 'hash': sha1(smart_bytes(settings.SECRET_KEY) \ + + smart_bytes(sql) \ + + smart_bytes(_params)).hexdigest(), 'stacktrace': stacktrace, 'start_time': start, 'stop_time': stop, From a30c83f20e9c57d9f533c07afa462f91b6b896cf Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 23:00:24 +0100 Subject: [PATCH 21/34] use text_type instead of unicode --- debug_toolbar/utils/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index f566a5226..33838dad4 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -9,6 +9,7 @@ from django.views.debug import linebreak_iter from django.utils import six from django.utils.six.moves import socketserver, map +from django.utils.six import text_type from django.utils.html import escape from django.utils.safestring import mark_safe @@ -59,7 +60,7 @@ 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)) + params_dict = dict((text_type(idx), v) for idx, v in enumerate(params)) try: stacktrace.append(u'%(0)s/' u'%(1)s' From 6e50c9fc0b2923deb4eacf096f13987858ba0916 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 9 Apr 2013 23:15:48 +0100 Subject: [PATCH 22/34] add DJANGO_VERSION=1.5 to allow_failures --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0c4b4ddc9..1b5bd8c35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ matrix: allow_failures: - python: 3.2 - python: 3.3 + - env: DJANGO_VERSION=1.5 install: - pip install Django==$DJANGO_VERSION - python setup.py install From 47ea6769e46f1973681c3f77207b65fc44868d42 Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Sun, 14 Apr 2013 23:20:57 +1200 Subject: [PATCH 23/34] fix print function => python2.6.5 --- debug_toolbar/management/commands/debugsqlshell.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index 68ad3d9d9..f01466b90 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -1,3 +1,4 @@ +from __future__ import print_function from datetime import datetime from django.db.backends import util @@ -15,8 +16,8 @@ def execute(self, sql, params=()): finally: raw_sql = self.db.ops.last_executed_query(self.cursor, sql, params) execution_time = datetime.now() - starttime - print sqlparse.format(raw_sql, reindent=True), - print ' [%.2fms]' % (ms_from_timedelta(execution_time),) - print + print( sqlparse.format(raw_sql, reindent=True),) + print( ' [%.2fms]' % (ms_from_timedelta(execution_time),)) + print() util.CursorDebugWrapper = PrintQueryWrapper From 76a8159a552c5b99a284dbc205364e2cf5357385 Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Mon, 15 Apr 2013 01:28:08 +1200 Subject: [PATCH 24/34] Update profiling.py, attr. "func_closure" ( py2.6) change attributes "func_code" -> "__code__" and "func_closure" -> "__closure__" ( => python2.6 ) --- debug_toolbar/panels/profiling.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 1658ee298..c7571c22c 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from __future__ import division from django.utils.six import iteritems @@ -156,12 +157,12 @@ def title(self): return _('Profiling') def _unwrap_closure_and_profile(self, func): - if not hasattr(func, 'func_code'): + if not hasattr(func, '__code__'): return self.line_profiler.add_function(func) - if func.func_closure: - for cell in func.func_closure: - if hasattr(cell.cell_contents, 'func_code'): + if func.__closure__: + for cell in func.__closure__: + if hasattr(cell.cell_contents, '__code__'): self._unwrap_closure_and_profile(cell.cell_contents) def process_view(self, request, view_func, view_args, view_kwargs): From f5d4cde3313b77cb6189184d9eed3a868778d5be Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Mon, 15 Apr 2013 03:06:15 +1200 Subject: [PATCH 25/34] remove u'' prefix, import __future__ unicode_lit.. Remove u'' prefix, and import __future__ unicode_literals. Now all strings without the u'' - are Unicode. >>> from __future__ import unicode_literals >>> hasattr(str, '__name__') True --- debug_toolbar/utils/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index 33838dad4..532cf6b18 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import inspect import os.path import django @@ -62,11 +63,11 @@ def render_stacktrace(trace): params = map(escape, frame[0].rsplit(os.path.sep, 1) + list(frame[1:])) params_dict = dict((text_type(idx), v) for idx, v in enumerate(params)) try: - stacktrace.append(u'%(0)s/' - u'%(1)s' - u' in %(3)s' - u'(%(2)s)\n' - u' %(4)s' + stacktrace.append('%(0)s/' + '%(1)s' + ' in %(3)s' + '(%(2)s)\n' + ' %(4)s' % params_dict) except KeyError: # This frame doesn't have the expected format, so skip it and move on to the next one From 0e6983ac5bbddeef9f120e4aced86a6cb4fe6bea Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 14 Apr 2013 20:40:04 +0100 Subject: [PATCH 26/34] use force_bytes to encode before hashing --- debug_toolbar/views.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index 4b4ebc980..31b5db79f 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -12,6 +12,8 @@ from debug_toolbar.utils.compat.db import connections +from django.utils.encoding import force_bytes + try: import json except ImportError: # python < 2.6 @@ -23,6 +25,10 @@ from django.utils.hashcompat import sha_constructor as sha1 +def sha1_hexdigest(text_or_bytes): + return sha1(force_bytes(text_or_bytes)).hexdigest() + + class InvalidSQLError(Exception): def __init__(self, value): self.value = value @@ -45,7 +51,7 @@ def sql_select(request): sql = request.GET.get('sql', '') params = request.GET.get('params', '') alias = request.GET.get('alias', 'default') - hash = sha1(settings.SECRET_KEY + sql + params).hexdigest() + hash = sha1_hexdigest(settings.SECRET_KEY + sql + params) if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert if sql.lower().strip().startswith('select'): @@ -80,7 +86,7 @@ def sql_explain(request): sql = request.GET.get('sql', '') params = request.GET.get('params', '') alias = request.GET.get('alias', 'default') - hash = sha1(settings.SECRET_KEY + sql + params).hexdigest() + hash = sha1_hexdigest(settings.SECRET_KEY + sql + params) if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert if sql.lower().strip().startswith('select'): @@ -128,7 +134,7 @@ def sql_profile(request): sql = request.GET.get('sql', '') params = request.GET.get('params', '') alias = request.GET.get('alias', 'default') - hash = sha1(settings.SECRET_KEY + sql + params).hexdigest() + hash = sha1_hexdigest(settings.SECRET_KEY + sql + params) if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert if sql.lower().strip().startswith('select'): From a088ed0df0c42ad04253b6e2c306f3184f1e050f Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Mon, 15 Apr 2013 03:55:46 +1200 Subject: [PATCH 27/34] list expected --- debug_toolbar/toolbar/loader.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index d5e21624e..873241e62 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals """ The main DebugToolbar class that loads and renders the Toolbar. """ @@ -19,7 +20,7 @@ def __init__(self, request): base_url = self.request.META.get('SCRIPT_NAME', '') self.config = { 'INTERCEPT_REDIRECTS': True, - 'MEDIA_URL': u'%s/__debug__/m/' % base_url + 'MEDIA_URL': '%s/__debug__/m/' % base_url } # Check if settings has a DEBUG_TOOLBAR_CONFIG and updated config self.config.update(getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {})) @@ -33,7 +34,7 @@ def __init__(self, request): self.stats = {} def _get_panels(self): - return self._panels.values() + return list(self._panels.values()) panels = property(_get_panels) def get_panel(self, cls): From 3aa8c4c334e8e3b3de55309c311d44024c05f267 Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Mon, 15 Apr 2013 04:00:16 +1200 Subject: [PATCH 28/34] Update middleware.py from __future__ import unicode_literals --- debug_toolbar/middleware.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index f60684e83..7ed30a5d9 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals """ Debug Toolbar middleware """ @@ -48,7 +49,7 @@ def __init__(self): self.show_toolbar = self._show_toolbar # default # The tag to attach the toolbar to - self.tag = u'' + self.tag = '' if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): show_toolbar_callback = settings.DEBUG_TOOLBAR_CONFIG.get( @@ -58,7 +59,7 @@ def __init__(self): tag = settings.DEBUG_TOOLBAR_CONFIG.get('TAG', None) if tag: - self.tag = u'' + self.tag = '' def _show_toolbar(self, request): if getattr(settings, 'TEST', False): From 7a21c54eb9861cc8bdba2c954f1aec18bfc416f0 Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Tue, 16 Apr 2013 00:49:41 +1200 Subject: [PATCH 29/34] cache.py, fix python3.2 support --- debug_toolbar/panels/cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index e21b50229..c3dc5dc7f 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import inspect import sys import time @@ -60,7 +61,7 @@ def __init__(self, cache): self.cache = cache def __repr__(self): - return u"" % self.cache.__repr__() + return "" % self.cache.__repr__() def _get_func_info(self): frame = sys._getframe(3) From fa40aa1079d24158331f30975e9e8c8c13a4c5c9 Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Tue, 16 Apr 2013 20:45:14 +1200 Subject: [PATCH 30/34] Masterbranch can pass all original tests on 1.5ver This commit allows masterbranch to pass all original tests with Django 1.5 (python2). Django 1.5 introduce python's bultins vars "True", "False", "None" to the Context, as part of BaseContext class: """ class BaseContext(object): def __init__(self, dict_=None): self._reset_dicts(dict_) def _reset_dicts(self, value=None): builtins = {'True': True, 'False': False, 'None': None} if value: builtins.update(value) self.dicts = [builtins] """ https://github.com/django/django/commit/93240b7d903af86e135c3b1724a81d2daf31441f So, now we always have List's first element that contains these vars. Should we see this in the django-debug-toolbar? --- tests/tests.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index a7d414085..dd6613ba9 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,5 @@ from __future__ import with_statement - +import django from django.utils import six from django.utils.six.moves import _thread from django.utils.six import iteritems, string_types @@ -266,7 +266,11 @@ def test_queryset_hook(self): t.render(c) # ensure the query was NOT logged self.assertEqual(len(sql_panel._queries), 0) - ctx = template_panel.templates[0]['context'][0] + tvar = template_panel.templates[0]['context'] + if django.VERSION[0:2] >= (1, 5): + ctx = tvar[1] + else: + ctx = tvar[0] self.assertIn('<>', ctx) self.assertIn('<>', ctx) From ee44437ccc6b736e83f5aa944636c4b5711075bc Mon Sep 17 00:00:00 2001 From: Wladimir Diedel Date: Tue, 16 Apr 2013 16:53:02 +1200 Subject: [PATCH 31/34] Update .travis.yml don't allow failures --- .travis.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1b5bd8c35..bdd78d3de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,17 +4,13 @@ python: - "2.7" env: - DJANGO_VERSION=1.4.5 - - DJANGO_VERSION=1.5 + - DJANGO_VERSION=1.5.1 matrix: include: - python: 3.2 - env: DJANGO_VERSION=1.5 + env: DJANGO_VERSION=1.5.1 - python: 3.3 - env: DJANGO_VERSION=1.5 - allow_failures: - - python: 3.2 - - python: 3.3 - - env: DJANGO_VERSION=1.5 + env: DJANGO_VERSION=1.5.1 install: - pip install Django==$DJANGO_VERSION - python setup.py install From fd2bb663387000356a80b7ded2cdedb01c4431a5 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 23 Apr 2013 16:20:19 +0100 Subject: [PATCH 32/34] use unittest.skipIf to skip tests invalid on PY3 --- tests/tests.py | 263 +++++++++++++++++++++++++------------------------ 1 file changed, 132 insertions(+), 131 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index dd6613ba9..653d9b62a 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -279,134 +279,135 @@ def module_func(*args, **kwargs): """Used by dispatch tests""" return 'blah' -if not six.PY3: - class TrackingTestCase(BaseTestCase): - @classmethod - def class_method(cls, *args, **kwargs): - return 'blah' - - def class_func(self, *args, **kwargs): - """Used by dispatch tests""" - return 'blah' - - def test_pre_hook(self): - foo = {} - - @pre_dispatch(module_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - module_func('hi', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'module_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 1) - self.assertEqual(foo['args'][0], 'hi') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['before'] = {} - - @pre_dispatch(TrackingTestCase.class_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - self.class_func('hello', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 2) - self.assertEqual(foo['args'][1], 'hello') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['before'] = {} - - @pre_dispatch(TrackingTestCase.class_method) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) - self.assertEqual(len(callbacks['before']), 1) - - TrackingTestCase.class_method() - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_method') - self.assertTrue('start' in foo, foo) - self.assertTrue('stop' not in foo, foo) - self.assertTrue('args' in foo, foo) - - def test_post_hook(self): - foo = {} - - @post_dispatch(module_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(module_func, '__wrapped__')) - self.assertEqual(len(callbacks['after']), 1) - - module_func('hi', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'module_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' in foo, foo) - self.assertTrue(foo['stop'] > foo['start']) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 1) - self.assertEqual(foo['args'][0], 'hi') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') - - callbacks['after'] = {} - - @post_dispatch(TrackingTestCase.class_func) - def test(**kwargs): - foo.update(kwargs) - - self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) - self.assertEqual(len(callbacks['after']), 1) - - self.class_func('hello', foo='bar') - - self.assertTrue('sender' in foo, foo) - # best we can do - self.assertEqual(foo['sender'].__name__, 'class_func') - self.assertTrue('start' in foo, foo) - self.assertTrue(foo['start'] > 0) - self.assertTrue('stop' in foo, foo) - self.assertTrue(foo['stop'] > foo['start']) - self.assertTrue('args' in foo, foo) - self.assertTrue(len(foo['args']), 2) - self.assertEqual(foo['args'][1], 'hello') - self.assertTrue('kwargs' in foo, foo) - self.assertTrue(len(foo['kwargs']), 1) - self.assertTrue('foo' in foo['kwargs']) - self.assertEqual(foo['kwargs']['foo'], 'bar') + +@unittest.skipIf(six.PY3, "replace_call is not supported on Python 3") +class TrackingTestCase(BaseTestCase): + @classmethod + def class_method(cls, *args, **kwargs): + return 'blah' + + def class_func(self, *args, **kwargs): + """Used by dispatch tests""" + return 'blah' + + def test_pre_hook(self): + foo = {} + + @pre_dispatch(module_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(module_func, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + module_func('hi', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'module_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 1) + self.assertEqual(foo['args'][0], 'hi') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['before'] = {} + + @pre_dispatch(TrackingTestCase.class_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + self.class_func('hello', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 2) + self.assertEqual(foo['args'][1], 'hello') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['before'] = {} + + @pre_dispatch(TrackingTestCase.class_method) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_method, '__wrapped__')) + self.assertEqual(len(callbacks['before']), 1) + + TrackingTestCase.class_method() + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_method') + self.assertTrue('start' in foo, foo) + self.assertTrue('stop' not in foo, foo) + self.assertTrue('args' in foo, foo) + + def test_post_hook(self): + foo = {} + + @post_dispatch(module_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(module_func, '__wrapped__')) + self.assertEqual(len(callbacks['after']), 1) + + module_func('hi', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'module_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' in foo, foo) + self.assertTrue(foo['stop'] > foo['start']) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 1) + self.assertEqual(foo['args'][0], 'hi') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') + + callbacks['after'] = {} + + @post_dispatch(TrackingTestCase.class_func) + def test(**kwargs): + foo.update(kwargs) + + self.assertTrue(hasattr(TrackingTestCase.class_func, '__wrapped__')) + self.assertEqual(len(callbacks['after']), 1) + + self.class_func('hello', foo='bar') + + self.assertTrue('sender' in foo, foo) + # best we can do + self.assertEqual(foo['sender'].__name__, 'class_func') + self.assertTrue('start' in foo, foo) + self.assertTrue(foo['start'] > 0) + self.assertTrue('stop' in foo, foo) + self.assertTrue(foo['stop'] > foo['start']) + self.assertTrue('args' in foo, foo) + self.assertTrue(len(foo['args']), 2) + self.assertEqual(foo['args'][1], 'hello') + self.assertTrue('kwargs' in foo, foo) + self.assertTrue(len(foo['kwargs']), 1) + self.assertTrue('foo' in foo['kwargs']) + self.assertEqual(foo['kwargs']['foo'], 'bar') From 09203c62706769a2b916471c904a23135ad9b65a Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 25 Apr 2013 23:06:56 +0200 Subject: [PATCH 33/34] bump django version requirements to 1.4.5 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6b3208fd2..e150bff0a 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ packages=find_packages(exclude=('tests', 'example')), install_requires=[ 'sqlparse', - 'django>=1.4.2,<1.6', + 'django>=1.4.5,<1.6', ], test_suite='runtests.runtests', include_package_data=True, From 9a924cd6cbaa69fe15fa19ae7f703b8ee51ddb28 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 25 Apr 2013 23:09:59 +0200 Subject: [PATCH 34/34] remove spaces from print functions --- debug_toolbar/management/commands/debugsqlshell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index f01466b90..eb02bf65c 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -16,8 +16,8 @@ def execute(self, sql, params=()): finally: raw_sql = self.db.ops.last_executed_query(self.cursor, sql, params) execution_time = datetime.now() - starttime - print( sqlparse.format(raw_sql, reindent=True),) - print( ' [%.2fms]' % (ms_from_timedelta(execution_time),)) + print(sqlparse.format(raw_sql, reindent=True),) + print(' [%.2fms]' % (ms_from_timedelta(execution_time),)) print() util.CursorDebugWrapper = PrintQueryWrapper
{{ name }}
{{ value }}