Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
VeldaKiara committed Mar 11, 2024
2 parents 2c21976 + cfd4801 commit e944857
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 121 deletions.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ repos:
args:
- --trailing-comma=es5
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.0.0-beta.0
rev: v9.0.0-beta.1
hooks:
- id: eslint
additional_dependencies:
- "eslint@v9.0.0-beta.0"
- "@eslint/js@v9.0.0-beta.0"
- "eslint@v9.0.0-beta.1"
- "@eslint/js@v9.0.0-beta.1"
- "globals"
files: \.js?$
types: [file]
args:
- --fix
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.2.2'
rev: 'v0.3.0'
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
25 changes: 0 additions & 25 deletions debug_toolbar/panels/staticfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from django.conf import settings
from django.contrib.staticfiles import finders, storage
from django.core.checks import Warning
from django.utils.functional import LazyObject
from django.utils.translation import gettext_lazy as _, ngettext

Expand Down Expand Up @@ -178,27 +177,3 @@ def get_staticfiles_apps(self):
if app not in apps:
apps.append(app)
return apps

@classmethod
def run_checks(cls):
"""
Check that the integration is configured correctly for the panel.
Specifically look for static files that haven't been collected yet.
Return a list of :class: `django.core.checks.CheckMessage` instances.
"""
errors = []
for finder in finders.get_finders():
try:
for path, finder_storage in finder.list([]):
finder_storage.path(path)
except OSError:
errors.append(
Warning(
"debug_toolbar requires the STATICFILES_DIRS directories to exist.",
hint="Running manage.py collectstatic may help uncover the issue.",
id="debug_toolbar.staticfiles.W001",
)
)
return errors
6 changes: 3 additions & 3 deletions debug_toolbar/panels/templates/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ def process_context_list(self, context_layers):
# QuerySet would trigger the database: user can run the
# query from SQL Panel
elif isinstance(value, (QuerySet, RawQuerySet)):
temp_layer[
key
] = f"<<{value.__class__.__name__.lower()} of {value.model._meta.label}>>"
temp_layer[key] = (
f"<<{value.__class__.__name__.lower()} of {value.model._meta.label}>>"
)
else:
token = allow_sql.set(False) # noqa: FBT003
try:
Expand Down
84 changes: 84 additions & 0 deletions docs/architecture.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Architecture
============

The Django Debug Toolbar is designed to be flexible and extensible for
developers and third-party panel creators.

Core Components
---------------

While there are several components, the majority of logic and complexity
lives within the following:

- ``debug_toolbar.middleware.DebugToolbarMiddleware``
- ``debug_toolbar.toolbar.DebugToolbar``
- ``debug_toolbar.panels``

^^^^^^^^^^^^^^^^^^^^^^
DebugToolbarMiddleware
^^^^^^^^^^^^^^^^^^^^^^

The middleware is how the toolbar integrates with Django projects.
It determines if the toolbar should instrument the request, which
panels to use, facilitates the processing of the request and augmenting
the response with the toolbar. Most logic for how the toolbar interacts
with the user's Django project belongs here.

^^^^^^^^^^^^
DebugToolbar
^^^^^^^^^^^^

The ``DebugToolbar`` class orchestrates the processing of a request
for each of the panels. It contains the logic that needs to be aware
of all the panels, but doesn't need to interact with the user's Django
project.

^^^^^^
Panels
^^^^^^

The majority of the complex logic lives within the panels themselves. This
is because the panels are responsible for collecting the various metrics.
Some of the metrics are collected via
`monkey-patching <https://stackoverflow.com/a/5626250>`_, such as
``TemplatesPanel``. Others, such as ``SettingsPanel`` don't need to collect
anything and include the data directly in the response.

Some panels such as ``SQLPanel`` have additional functionality. This tends
to involve a user clicking on something, and the toolbar presenting a new
page with additional data. That additional data is handled in views defined
in the panels package (for example, ``debug_toolbar.panels.sql.views``).

Logic Flow
----------

