From a2c8acafabc97c2b4e28bbfa180d3d91e2cbee9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 20 Nov 2020 13:51:45 +0100 Subject: [PATCH 1/8] Config setting to enable token grouping in SQL panel --- debug_toolbar/panels/sql/utils.py | 5 ++++- debug_toolbar/settings.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/panels/sql/utils.py b/debug_toolbar/panels/sql/utils.py index a5645f6da..eaf895f34 100644 --- a/debug_toolbar/panels/sql/utils.py +++ b/debug_toolbar/panels/sql/utils.py @@ -4,6 +4,8 @@ from django.utils.html import escape from sqlparse import tokens as T +from debug_toolbar import settings as dt_settings + class BoldKeywordFilter: """sqlparse filter to bold SQL keywords""" @@ -31,7 +33,8 @@ def reformat_sql(sql, with_toggle=False): def parse_sql(sql, aligned_indent=False): stack = sqlparse.engine.FilterStack() - stack.enable_grouping() + if dt_settings.get_config()["SQL_STACK_GROUPING"]: + stack.enable_grouping() if aligned_indent: stack.stmtprocess.append( sqlparse.filters.AlignedIndentFilter(char=" ", n="
") diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index 7ebedff39..35412883f 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -41,6 +41,7 @@ "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), "SQL_WARNING_THRESHOLD": 500, # milliseconds + "SQL_STACK_GROUPING": True, } From bd4d1c625e5ee0c0a5cecd0a44689422ed1c59a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 20 Nov 2020 14:01:09 +0100 Subject: [PATCH 2/8] Documentation for SQL_TOKEN_GROUPING setting --- debug_toolbar/panels/sql/utils.py | 2 +- debug_toolbar/settings.py | 2 +- docs/configuration.rst | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/panels/sql/utils.py b/debug_toolbar/panels/sql/utils.py index eaf895f34..f6aa029ad 100644 --- a/debug_toolbar/panels/sql/utils.py +++ b/debug_toolbar/panels/sql/utils.py @@ -33,7 +33,7 @@ def reformat_sql(sql, with_toggle=False): def parse_sql(sql, aligned_indent=False): stack = sqlparse.engine.FilterStack() - if dt_settings.get_config()["SQL_STACK_GROUPING"]: + if dt_settings.get_config()["SQL_TOKEN_GROUPING"]: stack.enable_grouping() if aligned_indent: stack.stmtprocess.append( diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index 35412883f..bced1990c 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -41,7 +41,7 @@ "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), "SQL_WARNING_THRESHOLD": 500, # milliseconds - "SQL_STACK_GROUPING": True, + "SQL_TOKEN_GROUPING": True, } diff --git a/docs/configuration.rst b/docs/configuration.rst index a1618b44f..9d583f324 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -221,6 +221,15 @@ Panel options The SQL panel highlights queries that took more that this amount of time, in milliseconds, to execute. +* ``SQL_TOKEN_GROUPING`` + + Default: ``True`` + + Panel: SQL + + Disable SQL token grouping, that might cause render slowdowns, + when a view make long SQL textual queries. + Here's what a slightly customized toolbar configuration might look like:: # This example is unlikely to be appropriate for your project. From 8ba70f74a462d230e216ee756a273fcdc69992ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 20 Nov 2020 14:02:45 +0100 Subject: [PATCH 3/8] Fixes formulation of setting doc --- docs/configuration.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 9d583f324..ae093e508 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -227,7 +227,8 @@ Panel options Panel: SQL - Disable SQL token grouping, that might cause render slowdowns, + Controls SQL token grouping. + When set to ``True``, it might cause render slowdowns when a view make long SQL textual queries. Here's what a slightly customized toolbar configuration might look like:: From 300d61e4528889657c9f6716f1f08e1c842edc11 Mon Sep 17 00:00:00 2001 From: alkihis <32335180+alkihis@users.noreply.github.com> Date: Fri, 4 Dec 2020 15:49:05 +0100 Subject: [PATCH 4/8] Merged master from source (#1) * fix AttributeError: 'dict' object has no attribute 'getlist' #1406 * Revert "fix AttributeError: 'dict' object has no attribute 'getlist' #1406" * Remove support for Python 3.5 * Remove py35 from tox.ini too; make black target Python 3.6 * Migrate to GitHub Actions. (#1410) * add test case for generate_stats * add get_sorted_request_variable and support dict in request data * fix import * style check * style check * fix style check * fix request.POST * History panel: Do not crash when receiving invalid JSON Fixes #1403 * Add Python3.9 support. * Add Python and Django supported versions badges to README.rst * Add two template blocks to allow overriding CSS and JS * Use alternating quotes (fix highlighting in IDEs) * django-debug-toolbar 3.2 Co-authored-by: folt Co-authored-by: Matthias Kestenholz Co-authored-by: Jannis Leidel Co-authored-by: Hasan Ramezani Co-authored-by: Peter Bittner --- .github/workflows/release.yml | 40 ++++ .github/workflows/test.yml | 222 ++++++++++++++++++ .gitignore | 1 + .travis.yml | 100 -------- Makefile | 6 +- README.rst | 14 +- debug_toolbar/panels/cache.py | 2 +- debug_toolbar/panels/history/panel.py | 14 +- debug_toolbar/panels/request.py | 11 +- .../templates/debug_toolbar/base.html | 10 +- debug_toolbar/utils.py | 10 + docs/changes.rst | 21 +- docs/conf.py | 2 +- example/README.rst | 6 +- example/settings.py | 6 +- setup.cfg | 6 +- tests/panels/test_history.py | 3 +- tests/panels/test_request.py | 54 +++++ tests/settings.py | 24 +- tests/test_integration.py | 2 +- tox.ini | 38 ++- 21 files changed, 430 insertions(+), 162 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..906d6846b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + build: + if: github.repository == 'jazzband/django-debug-toolbar' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U setuptools twine wheel + + - name: Build package + run: | + python setup.py --version + python setup.py sdist --format=gztar bdist_wheel + twine check dist/* + + - name: Upload packages to Jazzband + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@master + with: + user: jazzband + password: ${{ secrets.JAZZBAND_RELEASE_KEY }} + repository_url: https://jazzband.co/projects/django-debug-toolbar/upload diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..0fd253887 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,222 @@ +name: Test + +on: [push, pull_request] + +jobs: + mysql: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9'] + + services: + mariadb: + image: mariadb:10.3 + env: + MYSQL_ROOT_PASSWORD: mysql + MYSQL_DATABASE: mysql + options: >- + --health-cmd "mysqladmin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install enchant (only for docs) + run: | + sudo apt-get -qq update + sudo apt-get -y install enchant + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: mysql + DB_NAME: mysql + DB_USER: root + DB_PASSWORD: mysql + DB_HOST: "127.0.0.1" + DB_PORT: "3306" + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} + + postgres: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9'] + + services: + postgres: + image: 'postgres:9.5' + env: + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install enchant (only for docs) + run: | + sudo apt-get -qq update + sudo apt-get -y install enchant + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: postgresql + DB_NAME: postgres + DB_USER: postgres + DB_PASSWORD: postgres + DB_HOST: localhost + DB_PORT: 5432 + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} + + sqlite: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9'] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: sqlite3 + DB_NAME: ":memory:" + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} + + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox + + - name: Test with tox + run: tox -e docs,style,readme diff --git a/.gitignore b/.gitignore index 836dfe93d..564e7b8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ htmlcov node_modules package-lock.json geckodriver.log +coverage.xml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 76998f37a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,100 +0,0 @@ -dist: bionic -language: python -cache: pip -matrix: - fast_finish: true - include: - - env: TOXENV=docs - before_install: - - sudo apt-get -y install libenchant1c2a - - env: TOXENV=style - - env: TOXENV=readme - - python: 3.5 - env: TOXENV=py35-dj22-sqlite - - python: 3.6 - env: TOXENV=py36-dj22-sqlite - - python: 3.7 - env: TOXENV=py37-dj22-sqlite - - python: 3.8 - env: TOXENV=py38-dj22-sqlite - - python: 3.6 - env: TOXENV=py36-dj30-sqlite - - python: 3.7 - env: TOXENV=py37-dj30-sqlite - - python: 3.8 - env: TOXENV=py38-dj30-sqlite - - python: 3.6 - env: TOXENV=py36-dj31-sqlite - - python: 3.7 - env: TOXENV=py37-dj31-sqlite - - python: 3.8 - env: TOXENV=py38-dj31-sqlite - - python: 3.6 - env: TOXENV=py36-djmaster-sqlite - - python: 3.7 - env: TOXENV=py37-djmaster-sqlite - - python: 3.8 - env: TOXENV=py38-djmaster-sqlite - - python: 3.8 - env: TOXENV=py38-dj22-postgresql - addons: - postgresql: "9.5" - - python: 3.8 - env: TOXENV=py38-dj30-postgresql - addons: - postgresql: "9.5" - - python: 3.8 - env: TOXENV=py38-dj31-postgresql DJANGO_SELENIUM_TESTS=True - before_install: - - wget https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-linux64.tar.gz - - mkdir geckodriver && tar zxvf geckodriver-v0.27.0-linux64.tar.gz -C geckodriver - - export PATH=$PATH:$PWD/geckodriver - addons: - firefox: latest - postgresql: "9.5" - - python: 3.8 - env: TOXENV=py38-dj22-mariadb - addons: - mariadb: "10.3" - script: - # working around https://travis-ci.community/t/mariadb-build-error-with-xenial/3160 - - mysql -u root -e "DROP USER IF EXISTS 'travis'@'%';" - - mysql -u root -e "CREATE USER 'travis'@'%';" - - mysql -u root -e "CREATE DATABASE debug_toolbar;" - - mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'%';"; - - tox -v - - python: 3.8 - env: TOXENV=py38-dj30-mariadb - addons: - mariadb: "10.3" - script: - # working around https://travis-ci.community/t/mariadb-build-error-with-xenial/3160 - - mysql -u root -e "DROP USER IF EXISTS 'travis'@'%';" - - mysql -u root -e "CREATE USER 'travis'@'%';" - - mysql -u root -e "CREATE DATABASE debug_toolbar;" - - mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'%';"; - - tox -v - - python: 3.8 - env: TOXENV=py38-dj31-mariadb - addons: - mariadb: "10.3" - script: - # working around https://travis-ci.community/t/mariadb-build-error-with-xenial/3160 - - mysql -u root -e "DROP USER IF EXISTS 'travis'@'%';" - - mysql -u root -e "CREATE USER 'travis'@'%';" - - mysql -u root -e "CREATE DATABASE debug_toolbar;" - - mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'travis'@'%';"; - - tox -v - allow_failures: - - env: TOXENV=py36-djmaster-sqlite - - env: TOXENV=py37-djmaster-sqlite - - env: TOXENV=py38-djmaster-sqlite - - env: TOXENV=py38-djmaster-postgresql - - env: TOXENV=py38-djmaster-mariadb - -install: - - pip install tox codecov -script: - - tox -v -after_success: - - codecov diff --git a/Makefile b/Makefile index 05ff29203..0ddde5fe7 100644 --- a/Makefile +++ b/Makefile @@ -4,14 +4,14 @@ PRETTIER_TARGETS = '**/*.(css|js)' style: package-lock.json isort . - black --target-version=py35 . + black --target-version=py36 . flake8 npx eslint --ignore-path .gitignore --fix . npx prettier --ignore-path .gitignore --write $(PRETTIER_TARGETS) style_check: package-lock.json isort -c . - black --target-version=py35 --check . + black --target-version=py36 --check . flake8 npx eslint --ignore-path .gitignore . npx prettier --ignore-path .gitignore --check $(PRETTIER_TARGETS) @@ -36,11 +36,11 @@ test_selenium: coverage: python --version - coverage erase DJANGO_SETTINGS_MODULE=tests.settings \ python -b -W always -m coverage run -m django test -v2 $${TEST_ARGS:-tests} coverage report coverage html + coverage xml translatable_strings: cd debug_toolbar && python -m django makemessages -l en --no-obsolete diff --git a/README.rst b/README.rst index 5e02e88b3..ef98a9f70 100644 --- a/README.rst +++ b/README.rst @@ -6,14 +6,22 @@ Django Debug Toolbar :target: https://jazzband.co/ :alt: Jazzband -.. image:: https://travis-ci.org/jazzband/django-debug-toolbar.svg?branch=master - :target: https://travis-ci.org/jazzband/django-debug-toolbar +.. image:: https://github.com/jazzband/django-debug-toolbar/workflows/Test/badge.svg + :target: https://github.com/jazzband/django-debug-toolbar/actions :alt: Build Status .. image:: https://codecov.io/gh/jazzband/django-debug-toolbar/branch/master/graph/badge.svg :target: https://codecov.io/gh/jazzband/django-debug-toolbar :alt: Test coverage status +.. image:: https://img.shields.io/pypi/pyversions/django-debug-toolbar + :target: https://pypi.python.org/pypi/django-debug-toolbar + :alt: Supported Python versions + +.. image:: https://img.shields.io/pypi/djversions/django-debug-toolbar + :target: https://pypi.org/project/django-debug-toolbar + :alt: Supported Django versions + The Django Debug Toolbar is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel's content. @@ -26,7 +34,7 @@ Here's a screenshot of the toolbar in action: In addition to the built-in panels, a number of third-party panels are contributed by the community. -The current stable version of the Debug Toolbar is 3.1. It works on +The current stable version of the Debug Toolbar is 3.2. It works on Django ≥ 2.2. Documentation, including installation and configuration instructions, is diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index bd65d7d1c..bfaf7018b 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -183,7 +183,7 @@ def _store_call_info( trace=None, template_info=None, backend=None, - **kw + **kw, ): if name == "get": if return_value is None: diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py index 64c985b84..e80d8c93a 100644 --- a/debug_toolbar/panels/history/panel.py +++ b/debug_toolbar/panels/history/panel.py @@ -1,8 +1,6 @@ import json -import sys from collections import OrderedDict -from django.conf import settings from django.http.request import RawPostDataException from django.template.loader import render_to_string from django.templatetags.static import static @@ -51,14 +49,10 @@ def generate_stats(self, request, response): and request.body and request.META.get("CONTENT_TYPE") == "application/json" ): - # Python <= 3.5's json.loads expects a string. - data = json.loads( - request.body - if sys.version_info[:2] > (3, 5) - else request.body.decode( - request.encoding or settings.DEFAULT_CHARSET - ) - ) + try: + data = json.loads(request.body) + except ValueError: + pass except RawPostDataException: # It is not guaranteed that we may read the request data (again). data = None diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py index c75d0077b..95430e5bc 100644 --- a/debug_toolbar/panels/request.py +++ b/debug_toolbar/panels/request.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from debug_toolbar.panels import Panel -from debug_toolbar.utils import get_name_from_obj +from debug_toolbar.utils import get_name_from_obj, get_sorted_request_variable class RequestPanel(Panel): @@ -26,13 +26,12 @@ def nav_subtitle(self): def generate_stats(self, request, response): self.record_stats( { - "get": [(k, request.GET.getlist(k)) for k in sorted(request.GET)], - "post": [(k, request.POST.getlist(k)) for k in sorted(request.POST)], - "cookies": [ - (k, request.COOKIES.get(k)) for k in sorted(request.COOKIES) - ], + "get": get_sorted_request_variable(request.GET), + "post": get_sorted_request_variable(request.POST), + "cookies": get_sorted_request_variable(request.COOKIES), } ) + view_info = { "view_func": _(""), "view_args": "None", diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index 28003e224..78b9b7fe2 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -1,21 +1,25 @@ -{% load i18n %}{% load static %} +{% load i18n static %} +{% block css %} +{% endblock %} +{% block js %} +{% endblock %}
-
+
DJDT
diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index 30344dd03..cc5d74477 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -210,6 +210,16 @@ def getframeinfo(frame, context=1): return (filename, lineno, frame.f_code.co_name, lines, index) +def get_sorted_request_variable(variable): + """ + Get a sorted list of variables from the request data. + """ + if isinstance(variable, dict): + return [(k, variable.get(k)) for k in sorted(variable)] + else: + return [(k, variable.getlist(k)) for k in sorted(variable)] + + def get_stack(context=1): """ Get a list of records for a frame and all higher (calling) frames. diff --git a/docs/changes.rst b/docs/changes.rst index 76a4fe1b4..b2c8a3709 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -1,6 +1,23 @@ Change log ========== +3.2 (2020-12-03) +---------------- + +* Moved CI to GitHub Actions: https://github.com/jazzband/django-debug-toolbar/actions +* Stopped crashing when ``request.GET`` and ``request.POST`` are + dictionaries instead of ``QueryDict`` instances. This isn't a valid + use of Django but django-debug-toolbar shouldn't crash anyway. +* Fixed a crash in the history panel when sending a JSON POST request + with invalid JSON. +* Added missing signals to the signals panel by default. +* Documented how to avoid CORS errors now that we're using JavaScript + modules. +* Verified support for Python 3.9. +* Added a ``css`` and a ``js`` template block to + ``debug_toolbar/base.html`` to allow overriding CSS and JS. + + 3.2a1 (2020-10-19) ------------------ @@ -10,8 +27,7 @@ Change log * Continued refactoring the HTML and CSS code for simplicity, continued improving the use of semantic HTML. * Stopped caring about prehistoric browsers for good. Started splitting - up the JavaScript code to take advantage of - JavaScript modules. + up the JavaScript code to take advantage of JavaScript modules. * Continued removing unused CSS. * Started running Selenium tests on Travis CI. * Added a system check which prevents using django-debug-toolbar without @@ -29,6 +45,7 @@ Change log * Started spellchecking the documentation. * Removed calls to the deprecated ``request.is_ajax()`` method. These calls were unnecessary now that most endpoints return JSON anyway. +* Removed support for Python 3.5. 3.1 (2020-09-21) diff --git a/docs/conf.py b/docs/conf.py index 59d886ada..e29866a82 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ copyright = copyright.format(datetime.date.today().year) # The full version, including alpha/beta/rc tags -release = "3.2a1" +release = "3.2" # -- General configuration --------------------------------------------------- diff --git a/example/README.rst b/example/README.rst index 2b9f41fc2..94c09f8e5 100644 --- a/example/README.rst +++ b/example/README.rst @@ -41,8 +41,8 @@ Run the Django development server:: $ python example/manage.py runserver -You can change the database used by specifying the ``DJANGO_DATABASE_ENGINE`` +You can change the database used by specifying the ``DB_BACKEND`` environment variable:: - $ DJANGO_DATABASE_ENGINE=postgresql python example/manage.py migrate - $ DJANGO_DATABASE_ENGINE=postgresql python example/manage.py runserver + $ DB_BACKEND=postgresql python example/manage.py migrate + $ DB_BACKEND=postgresql python example/manage.py runserver diff --git a/example/settings.py b/example/settings.py index b04d283c8..dae1b591e 100644 --- a/example/settings.py +++ b/example/settings.py @@ -71,8 +71,8 @@ } } -# To use another database, set the DJANGO_DATABASE_ENGINE environment variable. -if os.environ.get("DJANGO_DATABASE_ENGINE", "").lower() == "postgresql": +# To use another database, set the DB_BACKEND environment variable. +if os.environ.get("DB_BACKEND", "").lower() == "postgresql": # % su postgres # % createuser debug_toolbar # % createdb debug_toolbar -O debug_toolbar @@ -83,7 +83,7 @@ "USER": "debug_toolbar", } } -if os.environ.get("DJANGO_DATABASE_ENGINE", "").lower() == "mysql": +if os.environ.get("DB_BACKEND", "").lower() == "mysql": # % mysql # mysql> CREATE DATABASE debug_toolbar; # mysql> CREATE USER 'debug_toolbar'@'localhost'; diff --git a/setup.cfg b/setup.cfg index 0f95e92dc..93a297b31 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = django-debug-toolbar -version = 3.2a1 +version = 3.2 description = A configurable set of panels that display various debug information about the current request/response. long_description = file: README.rst author = Rob Hudson @@ -22,14 +22,14 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Software Development :: Libraries :: Python Modules [options] -python_requires = >=3.5 +python_requires = >=3.6 install_requires = Django >= 2.2 sqlparse >= 0.2.0 diff --git a/tests/panels/test_history.py b/tests/panels/test_history.py index 6d65b6e9d..6d592bccf 100644 --- a/tests/panels/test_history.py +++ b/tests/panels/test_history.py @@ -30,7 +30,8 @@ def test_post(self): def test_post_json(self): for data, expected_stats_data in ( ({"foo": "bar"}, {"foo": "bar"}), - ("", {}), + ("", {}), # Empty JSON + ("'", {}), # Invalid JSON ): with self.subTest(data=data): self.request = rf.post( diff --git a/tests/panels/test_request.py b/tests/panels/test_request.py index 9e7f487fb..54410b81d 100644 --- a/tests/panels/test_request.py +++ b/tests/panels/test_request.py @@ -1,3 +1,5 @@ +from django.http import QueryDict + from ..base import BaseTestCase @@ -30,3 +32,55 @@ def test_insert_content(self): content = self.panel.content self.assertIn("nôt åscíì", content) self.assertValidHTML(content) + + def test_query_dict_for_request_in_method_get(self): + """ + Test verifies the correctness of the statistics generation method + in the case when the GET request is class QueryDict + """ + self.request.GET = QueryDict("foo=bar") + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + # ensure the panel GET request data is processed correctly. + content = self.panel.content + self.assertIn("foo", content) + self.assertIn("bar", content) + + def test_dict_for_request_in_method_get(self): + """ + Test verifies the correctness of the statistics generation method + in the case when the GET request is class Dict + """ + self.request.GET = {"foo": "bar"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + # ensure the panel GET request data is processed correctly. + content = self.panel.content + self.assertIn("foo", content) + self.assertIn("bar", content) + + def test_query_dict_for_request_in_method_post(self): + """ + Test verifies the correctness of the statistics generation method + in the case when the POST request is class QueryDict + """ + self.request.POST = QueryDict("foo=bar") + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + # ensure the panel POST request data is processed correctly. + content = self.panel.content + self.assertIn("foo", content) + self.assertIn("bar", content) + + def test_dict_for_request_in_method_post(self): + """ + Test verifies the correctness of the statistics generation method + in the case when the POST request is class Dict + """ + self.request.POST = {"foo": "bar"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + # ensure the panel POST request data is processed correctly. + content = self.panel.content + self.assertIn("foo", content) + self.assertIn("bar", content) diff --git a/tests/settings.py b/tests/settings.py index dbbbb79b2..0808c2e8c 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -79,17 +79,19 @@ "second": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}, } -if os.environ.get("DJANGO_DATABASE_ENGINE") == "postgresql": - DATABASES = { - "default": {"ENGINE": "django.db.backends.postgresql", "NAME": "debug-toolbar"} - } -elif os.environ.get("DJANGO_DATABASE_ENGINE") == "mysql": - DATABASES = { - "default": {"ENGINE": "django.db.backends.mysql", "NAME": "debug_toolbar"} - } -else: - DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3"}} - +DATABASES = { + "default": { + "ENGINE": "django.db.backends.%s" % os.getenv("DB_BACKEND"), + "NAME": os.getenv("DB_NAME"), + "USER": os.getenv("DB_USER"), + "PASSWORD": os.getenv("DB_PASSWORD"), + "HOST": os.getenv("DB_HOST", ""), + "PORT": os.getenv("DB_PORT", ""), + "TEST": { + "USER": "default_test", + }, + }, +} # Debug Toolbar configuration diff --git a/tests/test_integration.py b/tests/test_integration.py index 1ab053ab9..8c28f6c6e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -350,7 +350,7 @@ class DebugToolbarLiveTestCase(StaticLiveServerTestCase): def setUpClass(cls): super().setUpClass() options = Options() - options.headless = bool(os.environ.get("TRAVIS")) + options.headless = bool(os.environ.get("CI")) cls.selenium = webdriver.Firefox(options=options) @classmethod diff --git a/tox.ini b/tox.ini index b8b930b1c..1b0e2842f 100644 --- a/tox.ini +++ b/tox.ini @@ -3,10 +3,10 @@ envlist = docs style readme - py{35,36,37,38}-dj22-sqlite - py{36,37,38}-dj{30,31}-sqlite - py{36,37,38}-djmaster-sqlite - py{37,38}-dj{22,30,31}-{postgresql,mariadb} + py{36,37,38,39}-dj22-sqlite + py{36,37,38,39}-dj{30,31}-sqlite + py{36,37,38,39}-djmaster-sqlite + py{37,38,39}-dj{22,30,31}-{postgresql,mysql} [testenv] deps = @@ -15,7 +15,7 @@ deps = dj31: Django==3.1.* sqlite: mock postgresql: psycopg2-binary - mariadb: mysqlclient + mysql: mysqlclient djmaster: https://github.com/django/django/archive/master.tar.gz coverage Jinja2 @@ -23,13 +23,18 @@ deps = selenium sqlparse passenv= - TRAVIS - DJANGO_SELENIUM_TESTS + CI + DB_BACKEND + DB_NAME + DB_USER + DB_PASSWORD + DB_HOST + DB_PORT + GITHUB_* setenv = PYTHONPATH = {toxinidir} PYTHONWARNINGS = d - postgresql: DJANGO_DATABASE_ENGINE = postgresql - mariadb: DJANGO_DATABASE_ENGINE = mysql + py38-dj31-postgresql: DJANGO_SELENIUM_TESTS = true whitelist_externals = make pip_pre = True commands = make coverage TEST_ARGS='{posargs:tests}' @@ -41,7 +46,6 @@ deps = sphinxcontrib-spelling [testenv:style] -basepython = python3 commands = make style_check deps = black>=19.10b0 @@ -50,7 +54,19 @@ deps = skip_install = true [testenv:readme] -basepython = python3 commands = python setup.py check -r -s deps = readme_renderer skip_install = true + +[gh-actions] +python = + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39 + +[gh-actions:env] +DB_BACKEND = + mysql: mysql + postgresql: postgresql + sqlite3: sqlite From 3ef1a33f4832de7603922ac33e2b6eb2650e9755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 4 Dec 2020 16:03:39 +0100 Subject: [PATCH 5/8] Renamed setting and better explaination --- debug_toolbar/panels/sql/utils.py | 2 +- debug_toolbar/settings.py | 2 +- docs/configuration.rst | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/debug_toolbar/panels/sql/utils.py b/debug_toolbar/panels/sql/utils.py index f6aa029ad..b53cb2c80 100644 --- a/debug_toolbar/panels/sql/utils.py +++ b/debug_toolbar/panels/sql/utils.py @@ -33,7 +33,7 @@ def reformat_sql(sql, with_toggle=False): def parse_sql(sql, aligned_indent=False): stack = sqlparse.engine.FilterStack() - if dt_settings.get_config()["SQL_TOKEN_GROUPING"]: + if dt_settings.get_config()["ENABLE_SQL_TOKEN_GROUPING"]: stack.enable_grouping() if aligned_indent: stack.stmtprocess.append( diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index bced1990c..a1344421a 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -41,7 +41,7 @@ "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), "SQL_WARNING_THRESHOLD": 500, # milliseconds - "SQL_TOKEN_GROUPING": True, + "ENABLE_SQL_TOKEN_GROUPING": False, } diff --git a/docs/configuration.rst b/docs/configuration.rst index ae093e508..8ab7d660b 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -221,16 +221,44 @@ Panel options The SQL panel highlights queries that took more that this amount of time, in milliseconds, to execute. -* ``SQL_TOKEN_GROUPING`` +* ``ENABLE_SQL_TOKEN_GROUPING`` Default: ``True`` Panel: SQL Controls SQL token grouping. + + Token grouping allows pretty print of similar tokens, + like aligned indentation for every selected field. + When set to ``True``, it might cause render slowdowns when a view make long SQL textual queries. + Without grouping:: + + SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" + FROM "auth_user" + WHERE "auth_user"."username" = '''dzqqq''' + LIMIT 21 + + With grouping:: + + SELECT "auth_user"."id", + "auth_user"."password", + "auth_user"."last_login", + "auth_user"."is_superuser", + "auth_user"."username", + "auth_user"."first_name", + "auth_user"."last_name", + "auth_user"."email", + "auth_user"."is_staff", + "auth_user"."is_active", + "auth_user"."date_joined" + FROM "auth_user" + WHERE "auth_user"."username" = '''dqz''' + LIMIT 21 + Here's what a slightly customized toolbar configuration might look like:: # This example is unlikely to be appropriate for your project. From 3764cf7f5896c2438197368f47ecb48d74d9a615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 4 Dec 2020 16:05:28 +0100 Subject: [PATCH 6/8] Bold in config --- docs/configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 8ab7d660b..ceb4e7a39 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -235,14 +235,14 @@ Panel options When set to ``True``, it might cause render slowdowns when a view make long SQL textual queries. - Without grouping:: + **Without grouping**:: SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."username" = '''dzqqq''' LIMIT 21 - With grouping:: + **With grouping**:: SELECT "auth_user"."id", "auth_user"."password", From 67dc4fde15206cd6479908dc79d85bf6c8a0d3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 4 Dec 2020 16:06:43 +0100 Subject: [PATCH 7/8] Shorter example --- docs/configuration.rst | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index ceb4e7a39..39b648183 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -237,9 +237,9 @@ Panel options **Without grouping**:: - SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" + SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name" FROM "auth_user" - WHERE "auth_user"."username" = '''dzqqq''' + WHERE "auth_user"."username" = '''test_username''' LIMIT 21 **With grouping**:: @@ -251,12 +251,8 @@ Panel options "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", - "auth_user"."email", - "auth_user"."is_staff", - "auth_user"."is_active", - "auth_user"."date_joined" FROM "auth_user" - WHERE "auth_user"."username" = '''dqz''' + WHERE "auth_user"."username" = '''test_username''' LIMIT 21 Here's what a slightly customized toolbar configuration might look like:: From 137d705d68a67d127649260442cd83d43449808f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20B=C3=A9ranger?= Date: Fri, 4 Dec 2020 16:11:37 +0100 Subject: [PATCH 8/8] Enable token grouping by default --- debug_toolbar/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index a1344421a..6d97e8d9d 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -41,7 +41,7 @@ "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), "SQL_WARNING_THRESHOLD": 500, # milliseconds - "ENABLE_SQL_TOKEN_GROUPING": False, + "ENABLE_SQL_TOKEN_GROUPING": True, }