From 0747c122d32508d301c187f143ac38749b84fec1 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:02:20 +0200 Subject: [PATCH 01/21] Bump requirements to Django >= 1.4 & Python >= 2.6 --- .travis.yml | 5 ++--- README.rst | 9 ++++++++- setup.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5385b0b5..67b07a492 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,8 @@ python: - "2.6" - "2.7" env: - - DJANGO_VERSION=1.3.7 - - DJANGO_VERSION=1.4.5 - - DJANGO_VERSION=1.5.1 + - DJANGO_VERSION=1.4.8 + - DJANGO_VERSION=1.5.4 install: - pip install Django==$DJANGO_VERSION script: python setup.py test diff --git a/README.rst b/README.rst index 1a3e541b0..1ba2e0690 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,14 @@ There is also one Django management command currently: If you have ideas for other panels please let us know. -* Note: The Debug Toolbar only works on Django 1.3 and newer. +Requirements +============ + +The current version of the Debug Toolbar is 0.9.4. It works on Django 1.3 and +1.4. + +The next version will work on Django 1.4 and 1.5. In addition, it will require +Python 2.6 or later. Installation ============ diff --git a/setup.py b/setup.py index 06cd86f30..ced3b2d8f 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.6', + 'django>=1.4,<1.6', ], test_suite='runtests.runtests', include_package_data=True, From b5d7470684c6943a1d00c4f6ce9aa2f28c662f19 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 21:58:11 +0200 Subject: [PATCH 02/21] Add Python 3 to tox configurations. Tests are obviously failing at this point. --- .travis.yml | 8 ++++++++ tox.ini | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 67b07a492..f1d2e290d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,17 @@ language: python python: - "2.6" - "2.7" + - "3.2" + - "3.3" env: - DJANGO_VERSION=1.4.8 - DJANGO_VERSION=1.5.4 +matrix: + exclude: + - python: "3.2" + env: DJANGO_VERSION=1.4.8 + - python: "3.3" + env: DJANGO_VERSION=1.4.8 install: - pip install Django==$DJANGO_VERSION script: python setup.py test diff --git a/tox.ini b/tox.ini index 8e37e4300..74765b37b 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,9 @@ envlist = py26-django14, py27-django14, py26-django15, - py27-django15 + py27-django15, + py32-django15, + py33-django15 [testenv] commands = python runtests.py @@ -23,3 +25,11 @@ deps = Django>=1.5,<1.6 [testenv:py27-django15] basepython = python2.7 deps = Django>=1.5,<1.6 + +[testenv:py32-django15] +basepython = python3.2 +deps = Django>=1.5,<1.6 + +[testenv:py33-django15] +basepython = python3.3 +deps = Django>=1.5,<1.6 From 856a5d43596462c1cf87f04d2edad5b55780a1d8 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:08:43 +0200 Subject: [PATCH 03/21] Remove unused imports. --- debug_toolbar/management/commands/debugsqlshell.py | 1 - debug_toolbar/toolbar/loader.py | 3 --- debug_toolbar/utils/tracking/db.py | 13 ++----------- debug_toolbar/views.py | 10 ---------- example/urls.py | 1 - 5 files changed, 2 insertions(+), 26 deletions(-) diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index 1774a76d2..c0e1fc1d4 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -1,7 +1,6 @@ from datetime import datetime from django.db.backends import util -from django.core.management.commands.shell import Command from debug_toolbar.utils import ms_from_timedelta, sqlparse diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index 570111848..c9d221353 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -1,14 +1,11 @@ """ The main DebugToolbar class that loads and renders the Toolbar. """ -import os -import os.path from django.conf import settings from django.template.loader import render_to_string from django.utils.datastructures import SortedDict from django.utils.importlib import import_module -from django.utils.safestring import mark_safe class DebugToolbar(object): diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index d64998476..1906cb212 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -1,25 +1,16 @@ import sys from datetime import datetime +import json from threading import local 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_unicode from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack -from debug_toolbar.utils.compat.db import connections -try: - import json -except ImportError: # python < 2.6 - from django.utils import simplejson as json - -try: - from hashlib import sha1 -except ImportError: # python < 2.5 - from django.utils.hashcompat import sha_constructor as sha1 # TODO:This should be set in the toolbar loader as a default and panels should # get a copy of the toolbar object with access to its config dictionary diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index ee650ba57..1445360ad 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -8,16 +8,6 @@ from django.shortcuts import render_to_response from django.views.decorators.csrf import csrf_exempt -try: - import json -except ImportError: # python < 2.6 - from django.utils import simplejson as json - -try: - from hashlib import sha1 -except ImportError: # python < 2.5 - from django.utils.hashcompat import sha_constructor as sha1 - from debug_toolbar.forms import SQLSelectForm diff --git a/example/urls.py b/example/urls.py index 7c62fa39b..7b3ff3597 100644 --- a/example/urls.py +++ b/example/urls.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.conf.urls import patterns, include from django.contrib import admin from django.views.generic import TemplateView From 0366b0110408f1441c9450a91d74b80a8f1f39c1 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:14:41 +0200 Subject: [PATCH 04/21] Remove compatibility code for unsupported version of Django. --- debug_toolbar/forms.py | 20 ++++++----------- debug_toolbar/panels/sql.py | 2 +- debug_toolbar/panels/template.py | 11 +++------- debug_toolbar/urls.py | 6 ++---- debug_toolbar/utils/compat/__init__.py | 0 debug_toolbar/utils/compat/db.py | 13 ----------- debug_toolbar/utils/functional.py | 14 ------------ debug_toolbar/views.py | 30 +++++++++++--------------- tests/tests.py | 2 +- tests/urls.py | 8 +++---- 10 files changed, 29 insertions(+), 77 deletions(-) delete mode 100644 debug_toolbar/utils/compat/__init__.py delete mode 100644 debug_toolbar/utils/compat/db.py delete mode 100644 debug_toolbar/utils/functional.py diff --git a/debug_toolbar/forms.py b/debug_toolbar/forms.py index f93abf7c2..c661c9efc 100644 --- a/debug_toolbar/forms.py +++ b/debug_toolbar/forms.py @@ -1,23 +1,15 @@ +import json +import hashlib + from django import forms from django.conf import settings +from django.db import connections from django.utils.encoding import smart_str +from django.utils.functional import cached_property from django.core.exceptions import ValidationError -from debug_toolbar.utils.functional import cached_property from debug_toolbar.utils.sql import reformat_sql -try: - import json -except ImportError: - from django.utils import simplejson as json - -try: - from hashlib import sha1 -except ImportError: - from django.utils.hashcompat import sha_constructor as sha1 - -from debug_toolbar.utils.compat.db import connections - class SQLSelectForm(forms.Form): """ @@ -83,7 +75,7 @@ def reformat_sql(self): def make_hash(self, data): params = settings.SECRET_KEY + data['sql'] + data['params'] - return sha1(smart_str(params)).hexdigest() + return hashlib.sha1(smart_str(params)).hexdigest() @property def connection(self): diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 4bfdd2717..4a0fc8298 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,11 +1,11 @@ import uuid from copy import copy +from django.db import connections from django.db.backends import BaseDatabaseWrapper from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __ from debug_toolbar.forms import SQLSelectForm -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 render_stacktrace diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index 390c1e15d..7b01291fa 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -19,14 +19,9 @@ from django.test.utils import instrumented_test_render from django.template import Template -if not hasattr(Template, '_render'): # Django < 1.2 - if Template.render != instrumented_test_render: - Template.original_render = Template.render - Template.render = instrumented_test_render -else: - if Template._render != instrumented_test_render: - Template.original_render = Template._render - Template._render = instrumented_test_render +if Template._render != instrumented_test_render: + Template.original_render = Template._render + Template._render = instrumented_test_render # MONSTER monkey-patch diff --git a/debug_toolbar/urls.py b/debug_toolbar/urls.py index b37bf2d3b..4280a1830 100644 --- a/debug_toolbar/urls.py +++ b/debug_toolbar/urls.py @@ -4,10 +4,8 @@ These should not be loaded explicitly; the debug toolbar middleware will patch this into the urlconf for the request. """ -try: - from django.conf.urls import patterns, url -except ImportError: # django < 1.4 - from django.conf.urls.defaults import patterns, url + +from django.conf.urls import patterns, url _PREFIX = '__debug__' diff --git a/debug_toolbar/utils/compat/__init__.py b/debug_toolbar/utils/compat/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/debug_toolbar/utils/compat/db.py b/debug_toolbar/utils/compat/db.py deleted file mode 100644 index 6ff09ec30..000000000 --- a/debug_toolbar/utils/compat/db.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.conf import settings -try: - from django.db import connections - dbconf = settings.DATABASES -except ImportError: - # Compat with < Django 1.2 - from django.db import connection - connections = {'default': connection} - dbconf = { - 'default': { - 'ENGINE': settings.DATABASE_ENGINE, - } - } diff --git a/debug_toolbar/utils/functional.py b/debug_toolbar/utils/functional.py deleted file mode 100644 index 1dbb7341c..000000000 --- a/debug_toolbar/utils/functional.py +++ /dev/null @@ -1,14 +0,0 @@ -try: - from django.utils.functional import cached_property -except ImportError: # Django < 1.4 - class cached_property(object): - """ - Decorator that creates converts a method with a single - self argument into a property cached on the instance. - """ - def __init__(self, func): - self.func = func - - def __get__(self, instance, type): - res = instance.__dict__[self.func.__name__] = self.func(instance) - return res diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index 1445360ad..ab91cde5a 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -120,23 +120,19 @@ def template_source(request): if template_name is None: return HttpResponseBadRequest('"template" key is required') - try: # Django 1.2 ... - from django.template.loader import find_template_loader, make_origin - loaders = [] - for loader_name in settings.TEMPLATE_LOADERS: - loader = find_template_loader(loader_name) - if loader is not None: - loaders.append(loader) - for loader in loaders: - try: - source, display_name = loader.load_template_source(template_name) - origin = make_origin(display_name, loader, template_name, settings.TEMPLATE_DIRS) - break - except TemplateDoesNotExist: - source = "Template Does Not Exist: %s" % (template_name,) - except (ImportError, AttributeError): # Django 1.1 ... - from django.template.loader import find_template_source - source, origin = find_template_source(template_name) + from django.template.loader import find_template_loader, make_origin + loaders = [] + for loader_name in settings.TEMPLATE_LOADERS: + loader = find_template_loader(loader_name) + if loader is not None: + loaders.append(loader) + for loader in loaders: + try: + source, display_name = loader.load_template_source(template_name) + origin = make_origin(display_name, loader, template_name, settings.TEMPLATE_DIRS) + break + except TemplateDoesNotExist: + source = "Template Does Not Exist: %s" % (template_name,) try: from pygments import highlight diff --git a/tests/tests.py b/tests/tests.py index fcb6c1ecc..4c6f246d3 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -159,7 +159,7 @@ 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_urlname'], 'positional-resolving') self.assertEquals(stats['view_func'], 'tests.views.resolving_view') self.assertEquals(stats['view_args'], ('a', 'b')) self.assertEquals(stats['view_kwargs'], {}) diff --git a/tests/urls.py b/tests/urls.py index 778f417c2..326c61a34 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,14 +1,12 @@ """ -URLpatterns for the debug toolbar. +URLpatterns for the debug toolbar. These should not be loaded explicitly; the debug toolbar middleware will patch this into the urlconf for the request. """ + +from django.conf.urls import patterns, url from django.contrib import admin -try: - from django.conf.urls import patterns, url -except ImportError: # django < 1.4 - from django.conf.urls.defaults import patterns, url admin.autodiscover() From df492cae261f6246a7e6ee19adad04f5ac27733b Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:29:51 +0200 Subject: [PATCH 05/21] Switch to an external version of sqlparse. --- .../management/commands/debugsqlshell.py | 4 +- debug_toolbar/utils/sql.py | 20 +- 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 | 439 -------------- 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 +- tox.ini | 26 +- 14 files changed, 43 insertions(+), 2630 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 c0e1fc1d4..e96f46d6d 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -2,7 +2,9 @@ from django.db.backends import util -from debug_toolbar.utils import ms_from_timedelta, sqlparse +import sqlparse + +from debug_toolbar.utils import ms_from_timedelta class PrintQueryWrapper(util.CursorDebugWrapper): diff --git a/debug_toolbar/utils/sql.py b/debug_toolbar/utils/sql.py index 909edf7f9..5de7da59e 100644 --- a/debug_toolbar/utils/sql.py +++ b/debug_toolbar/utils/sql.py @@ -1,6 +1,22 @@ import re -from debug_toolbar.utils import sqlparse -from debug_toolbar.utils.sqlparse.filters import BoldKeywordFilter + +from django.utils.html import escape + +import sqlparse +from sqlparse import tokens as T + + +class BoldKeywordFilter: + """sqlparse filter to bold SQL keywords""" + def process(self, stack, stream): + """Process the token stream""" + for token_type, value in stream: + is_keyword = token_type in T.Keyword + if is_keyword: + yield T.Text, '' + yield token_type, escape(value) + if is_keyword: + yield T.Text, '' def reformat_sql(sql): 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 6443a3cbc..000000000 --- a/debug_toolbar/utils/sqlparse/filters.py +++ /dev/null @@ -1,439 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -from django.utils.html import escape - -from debug_toolbar.utils.sqlparse import tokens as T -from debug_toolbar.utils.sqlparse import sql - - -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 - - -class BoldKeywordFilter(Filter): - """sqlparse filter to bold SQL keywords""" - def process(self, stack, stream): - """Process the token stream""" - for token_type, value in stream: - is_keyword = token_type in T.Keyword - if is_keyword: - yield T.Text, '' - yield token_type, escape(value) - if is_keyword: - yield T.Text, '' 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 ced3b2d8f..066c339cf 100644 --- a/setup.py +++ b/setup.py @@ -12,8 +12,9 @@ download_url='https://github.com/django-debug-toolbar/django-debug-toolbar/downloads', license='BSD', packages=find_packages(exclude=('tests', 'example')), - tests_require=[ + install_requires=[ 'django>=1.4,<1.6', + 'sqlparse', ], test_suite='runtests.runtests', include_package_data=True, diff --git a/tox.ini b/tox.ini index 74765b37b..384bc31b9 100644 --- a/tox.ini +++ b/tox.ini @@ -9,27 +9,41 @@ envlist = [testenv] commands = python runtests.py +deps = + sqlparse [testenv:py26-django14] basepython = python2.6 -deps = Django>=1.4,<1.5 +deps = + Django>=1.4,<1.5 + {[testenv]deps} [testenv:py27-django14] basepython = python2.7 -deps = Django>=1.4,<1.5 +deps = + Django>=1.4,<1.5 + {[testenv]deps} [testenv:py26-django15] basepython = python2.6 -deps = Django>=1.5,<1.6 +deps = + Django>=1.5,<1.6 + {[testenv]deps} [testenv:py27-django15] basepython = python2.7 -deps = Django>=1.5,<1.6 +deps = + Django>=1.5,<1.6 + {[testenv]deps} [testenv:py32-django15] basepython = python3.2 -deps = Django>=1.5,<1.6 +deps = + Django>=1.5,<1.6 + {[testenv]deps} [testenv:py33-django15] basepython = python3.3 -deps = Django>=1.5,<1.6 +deps = + Django>=1.5,<1.6 + {[testenv]deps} From 91213848b1a3541bd669af6c6a4d633c536608c3 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:43:10 +0200 Subject: [PATCH 06/21] Modernize exception catching syntax. --- debug_toolbar/__init__.py | 2 +- debug_toolbar/toolbar/loader.py | 2 +- debug_toolbar/utils/tracking/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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' diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index c9d221353..f26d8140e 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -90,7 +90,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)) 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 6a8d2facf935ac40e4865deb3132f440183bad6f Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:43:30 +0200 Subject: [PATCH 07/21] Avoid naked except clauses. --- debug_toolbar/panels/cache.py | 2 +- debug_toolbar/toolbar/loader.py | 5 +---- debug_toolbar/utils/tracking/db.py | 2 +- debug_toolbar/views.py | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index e25f08b8e..852327d97 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -42,7 +42,7 @@ def wrapped(self, *args, **kwargs): template_info = get_template_info(node.source) break cur_frame = cur_frame.f_back - except: + except Exception: pass del cur_frame cache_called.send(sender=self.__class__, time_taken=t, diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index f26d8140e..5f01aa7ea 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -42,10 +42,7 @@ def load_panels(self): """ global panel_classes for panel_class in panel_classes: - try: - panel_instance = panel_class(context=self.template_context) - except: - raise # Bubble up problem loading panel + panel_instance = panel_class(context=self.template_context) self._panels[panel_class] = panel_instance diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 1906cb212..ac8ff3415 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -117,7 +117,7 @@ def execute(self, sql, params=()): template_info = get_template_info(node.source) break cur_frame = cur_frame.f_back - except: + except Exception: pass del cur_frame diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index ab91cde5a..045e5ccd5 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -92,7 +92,7 @@ def sql_profile(request): cursor.execute("SELECT * FROM information_schema.profiling WHERE query_id=(SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)") headers = [d[0] for d in cursor.description] result = cursor.fetchall() - except: + except Exception: result_error = "Profiling is either not available or not supported by your database." cursor.close() context = { From f2fb72a5a09773b5c1e84fd2b55ec1543c60d51d Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 22:51:16 +0200 Subject: [PATCH 08/21] Replace print statement by print function. --- debug_toolbar/management/commands/debugsqlshell.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index e96f46d6d..45a23c285 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from datetime import datetime from django.db.backends import util @@ -14,9 +16,9 @@ def execute(self, sql, params=()): return self.cursor.execute(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 + execution_time = ms_from_timedelta(datetime.now() - starttime) + formatted_sql = sqlparse.format(raw_sql, reindent=True) + print('%s [%.2fms]' % (formatted_sql, execution_time)) + util.CursorDebugWrapper = PrintQueryWrapper From 133ea225dfb69087dff9ca58dd23150879fc5aec Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:03:07 +0200 Subject: [PATCH 09/21] Enable unicode_literals in all non-empty modules. --- debug_toolbar/__init__.py | 2 ++ debug_toolbar/forms.py | 2 ++ debug_toolbar/management/commands/debugsqlshell.py | 2 +- debug_toolbar/middleware.py | 7 +++++-- debug_toolbar/models.py | 2 ++ debug_toolbar/panels/__init__.py | 2 ++ debug_toolbar/panels/cache.py | 4 +++- debug_toolbar/panels/headers.py | 2 ++ debug_toolbar/panels/logger.py | 2 ++ debug_toolbar/panels/profiling.py | 2 +- debug_toolbar/panels/request_vars.py | 2 ++ debug_toolbar/panels/settings_vars.py | 2 ++ debug_toolbar/panels/signals.py | 2 ++ debug_toolbar/panels/sql.py | 2 ++ debug_toolbar/panels/template.py | 2 ++ debug_toolbar/panels/timer.py | 2 ++ debug_toolbar/panels/version.py | 2 ++ debug_toolbar/templatetags/debug_toolbar_utils.py | 1 + debug_toolbar/toolbar/loader.py | 4 +++- debug_toolbar/urls.py | 2 ++ debug_toolbar/utils/__init__.py | 12 +++++++----- debug_toolbar/utils/sql.py | 2 ++ debug_toolbar/utils/tracking/__init__.py | 2 ++ debug_toolbar/utils/tracking/db.py | 2 ++ debug_toolbar/views.py | 2 ++ tests/tests.py | 2 +- tests/urls.py | 2 ++ tests/views.py | 4 +++- 28 files changed, 63 insertions(+), 13 deletions(-) diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index feb6925a2..3a8f300c4 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + __all__ = ('VERSION',) try: diff --git a/debug_toolbar/forms.py b/debug_toolbar/forms.py index c661c9efc..aad308a32 100644 --- a/debug_toolbar/forms.py +++ b/debug_toolbar/forms.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import json import hashlib diff --git a/debug_toolbar/management/commands/debugsqlshell.py b/debug_toolbar/management/commands/debugsqlshell.py index 45a23c285..e94e4e2cd 100644 --- a/debug_toolbar/management/commands/debugsqlshell.py +++ b/debug_toolbar/management/commands/debugsqlshell.py @@ -1,4 +1,4 @@ -from __future__ import print_function +from __future__ import print_function, unicode_literals from datetime import datetime diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 090e206d8..82f83da8e 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -1,6 +1,9 @@ """ Debug Toolbar middleware """ + +from __future__ import unicode_literals + import imp import threading @@ -48,7 +51,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 +61,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): diff --git a/debug_toolbar/models.py b/debug_toolbar/models.py index 7ef860186..510ce88c4 100644 --- a/debug_toolbar/models.py +++ b/debug_toolbar/models.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.conf import settings from django.utils.importlib import import_module diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py index f1751d83b..999819622 100644 --- a/debug_toolbar/panels/__init__.py +++ b/debug_toolbar/panels/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.template.defaultfilters import slugify from django.template.loader import render_to_string from debug_toolbar.middleware import DebugToolbarMiddleware diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 852327d97..80a3b46ab 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import inspect import sys import time @@ -59,7 +61,7 @@ def __init__(self, cache): self.cache = cache def __repr__(self): - return u"" % self.cache.__repr__() + return str("") % repr(self.cache) def _get_func_info(self): frame = sys._getframe(3) diff --git a/debug_toolbar/panels/headers.py b/debug_toolbar/panels/headers.py index 9824d0ece..4a10b5f30 100644 --- a/debug_toolbar/panels/headers.py +++ b/debug_toolbar/panels/headers.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel diff --git a/debug_toolbar/panels/logger.py b/debug_toolbar/panels/logger.py index 816e7186f..c6c1bd141 100644 --- a/debug_toolbar/panels/logger.py +++ b/debug_toolbar/panels/logger.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import datetime import logging try: diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index f21c34555..c93c3b3bf 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,4 +1,4 @@ -from __future__ import division +from __future__ import division, unicode_literals from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe diff --git a/debug_toolbar/panels/request_vars.py b/debug_toolbar/panels/request_vars.py index ade23f342..936c96464 100644 --- a/debug_toolbar/panels/request_vars.py +++ b/debug_toolbar/panels/request_vars.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.core.urlresolvers import resolve from django.http import Http404 from django.utils.translation import ugettext_lazy as _ diff --git a/debug_toolbar/panels/settings_vars.py b/debug_toolbar/panels/settings_vars.py index 54791c238..b5721ee11 100644 --- a/debug_toolbar/panels/settings_vars.py +++ b/debug_toolbar/panels/settings_vars.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.conf import settings from django.views.debug import get_safe_settings from django.utils.translation import ugettext_lazy as _ diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py index 984b06493..d6a5b2ea6 100644 --- a/debug_toolbar/panels/signals.py +++ b/debug_toolbar/panels/signals.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.conf import settings from django.core.signals import (request_started, request_finished, got_request_exception) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 4a0fc8298..7afc6eb80 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import uuid from copy import copy diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index 7b01291fa..8ecf5d6ac 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from os.path import normpath from pprint import pformat diff --git a/debug_toolbar/panels/timer.py b/debug_toolbar/panels/timer.py index fa2361726..9b420cb2b 100644 --- a/debug_toolbar/panels/timer.py +++ b/debug_toolbar/panels/timer.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + try: import resource except ImportError: diff --git a/debug_toolbar/panels/version.py b/debug_toolbar/panels/version.py index ccb0ad8d8..b19d306af 100644 --- a/debug_toolbar/panels/version.py +++ b/debug_toolbar/panels/version.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import sys import django diff --git a/debug_toolbar/templatetags/debug_toolbar_utils.py b/debug_toolbar/templatetags/debug_toolbar_utils.py index c0573128f..7917a67ba 100644 --- a/debug_toolbar/templatetags/debug_toolbar_utils.py +++ b/debug_toolbar/templatetags/debug_toolbar_utils.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from django import template from django.utils.numberformat import format diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index 5f01aa7ea..73ab0b658 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -2,6 +2,8 @@ The main DebugToolbar class that loads and renders the Toolbar. """ +from __future__ import unicode_literals + from django.conf import settings from django.template.loader import render_to_string from django.utils.datastructures import SortedDict @@ -16,7 +18,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', {})) diff --git a/debug_toolbar/urls.py b/debug_toolbar/urls.py index 4280a1830..524710dbb 100644 --- a/debug_toolbar/urls.py +++ b/debug_toolbar/urls.py @@ -5,6 +5,8 @@ this into the urlconf for the request. """ +from __future__ import unicode_literals + from django.conf.urls import patterns, url _PREFIX = '__debug__' diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index b8bb4a135..fa8c78ada 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import inspect import os.path import django @@ -58,11 +60,11 @@ def render_stacktrace(trace): params = map(escape, frame[0].rsplit(os.path.sep, 1) + list(frame[1:])) params_dict = dict((unicode(idx), v) for idx, v in enumerate(params)) try: - stacktrace.append(u'%(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 diff --git a/debug_toolbar/utils/sql.py b/debug_toolbar/utils/sql.py index 5de7da59e..4e0d70d7e 100644 --- a/debug_toolbar/utils/sql.py +++ b/debug_toolbar/utils/sql.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import re from django.utils.html import escape diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index 9a5927394..ac00f4c07 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import logging import time import types diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index ac8ff3415..3497172cb 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import sys from datetime import datetime diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index 045e5ccd5..f6252246e 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -4,6 +4,8 @@ views in any other way is generally not advised. """ +from __future__ import unicode_literals + from django.http import HttpResponseBadRequest from django.shortcuts import render_to_response from django.views.decorators.csrf import csrf_exempt diff --git a/tests/tests.py b/tests/tests.py index 4c6f246d3..5dcb03451 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,4 +1,4 @@ -from __future__ import with_statement +from __future__ import unicode_literals import thread import django diff --git a/tests/urls.py b/tests/urls.py index 326c61a34..9c24c1ec8 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -5,6 +5,8 @@ this into the urlconf for the request. """ +from __future__ import unicode_literals + from django.conf.urls import patterns, url from django.contrib import admin diff --git a/tests/views.py b/tests/views.py index 909a784ff..ec9f30ed1 100644 --- a/tests/views.py +++ b/tests/views.py @@ -1,9 +1,11 @@ +from __future__ import unicode_literals + from django.contrib.auth.models import User from django.http import HttpResponse def execute_sql(request): list(User.objects.all()) - + return HttpResponse() def resolving_view(request, arg1, arg2): From 25788a010edee43d7217d32cd897f04eedec6c40 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:08:03 +0200 Subject: [PATCH 10/21] Switch from str & unicode to bytes & text. --- debug_toolbar/forms.py | 4 ++-- debug_toolbar/middleware.py | 6 +++--- debug_toolbar/utils/__init__.py | 3 ++- debug_toolbar/utils/tracking/db.py | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/debug_toolbar/forms.py b/debug_toolbar/forms.py index aad308a32..60d339cba 100644 --- a/debug_toolbar/forms.py +++ b/debug_toolbar/forms.py @@ -6,7 +6,7 @@ from django import forms from django.conf import settings from django.db import connections -from django.utils.encoding import smart_str +from django.utils.encoding import smart_bytes from django.utils.functional import cached_property from django.core.exceptions import ValidationError @@ -77,7 +77,7 @@ def reformat_sql(self): def make_hash(self, data): params = settings.SECRET_KEY + data['sql'] + data['params'] - return hashlib.sha1(smart_str(params)).hexdigest() + return hashlib.sha1(smart_bytes(params)).hexdigest() @property def connection(self): diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 82f83da8e..520a81e32 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -10,7 +10,7 @@ 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 force_text from django.utils.importlib import import_module import debug_toolbar.urls @@ -138,9 +138,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), + force_text(response.content), self.tag, - smart_unicode(toolbar.render_toolbar() + self.tag)) + force_text(toolbar.render_toolbar() + self.tag)) if response.get('Content-Length', None): response['Content-Length'] = len(response.content) del self.__class__.debug_toolbars[ident] diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index fa8c78ada..7efb106fd 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -10,6 +10,7 @@ from django.views.debug import linebreak_iter from django.utils.html import escape from django.utils.safestring import mark_safe +from django.utils import six # Figure out some paths django_path = os.path.realpath(os.path.dirname(django.__file__)) @@ -58,7 +59,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((six.text_type(idx), v) for idx, v in enumerate(params)) try: stacktrace.append('%(0)s/' '%(1)s' diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 3497172cb..8f4219985 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -8,7 +8,7 @@ from django.conf import settings from django.template import Node -from django.utils.encoding import force_unicode +from django.utils.encoding import force_text from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -86,7 +86,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 7aac5d4ce5ed7f01111c6f37516e6f545fe7acb6 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:20:08 +0200 Subject: [PATCH 11/21] Replace basestring by six.string_types. --- debug_toolbar/middleware.py | 3 ++- debug_toolbar/utils/tracking/db.py | 3 ++- tests/tests.py | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 520a81e32..c361a498d 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -12,6 +12,7 @@ from django.shortcuts import render_to_response from django.utils.encoding import force_text from django.utils.importlib import import_module +from django.utils import six import debug_toolbar.urls from debug_toolbar.toolbar.loader import DebugToolbar @@ -80,7 +81,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, six.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 8f4219985..e9454d55c 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -9,6 +9,7 @@ from django.conf import settings from django.template import Node from django.utils.encoding import force_text +from django.utils import six from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, \ get_template_info, get_stack @@ -72,7 +73,7 @@ def __init__(self, cursor, db, logger): self.logger = logger def _quote_expr(self, element): - if isinstance(element, basestring): + if isinstance(element, six.string_types): element = element.replace("'", "''") return "'%s'" % element else: diff --git a/tests/tests.py b/tests/tests.py index 5dcb03451..def4f8c84 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -8,6 +8,7 @@ from django.http import HttpResponse from django.test import TestCase, RequestFactory from django.template import Template, Context +from django.utils import six from django.utils import unittest from debug_toolbar.middleware import DebugToolbarMiddleware @@ -105,7 +106,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, six.string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -120,7 +121,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, six.string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -133,7 +134,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, six.string_types)) self.assertTrue(hasattr(request.urlconf.urlpatterns[1], '_callback_str')) self.assertEquals(request.urlconf.urlpatterns[-1]._callback_str, 'tests.views.execute_sql') @@ -146,7 +147,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, six.string_types)) def _resolve_stats(self, path): # takes stats from RequestVars panel From 25350f376e92d4c7d9e37cd4d6d07d3c3f777504 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:11:26 +0200 Subject: [PATCH 12/21] Update imports for renamed modules. --- debug_toolbar/panels/profiling.py | 4 ++-- debug_toolbar/utils/__init__.py | 6 +++--- tests/tests.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index c93c3b3bf..6adb2791c 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe +from django.utils.six.moves import cStringIO from debug_toolbar.panels import DebugPanel try: @@ -11,7 +12,6 @@ DJ_PROFILE_USE_LINE_PROFILER = False -from cStringIO import StringIO import cProfile from pstats import Stats from colorsys import hsv_to_rgb @@ -128,7 +128,7 @@ def line_stats_text(self): if self._line_stats_text is None and DJ_PROFILE_USE_LINE_PROFILER: lstats = self.statobj.line_stats if self.func in lstats.timings: - out = StringIO() + out = cStringIO.StringIO() fn, lineno, name = self.func show_func(fn, lineno, name, lstats.timings[self.func], lstats.unit, stream=out) self._line_stats_text = out.getvalue() diff --git a/debug_toolbar/utils/__init__.py b/debug_toolbar/utils/__init__.py index 7efb106fd..ebd8b84b8 100644 --- a/debug_toolbar/utils/__init__.py +++ b/debug_toolbar/utils/__init__.py @@ -3,7 +3,6 @@ import inspect import os.path import django -import SocketServer import sys from django.conf import settings @@ -11,10 +10,11 @@ from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils import six +from django.utils.six.moves import socketserver # 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): @@ -31,7 +31,7 @@ def tidy_stacktrace(stack): """ Clean up stacktrace and remove all entries that: 1. Are part of Django (except contrib apps) - 2. Are part of SocketServer (used by Django's dev server) + 2. Are part of socketserver (used by Django's dev server) 3. Are the last entry (which is part of our stacktracing code) ``stack`` should be a list of frame tuples from ``inspect.stack()`` diff --git a/tests/tests.py b/tests/tests.py index def4f8c84..cdf1a9e18 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,4 @@ from __future__ import unicode_literals -import thread import django from django.conf import settings @@ -9,6 +8,7 @@ from django.test import TestCase, RequestFactory from django.template import Template, Context from django.utils import six +from django.utils.six.moves import _thread from django.utils import unittest from debug_toolbar.middleware import DebugToolbarMiddleware @@ -50,7 +50,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 2622a2694595fad65f5be8ccfb9d42caa482fbd0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:17:59 +0200 Subject: [PATCH 13/21] Stopped using dict.iter*. Since performance isn't a primary concern, the non-iterable versions will do just fine on Python 2. --- debug_toolbar/panels/cache.py | 2 +- debug_toolbar/panels/profiling.py | 4 ++-- debug_toolbar/panels/request_vars.py | 2 +- debug_toolbar/panels/sql.py | 2 +- debug_toolbar/templates/debug_toolbar/panels/cache.html | 6 +++--- 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 | 2 +- tests/tests.py | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 80a3b46ab..1272dfce2 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -168,7 +168,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 return_value.items(): if value is None: self.misses += 1 else: diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 6adb2791c..f71472b1f 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -23,7 +23,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 self.stats.items(): if len(callers) == 0: self.__root = func break @@ -80,7 +80,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 self.statobj.all_callees[self.func].items(): i += 1 h1 = h + (i / count) / (self.depth + 1) if stats[3] == 0: diff --git a/debug_toolbar/panels/request_vars.py b/debug_toolbar/panels/request_vars.py index 936c96464..5bf3e7fab 100644 --- a/debug_toolbar/panels/request_vars.py +++ b/debug_toolbar/panels/request_vars.py @@ -55,5 +55,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 self.request.session.keys()] }) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 7afc6eb80..bc70b9701 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -139,7 +139,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(self._databases.values()): 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..95794d518 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 %} @@ -66,4 +66,4 @@