When a request comes in, the toolbar first interacts with it in the
middleware. If the middleware determines the request should be instrumented,
it will instantiate the toolbar and pass the request for processing. The
toolbar will use the enabled panels to collect information on the request
and/or response. When the toolbar has completed collecting its metrics on
both the request and response, the middleware will collect the results
from the toolbar. It will inject the HTML and JavaScript to render the
toolbar as well as any headers into the response.

After the browser renders the panel and the user interacts with it, the
toolbar's JavaScript will send requests to the server. If the view handling
the request needs to fetch data from the toolbar, the request must supply
the store ID. This is so that the toolbar can load the collected metrics
for that particular request.

The history panel allows a user to view the metrics for any request since
the application was started. The toolbar maintains its state entirely in
memory for the process running ``runserver``. If the application is
restarted the toolbar will lose its state.

Problematic Parts
-----------------

- ``debug.panels.templates.panel``: This monkey-patches template rendering
when the panel module is loaded
- ``debug.panels.sql``: This package is particularly complex, but provides
the main benefit of the toolbar
- Support for async and multi-threading: This is currently unsupported, but
is being implemented as per the
`Async compatible toolbar project <https://github.com/orgs/jazzband/projects/9>`_.
7 changes: 7 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ Pending
* Display a better error message when the toolbar's requests
return invalid json.
* Render forms with ``as_div`` to silence Django 5.0 deprecation warnings.
* Stayed on top of pre-commit hook updates.
* Added :doc:`architecture documentation <architecture>` to help
on-board new contributors.
* Removed the static file path validation check in
:class:`StaticFilesPanel <debug_toolbar.panels.staticfiles.StaticFilesPanel>`
since that check is made redundant by a similar check in Django 4.0 and
later.

4.3.0 (2024-02-01)
------------------
Expand Down
6 changes: 6 additions & 0 deletions docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ For convenience, there's an alias for the second command::
Look at ``example/settings.py`` for running the example with another database
than SQLite.

Architecture
------------

There is high-level information on how the Django Debug Toolbar is structured
in the :doc:`architecture documentation <architecture>`.

Tests
-----

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Django Debug Toolbar
commands
changes
contributing
architecture
37 changes: 0 additions & 37 deletions tests/panels/test_staticfiles.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import os
import unittest

import django
from django.conf import settings
from django.contrib.staticfiles import finders
from django.test.utils import override_settings

from ..base import BaseTestCase

PATH_DOES_NOT_EXIST = os.path.join(settings.BASE_DIR, "tests", "invalid_static")


class StaticFilesPanelTestCase(BaseTestCase):
panel_id = "StaticFilesPanel"
Expand Down Expand Up @@ -52,33 +45,3 @@ def test_insert_content(self):
"django.contrib.staticfiles.finders.AppDirectoriesFinder", content
)
self.assertValidHTML(content)

