pax_global_header00006660000000000000000000000064141057545670014530gustar00rootroot0000000000000052 comment=f65d86ba7b99457ecb88dea3eaca3fd964c10bf1 django-debug-toolbar-3.2.2/000077500000000000000000000000001410575456700155225ustar00rootroot00000000000000django-debug-toolbar-3.2.2/.coveragerc000066400000000000000000000001161410575456700176410ustar00rootroot00000000000000[run] source = debug_toolbar branch = 1 [report] omit = *tests*,*migrations* django-debug-toolbar-3.2.2/.editorconfig000066400000000000000000000004051410575456700201760ustar00rootroot00000000000000# https://editorconfig.org/ root = true [*] indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true end_of_line = lf charset = utf-8 [*.html] indent_size = 2 [Makefile] indent_style = tab [*.bat] indent_style = tab django-debug-toolbar-3.2.2/.eslintrc.json000066400000000000000000000006451410575456700203230ustar00rootroot00000000000000{ "env": { "browser": true, "es6": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "rules": { "curly": ["error", "all"], "dot-notation": "error", "eqeqeq": "error", "no-eval": "error", "no-var": "error", "prefer-const": "error", "semi": "error" } } django-debug-toolbar-3.2.2/.github/000077500000000000000000000000001410575456700170625ustar00rootroot00000000000000django-debug-toolbar-3.2.2/.github/workflows/000077500000000000000000000000001410575456700211175ustar00rootroot00000000000000django-debug-toolbar-3.2.2/.github/workflows/release.yml000066400000000000000000000017641410575456700232720ustar00rootroot00000000000000name: 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 django-debug-toolbar-3.2.2/.github/workflows/test.yml000066400000000000000000000125051410575456700226240ustar00rootroot00000000000000name: 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: debug_toolbar 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_USER: root DB_PASSWORD: debug_toolbar 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_DB: debug_toolbar POSTGRES_USER: debug_toolbar POSTGRES_PASSWORD: debug_toolbar 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_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,packaging django-debug-toolbar-3.2.2/.gitignore000066400000000000000000000002651410575456700175150ustar00rootroot00000000000000*.pyc *.DS_Store *~ .idea build .coverage dist django_debug_toolbar.egg-info docs/_build example/db.sqlite3 htmlcov .tox node_modules package-lock.json geckodriver.log coverage.xml django-debug-toolbar-3.2.2/.tx/000077500000000000000000000000001410575456700162335ustar00rootroot00000000000000django-debug-toolbar-3.2.2/.tx/config000066400000000000000000000003571410575456700174300ustar00rootroot00000000000000[main] host = https://www.transifex.com lang_map = sr@latin:sr_Latn [django-debug-toolbar.main] file_filter = debug_toolbar/locale//LC_MESSAGES/django.po source_file = debug_toolbar/locale/en/LC_MESSAGES/django.po source_lang = en django-debug-toolbar-3.2.2/CONTRIBUTING.md000066400000000000000000000006761410575456700177640ustar00rootroot00000000000000[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/) This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the [guidelines](https://jazzband.co/about/guidelines). Please see the [full contributing documentation](https://django-debug-toolbar.readthedocs.io/en/stable/contributing.html) for more help. django-debug-toolbar-3.2.2/LICENSE000066400000000000000000000030061410575456700165260ustar00rootroot00000000000000Copyright (c) Rob Hudson and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Django nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-debug-toolbar-3.2.2/MANIFEST.in000066400000000000000000000002711410575456700172600ustar00rootroot00000000000000include LICENSE include README.rst include CONTRIBUTING.md recursive-include debug_toolbar/locale * recursive-include debug_toolbar/static * recursive-include debug_toolbar/templates * django-debug-toolbar-3.2.2/Makefile000066400000000000000000000034051410575456700171640ustar00rootroot00000000000000.PHONY: flake8 example test coverage translatable_strings update_translations PRETTIER_TARGETS = '**/*.(css|js)' style: package-lock.json isort . black --target-version=py36 . flake8 npx eslint --ignore-path .gitignore --fix . npx prettier --ignore-path .gitignore --write $(PRETTIER_TARGETS) ! grep -r '\(style=\|onclick=\| {% endblock %}
  • {% trans "Hide" %} »
  • {% for panel in toolbar.panels %} {% include "debug_toolbar/includes/panel_button.html" %} {% endfor %}