{% trans "Calls" %}

{% endfor %}
{{ name }}
{{ value }}
-{% endif %} \ No newline at end of file +{% endif %} 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 e9454d55c..98b2c9737 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -82,7 +82,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 params.items()) return map(self._quote_expr, params) def _decode(self, param): diff --git a/tests/tests.py b/tests/tests.py index cdf1a9e18..dd7391d89 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -32,12 +32,12 @@ def __init__(self, **overrides): self._orig = {} def __enter__(self): - for k, v in self.overrides.iteritems(): + for k, v in self.overrides.items(): 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 self._orig.items(): if v is self.NotDefined: delattr(settings, k) else: From 725719141a86603ec79e1889c08cb0fd537a651d Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 10:35:10 +0200 Subject: [PATCH 14/21] Allow repeated iteration on DebugToolbar.panels. --- 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 73ab0b658..ee1de6719 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -32,7 +32,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 c19d8b0743a6fdd784343258c6f502e84156582c Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 10:18:45 +0200 Subject: [PATCH 15/21] Ensure _quote_params returns a list. This fixes a few test failures. --- debug_toolbar/utils/tracking/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 98b2c9737..53c298d76 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -83,7 +83,7 @@ def _quote_params(self, params): if isinstance(params, dict): return dict((key, self._quote_expr(value)) for key, value in params.items()) - return map(self._quote_expr, params) + return list(map(self._quote_expr, params)) def _decode(self, param): try: From a2da974c39538beb4de61a29b272dbf406c3f8ee Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 16 Oct 2013 23:23:42 +0200 Subject: [PATCH 16/21] Replace assertEquals by assertEqual. --- tests/tests.py | 90 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index dd7391d89..67b67bf20 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -64,7 +64,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('/') @@ -109,7 +109,7 @@ def test_request_urlconf_string(self): self.assertFalse(isinstance(request.urlconf, six.string_types)) 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('/') @@ -124,7 +124,7 @@ def test_request_urlconf_string_per_request(self): self.assertFalse(isinstance(request.urlconf, six.string_types)) 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('/') @@ -137,7 +137,7 @@ def test_request_urlconf_module(self): self.assertFalse(isinstance(request.urlconf, six.string_types)) 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('/') @@ -160,27 +160,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') - 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') + 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): @@ -188,30 +188,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]) @@ -234,21 +234,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): @@ -264,7 +264,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) base_ctx_idx = 1 if django.VERSION[:2] >= (1, 5) else 0 ctx = template_panel.templates[0]['context'][base_ctx_idx] self.assertIn('<>', ctx) @@ -293,23 +293,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'] = {} @@ -318,23 +318,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'] = {} @@ -343,13 +343,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) @@ -362,24 +362,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'] = {} @@ -388,21 +388,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 64af948487f9839455baeb7f3d1056dd0ce90278 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 09:03:06 +0200 Subject: [PATCH 17/21] Update introspection code. --- debug_toolbar/panels/profiling.py | 8 ++++---- debug_toolbar/panels/signals.py | 6 +++--- debug_toolbar/utils/tracking/__init__.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index f71472b1f..995cccb09 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -155,12 +155,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): diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py index d6a5b2ea6..d3d9cfc19 100644 --- a/debug_toolbar/panels/signals.py +++ b/debug_toolbar/panels/signals.py @@ -89,9 +89,9 @@ def process_response(self, request, response): receiver = getattr(receiver, '__wraps__', receiver) receiver_name = getattr(receiver, '__name__', str(receiver)) - if getattr(receiver, 'im_self', None) is not None: - text = "%s.%s" % (getattr(receiver.im_self, '__class__', type).__name__, receiver_name) - elif getattr(receiver, 'im_class', None) is not None: + if getattr(receiver, '__self__', None) is not None: + text = "%s.%s" % (getattr(receiver.__self__, '__class__', type).__name__, receiver_name) + elif getattr(receiver, 'im_class', None) is not None: # Python 2 only text = "%s.%s" % (receiver.im_class.__name__, receiver_name) else: text = "%s" % receiver_name diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index ac00f4c07..4fbefa55d 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -52,10 +52,10 @@ def _replace_function(func, wrapped): else: module = import_module(func.__module__) setattr(module, func.__name__, wrapped) - elif getattr(func, 'im_self', None): - setattr(func.im_self, func.__name__, classmethod(wrapped)) + elif getattr(func, '__self__', None): + setattr(func.__self__, func.__name__, classmethod(wrapped)) elif hasattr(func, 'im_class'): - # for unbound methods + # for unbound methods - Python 2 only setattr(func.im_class, func.__name__, wrapped) else: raise NotImplementedError From d1e1cb42f2f439805065d637d91787e51a2d7e85 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 09:22:46 +0200 Subject: [PATCH 18/21] Update usage of the threading module. --- debug_toolbar/middleware.py | 8 ++++---- tests/tests.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index c361a498d..6512de6a9 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -43,7 +43,7 @@ class DebugToolbarMiddleware(object): @classmethod def get_current(cls): - return cls.debug_toolbars.get(threading.currentThread().ident) + return cls.debug_toolbars.get(threading.current_thread().ident) def __init__(self): self._urlconfs = {} @@ -103,11 +103,11 @@ def process_request(self, request): toolbar = DebugToolbar(request) for panel in toolbar.panels: panel.process_request(request) - self.__class__.debug_toolbars[threading.currentThread().ident] = toolbar + self.__class__.debug_toolbars[threading.current_thread().ident] = toolbar def process_view(self, request, view_func, view_args, view_kwargs): __traceback_hide__ = True - toolbar = self.__class__.debug_toolbars.get(threading.currentThread().ident) + toolbar = self.__class__.debug_toolbars.get(threading.current_thread().ident) if not toolbar: return result = None @@ -119,7 +119,7 @@ def process_view(self, request, view_func, view_args, view_kwargs): def process_response(self, request, response): __traceback_hide__ = True - ident = threading.currentThread().ident + ident = threading.current_thread().ident toolbar = self.__class__.debug_toolbars.get(ident) if not toolbar or request.is_ajax() or getattr(response, 'streaming', False): return response diff --git a/tests/tests.py b/tests/tests.py index 67b67bf20..32f8d6b4c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +import threading + import django from django.conf import settings from django.contrib.auth.models import User @@ -8,7 +10,6 @@ from django.test import TestCase, RequestFactory from django.template import Template, Context from django.utils import six -from django.utils.six.moves import _thread from django.utils import unittest from debug_toolbar.middleware import DebugToolbarMiddleware @@ -50,7 +51,7 @@ def setUp(self): response = HttpResponse() toolbar = DebugToolbar(request) - DebugToolbarMiddleware.debug_toolbars[_thread.get_ident()] = toolbar + DebugToolbarMiddleware.debug_toolbars[threading.current_thread().ident] = toolbar self.request = request self.response = response From e5d2c7e7a6988f490448ea2871de1e0d9ed197bd Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 09:42:19 +0200 Subject: [PATCH 19/21] Implement method replacement for Python 3. Python 3 doesn't have unbound methods. --- debug_toolbar/panels/sql.py | 9 ++++----- debug_toolbar/utils/tracking/__init__.py | 11 +++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index bc70b9701..709a5f5c4 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -13,13 +13,12 @@ from debug_toolbar.utils import render_stacktrace from debug_toolbar.utils.sql import reformat_sql from debug_toolbar.utils.tracking.db import CursorWrapper -from debug_toolbar.utils.tracking import replace_call +from debug_toolbar.utils.tracking import replace_method -# Inject our tracking cursor -@replace_call(BaseDatabaseWrapper.cursor) -def cursor(func, self): - result = func(self) +@replace_method(BaseDatabaseWrapper, 'cursor') +def cursor(original, self): + result = original(self) djdt = DebugToolbarMiddleware.get_current() if not djdt: diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index 4fbefa55d..166b1b530 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -20,18 +20,21 @@ def wrapped(callback): return wrapped -def replace_call(func): +def replace_method(klass, method_name): + original = getattr(klass, method_name) + def inner(callback): def wrapped(*args, **kwargs): - return callback(func, *args, **kwargs) + return callback(original, *args, **kwargs) - actual = getattr(func, '__wrapped__', func) + actual = getattr(original, '__wrapped__', original) wrapped.__wrapped__ = actual wrapped.__doc__ = getattr(actual, '__doc__', None) wrapped.__name__ = actual.__name__ - _replace_function(func, wrapped) + setattr(klass, method_name, wrapped) return wrapped + return inner From 7127420c50a6f26d7a6daceb8f10f09fd3d0af98 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 10:22:32 +0200 Subject: [PATCH 20/21] Remove dead code. These features were still tested, but they weren't used anywhere. --- debug_toolbar/utils/tracking/__init__.py | 77 ------------- tests/tests.py | 139 +---------------------- 2 files changed, 1 insertion(+), 215 deletions(-) diff --git a/debug_toolbar/utils/tracking/__init__.py b/debug_toolbar/utils/tracking/__init__.py index 166b1b530..420de1eb6 100644 --- a/debug_toolbar/utils/tracking/__init__.py +++ b/debug_toolbar/utils/tracking/__init__.py @@ -6,20 +6,6 @@ from django.utils.importlib import import_module -def post_dispatch(func): - def wrapped(callback): - register_hook(func, 'after', callback) - return callback - return wrapped - - -def pre_dispatch(func): - def wrapped(callback): - register_hook(func, 'before', callback) - return callback - return wrapped - - def replace_method(klass, method_name): original = getattr(klass, method_name) @@ -36,66 +22,3 @@ def wrapped(*args, **kwargs): return wrapped return inner - - -def fire_hook(hook, sender, **kwargs): - try: - for callback in callbacks[hook].get(id(sender), []): - callback(sender=sender, **kwargs) - except Exception as e: - # Log the exception, dont mess w/ the underlying function - logging.exception(e) - - -def _replace_function(func, wrapped): - if isinstance(func, types.FunctionType): - if func.__module__ == '__builtin__': - # oh shit - __builtins__[func] = wrapped - else: - module = import_module(func.__module__) - setattr(module, func.__name__, wrapped) - elif getattr(func, '__self__', None): - setattr(func.__self__, func.__name__, classmethod(wrapped)) - elif hasattr(func, 'im_class'): - # for unbound methods - Python 2 only - setattr(func.im_class, func.__name__, wrapped) - else: - raise NotImplementedError - -callbacks = { - 'before': {}, - 'after': {}, -} - - -def register_hook(func, hook, callback): - """ - def myhook(sender, args, kwargs): - print func, "executed - print "args:", args - print "kwargs:", kwargs - register_hook(BaseDatabaseWrapper.cursor, 'before', myhook) - """ - - assert hook in ('before', 'after') - - def wrapped(*args, **kwargs): - start = time.time() - fire_hook('before', sender=wrapped.__wrapped__, args=args, kwargs=kwargs, - start=start) - result = wrapped.__wrapped__(*args, **kwargs) - stop = time.time() - fire_hook('after', sender=wrapped.__wrapped__, args=args, kwargs=kwargs, - result=result, start=start, stop=stop) - actual = getattr(func, '__wrapped__', func) - wrapped.__wrapped__ = actual - wrapped.__doc__ = getattr(actual, '__doc__', None) - wrapped.__name__ = actual.__name__ - - id_ = id(actual) - if id_ not in callbacks[hook]: - callbacks[hook][id_] = [] - callbacks[hook][id_].append(callback) - - _replace_function(func, wrapped) diff --git a/tests/tests.py b/tests/tests.py index 32f8d6b4c..2af03f3e5 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -18,7 +18,7 @@ from debug_toolbar.panels.template import TemplateDebugPanel from debug_toolbar.toolbar.loader import DebugToolbar from debug_toolbar.utils import get_name_from_obj -from debug_toolbar.utils.tracking import pre_dispatch, post_dispatch, callbacks + rf = RequestFactory() @@ -270,140 +270,3 @@ def test_queryset_hook(self): ctx = template_panel.templates[0]['context'][base_ctx_idx] self.assertIn('<>', ctx) self.assertIn('<>', ctx) - - -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') From faee392e0234fa1be9ec8c11ce447cff7f849c45 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 17 Oct 2013 11:05:11 +0200 Subject: [PATCH 21/21] The actual requirement is Django >= 1.4.2. Compatibility features for supporting Python 2 and 3 with a single code base were added in Django 1.4.2. --- README.rst | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 1ba2e0690..0f818dca1 100644 --- a/README.rst +++ b/README.rst @@ -35,8 +35,8 @@ Requirements The current version of the Debug Toolbar is 0.9.4. It works on Django 1.3 and 1.4. -The next version will work on Django 1.4 and 1.5. In addition, it will require -Python 2.6 or later. +The next version will work on Django 1.4 (1.4.2 or later) and 1.5. In +addition, it will require Python 2.6 or later. Installation ============ diff --git a/setup.py b/setup.py index 066c339cf..b508d4805 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ license='BSD', packages=find_packages(exclude=('tests', 'example')), install_requires=[ - 'django>=1.4,<1.6', + 'django>=1.4.2,<1.6', 'sqlparse', ], test_suite='runtests.runtests',