@unittest.skipIf(django.VERSION >= (4,), "Django>=4 handles missing dirs itself.")
@override_settings(
STATICFILES_DIRS=[PATH_DOES_NOT_EXIST] + settings.STATICFILES_DIRS,
STATIC_ROOT=PATH_DOES_NOT_EXIST,
)
def test_finder_directory_does_not_exist(self):
"""Misconfigure the static files settings and verify the toolbar runs.
The test case is that the STATIC_ROOT is in STATICFILES_DIRS and that
the directory of STATIC_ROOT does not exist.
"""
response = self.panel.process_request(self.request)
self.panel.generate_stats(self.request, response)
content = self.panel.content
self.assertIn(
"django.contrib.staticfiles.finders.AppDirectoriesFinder", content
)
self.assertNotIn(
"django.contrib.staticfiles.finders.FileSystemFinder (2 files)", content
)
self.assertEqual(self.panel.num_used, 0)
self.assertNotEqual(self.panel.num_found, 0)
expected_apps = ["django.contrib.admin", "debug_toolbar"]
if settings.USE_GIS:
expected_apps = ["django.contrib.gis"] + expected_apps
self.assertEqual(self.panel.get_staticfiles_apps(), expected_apps)
self.assertEqual(
self.panel.get_staticfiles_dirs(), finders.FileSystemFinder().locations
)
1 change: 1 addition & 0 deletions tests/sync.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Taken from channels.db
"""

from asgiref.sync import SyncToAsync
from django.db import close_old_connections

Expand Down
53 changes: 1 addition & 52 deletions tests/test_checks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import os
import unittest
from unittest.mock import patch

import django
from django.conf import settings
from django.core.checks import Error, Warning, run_checks
Expand All @@ -13,6 +10,7 @@
PATH_DOES_NOT_EXIST = os.path.join(settings.BASE_DIR, "tests", "invalid_static")



class ChecksTestCase(SimpleTestCase):
@override_settings(
MIDDLEWARE=[
Expand Down Expand Up @@ -95,23 +93,6 @@ def test_check_middleware_classes_error(self):
messages,
)

@unittest.skipIf(django.VERSION >= (4,), "Django>=4 handles missing dirs itself.")
@override_settings(
STATICFILES_DIRS=[PATH_DOES_NOT_EXIST],
)
def test_panel_check_errors(self):
messages = run_checks()
self.assertEqual(
messages,
[
Warning(
"debug_toolbar requires the STATICFILES_DIRS directories to exist.",
hint="Running manage.py collectstatic may help uncover the issue.",
id="debug_toolbar.staticfiles.W001",
)
],
)

@override_settings(DEBUG_TOOLBAR_PANELS=[])
def test_panels_is_empty(self):
errors = run_checks()
Expand Down Expand Up @@ -272,38 +253,6 @@ def test_check_w007_invalid(self, mocked_guess_type):
],
)

# def test_debug_toolbar_installed_when_running_tests(self, mocked_get_config):
# with self.settings(DEBUG=False):
# dt_settings.get_config()["IS_RUNNING_TESTS"] = False
# errors = debug_toolbar_installed_when_running_tests_check(None)
# self.assertEqual(len(errors), 0)
# with self.settings(DEBUG=True):
# dt_settings.get_config()["IS_RUNNING_TESTS"] = True
# errors = debug_toolbar_installed_when_running_tests_check(None)
# self.assertEqual(len(errors), 0)
# with self.settings(DEBUG=True):
# dt_settings.get_config()["IS_RUNNING_TESTS"] = False
# errors = debug_toolbar_installed_when_running_tests_check(None)
# self.assertEqual(len(errors), 0)
# with self.settings(DEBUG=False):
# dt_settings.get_config()["IS_RUNNING_TESTS"] = True
# errors = debug_toolbar_installed_when_running_tests_check(None)
# self.assertEqual(
# errors,
# [
# Error(
# "The Django Debug Toolbar can't be used with tests",
# hint="Django changes the DEBUG setting to False when running "
# "tests. By default the Django Debug Toolbar is installed because "
# "DEBUG is set to True. For most cases, you need to avoid installing "
# "the toolbar when running tests. If you feel this check is in error, "
# "you can set `DEBUG_TOOLBAR_CONFIG['IS_RUNNING_TESTS'] = False` to "
# "bypass this check.",
# id="debug_toolbar.W008",
# )
# ],

# )

def test_debug_toolbar_installed_when_running_tests(self):
with self.settings(DEBUG=False):
Expand Down
1 change: 1 addition & 0 deletions tests/urls_invalid.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
"""Invalid urls.py file for testing"""

urlpatterns = []
1 change: 1 addition & 0 deletions tests/urls_use_package_urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""urls.py to test using debug_toolbar.urls in include"""

from django.urls import include, path

import debug_toolbar
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ passenv=
DB_PASSWORD
DB_HOST
DB_PORT
DISPLAY
DJANGO_SELENIUM_TESTS
GITHUB_*
setenv =
PYTHONPATH = {toxinidir}
Expand Down

0 comments on commit e944857

Please sign in to comment.