DJDT
{% for panel in toolbar.panels %} {% include "debug_toolbar/includes/panel_content.html" %} {% endfor %}
django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/includes/000077500000000000000000000000001410575456700267465ustar00rootroot00000000000000django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/includes/panel_button.html000066400000000000000000000015171410575456700323320ustar00rootroot00000000000000{% load i18n %}
  • {% if panel.has_content and panel.enabled %} {% else %} {% endif %}
  • django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/includes/panel_content.html000066400000000000000000000010651410575456700324670ustar00rootroot00000000000000{% load static %} {% if panel.has_content and panel.enabled %}

    {{ panel.title }}

    {% if toolbar.should_render_panels %}
    {{ panel.content }}
    {% else %}
    {% endif %}
    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/000077500000000000000000000000001410575456700264225ustar00rootroot00000000000000django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/cache.html000066400000000000000000000034551410575456700303620ustar00rootroot00000000000000{% load i18n %}

    {% trans "Summary" %}

    {% trans "Total calls" %} {% trans "Total time" %} {% trans "Cache hits" %} {% trans "Cache misses" %}
    {{ total_calls }} {{ total_time }} ms {{ hits }} {{ misses }}

    {% trans "Commands" %}

    {% for name in counts.keys %} {% endfor %} {% for value in counts.values %} {% endfor %}
    {{ name }}
    {{ value }}
    {% if calls %}

    {% trans "Calls" %}

    {% for call in calls %} {% endfor %}
    {% trans "Time (ms)" %} {% trans "Type" %} {% trans "Arguments" %} {% trans "Keyword arguments" %} {% trans "Backend" %}
    {{ call.time|floatformat:"4" }} {{ call.name|escape }} {{ call.args|escape }} {{ call.kwargs|escape }} {{ call.backend }}
    {{ call.trace }}
    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/headers.html000066400000000000000000000022271410575456700307260ustar00rootroot00000000000000{% load i18n %}

    {% trans "Request headers" %}

    {% for key, value in request_headers.items %} {% endfor %}
    {% trans "Key" %} {% trans "Value" %}
    {{ key|escape }} {{ value|escape }}

    {% trans "Response headers" %}

    {% for key, value in response_headers.items %} {% endfor %}
    {% trans "Key" %} {% trans "Value" %}
    {{ key|escape }} {{ value|escape }}

    {% trans "WSGI environ" %}

    {% trans "Since the WSGI environ inherits the environment of the server, only a significant subset is shown below." %}

    {% for key, value in environ.items %} {% endfor %}
    {% trans "Key" %} {% trans "Value" %}
    {{ key|escape }} {{ value|escape }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/history.html000066400000000000000000000013221410575456700310070ustar00rootroot00000000000000{% load i18n %}{% load static %}
    {{ refresh_form }}
    {% for id, store_context in stores.items %} {% include "debug_toolbar/panels/history_tr.html" %} {% endfor %}
    {% trans "Time" %} {% trans "Method" %} {% trans "Path" %} {% trans "Request Variables" %} {% trans "Status" %} {% trans "Action" %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/history_tr.html000066400000000000000000000033451410575456700315230ustar00rootroot00000000000000{% load i18n %} {{ store_context.toolbar.stats.HistoryPanel.time|escape }}

    {{ store_context.toolbar.stats.HistoryPanel.request_method|escape }}

    {{ store_context.toolbar.stats.HistoryPanel.request_url|truncatechars:100|escape }}

    {% for key, value in store_context.toolbar.stats.HistoryPanel.data.items %} {% empty %} {% endfor %}
    {% trans "Variable" %} {% trans "Value" %}
    {{ key|pprint }} {{ value|pprint }}
    No data

    {{ store_context.toolbar.stats.HistoryPanel.status_code|escape }}

    {{ store_context.form }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/logging.html000066400000000000000000000013161410575456700307370ustar00rootroot00000000000000{% load i18n %} {% if records %} {% for record in records %} {% endfor %}
    {% trans "Level" %} {% trans "Time" %} {% trans "Channel" %} {% trans "Message" %} {% trans "Location" %}
    {{ record.level }} {{ record.time|date:"h:i:s m/d/Y" }} {{ record.channel|default:"-" }} {{ record.message|linebreaksbr }} {{ record.file }}:{{ record.line }}
    {% else %}

    {% trans "No messages logged" %}.

    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/profiling.html000066400000000000000000000023151410575456700313020ustar00rootroot00000000000000{% load i18n %} {% for call in func_list %} {% endfor %}
    {% trans "Call" %} {% trans "CumTime" %} {% trans "Per" %} {% trans "TotTime" %} {% trans "Per" %} {% trans "Count" %}
    {% if call.has_subfuncs %} {% else %} {% endif %} {{ call.func_std_string }}
    {{ call.cumtime|floatformat:3 }} {{ call.cumtime_per_call|floatformat:3 }} {{ call.tottime|floatformat:3 }} {{ call.tottime_per_call|floatformat:3 }} {{ call.count }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/request.html000066400000000000000000000024221410575456700310000ustar00rootroot00000000000000{% load i18n %}

    {% trans "View information" %}

    {% trans "View function" %} {% trans "Arguments" %} {% trans "Keyword arguments" %} {% trans "URL name" %}
    {{ view_func }} {{ view_args|pprint }} {{ view_kwargs|pprint }} {{ view_urlname }}
    {% if cookies %}

    {% trans "Cookies" %}

    {% include 'debug_toolbar/panels/request_variables.html' with variables=cookies %} {% else %}

    {% trans "No cookies" %}

    {% endif %} {% if session %}

    {% trans "Session data" %}

    {% include 'debug_toolbar/panels/request_variables.html' with variables=session %} {% else %}

    {% trans "No session data" %}

    {% endif %} {% if get %}

    {% trans "GET data" %}

    {% include 'debug_toolbar/panels/request_variables.html' with variables=get %} {% else %}

    {% trans "No GET data" %}

    {% endif %} {% if post %}

    {% trans "POST data" %}

    {% include 'debug_toolbar/panels/request_variables.html' with variables=post %} {% else %}

    {% trans "No POST data" %}

    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/request_variables.html000066400000000000000000000006331410575456700330320ustar00rootroot00000000000000{% load i18n %} {% for key, value in variables %} {% endfor %}
    {% trans "Variable" %} {% trans "Value" %}
    {{ key|pprint }} {{ value|pprint }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/settings.html000066400000000000000000000005071410575456700311520ustar00rootroot00000000000000{% load i18n %} {% for name, value in settings.items %} {% endfor %}
    {% trans "Setting" %} {% trans "Value" %}
    {{ name }} {{ value|pprint }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/signals.html000066400000000000000000000005101410575456700307440ustar00rootroot00000000000000{% load i18n %} {% for name, receivers in signals %} {% endfor %}
    {% trans "Signal" %} {% trans "Receivers" %}
    {{ name|escape }} {{ receivers|join:", " }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/sql.html000066400000000000000000000132031410575456700301060ustar00rootroot00000000000000{% load i18n l10n %} {% if queries %} {% for query in queries %} {% endfor %}
    {% trans "Query" %} {% trans "Timeline" %} {% trans "Time (ms)" %} {% trans "Action" %}
    {{ query.sql|safe }}
    {% if query.similar_count %} {% blocktrans with count=query.similar_count %}{{ count }} similar queries.{% endblocktrans %} {% endif %} {% if query.duplicate_count %} {% blocktrans with dupes=query.duplicate_count %}Duplicated {{ dupes }} times.{% endblocktrans %} {% endif %}
    {% if query.starts_trans %} {% endif %} {% if query.ends_trans %} {% endif %} {{ query.duration|floatformat:"2" }} {% if query.params %} {% if query.is_select %}
    {{ query.form }} {% if query.vendor == 'mysql' %} {% endif %}
    {% endif %} {% endif %}

    {% trans "Connection:" %} {{ query.alias }}

    {% if query.iso_level %}

    {% trans "Isolation level:" %} {{ query.iso_level }}

    {% endif %} {% if query.trans_status %}

    {% trans "Transaction status:" %} {{ query.trans_status }}

    {% endif %} {% if query.stacktrace %}
    {{ query.stacktrace }}
    {% endif %} {% if query.template_info %} {% for line in query.template_info.context %} {% endfor %}
    {{ line.num }} {{ line.content }}

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

    {% endif %}
    {% else %}

    {% trans "No SQL queries were recorded during this request." %}

    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html000066400000000000000000000016261410575456700316340ustar00rootroot00000000000000{% load i18n %}

    {% trans "SQL explained" %}

    {% trans "Executed SQL" %}
    {{ sql|safe }}
    {% trans "Time" %}
    {{ duration }} ms
    {% trans "Database" %}
    {{ alias }}
    {% for h in headers %} {% endfor %} {% for row in result %} {% for column in row %} {% endfor %} {% endfor %}
    {{ h|upper }}
    {% if forloop.last %}{% endif %}{{ column|escape }}{% if forloop.last %}{% endif %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html000066400000000000000000000020171410575456700316270ustar00rootroot00000000000000{% load i18n %}

    {% trans "SQL profiled" %}

    {% if result %}
    {% trans "Executed SQL" %}
    {{ sql|safe }}
    {% trans "Time" %}
    {{ duration }} ms
    {% trans "Database" %}
    {{ alias }}
    {% for h in headers %} {% endfor %} {% for row in result %} {% for column in row %} {% endfor %} {% endfor %}
    {{ h|upper }}
    {{ column|escape }}
    {% else %}
    {% trans "Error" %}
    {{ result_error }}
    {% endif %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/sql_select.html000066400000000000000000000017041410575456700314500ustar00rootroot00000000000000{% load i18n %}

    {% trans "SQL selected" %}

    {% trans "Executed SQL" %}
    {{ sql|safe }}
    {% trans "Time" %}
    {{ duration }} ms
    {% trans "Database" %}
    {{ alias }}
    {% if result %} {% for h in headers %} {% endfor %} {% for row in result %} {% for column in row %} {% endfor %} {% endfor %}
    {{ h|upper }}
    {{ column|escape }}
    {% else %}

    {% trans "Empty set" %}

    {% endif %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html000066400000000000000000000005001410575456700323060ustar00rootroot00000000000000{% for s in stacktrace %}{{s.0}}/{{s.1}} in {{s.3}}({{s.2}}) {{s.4}} {% if show_locals %}
    {{s.5|pprint}}
    {% endif %} {% endfor %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html000066400000000000000000000033211410575456700316210ustar00rootroot00000000000000{% load i18n %}

    {% blocktrans count staticfiles_dirs|length as dirs_count %}Static file path{% plural %}Static file paths{% endblocktrans %}

    {% if staticfiles_dirs %}
      {% for prefix, staticfiles_dir in staticfiles_dirs %}
    1. {{ staticfiles_dir }}{% if prefix %} {% blocktrans %}(prefix {{ prefix }}){% endblocktrans %}{% endif %}
    2. {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %}

    {% blocktrans count staticfiles_apps|length as apps_count %}Static file app{% plural %}Static file apps{% endblocktrans %}

    {% if staticfiles_apps %}
      {% for static_app in staticfiles_apps %}
    1. {{ static_app }}
    2. {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %}

    {% blocktrans count staticfiles|length as staticfiles_count %}Static file{% plural %}Static files{% endblocktrans %}

    {% if staticfiles %}
    {% for staticfile in staticfiles %}
    {{ staticfile }}
    {{ staticfile.real_path }}
    {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %} {% for finder, payload in staticfiles_finders.items %}

    {{ finder }} ({% blocktrans count payload|length as payload_count %}{{ payload_count }} file{% plural %}{{ payload_count }} files{% endblocktrans %})

    {% for path, real_path in payload %} {% endfor %}
    {% trans 'Path' %} {% trans 'Location' %}
    {{ path }} {{ real_path }}
    {% endfor %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/template_source.html000066400000000000000000000005771410575456700325140ustar00rootroot00000000000000{% load i18n %}

    {% trans "Template source:" %} {{ template_name }}

    {% if not source.pygmentized %} {{ source }} {% else %} {{ source }} {% endif %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/templates.html000066400000000000000000000032451410575456700313120ustar00rootroot00000000000000{% load i18n %}

    {% blocktrans count template_dirs|length as template_count %}Template path{% plural %}Template paths{% endblocktrans %}

    {% if template_dirs %}
      {% for template in template_dirs %}
    1. {{ template }}
    2. {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %}

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

    {% if templates %}
    {% for template in templates %}
    {{ template.template.name|addslashes }}
    {{ template.template.origin_name|addslashes }}
    {% if template.context %}
    {% trans "Toggle context" %} {{ template.context }}
    {% endif %} {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %}

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

    {% if context_processors %}
    {% for key, value in context_processors.items %}
    {{ key|escape }}
    {% trans "Toggle context" %} {{ value|escape }}
    {% endfor %}
    {% else %}

    {% trans "None" %}

    {% endif %} django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/timer.html000066400000000000000000000020001410575456700304200ustar00rootroot00000000000000{% load i18n %}

    {% trans "Resource usage" %}

    {% for key, value in rows %} {% endfor %}
    {% trans "Resource" %} {% trans "Value" %}
    {{ key|escape }} {{ value|escape }}

    {% trans "Browser timing" %}

    {% trans "Timing attribute" %} {% trans "Timeline" %} {% trans "Milliseconds since navigation start (+length)" %}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/panels/versions.html000066400000000000000000000007421410575456700311630ustar00rootroot00000000000000{% load i18n %} {% for package, name, version in versions %} {% endfor %}
    {% trans "Package" %} {% trans "Name" %} {% trans "Version" %}
    {{ package }} {{ name }} {{ version }}
    django-debug-toolbar-3.2.2/debug_toolbar/templates/debug_toolbar/redirect.html000066400000000000000000000011571410575456700276330ustar00rootroot00000000000000{% load i18n static %} Django Debug Toolbar Redirects Panel: {{ status_line }}

    {{ status_line }}

    {% trans "Location:" %} {{ redirect_to }}

    {% trans "The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes. You can click the above link to continue with the redirect as normal." %}

    django-debug-toolbar-3.2.2/debug_toolbar/templatetags/000077500000000000000000000000001410575456700230245ustar00rootroot00000000000000django-debug-toolbar-3.2.2/debug_toolbar/templatetags/__init__.py000066400000000000000000000000001410575456700251230ustar00rootroot00000000000000django-debug-toolbar-3.2.2/debug_toolbar/toolbar.py000066400000000000000000000117241410575456700223530ustar00rootroot00000000000000""" The main DebugToolbar class that loads and renders the Toolbar. """ import uuid from collections import OrderedDict from django.apps import apps from django.core.exceptions import ImproperlyConfigured from django.template import TemplateSyntaxError from django.template.loader import render_to_string from django.urls import path, resolve from django.urls.exceptions import Resolver404 from django.utils.module_loading import import_string from debug_toolbar import settings as dt_settings class DebugToolbar: def __init__(self, request, get_response): self.request = request self.config = dt_settings.get_config().copy() panels = [] for panel_class in reversed(self.get_panel_classes()): panel = panel_class(self, get_response) panels.append(panel) if panel.enabled: get_response = panel.process_request self.process_request = get_response self._panels = OrderedDict() while panels: panel = panels.pop() self._panels[panel.panel_id] = panel self.stats = {} self.server_timing_stats = {} self.store_id = None # Manage panels @property def panels(self): """ Get a list of all available panels. """ return list(self._panels.values()) @property def enabled_panels(self): """ Get a list of panels enabled for the current request. """ return [panel for panel in self._panels.values() if panel.enabled] def get_panel_by_id(self, panel_id): """ Get the panel with the given id, which is the class name by default. """ return self._panels[panel_id] # Handle rendering the toolbar in HTML def render_toolbar(self): """ Renders the overall Toolbar with panels inside. """ if not self.should_render_panels(): self.store() try: context = {"toolbar": self} return render_to_string("debug_toolbar/base.html", context) except TemplateSyntaxError: if not apps.is_installed("django.contrib.staticfiles"): raise ImproperlyConfigured( "The debug toolbar requires the staticfiles contrib app. " "Add 'django.contrib.staticfiles' to INSTALLED_APPS and " "define STATIC_URL in your settings." ) else: raise def should_render_panels(self): """Determine whether the panels should be rendered during the request If False, the panels will be loaded via Ajax. """ render_panels = self.config["RENDER_PANELS"] if render_panels is None: render_panels = self.request.META["wsgi.multiprocess"] return render_panels # Handle storing toolbars in memory and fetching them later on _store = OrderedDict() def store(self): # Store already exists. if self.store_id: return self.store_id = uuid.uuid4().hex self._store[self.store_id] = self for _ in range(self.config["RESULTS_CACHE_SIZE"], len(self._store)): self._store.popitem(last=False) @classmethod def fetch(cls, store_id): return cls._store.get(store_id) # Manually implement class-level caching of panel classes and url patterns # because it's more obvious than going through an abstraction. _panel_classes = None @classmethod def get_panel_classes(cls): if cls._panel_classes is None: # Load panels in a temporary variable for thread safety. panel_classes = [ import_string(panel_path) for panel_path in dt_settings.get_panels() ] cls._panel_classes = panel_classes return cls._panel_classes _urlpatterns = None @classmethod def get_urls(cls): if cls._urlpatterns is None: from . import views # Load URLs in a temporary variable for thread safety. # Global URLs urlpatterns = [ path("render_panel/", views.render_panel, name="render_panel") ] # Per-panel URLs for panel_class in cls.get_panel_classes(): urlpatterns += panel_class.get_urls() cls._urlpatterns = urlpatterns return cls._urlpatterns @classmethod def is_toolbar_request(cls, request): """ Determine if the request is for a DebugToolbar view. """ # The primary caller of this function is in the middleware which may # not have resolver_match set. try: resolver_match = request.resolver_match or resolve( request.path, getattr(request, "urlconf", None) ) except Resolver404: return False return resolver_match.namespaces and resolver_match.namespaces[-1] == app_name app_name = "djdt" urlpatterns = DebugToolbar.get_urls() django-debug-toolbar-3.2.2/debug_toolbar/utils.py000066400000000000000000000215041410575456700220460ustar00rootroot00000000000000import inspect import os.path import re import sys from importlib import import_module from itertools import chain import django from django.core.exceptions import ImproperlyConfigured from django.template import Node from django.template.loader import render_to_string from django.utils.safestring import mark_safe from debug_toolbar import settings as dt_settings try: import threading except ImportError: threading = None # Figure out some paths django_path = os.path.realpath(os.path.dirname(django.__file__)) def get_module_path(module_name): try: module = import_module(module_name) except ImportError as e: raise ImproperlyConfigured("Error importing HIDE_IN_STACKTRACES: {}".format(e)) else: source_path = inspect.getsourcefile(module) if source_path.endswith("__init__.py"): source_path = os.path.dirname(source_path) return os.path.realpath(source_path) hidden_paths = [ get_module_path(module_name) for module_name in dt_settings.get_config()["HIDE_IN_STACKTRACES"] ] def omit_path(path): return any(path.startswith(hidden_path) for hidden_path in hidden_paths) 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) 3. Are the last entry (which is part of our stacktracing code) ``stack`` should be a list of frame tuples from ``inspect.stack()`` """ trace = [] for frame, path, line_no, func_name, text in (f[:5] for f in stack): if omit_path(os.path.realpath(path)): continue text = "".join(text).strip() if text else "" frame_locals = ( frame.f_locals if dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"] else None ) trace.append((path, line_no, func_name, text, frame_locals)) return trace def render_stacktrace(trace): stacktrace = [] for frame in trace: params = (v for v in chain(frame[0].rsplit(os.path.sep, 1), frame[1:])) params_dict = {str(idx): v for idx, v in enumerate(params)} try: stacktrace.append(params_dict) except KeyError: # This frame doesn't have the expected format, so skip it and move # on to the next one continue return mark_safe( render_to_string( "debug_toolbar/panels/sql_stacktrace.html", { "stacktrace": stacktrace, "show_locals": dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"], }, ) ) def get_template_info(): template_info = None cur_frame = sys._getframe().f_back try: while cur_frame is not None: in_utils_module = cur_frame.f_code.co_filename.endswith( "/debug_toolbar/utils.py" ) is_get_template_context = ( cur_frame.f_code.co_name == get_template_context.__name__ ) if in_utils_module and is_get_template_context: # If the method in the stack trace is this one # then break from the loop as it's being check recursively. break elif cur_frame.f_code.co_name == "render": node = cur_frame.f_locals["self"] context = cur_frame.f_locals["context"] if isinstance(node, Node): template_info = get_template_context(node, context) break cur_frame = cur_frame.f_back except Exception: pass del cur_frame return template_info def get_template_context(node, context, context_lines=3): line, source_lines, name = get_template_source_from_exception_info(node, context) debug_context = [] start = max(1, line - context_lines) end = line + 1 + context_lines for line_num, content in source_lines: if start <= line_num <= end: debug_context.append( {"num": line_num, "content": content, "highlight": (line_num == line)} ) return {"name": name, "context": debug_context} def get_template_source_from_exception_info(node, context): if context.template.origin == node.origin: exception_info = context.template.get_exception_info( Exception("DDT"), node.token ) else: exception_info = context.render_context.template.get_exception_info( Exception("DDT"), node.token ) line = exception_info["line"] source_lines = exception_info["source_lines"] name = exception_info["name"] return line, source_lines, name def get_name_from_obj(obj): if hasattr(obj, "__name__"): name = obj.__name__ else: name = obj.__class__.__name__ if hasattr(obj, "__module__"): module = obj.__module__ name = "{}.{}".format(module, name) return name def getframeinfo(frame, context=1): """ Get information about a frame or traceback object. A tuple of five things is returned: the filename, the line number of the current line, the function name, a list of lines of context from the source code, and the index of the current line within that list. The optional second argument specifies the number of lines of context to return, which are centered around the current line. This originally comes from ``inspect`` but is modified to handle issues with ``findsource()``. """ if inspect.istraceback(frame): lineno = frame.tb_lineno frame = frame.tb_frame else: lineno = frame.f_lineno if not inspect.isframe(frame): raise TypeError("arg is not a frame or traceback object") filename = inspect.getsourcefile(frame) or inspect.getfile(frame) if context > 0: start = lineno - 1 - context // 2 try: lines, lnum = inspect.findsource(frame) except Exception: # findsource raises platform-dependant exceptions first_lines = lines = index = None else: start = max(start, 1) start = max(0, min(start, len(lines) - context)) first_lines = lines[:2] lines = lines[start : (start + context)] index = lineno - 1 - start else: first_lines = lines = index = None # Code taken from Django's ExceptionReporter._get_lines_from_file if first_lines and isinstance(first_lines[0], bytes): encoding = "ascii" for line in first_lines[:2]: # File coding may be specified. Match pattern from PEP-263 # (https://www.python.org/dev/peps/pep-0263/) match = re.search(br"coding[:=]\s*([-\w.]+)", line) if match: encoding = match.group(1).decode("ascii") break lines = [line.decode(encoding, "replace") for line in lines] if hasattr(inspect, "Traceback"): return inspect.Traceback(filename, lineno, frame.f_code.co_name, lines, index) else: 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. Each record contains a frame object, filename, line number, function name, a list of lines of context, and index within the context. Modified version of ``inspect.stack()`` which calls our own ``getframeinfo()`` """ frame = sys._getframe(1) framelist = [] while frame: framelist.append((frame,) + getframeinfo(frame, context)) frame = frame.f_back return framelist class ThreadCollector: def __init__(self): if threading is None: raise NotImplementedError( "threading module is not available, " "this panel cannot be used without it" ) self.collections = {} # a dictionary that maps threads to collections def get_collection(self, thread=None): """ Returns a list of collected items for the provided thread, of if none is provided, returns a list for the current thread. """ if thread is None: thread = threading.current_thread() if thread not in self.collections: self.collections[thread] = [] return self.collections[thread] def clear_collection(self, thread=None): if thread is None: thread = threading.current_thread() if thread in self.collections: del self.collections[thread] def collect(self, item, thread=None): self.get_collection(thread).append(item) django-debug-toolbar-3.2.2/debug_toolbar/views.py000066400000000000000000000014611410575456700220430ustar00rootroot00000000000000from django.http import JsonResponse from django.utils.html import escape from django.utils.translation import gettext as _ from debug_toolbar.decorators import require_show_toolbar from debug_toolbar.toolbar import DebugToolbar @require_show_toolbar def render_panel(request): """Render the contents of a panel""" toolbar = DebugToolbar.fetch(request.GET["store_id"]) if toolbar is None: content = _( "Data for this panel isn't available anymore. " "Please reload the page and retry." ) content = "

    %s

    " % escape(content) scripts = [] else: panel = toolbar.get_panel_by_id(request.GET["panel_id"]) content = panel.content scripts = panel.scripts return JsonResponse({"content": content, "scripts": scripts}) django-debug-toolbar-3.2.2/docs/000077500000000000000000000000001410575456700164525ustar00rootroot00000000000000django-debug-toolbar-3.2.2/docs/Makefile000066400000000000000000000012001410575456700201030ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= -n -W SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) django-debug-toolbar-3.2.2/docs/changes.rst000066400000000000000000000407131410575456700206210ustar00rootroot00000000000000Change log ========== Next version ------------ 3.2.2 (2021-08-14) ------------------ * Ensured that the handle stays within bounds when resizing the window. * Disabled ``HistoryPanel`` when ``RENDER_PANELS`` is ``True`` or if ``RENDER_PANELS`` is ``None`` and the WSGI container is running with multiple processes. * Fixed ``RENDER_PANELS`` functionality so that when ``True`` panels are rendered during the request and not loaded asynchronously. * HistoryPanel now shows status codes of responses. * Support ``request.urlconf`` override when checking for toolbar requests. 3.2.1 (2021-04-14) ------------------ * Fixed SQL Injection vulnerability, CVE-2021-30459. The toolbar now calculates a signature on all fields for the SQL select, explain, and analyze forms. * Changed ``djdt.cookie.set()`` to set ``sameSite=Lax`` by default if callers do not provide a value. * Added ``PRETTIFY_SQL`` configuration option to support controlling SQL token grouping. By default it's set to True. When set to False, a performance improvement can be seen by the SQL panel. * Added a JavaScript event when a panel loads of the format ``djdt.panel.[PanelId]`` where PanelId is the ``panel_id`` property of the panel's Python class. Listening for this event corrects the bug in the Timer Panel in which it didn't insert the browser timings after switching requests in the History Panel. * Fixed issue with the toolbar expecting URL paths to start with ``/__debug__/`` while the documentation indicates it's not required. 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) ------------------ * Fixed a regression where the JavaScript code crashed with an invalid CSS selector when searching for an element to replace. * Replaced remaining images with CSS. * 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. * Continued removing unused CSS. * Started running Selenium tests on Travis CI. * Added a system check which prevents using django-debug-toolbar without any enabled panels. * Added :meth:`Panel.run_checks() ` for panels to verify the configuration before the application starts. * Validate the static file paths specified in ``STATICFILES_DIRS`` exist via :class:`~debug_toolbar.panels.staticfiles.StaticFilesPanel` * Introduced `prettier `__ to format the frontend code. * Started accessing history views using GET requests since they do not change state on the server. * Fixed a bug where unsuccessful requests (e.g. network errors) were silently ignored. * 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) ---------------- * Fixed a crash in the history panel when sending an empty JSON POST request. * Made ``make example`` also set up the database and a superuser account. * Added a Makefile target for regenerating the django-debug-toolbar screenshot. * Added automatic escaping of panel titles resp. disallowed HTML tags. * Removed some CSS * Restructured the SQL stats template. * Changed command line examples to prefer ``python -m pip`` to ``pip``. 3.0 (2020-09-20) ---------------- * Added an ``.editorconfig`` file specifying indentation rules etc. * Updated the Italian translation. * Added support for Django 3.1a1. ``fetch()`` and ``jQuery.ajax`` requests are now detected by the absence of a ``Accept: text/html`` header instead of the jQuery-specific ``X-Requested-With`` header on Django 3.1 or better. * Pruned unused CSS and removed hacks for ancient browsers. * Added the new :attr:`Panel.scripts ` property. This property should return a list of JavaScript resources to be loaded in the browser when displaying the panel. Right now, this is used by a single panel, the Timer panel. Third party panels can use this property to add scripts rather then embedding them in the content HTML. * Switched from JSHint to ESLint. Added an ESLint job to the Travis CI matrix. * Debug toolbar state which is only needed in the JavaScript code now uses ``localStorage``. * Updated the code to avoid a few deprecation warnings and resource warnings. * Started loading JavaScript as ES6 modules. * Added support for :meth:`cache.touch() ` when using django-debug-toolbar. * Eliminated more inline CSS. * Updated ``tox.ini`` and ``Makefile`` to use isort>=5. * Increased RESULTS_CACHE_SIZE to 25 to better support AJAX requests. * Fixed the close button CSS by explicitly specifying the ``box-sizing`` property. * Simplified the ``isort`` configuration by taking advantage of isort's ``black`` profile. * Added :class:`~debug_toolbar.panels.history.HistoryPanel` including support for AJAX requests. **Backwards incompatible changes** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Loading panel content no longer executes the scripts elements embedded in the HTML. Third party panels that require JavaScript resources should now use the :attr:`Panel.scripts ` property. * Removed support for end of life Django 1.11. The minimum supported Django is now 2.2. * The Debug Toolbar now loads a `JavaScript module`_. Typical local development using Django ``runserver`` is not impacted. However, if your application server and static files server are at different origins, you may see CORS errors in your browser's development console. See the "Cross-Origin Request Blocked" section of the :doc:`installation docs ` for details on how to resolve this issue. .. _JavaScript module: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules 2.2 (2020-01-31) ---------------- * Removed support for end of life Django 2.0 and 2.1. * Added support for Python 3.8. * Add locals() option for SQL panel. * Added support for Django 3.0. 2.1 (2019-11-12) ---------------- * Changed the Travis CI matrix to run style checks first. * Exposed the ``djdt.init`` function too. * Small improvements to the code to take advantage of newer Django APIs and avoid warnings because of deprecated code. * Verified compatibility with the upcoming Django 3.0 (at the time of writing). 2.0 (2019-06-20) ---------------- * Updated :class:`~debug_toolbar.panels.staticfiles.StaticFilesPanel` to be compatible with Django 3.0. * The :class:`~debug_toolbar.panels.profiling.ProfilingPanel` is now enabled but inactive by default. * Fixed toggling of table rows in the profiling panel UI. * The :class:`~debug_toolbar.panels.profiling.ProfilingPanel` no longer skips remaining panels or middlewares. * Improved the installation documentation. * Fixed a possible crash in the template panel. * Added support for psycopg2 ``Composed`` objects. * Changed the Jinja2 tests to use Django's own Jinja2 template backend. * Added instrumentation to queries using server side cursors. * Too many small improvements and cleanups to list them all. **Backwards incompatible changes** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Removed support for Python 2. * Removed support for Django's deprecated ``MIDDLEWARE_CLASSES`` setting. * Restructured :class:`debug_toolbar.panels.Panel` to execute more like the new-style Django MIDDLEWARE. The ``Panel.__init__()`` method is now passed ``get_response`` as the first positional argument. The :meth:`debug_toolbar.panels.Panel.process_request` method must now always return a response. Usually this is the response returned by ``get_response()`` but the panel may also return a different response as is the case in the :class:`~debug_toolbar.panels.redirects.RedirectsPanel`. Third party panels must adjust to this new architecture. ``Panel.process_response()`` and ``Panel.process_view()`` have been removed as a result of this change. The deprecated API, ``debug_toolbar.panels.DebugPanel``, has been removed. Third party panels should use :class:`debug_toolbar.panels.Panel` instead. The following deprecated settings have been removed: * ``HIDDEN_STACKTRACE_MODULES`` * ``HIDE_DJANGO_SQL`` * ``INTERCEPT_REDIRECTS`` * ``RESULTS_STORE_SIZE`` * ``ROOT_TAG_ATTRS`` * ``TAG`` 1.11 (2018-12-03) ----------------- * Use ``defer`` on all ``

    jQuery Test

    If you see this, jQuery is working.

    django-debug-toolbar-3.2.2/example/templates/mootools/000077500000000000000000000000001410575456700230265ustar00rootroot00000000000000django-debug-toolbar-3.2.2/example/templates/mootools/index.html000066400000000000000000000011021410575456700250150ustar00rootroot00000000000000 MooTools Test

    MooTools Test

    If you see this, MooTools is working.

    django-debug-toolbar-3.2.2/example/templates/prototype/000077500000000000000000000000001410575456700232205ustar00rootroot00000000000000django-debug-toolbar-3.2.2/example/templates/prototype/index.html000066400000000000000000000011201410575456700252070ustar00rootroot00000000000000 Prototype Test

    Prototype Test

    If you see this, Prototype is working.

    django-debug-toolbar-3.2.2/example/urls.py000066400000000000000000000010541410575456700205140ustar00rootroot00000000000000from django.contrib import admin from django.urls import include, path from django.views.generic import TemplateView import debug_toolbar urlpatterns = [ path("", TemplateView.as_view(template_name="index.html")), path("jquery/", TemplateView.as_view(template_name="jquery/index.html")), path("mootools/", TemplateView.as_view(template_name="mootools/index.html")), path("prototype/", TemplateView.as_view(template_name="prototype/index.html")), path("admin/", admin.site.urls), path("__debug__/", include(debug_toolbar.urls)), ] django-debug-toolbar-3.2.2/example/wsgi.py000066400000000000000000000003201410575456700204730ustar00rootroot00000000000000"""WSGI config for example project.""" import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") application = get_wsgi_application() django-debug-toolbar-3.2.2/package.json000066400000000000000000000001351410575456700200070ustar00rootroot00000000000000{ "devDependencies": { "eslint": "^7.10.0", "prettier": "^2.1.2" } } django-debug-toolbar-3.2.2/requirements_dev.txt000066400000000000000000000003041410575456700216410ustar00rootroot00000000000000# Runtime dependencies Django sqlparse Jinja2 # Testing coverage flake8 html5lib isort selenium tox black # Documentation Sphinx sphinxcontrib-spelling # Other tools transifex-client wheel django-debug-toolbar-3.2.2/setup.cfg000066400000000000000000000026621410575456700173510ustar00rootroot00000000000000[metadata] name = django-debug-toolbar version = 3.2.2 description = A configurable set of panels that display various debug information about the current request/response. long_description = file: README.rst long_description_content_type = text/x-rst author = Rob Hudson author_email = rob@cogit8.org url = https://github.com/jazzband/django-debug-toolbar download_url = https://pypi.org/project/django-debug-toolbar/ license = BSD license_files = LICENSE classifiers = Development Status :: 5 - Production/Stable Environment :: Web Environment Framework :: Django Framework :: Django :: 2.2 Framework :: Django :: 3.1 Framework :: Django :: 3.2 Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only 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.6 install_requires = Django >= 2.2 sqlparse >= 0.2.0 packages = find: include_package_data = true zip_safe = false [options.packages.find] exclude = example tests tests.* [flake8] extend-ignore = E203, E501 [isort] combine_as_imports = true profile = black django-debug-toolbar-3.2.2/setup.py000077500000000000000000000000761410575456700172420ustar00rootroot00000000000000#!/usr/bin/env python3 from setuptools import setup setup() django-debug-toolbar-3.2.2/tests/000077500000000000000000000000001410575456700166645ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/__init__.py000066400000000000000000000012511410575456700207740ustar00rootroot00000000000000# Refresh the debug toolbar's configuration when overriding settings. from django.dispatch import receiver from django.test.signals import setting_changed from debug_toolbar import settings as dt_settings from debug_toolbar.toolbar import DebugToolbar @receiver(setting_changed) def update_toolbar_config(**kwargs): if kwargs["setting"] == "DEBUG_TOOLBAR_CONFIG": dt_settings.get_config.cache_clear() @receiver(setting_changed) def update_toolbar_panels(**kwargs): if kwargs["setting"] == "DEBUG_TOOLBAR_PANELS": dt_settings.get_panels.cache_clear() DebugToolbar._panel_classes = None # Not implemented: invalidate debug_toolbar.urls. django-debug-toolbar-3.2.2/tests/additional_static/000077500000000000000000000000001410575456700223435ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/additional_static/base.css000066400000000000000000000000331410575456700237630ustar00rootroot00000000000000body { color: green; } django-debug-toolbar-3.2.2/tests/base.py000066400000000000000000000035371410575456700201600ustar00rootroot00000000000000import html5lib from django.http import HttpResponse from django.test import RequestFactory, TestCase from debug_toolbar.toolbar import DebugToolbar rf = RequestFactory() class BaseTestCase(TestCase): panel_id = None def setUp(self): super().setUp() self._get_response = lambda request: HttpResponse() self.request = rf.get("/") self.toolbar = DebugToolbar(self.request, self.get_response) self.toolbar.stats = {} if self.panel_id: self.panel = self.toolbar.get_panel_by_id(self.panel_id) self.panel.enable_instrumentation() else: self.panel = None def tearDown(self): if self.panel: self.panel.disable_instrumentation() super().tearDown() def get_response(self, request): return self._get_response(request) def assertValidHTML(self, content, msg=None): parser = html5lib.HTMLParser() parser.parseFragment(self.panel.content) if parser.errors: default_msg = ["Content is invalid HTML:"] lines = content.split("\n") for position, errorcode, datavars in parser.errors: default_msg.append(" %s" % html5lib.constants.E[errorcode] % datavars) default_msg.append(" %s" % lines[position[0] - 1]) msg = self._formatMessage(msg, "\n".join(default_msg)) raise self.failureException(msg) class IntegrationTestCase(TestCase): """Base TestCase for tests involving clients making requests.""" def setUp(self): # The HistoryPanel keeps track of previous stores in memory. # This bleeds into other tests and violates their idempotency. # Clear the store before each test. for key in list(DebugToolbar._store.keys()): del DebugToolbar._store[key] super().setUp() django-debug-toolbar-3.2.2/tests/commands/000077500000000000000000000000001410575456700204655ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/commands/__init__.py000066400000000000000000000000001410575456700225640ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/commands/test_debugsqlshell.py000066400000000000000000000024121410575456700247330ustar00rootroot00000000000000import io import sys import django from django.contrib.auth.models import User from django.core import management from django.db import connection from django.test import TestCase from django.test.utils import override_settings if connection.vendor == "postgresql" and django.VERSION >= (3, 0, 0): from django.db.backends.postgresql import base as base_module else: from django.db.backends import utils as base_module @override_settings(DEBUG=True) class DebugSQLShellTestCase(TestCase): def setUp(self): self.original_wrapper = base_module.CursorDebugWrapper # Since debugsqlshell monkey-patches django.db.backends.utils, we can # test it simply by loading it, without executing it. But we have to # undo the monkey-patch on exit. command_name = "debugsqlshell" app_name = management.get_commands()[command_name] management.load_command_class(app_name, command_name) def tearDown(self): base_module.CursorDebugWrapper = self.original_wrapper def test_command(self): original_stdout, sys.stdout = sys.stdout, io.StringIO() try: User.objects.count() self.assertIn("SELECT COUNT", sys.stdout.getvalue()) finally: sys.stdout = original_stdout django-debug-toolbar-3.2.2/tests/context_processors.py000066400000000000000000000000701410575456700232010ustar00rootroot00000000000000def broken(request): request.non_existing_attribute django-debug-toolbar-3.2.2/tests/forms.py000066400000000000000000000003341410575456700203640ustar00rootroot00000000000000from django import forms from django.contrib.auth.models import User class TemplateReprForm(forms.Form): user = forms.ModelChoiceField(queryset=User.objects.all()) def __repr__(self): return str(self) django-debug-toolbar-3.2.2/tests/loaders.py000066400000000000000000000005211410575456700206650ustar00rootroot00000000000000from django.contrib.auth.models import User from django.template.loaders.app_directories import Loader class LoaderWithSQL(Loader): def get_template(self, *args, **kwargs): # Force the template loader to run some SQL. Simulates a CMS. User.objects.all().count() return super().get_template(*args, **kwargs) django-debug-toolbar-3.2.2/tests/models.py000066400000000000000000000007411410575456700205230ustar00rootroot00000000000000from django.db import models class NonAsciiRepr: def __repr__(self): return "nôt åscíì" class Binary(models.Model): field = models.BinaryField() try: from django.db.models import JSONField except ImportError: # Django<3.1 try: from django.contrib.postgres.fields import JSONField except ImportError: # psycopg2 not installed JSONField = None if JSONField: class PostgresJSON(models.Model): field = JSONField() django-debug-toolbar-3.2.2/tests/panels/000077500000000000000000000000001410575456700201465ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/panels/__init__.py000066400000000000000000000000001410575456700222450ustar00rootroot00000000000000django-debug-toolbar-3.2.2/tests/panels/test_cache.py000066400000000000000000000043161410575456700226260ustar00rootroot00000000000000from django.core import cache from ..base import BaseTestCase class CachePanelTestCase(BaseTestCase): panel_id = "CachePanel" def test_recording(self): self.assertEqual(len(self.panel.calls), 0) cache.cache.set("foo", "bar") cache.cache.get("foo") cache.cache.delete("foo") self.assertFalse(cache.cache.touch("foo")) cache.cache.set("foo", "bar") self.assertTrue(cache.cache.touch("foo")) # Verify that the cache has a valid clear method. cache.cache.clear() self.assertEqual(len(self.panel.calls), 7) def test_recording_caches(self): self.assertEqual(len(self.panel.calls), 0) default_cache = cache.caches[cache.DEFAULT_CACHE_ALIAS] second_cache = cache.caches["second"] default_cache.set("foo", "bar") second_cache.get("foo") self.assertEqual(len(self.panel.calls), 2) def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ cache.cache.get("café") response = self.panel.process_request(self.request) # ensure the panel does not have content yet. self.assertNotIn("café", self.panel.content) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly. content = self.panel.content self.assertIn("café", content) self.assertValidHTML(content) def test_generate_server_timing(self): self.assertEqual(len(self.panel.calls), 0) cache.cache.set("foo", "bar") cache.cache.get("foo") cache.cache.delete("foo") self.assertEqual(len(self.panel.calls), 3) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.panel.generate_server_timing(self.request, response) stats = self.panel.get_stats() expected_data = { "total_time": { "title": "Cache {} Calls".format(stats["total_calls"]), "value": stats["total_time"], } } self.assertEqual(self.panel.get_server_timing_stats(), expected_data) django-debug-toolbar-3.2.2/tests/panels/test_custom.py000066400000000000000000000027751410575456700231040ustar00rootroot00000000000000from django.test import override_settings from debug_toolbar.panels import Panel from ..base import IntegrationTestCase class CustomPanel(Panel): def title(self): return "Title with special chars &\"'<>" @override_settings( DEBUG=True, DEBUG_TOOLBAR_PANELS=["tests.panels.test_custom.CustomPanel"] ) class CustomPanelTestCase(IntegrationTestCase): def test_escapes_panel_title(self): response = self.client.get("/regular/basic/") self.assertContains( response, """
  • Title with special chars &"'<>
  • """, html=True, ) self.assertContains( response, """

    Title with special chars &"'<>

    """, html=True, ) django-debug-toolbar-3.2.2/tests/panels/test_history.py000066400000000000000000000150041410575456700232600ustar00rootroot00000000000000import html from django.test import RequestFactory, override_settings from django.urls import resolve, reverse from debug_toolbar.forms import SignedDataForm from debug_toolbar.toolbar import DebugToolbar from ..base import BaseTestCase, IntegrationTestCase rf = RequestFactory() class HistoryPanelTestCase(BaseTestCase): panel_id = "HistoryPanel" def test_disabled(self): config = {"DISABLE_PANELS": {"debug_toolbar.panels.history.HistoryPanel"}} self.assertTrue(self.panel.enabled) with self.settings(DEBUG_TOOLBAR_CONFIG=config): self.assertFalse(self.panel.enabled) def test_post(self): self.request = rf.post("/", data={"foo": "bar"}) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) data = self.panel.get_stats()["data"] self.assertEqual(data["foo"], "bar") 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( "/", data=data, content_type="application/json", CONTENT_TYPE="application/json", # Force django test client to add the content-type even if no data ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) data = self.panel.get_stats()["data"] self.assertDictEqual(data, expected_stats_data) def test_urls(self): self.assertEqual( reverse("djdt:history_sidebar"), "/__debug__/history_sidebar/", ) self.assertEqual( resolve("/__debug__/history_sidebar/").url_name, "history_sidebar", ) self.assertEqual( reverse("djdt:history_refresh"), "/__debug__/history_refresh/", ) self.assertEqual( resolve("/__debug__/history_refresh/").url_name, "history_refresh", ) @override_settings(DEBUG=True) class HistoryViewsTestCase(IntegrationTestCase): PANEL_KEYS = { "VersionsPanel", "TimerPanel", "SettingsPanel", "HeadersPanel", "RequestPanel", "SQLPanel", "StaticFilesPanel", "TemplatesPanel", "CachePanel", "SignalsPanel", "LoggingPanel", "ProfilingPanel", } def test_history_panel_integration_content(self): """Verify the history panel's content renders properly..""" self.assertEqual(len(DebugToolbar._store), 0) data = {"foo": "bar"} self.client.get("/json_view/", data, content_type="application/json") # Check the history panel's stats to verify the toolbar rendered properly. self.assertEqual(len(DebugToolbar._store), 1) toolbar = list(DebugToolbar._store.values())[0] content = toolbar.get_panel_by_id("HistoryPanel").content self.assertIn("bar", content) def test_history_sidebar_invalid(self): response = self.client.get(reverse("djdt:history_sidebar")) self.assertEqual(response.status_code, 400) data = {"signed": SignedDataForm.sign({"store_id": "foo"}) + "invalid"} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 400) def test_history_sidebar(self): """Validate the history sidebar view.""" self.client.get("/json_view/") store_id = list(DebugToolbar._store)[0] data = {"signed": SignedDataForm.sign({"store_id": store_id})} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 200) self.assertEqual( set(response.json()), self.PANEL_KEYS, ) @override_settings( DEBUG_TOOLBAR_CONFIG={"RESULTS_CACHE_SIZE": 1, "RENDER_PANELS": False} ) def test_history_sidebar_expired_store_id(self): """Validate the history sidebar view.""" self.client.get("/json_view/") store_id = list(DebugToolbar._store)[0] data = {"signed": SignedDataForm.sign({"store_id": store_id})} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 200) self.assertEqual( set(response.json()), self.PANEL_KEYS, ) self.client.get("/json_view/") # Querying old store_id should return in empty response data = {"signed": SignedDataForm.sign({"store_id": store_id})} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), {}) # Querying with latest store_id latest_store_id = list(DebugToolbar._store)[0] data = {"signed": SignedDataForm.sign({"store_id": latest_store_id})} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 200) self.assertEqual( set(response.json()), self.PANEL_KEYS, ) def test_history_refresh_invalid_signature(self): response = self.client.get(reverse("djdt:history_refresh")) self.assertEqual(response.status_code, 400) data = {"signed": "eyJzdG9yZV9pZCI6ImZvbyIsImhhc2giOiI4YWFiMzIzZGZhODIyMW"} response = self.client.get(reverse("djdt:history_refresh"), data=data) self.assertEqual(response.status_code, 400) self.assertEqual(b"Invalid signature", response.content) def test_history_refresh(self): """Verify refresh history response has request variables.""" data = {"foo": "bar"} self.client.get("/json_view/", data, content_type="application/json") data = {"signed": SignedDataForm.sign({"store_id": "foo"})} response = self.client.get(reverse("djdt:history_refresh"), data=data) self.assertEqual(response.status_code, 200) data = response.json() self.assertEqual(len(data["requests"]), 1) store_id = list(DebugToolbar._store)[0] signature = SignedDataForm.sign({"store_id": store_id}) self.assertIn(html.escape(signature), data["requests"][0]["content"]) for val in ["foo", "bar"]: self.assertIn(val, data["requests"][0]["content"]) django-debug-toolbar-3.2.2/tests/panels/test_logging.py000066400000000000000000000057061410575456700232150ustar00rootroot00000000000000import logging from debug_toolbar.panels.logging import ( MESSAGE_IF_STRING_REPRESENTATION_INVALID, collector, ) from ..base import BaseTestCase from ..views import regular_view class LoggingPanelTestCase(BaseTestCase): panel_id = "LoggingPanel" def setUp(self): super().setUp() self.logger = logging.getLogger(__name__) collector.clear_collection() # Assume the root logger has been configured with level=DEBUG. # Previously DDT forcefully set this itself to 0 (NOTSET). logging.root.setLevel(logging.DEBUG) def test_happy_case(self): def view(request): self.logger.info("Nothing to see here, move along!") return regular_view(request, "logging") self._get_response = view response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) records = self.panel.get_stats()["records"] self.assertEqual(1, len(records)) self.assertEqual("Nothing to see here, move along!", records[0]["message"]) def test_formatting(self): def view(request): self.logger.info("There are %d %s", 5, "apples") return regular_view(request, "logging") self._get_response = view response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) records = self.panel.get_stats()["records"] self.assertEqual(1, len(records)) self.assertEqual("There are 5 apples", records[0]["message"]) def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ def view(request): self.logger.info("café") return regular_view(request, "logging") self._get_response = view response = self.panel.process_request(self.request) # ensure the panel does not have content yet. self.assertNotIn("café", self.panel.content) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly. content = self.panel.content self.assertIn("café", content) self.assertValidHTML(content) def test_failing_formatting(self): class BadClass: def __str__(self): raise Exception("Please not stringify me!") def view(request): # should not raise exception, but fail silently self.logger.debug("This class is misbehaving: %s", BadClass()) return regular_view(request, "logging") self._get_response = view response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) records = self.panel.get_stats()["records"] self.assertEqual(1, len(records)) self.assertEqual( MESSAGE_IF_STRING_REPRESENTATION_INVALID, records[0]["message"] ) django-debug-toolbar-3.2.2/tests/panels/test_profiling.py000066400000000000000000000061121410575456700235500ustar00rootroot00000000000000from django.contrib.auth.models import User from django.db import IntegrityError, transaction from django.http import HttpResponse from django.test.utils import override_settings from ..base import BaseTestCase, IntegrationTestCase from ..views import listcomp_view, regular_view @override_settings( DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.profiling.ProfilingPanel"] ) class ProfilingPanelTestCase(BaseTestCase): panel_id = "ProfilingPanel" def test_regular_view(self): self._get_response = lambda request: regular_view(request, "profiling") response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.assertIn("func_list", self.panel.get_stats()) self.assertIn("regular_view", self.panel.content) def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ self._get_response = lambda request: regular_view(request, "profiling") response = self.panel.process_request(self.request) # ensure the panel does not have content yet. self.assertNotIn("regular_view", self.panel.content) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly. content = self.panel.content self.assertIn("regular_view", content) self.assertValidHTML(content) def test_listcomp_escaped(self): self._get_response = lambda request: listcomp_view(request) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) content = self.panel.content self.assertNotIn('', content) self.assertIn('<listcomp>', content) def test_generate_stats_no_profiler(self): """ Test generating stats with no profiler. """ response = HttpResponse() self.assertIsNone(self.panel.generate_stats(self.request, response)) def test_generate_stats_no_root_func(self): """ Test generating stats using profiler without root function. """ response = self.panel.process_request(self.request) self.panel.profiler.clear() self.panel.profiler.enable() self.panel.profiler.disable() self.panel.generate_stats(self.request, response) self.assertNotIn("func_list", self.panel.get_stats()) @override_settings( DEBUG=True, DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.profiling.ProfilingPanel"] ) class ProfilingPanelIntegrationTestCase(IntegrationTestCase): def test_view_executed_once(self): self.assertEqual(User.objects.count(), 0) response = self.client.get("/new_user/") self.assertContains(response, "Profiling") self.assertEqual(User.objects.count(), 1) with self.assertRaises(IntegrityError): with transaction.atomic(): response = self.client.get("/new_user/") self.assertEqual(User.objects.count(), 1) django-debug-toolbar-3.2.2/tests/panels/test_redirects.py000066400000000000000000000055171410575456700235530ustar00rootroot00000000000000import copy from django.conf import settings from django.http import HttpResponse from ..base import BaseTestCase class RedirectsPanelTestCase(BaseTestCase): panel_id = "RedirectsPanel" def test_regular_response(self): not_redirect = HttpResponse() self._get_response = lambda request: not_redirect response = self.panel.process_request(self.request) self.assertTrue(response is not_redirect) def test_not_a_redirect(self): redirect = HttpResponse(status=304) self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertTrue(response is redirect) def test_redirect(self): redirect = HttpResponse(status=302) redirect["Location"] = "http://somewhere/else/" self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertFalse(response is redirect) self.assertContains(response, "302 Found") self.assertContains(response, "http://somewhere/else/") def test_redirect_with_broken_context_processor(self): TEMPLATES = copy.deepcopy(settings.TEMPLATES) TEMPLATES[1]["OPTIONS"]["context_processors"] = [ "tests.context_processors.broken" ] with self.settings(TEMPLATES=TEMPLATES): redirect = HttpResponse(status=302) redirect["Location"] = "http://somewhere/else/" self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertFalse(response is redirect) self.assertContains(response, "302 Found") self.assertContains(response, "http://somewhere/else/") def test_unknown_status_code(self): redirect = HttpResponse(status=369) redirect["Location"] = "http://somewhere/else/" self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertContains(response, "369 Unknown Status Code") def test_unknown_status_code_with_reason(self): redirect = HttpResponse(status=369, reason="Look Ma!") redirect["Location"] = "http://somewhere/else/" self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertContains(response, "369 Look Ma!") def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ redirect = HttpResponse(status=304) self._get_response = lambda request: redirect response = self.panel.process_request(self.request) self.assertIsNotNone(response) response = self.panel.generate_stats(self.request, redirect) self.assertIsNone(response) django-debug-toolbar-3.2.2/tests/panels/test_request.py000066400000000000000000000074041410575456700232540ustar00rootroot00000000000000from django.http import QueryDict from ..base import BaseTestCase class RequestPanelTestCase(BaseTestCase): panel_id = "RequestPanel" def test_non_ascii_session(self): self.request.session = {"où": "où"} response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.assertIn("où", self.panel.content) def test_object_with_non_ascii_repr_in_request_params(self): self.request.path = "/non_ascii_request/" response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.assertIn("nôt åscíì", self.panel.content) def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ self.request.path = "/non_ascii_request/" response = self.panel.process_request(self.request) # ensure the panel does not have content yet. self.assertNotIn("nôt åscíì", self.panel.content) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly. 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) def test_namespaced_url(self): self.request.path = "/admin/login/" response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) panel_stats = self.panel.get_stats() self.assertEqual(panel_stats["view_urlname"], "admin:login") django-debug-toolbar-3.2.2/tests/panels/test_settings.py000066400000000000000000000024301410575456700234160ustar00rootroot00000000000000from django.test import override_settings from ..base import IntegrationTestCase @override_settings(DEBUG=True) class SettingsIntegrationTestCase(IntegrationTestCase): def test_panel_title(self): response = self.client.get("/regular/basic/") # The settings module is None due to using Django's UserSettingsHolder # in tests. self.assertContains( response, """
  • Settings
  • """, html=True, ) self.assertContains( response, """

    Settings from None

    """, html=True, ) django-debug-toolbar-3.2.2/tests/panels/test_sql.py000066400000000000000000000404571410575456700223700ustar00rootroot00000000000000import datetime import os import unittest from unittest.mock import patch import django from django.contrib.auth.models import User from django.db import connection from django.db.models import Count from django.db.utils import DatabaseError from django.shortcuts import render from django.test.utils import override_settings import debug_toolbar.panels.sql.tracking as sql_tracking from debug_toolbar import settings as dt_settings from ..base import BaseTestCase try: from psycopg2._json import Json as PostgresJson except ImportError: PostgresJson = None if connection.vendor == "postgresql": from ..models import PostgresJSON as PostgresJSONModel else: PostgresJSONModel = None class SQLPanelTestCase(BaseTestCase): panel_id = "SQLPanel" def test_disabled(self): config = {"DISABLE_PANELS": {"debug_toolbar.panels.sql.SQLPanel"}} self.assertTrue(self.panel.enabled) with self.settings(DEBUG_TOOLBAR_CONFIG=config): self.assertFalse(self.panel.enabled) def test_recording(self): self.assertEqual(len(self.panel._queries), 0) list(User.objects.all()) # ensure query was logged self.assertEqual(len(self.panel._queries), 1) query = self.panel._queries[0] 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 populated self.assertTrue(len(query[1]["stacktrace"]) > 0) @unittest.skipUnless( connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) def test_recording_chunked_cursor(self): self.assertEqual(len(self.panel._queries), 0) list(User.objects.all().iterator()) # ensure query was logged self.assertEqual(len(self.panel._queries), 1) @patch("debug_toolbar.panels.sql.tracking.state", wraps=sql_tracking.state) def test_cursor_wrapper_singleton(self, mock_state): list(User.objects.all()) # ensure that cursor wrapping is applied only once self.assertEqual(mock_state.Wrapper.call_count, 1) @patch("debug_toolbar.panels.sql.tracking.state", wraps=sql_tracking.state) def test_chunked_cursor_wrapper_singleton(self, mock_state): list(User.objects.all().iterator()) # ensure that cursor wrapping is applied only once self.assertEqual(mock_state.Wrapper.call_count, 1) def test_generate_server_timing(self): self.assertEqual(len(self.panel._queries), 0) list(User.objects.all()) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.panel.generate_server_timing(self.request, response) # ensure query was logged self.assertEqual(len(self.panel._queries), 1) query = self.panel._queries[0] expected_data = { "sql_time": {"title": "SQL 1 queries", "value": query[1]["duration"]} } self.assertEqual(self.panel.get_server_timing_stats(), expected_data) def test_non_ascii_query(self): self.assertEqual(len(self.panel._queries), 0) # non-ASCII text query list(User.objects.extra(where=["username = 'apéro'"])) self.assertEqual(len(self.panel._queries), 1) # non-ASCII text parameters list(User.objects.filter(username="thé")) self.assertEqual(len(self.panel._queries), 2) # non-ASCII bytes parameters list(User.objects.filter(username="café".encode())) self.assertEqual(len(self.panel._queries), 3) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly self.assertIn("café", self.panel.content) def test_param_conversion(self): self.assertEqual(len(self.panel._queries), 0) list( User.objects.filter(first_name="Foo") .filter(is_staff=True) .filter(is_superuser=False) ) list( User.objects.annotate(group_count=Count("groups__id")) .filter(group_count__lt=10) .filter(group_count__gt=1) ) list(User.objects.filter(date_joined=datetime.datetime(2017, 12, 22, 16, 7, 1))) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) # ensure query was logged self.assertEqual(len(self.panel._queries), 3) if django.VERSION >= (3, 1): self.assertEqual( tuple([q[1]["params"] for q in self.panel._queries]), ('["Foo"]', "[10, 1]", '["2017-12-22 16:07:01"]'), ) else: self.assertEqual( tuple([q[1]["params"] for q in self.panel._queries]), ('["Foo", true, false]', "[10, 1]", '["2017-12-22 16:07:01"]'), ) @unittest.skipUnless( connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) def test_json_param_conversion(self): self.assertEqual(len(self.panel._queries), 0) list(PostgresJSONModel.objects.filter(field__contains={"foo": "bar"})) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) # ensure query was logged self.assertEqual(len(self.panel._queries), 1) self.assertEqual( self.panel._queries[0][1]["params"], '["{\\"foo\\": \\"bar\\"}"]', ) if django.VERSION < (3, 1): self.assertIsInstance( self.panel._queries[0][1]["raw_params"][0], PostgresJson, ) def test_binary_param_force_text(self): self.assertEqual(len(self.panel._queries), 0) with connection.cursor() as cursor: cursor.execute( "SELECT * FROM tests_binary WHERE field = %s", [connection.Database.Binary(b"\xff")], ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.assertEqual(len(self.panel._queries), 1) self.assertIn( "SELECT * FROM" " tests_binary WHERE field =", self.panel._queries[0][1]["sql"], ) @unittest.skipUnless(connection.vendor != "sqlite", "Test invalid for SQLite") def test_raw_query_param_conversion(self): self.assertEqual(len(self.panel._queries), 0) list( User.objects.raw( " ".join( [ "SELECT *", "FROM auth_user", "WHERE first_name = %s", "AND is_staff = %s", "AND is_superuser = %s", "AND date_joined = %s", ] ), params=["Foo", True, False, datetime.datetime(2017, 12, 22, 16, 7, 1)], ) ) list( User.objects.raw( " ".join( [ "SELECT *", "FROM auth_user", "WHERE first_name = %(first_name)s", "AND is_staff = %(is_staff)s", "AND is_superuser = %(is_superuser)s", "AND date_joined = %(date_joined)s", ] ), params={ "first_name": "Foo", "is_staff": True, "is_superuser": False, "date_joined": datetime.datetime(2017, 12, 22, 16, 7, 1), }, ) ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) # ensure query was logged self.assertEqual(len(self.panel._queries), 2) self.assertEqual( tuple([q[1]["params"] for q in self.panel._queries]), ( '["Foo", true, false, "2017-12-22 16:07:01"]', " ".join( [ '{"first_name": "Foo",', '"is_staff": true,', '"is_superuser": false,', '"date_joined": "2017-12-22 16:07:01"}', ] ), ), ) def test_insert_content(self): """ Test that the panel only inserts content after generate_stats and not the process_request. """ list(User.objects.filter(username="café".encode("utf-8"))) response = self.panel.process_request(self.request) # ensure the panel does not have content yet. self.assertNotIn("café", self.panel.content) self.panel.generate_stats(self.request, response) # ensure the panel renders correctly. content = self.panel.content self.assertIn("café", content) self.assertValidHTML(content) @override_settings(DEBUG_TOOLBAR_CONFIG={"ENABLE_STACKTRACES_LOCALS": True}) def test_insert_locals(self): """ Test that the panel inserts locals() content. """ local_var = "" # noqa list(User.objects.filter(username="café".encode("utf-8"))) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) self.assertIn("local_var", self.panel.content) # Verify the escape logic works content = self.panel.content self.assertNotIn("