diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee54d2d5d..852048216 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,10 +14,15 @@ repos: hooks: - id: doc8 - repo: https://github.com/adamchainz/django-upgrade - rev: 1.23.1 + rev: 1.24.0 hooks: - id: django-upgrade args: [--target-version, "4.2"] +- repo: https://github.com/adamchainz/djade-pre-commit + rev: "1.4.0" + hooks: + - id: djade + args: [--target-version, "4.2"] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: @@ -29,7 +34,7 @@ repos: - id: biome-check verbose: true - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.11.0' + rev: 'v0.11.7' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -39,6 +44,6 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.24 + rev: v0.24.1 hooks: - id: validate-pyproject diff --git a/README.rst b/README.rst index 3c831efa7..6c7da3615 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ Here's a screenshot of the toolbar in action: In addition to the built-in panels, a number of third-party panels are contributed by the community. -The current stable version of the Debug Toolbar is 5.1.0. It works on +The current stable version of the Debug Toolbar is 5.2.0. It works on Django ≥ 4.2.0. The Debug Toolbar has experimental support for `Django's asynchronous views diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index 5bdaa2dd1..770c5eeed 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -4,7 +4,7 @@ # Do not use pkg_resources to find the version but set it here directly! # see issue #1446 -VERSION = "5.1.0" +VERSION = "5.2.0" # Code that discovers files or modules in INSTALLED_APPS imports this module. urls = "debug_toolbar.urls", APP_NAME diff --git a/debug_toolbar/panels/redirects.py b/debug_toolbar/panels/redirects.py index 27cce4c17..8055c67ad 100644 --- a/debug_toolbar/panels/redirects.py +++ b/debug_toolbar/panels/redirects.py @@ -22,20 +22,8 @@ def _process_response(self, response): Common response processing logic. """ if 300 <= response.status_code < 400: - redirect_to = response.get("Location") - if redirect_to: - status_line = f"{response.status_code} {response.reason_phrase}" - cookies = response.cookies - context = { - "redirect_to": redirect_to, - "status_line": status_line, - "toolbar": self.toolbar, - } - # Using SimpleTemplateResponse avoids running global context processors. - response = SimpleTemplateResponse( - "debug_toolbar/redirect.html", context - ) - response.cookies = cookies + if redirect_to := response.get("Location"): + response = self.get_interception_response(response, redirect_to) response.render() return response @@ -53,3 +41,22 @@ def process_request(self, request): if iscoroutine(response): return self.aprocess_request(request, response) return self._process_response(response) + + def get_interception_response(self, response, redirect_to): + """ + Hook method to allow subclasses to customize the interception response. + """ + status_line = f"{response.status_code} {response.reason_phrase}" + cookies = response.cookies + original_response = response + context = { + "redirect_to": redirect_to, + "status_line": status_line, + "toolbar": self.toolbar, + "original_response": original_response, + } + # Using SimpleTemplateResponse avoids running global context processors. + response = SimpleTemplateResponse("debug_toolbar/redirect.html", context) + response.cookies = cookies + response.original_response = original_response + return response diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py index b77788637..5a24d6179 100644 --- a/debug_toolbar/panels/request.py +++ b/debug_toolbar/panels/request.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from debug_toolbar.panels import Panel -from debug_toolbar.utils import get_name_from_obj, get_sorted_request_variable +from debug_toolbar.utils import get_name_from_obj, sanitize_and_sort_request_vars class RequestPanel(Panel): @@ -26,9 +26,9 @@ def nav_subtitle(self): def generate_stats(self, request, response): self.record_stats( { - "get": get_sorted_request_variable(request.GET), - "post": get_sorted_request_variable(request.POST), - "cookies": get_sorted_request_variable(request.COOKIES), + "get": sanitize_and_sort_request_vars(request.GET), + "post": sanitize_and_sort_request_vars(request.POST), + "cookies": sanitize_and_sort_request_vars(request.COOKIES), } ) @@ -59,13 +59,5 @@ def generate_stats(self, request, response): self.record_stats(view_info) if hasattr(request, "session"): - try: - session_list = [ - (k, request.session.get(k)) for k in sorted(request.session.keys()) - ] - except TypeError: - session_list = [ - (k, request.session.get(k)) - for k in request.session.keys() # (it's not a dict) - ] - self.record_stats({"session": {"list": session_list}}) + session_data = dict(request.session) + self.record_stats({"session": sanitize_and_sort_request_vars(session_data)}) diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py index 898639c54..b8a0a376f 100644 --- a/debug_toolbar/panels/templates/views.py +++ b/debug_toolbar/panels/templates/views.py @@ -27,15 +27,18 @@ def template_source(request): template_name = request.GET.get("template", template_origin_name) final_loaders = [] - loaders = Engine.get_default().template_loaders + loaders = list(Engine.get_default().template_loaders) + + while loaders: + loader = loaders.pop(0) - for loader in loaders: if loader is not None: - # When the loader has loaders associated with it, - # append those loaders to the list. This occurs with - # django.template.loaders.cached.Loader + # Recursively unwrap loaders until we get to loaders which do not + # themselves wrap other loaders. This adds support for + # django.template.loaders.cached.Loader and the + # django-template-partials loader (possibly among others) if hasattr(loader, "loaders"): - final_loaders += loader.loaders + loaders.extend(loader.loaders) else: final_loaders.append(loader) diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index e0be35ea8..59d538a0b 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -45,7 +45,6 @@ "TOOLBAR_LANGUAGE": None, "IS_RUNNING_TESTS": "test" in sys.argv, "UPDATE_ON_FETCH": False, - "DEFAULT_THEME": "auto", } diff --git a/debug_toolbar/static/debug_toolbar/css/toolbar.css b/debug_toolbar/static/debug_toolbar/css/toolbar.css index 3d0d34e6c..f147bcdff 100644 --- a/debug_toolbar/static/debug_toolbar/css/toolbar.css +++ b/debug_toolbar/static/debug_toolbar/css/toolbar.css @@ -35,31 +35,8 @@ --djdt-raw-border-color: var(--djdt-table-border-color); } -@media (prefers-color-scheme: dark) { - :root { - --djdt-font-color: #f8f8f2; - --djdt-background-color: #1e293bff; - --djdt-panel-content-background-color: #0f1729ff; - --djdt-panel-title-background-color: #242432; - --djdt-djdt-panel-content-table-strip-background-color: #324154ff; - --djdt--highlighted-background-color: #2c2a7dff; - --djdt-toggle-template-background-color: #282755; - - --djdt-sql-font-color: var(--djdt-font-color); - --djdt-pre-text-color: var(--djdt-font-color); - --djdt-path-and-locals: #65758cff; - --djdt-stack-span-color: #7c8fa4; - --djdt-template-highlight-color: var(--djdt-stack-span-color); - - --djdt-table-border-color: #324154ff; - --djdt-button-border-color: var(--djdt-table-border-color); - --djdt-pre-border-color: var(--djdt-table-border-color); - --djdt-raw-border-color: var(--djdt-table-border-color); - } -} - #djDebug[data-theme="dark"] { - --djdt-font-color: #8393a7; + --djdt-font-color: #f8f8f2; --djdt-background-color: #1e293bff; --djdt-panel-content-background-color: #0f1729ff; --djdt-panel-content-table-background-color: var(--djdt-background-color); @@ -150,8 +127,10 @@ #djDebug button { margin: 0; padding: 0; - min-width: 0; + min-width: auto; width: auto; + min-height: auto; + height: auto; border: 0; outline: 0; font-size: 12px; @@ -569,266 +548,512 @@ To regenerate: from pygments.formatters import HtmlFormatter print(HtmlFormatter(wrapcode=True).get_style_defs()) */ -#djDebug .highlight pre { +#djDebug[data-theme="light"] .highlight pre { line-height: 125%; } -#djDebug .highlight td.linenos .normal { +#djDebug[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight span.linenos { +#djDebug[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight td.linenos .special { +#djDebug[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight span.linenos.special { +#djDebug[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight .hll { +#djDebug[data-theme="light"] .highlight .hll { background-color: #ffffcc; } -#djDebug .highlight .c { +#djDebug[data-theme="light"] .highlight .c { color: #3d7b7b; font-style: italic; } /* Comment */ -#djDebug .highlight .err { +#djDebug[data-theme="light"] .highlight .err { border: 1px solid #ff0000; } /* Error */ -#djDebug .highlight .k { +#djDebug[data-theme="light"] .highlight .k { color: #008000; font-weight: bold; } /* Keyword */ -#djDebug .highlight .o { +#djDebug[data-theme="light"] .highlight .o { color: #666666; } /* Operator */ -#djDebug .highlight .ch { +#djDebug[data-theme="light"] .highlight .ch { color: #3d7b7b; font-style: italic; } /* Comment.Hashbang */ -#djDebug .highlight .cm { +#djDebug[data-theme="light"] .highlight .cm { color: #3d7b7b; font-style: italic; } /* Comment.Multiline */ -#djDebug .highlight .cp { +#djDebug[data-theme="light"] .highlight .cp { color: #9c6500; } /* Comment.Preproc */ -#djDebug .highlight .cpf { +#djDebug[data-theme="light"] .highlight .cpf { color: #3d7b7b; font-style: italic; } /* Comment.PreprocFile */ -#djDebug .highlight .c1 { +#djDebug[data-theme="light"] .highlight .c1 { color: #3d7b7b; font-style: italic; } /* Comment.Single */ -#djDebug .highlight .cs { +#djDebug[data-theme="light"] .highlight .cs { color: #3d7b7b; font-style: italic; } /* Comment.Special */ -#djDebug .highlight .gd { +#djDebug[data-theme="light"] .highlight .gd { color: #a00000; } /* Generic.Deleted */ -#djDebug .highlight .ge { +#djDebug[data-theme="light"] .highlight .ge { font-style: italic; } /* Generic.Emph */ -#djDebug .highlight .ges { +#djDebug[data-theme="light"] .highlight .ges { font-weight: bold; font-style: italic; } /* Generic.EmphStrong */ -#djDebug .highlight .gr { +#djDebug[data-theme="light"] .highlight .gr { color: #e40000; } /* Generic.Error */ -#djDebug .highlight .gh { +#djDebug[data-theme="light"] .highlight .gh { color: #000080; font-weight: bold; } /* Generic.Heading */ -#djDebug .highlight .gi { +#djDebug[data-theme="light"] .highlight .gi { color: #008400; } /* Generic.Inserted */ -#djDebug .highlight .go { +#djDebug[data-theme="light"] .highlight .go { color: #717171; } /* Generic.Output */ -#djDebug .highlight .gp { +#djDebug[data-theme="light"] .highlight .gp { color: #000080; font-weight: bold; } /* Generic.Prompt */ -#djDebug .highlight .gs { +#djDebug[data-theme="light"] .highlight .gs { font-weight: bold; } /* Generic.Strong */ -#djDebug .highlight .gu { +#djDebug[data-theme="light"] .highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ -#djDebug .highlight .gt { +#djDebug[data-theme="light"] .highlight .gt { color: #0044dd; } /* Generic.Traceback */ -#djDebug .highlight .kc { +#djDebug[data-theme="light"] .highlight .kc { color: #008000; font-weight: bold; } /* Keyword.Constant */ -#djDebug .highlight .kd { +#djDebug[data-theme="light"] .highlight .kd { color: #008000; font-weight: bold; } /* Keyword.Declaration */ -#djDebug .highlight .kn { +#djDebug[data-theme="light"] .highlight .kn { color: #008000; font-weight: bold; } /* Keyword.Namespace */ -#djDebug .highlight .kp { +#djDebug[data-theme="light"] .highlight .kp { color: #008000; } /* Keyword.Pseudo */ -#djDebug .highlight .kr { +#djDebug[data-theme="light"] .highlight .kr { color: #008000; font-weight: bold; } /* Keyword.Reserved */ -#djDebug .highlight .kt { +#djDebug[data-theme="light"] .highlight .kt { color: #b00040; } /* Keyword.Type */ -#djDebug .highlight .m { +#djDebug[data-theme="light"] .highlight .m { color: #666666; } /* Literal.Number */ -#djDebug .highlight .s { +#djDebug[data-theme="light"] .highlight .s { color: #ba2121; } /* Literal.String */ -#djDebug .highlight .na { +#djDebug[data-theme="light"] .highlight .na { color: #687822; } /* Name.Attribute */ -#djDebug .highlight .nb { +#djDebug[data-theme="light"] .highlight .nb { color: #008000; } /* Name.Builtin */ -#djDebug .highlight .nc { +#djDebug[data-theme="light"] .highlight .nc { color: #0000ff; font-weight: bold; } /* Name.Class */ -#djDebug .highlight .no { +#djDebug[data-theme="light"] .highlight .no { color: #880000; } /* Name.Constant */ -#djDebug .highlight .nd { +#djDebug[data-theme="light"] .highlight .nd { color: #aa22ff; } /* Name.Decorator */ -#djDebug .highlight .ni { +#djDebug[data-theme="light"] .highlight .ni { color: #717171; font-weight: bold; } /* Name.Entity */ -#djDebug .highlight .ne { +#djDebug[data-theme="light"] .highlight .ne { color: #cb3f38; font-weight: bold; } /* Name.Exception */ -#djDebug .highlight .nf { +#djDebug[data-theme="light"] .highlight .nf { color: #0000ff; } /* Name.Function */ -#djDebug .highlight .nl { +#djDebug[data-theme="light"] .highlight .nl { color: #767600; } /* Name.Label */ -#djDebug .highlight .nn { +#djDebug[data-theme="light"] .highlight .nn { color: #0000ff; font-weight: bold; } /* Name.Namespace */ -#djDebug .highlight .nt { +#djDebug[data-theme="light"] .highlight .nt { color: #008000; font-weight: bold; } /* Name.Tag */ -#djDebug .highlight .nv { +#djDebug[data-theme="light"] .highlight .nv { color: #19177c; } /* Name.Variable */ -#djDebug .highlight .ow { +#djDebug[data-theme="light"] .highlight .ow { color: #aa22ff; font-weight: bold; } /* Operator.Word */ -#djDebug .highlight .w { +#djDebug[data-theme="light"] .highlight .w { color: #bbbbbb; white-space: pre-wrap; } /* Text.Whitespace */ -#djDebug .highlight .mb { +#djDebug[data-theme="light"] .highlight .mb { color: #666666; } /* Literal.Number.Bin */ -#djDebug .highlight .mf { +#djDebug[data-theme="light"] .highlight .mf { color: #666666; } /* Literal.Number.Float */ -#djDebug .highlight .mh { +#djDebug[data-theme="light"] .highlight .mh { color: #666666; } /* Literal.Number.Hex */ -#djDebug .highlight .mi { +#djDebug[data-theme="light"] .highlight .mi { color: #666666; } /* Literal.Number.Integer */ -#djDebug .highlight .mo { +#djDebug[data-theme="light"] .highlight .mo { color: #666666; } /* Literal.Number.Oct */ -#djDebug .highlight .sa { +#djDebug[data-theme="light"] .highlight .sa { color: #ba2121; } /* Literal.String.Affix */ -#djDebug .highlight .sb { +#djDebug[data-theme="light"] .highlight .sb { color: #ba2121; } /* Literal.String.Backtick */ -#djDebug .highlight .sc { +#djDebug[data-theme="light"] .highlight .sc { color: #ba2121; } /* Literal.String.Char */ -#djDebug .highlight .dl { +#djDebug[data-theme="light"] .highlight .dl { color: #ba2121; } /* Literal.String.Delimiter */ -#djDebug .highlight .sd { +#djDebug[data-theme="light"] .highlight .sd { color: #ba2121; font-style: italic; } /* Literal.String.Doc */ -#djDebug .highlight .s2 { +#djDebug[data-theme="light"] .highlight .s2 { color: #ba2121; } /* Literal.String.Double */ -#djDebug .highlight .se { +#djDebug[data-theme="light"] .highlight .se { color: #aa5d1f; font-weight: bold; } /* Literal.String.Escape */ -#djDebug .highlight .sh { +#djDebug[data-theme="light"] .highlight .sh { color: #ba2121; } /* Literal.String.Heredoc */ -#djDebug .highlight .si { +#djDebug[data-theme="light"] .highlight .si { color: #a45a77; font-weight: bold; } /* Literal.String.Interpol */ -#djDebug .highlight .sx { +#djDebug[data-theme="light"] .highlight .sx { color: #008000; } /* Literal.String.Other */ -#djDebug .highlight .sr { +#djDebug[data-theme="light"] .highlight .sr { color: #a45a77; } /* Literal.String.Regex */ -#djDebug .highlight .s1 { +#djDebug[data-theme="light"] .highlight .s1 { color: #ba2121; } /* Literal.String.Single */ -#djDebug .highlight .ss { +#djDebug[data-theme="light"] .highlight .ss { color: #19177c; } /* Literal.String.Symbol */ -#djDebug .highlight .bp { +#djDebug[data-theme="light"] .highlight .bp { color: #008000; } /* Name.Builtin.Pseudo */ -#djDebug .highlight .fm { +#djDebug[data-theme="light"] .highlight .fm { color: #0000ff; } /* Name.Function.Magic */ -#djDebug .highlight .vc { +#djDebug[data-theme="light"] .highlight .vc { color: #19177c; } /* Name.Variable.Class */ -#djDebug .highlight .vg { +#djDebug[data-theme="light"] .highlight .vg { color: #19177c; } /* Name.Variable.Global */ -#djDebug .highlight .vi { +#djDebug[data-theme="light"] .highlight .vi { color: #19177c; } /* Name.Variable.Instance */ -#djDebug .highlight .vm { +#djDebug[data-theme="light"] .highlight .vm { color: #19177c; } /* Name.Variable.Magic */ -#djDebug .highlight .il { +#djDebug[data-theme="light"] .highlight .il { color: #666666; } /* Literal.Number.Integer.Long */ +#djDebug[data-theme="dark"] .highlight .hll { + background-color: #f1fa8c; +} +#djDebug[data-theme="dark"] .highlight { + background: #282a36; + color: #f8f8f2; +} +#djDebug[data-theme="dark"] .highlight .c { + color: #6272a4; +} /* Comment */ +#djDebug[data-theme="dark"] .highlight .err { + color: #f8f8f2; +} /* Error */ +#djDebug[data-theme="dark"] .highlight .g { + color: #f8f8f2; +} /* Generic */ +#djDebug[data-theme="dark"] .highlight .k { + color: #ff79c6; +} /* Keyword */ +#djDebug[data-theme="dark"] .highlight .l { + color: #f8f8f2; +} /* Literal */ +#djDebug[data-theme="dark"] .highlight .n { + color: #f8f8f2; +} /* Name */ +#djDebug[data-theme="dark"] .highlight .o { + color: #ff79c6; +} /* Operator */ +#djDebug[data-theme="dark"] .highlight .x { + color: #f8f8f2; +} /* Other */ +#djDebug[data-theme="dark"] .highlight .p { + color: #f8f8f2; +} /* Punctuation */ +#djDebug[data-theme="dark"] .highlight .ch { + color: #6272a4; +} /* Comment.Hashbang */ +#djDebug[data-theme="dark"] .highlight .cm { + color: #6272a4; +} /* Comment.Multiline */ +#djDebug[data-theme="dark"] .highlight .cp { + color: #ff79c6; +} /* Comment.Preproc */ +#djDebug[data-theme="dark"] .highlight .cpf { + color: #6272a4; +} /* Comment.PreprocFile */ +#djDebug[data-theme="dark"] .highlight .c1 { + color: #6272a4; +} /* Comment.Single */ +#djDebug[data-theme="dark"] .highlight .cs { + color: #6272a4; +} /* Comment.Special */ +#djDebug[data-theme="dark"] .highlight .gd { + color: #8b080b; +} /* Generic.Deleted */ +#djDebug[data-theme="dark"] .highlight .ge { + color: #f8f8f2; + text-decoration: underline; +} /* Generic.Emph */ +#djDebug[data-theme="dark"] .highlight .gr { + color: #f8f8f2; +} /* Generic.Error */ +#djDebug[data-theme="dark"] .highlight .gh { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Heading */ +#djDebug[data-theme="dark"] .highlight .gi { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Inserted */ +#djDebug[data-theme="dark"] .highlight .go { + color: #44475a; +} /* Generic.Output */ +#djDebug[data-theme="dark"] .highlight .gp { + color: #f8f8f2; +} /* Generic.Prompt */ +#djDebug[data-theme="dark"] .highlight .gs { + color: #f8f8f2; +} /* Generic.Strong */ +#djDebug[data-theme="dark"] .highlight .gu { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Subheading */ +#djDebug[data-theme="dark"] .highlight .gt { + color: #f8f8f2; +} /* Generic.Traceback */ +#djDebug[data-theme="dark"] .highlight .kc { + color: #ff79c6; +} /* Keyword.Constant */ +#djDebug[data-theme="dark"] .highlight .kd { + color: #8be9fd; + font-style: italic; +} /* Keyword.Declaration */ +#djDebug[data-theme="dark"] .highlight .kn { + color: #ff79c6; +} /* Keyword.Namespace */ +#djDebug[data-theme="dark"] .highlight .kp { + color: #ff79c6; +} /* Keyword.Pseudo */ +#djDebug[data-theme="dark"] .highlight .kr { + color: #ff79c6; +} /* Keyword.Reserved */ +#djDebug[data-theme="dark"] .highlight .kt { + color: #8be9fd; +} /* Keyword.Type */ +#djDebug[data-theme="dark"] .highlight .ld { + color: #f8f8f2; +} /* Literal.Date */ +#djDebug[data-theme="dark"] .highlight .m { + color: #bd93f9; +} /* Literal.Number */ +#djDebug[data-theme="dark"] .highlight .s { + color: #f1fa8c; +} /* Literal.String */ +#djDebug[data-theme="dark"] .highlight .na { + color: #50fa7b; +} /* Name.Attribute */ +#djDebug[data-theme="dark"] .highlight .nb { + color: #8be9fd; + font-style: italic; +} /* Name.Builtin */ +#djDebug[data-theme="dark"] .highlight .nc { + color: #50fa7b; +} /* Name.Class */ +#djDebug[data-theme="dark"] .highlight .no { + color: #f8f8f2; +} /* Name.Constant */ +#djDebug[data-theme="dark"] .highlight .nd { + color: #f8f8f2; +} /* Name.Decorator */ +#djDebug[data-theme="dark"] .highlight .ni { + color: #f8f8f2; +} /* Name.Entity */ +#djDebug[data-theme="dark"] .highlight .ne { + color: #f8f8f2; +} /* Name.Exception */ +#djDebug[data-theme="dark"] .highlight .nf { + color: #50fa7b; +} /* Name.Function */ +#djDebug[data-theme="dark"] .highlight .nl { + color: #8be9fd; + font-style: italic; +} /* Name.Label */ +#djDebug[data-theme="dark"] .highlight .nn { + color: #f8f8f2; +} /* Name.Namespace */ +#djDebug[data-theme="dark"] .highlight .nx { + color: #f8f8f2; +} /* Name.Other */ +#djDebug[data-theme="dark"] .highlight .py { + color: #f8f8f2; +} /* Name.Property */ +#djDebug[data-theme="dark"] .highlight .nt { + color: #ff79c6; +} /* Name.Tag */ +#djDebug[data-theme="dark"] .highlight .nv { + color: #8be9fd; + font-style: italic; +} /* Name.Variable */ +#djDebug[data-theme="dark"] .highlight .ow { + color: #ff79c6; +} /* Operator.Word */ +#djDebug[data-theme="dark"] .highlight .w { + color: #f8f8f2; +} /* Text.Whitespace */ +#djDebug[data-theme="dark"] .highlight .mb { + color: #bd93f9; +} /* Literal.Number.Bin */ +#djDebug[data-theme="dark"] .highlight .mf { + color: #bd93f9; +} /* Literal.Number.Float */ +#djDebug[data-theme="dark"] .highlight .mh { + color: #bd93f9; +} /* Literal.Number.Hex */ +#djDebug[data-theme="dark"] .highlight .mi { + color: #bd93f9; +} /* Literal.Number.Integer */ +#djDebug[data-theme="dark"] .highlight .mo { + color: #bd93f9; +} /* Literal.Number.Oct */ +#djDebug[data-theme="dark"] .highlight .sa { + color: #f1fa8c; +} /* Literal.String.Affix */ +#djDebug[data-theme="dark"] .highlight .sb { + color: #f1fa8c; +} /* Literal.String.Backtick */ +#djDebug[data-theme="dark"] .highlight .sc { + color: #f1fa8c; +} /* Literal.String.Char */ +#djDebug[data-theme="dark"] .highlight .dl { + color: #f1fa8c; +} /* Literal.String.Delimiter */ +#djDebug[data-theme="dark"] .highlight .sd { + color: #f1fa8c; +} /* Literal.String.Doc */ +#djDebug[data-theme="dark"] .highlight .s2 { + color: #f1fa8c; +} /* Literal.String.Double */ +#djDebug[data-theme="dark"] .highlight .se { + color: #f1fa8c; +} /* Literal.String.Escape */ +#djDebug[data-theme="dark"] .highlight .sh { + color: #f1fa8c; +} /* Literal.String.Heredoc */ +#djDebug[data-theme="dark"] .highlight .si { + color: #f1fa8c; +} /* Literal.String.Interpol */ +#djDebug[data-theme="dark"] .highlight .sx { + color: #f1fa8c; +} /* Literal.String.Other */ +#djDebug[data-theme="dark"] .highlight .sr { + color: #f1fa8c; +} /* Literal.String.Regex */ +#djDebug[data-theme="dark"] .highlight .s1 { + color: #f1fa8c; +} /* Literal.String.Single */ +#djDebug[data-theme="dark"] .highlight .ss { + color: #f1fa8c; +} /* Literal.String.Symbol */ +#djDebug[data-theme="dark"] .highlight .bp { + color: #f8f8f2; + font-style: italic; +} /* Name.Builtin.Pseudo */ +#djDebug[data-theme="dark"] .highlight .fm { + color: #50fa7b; +} /* Name.Function.Magic */ +#djDebug[data-theme="dark"] .highlight .vc { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Class */ +#djDebug[data-theme="dark"] .highlight .vg { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Global */ +#djDebug[data-theme="dark"] .highlight .vi { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Instance */ +#djDebug[data-theme="dark"] .highlight .vm { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Magic */ +#djDebug[data-theme="dark"] .highlight .il { + color: #bd93f9; +} /* Literal.Number.Integer.Long */ + #djDebug svg.djDebugLineChart { width: 100%; height: 1.5em; @@ -947,9 +1172,9 @@ To regenerate: #djToggleThemeButton > svg { margin-left: auto; } -#djDebug[data-theme="light"] #djToggleThemeButton svg.theme-light, -#djDebug[data-theme="dark"] #djToggleThemeButton svg.theme-dark, -#djDebug[data-theme="auto"] #djToggleThemeButton svg.theme-auto { +#djDebug[data-user-theme="light"] #djToggleThemeButton svg.theme-light, +#djDebug[data-user-theme="dark"] #djToggleThemeButton svg.theme-dark, +#djDebug[data-user-theme="auto"] #djToggleThemeButton svg.theme-auto { display: block; height: 1rem; width: 1rem; diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js index 077bc930a..19658f76e 100644 --- a/debug_toolbar/static/debug_toolbar/js/toolbar.js +++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js @@ -212,27 +212,30 @@ const djdt = { djdt.updateOnAjax(); } + const prefersDark = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches; + const themeList = prefersDark + ? ["auto", "light", "dark"] + : ["auto", "dark", "light"]; + const setTheme = (theme) => { + djDebug.setAttribute( + "data-theme", + theme === "auto" ? (prefersDark ? "dark" : "light") : theme + ); + djDebug.setAttribute("data-user-theme", theme); + }; + // Updates the theme using user settings - const userTheme = localStorage.getItem("djdt.user-theme"); - if (userTheme !== null) { - djDebug.setAttribute("data-theme", userTheme); - } + let userTheme = localStorage.getItem("djdt.user-theme") || "auto"; + setTheme(userTheme); + // Adds the listener to the Theme Toggle Button $$.on(djDebug, "click", "#djToggleThemeButton", () => { - switch (djDebug.getAttribute("data-theme")) { - case "auto": - djDebug.setAttribute("data-theme", "light"); - localStorage.setItem("djdt.user-theme", "light"); - break; - case "light": - djDebug.setAttribute("data-theme", "dark"); - localStorage.setItem("djdt.user-theme", "dark"); - break; - default: /* dark is the default */ - djDebug.setAttribute("data-theme", "auto"); - localStorage.setItem("djdt.user-theme", "auto"); - break; - } + const index = themeList.indexOf(userTheme); + userTheme = themeList[(index + 1) % themeList.length]; + localStorage.setItem("djdt.user-theme", userTheme); + setTheme(userTheme); }); }, hidePanels() { diff --git a/debug_toolbar/static/debug_toolbar/js/utils.js b/debug_toolbar/static/debug_toolbar/js/utils.js index c42963fe3..0cfa80474 100644 --- a/debug_toolbar/static/debug_toolbar/js/utils.js +++ b/debug_toolbar/static/debug_toolbar/js/utils.js @@ -90,7 +90,7 @@ function ajax(url, init) { }) .catch((error) => { const win = document.getElementById("djDebugWindow"); - win.innerHTML = `

${error.message}

`; + win.innerHTML = `

${error.message}

`; $$.show(win); throw error; }); diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index a9983250d..607863104 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -2,10 +2,10 @@ {% block css %} -{% endblock %} +{% endblock css %} {% block js %} -{% endblock %} +{% endblock js %}
+ {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }} data-update-on-fetch="{{ toolbar.config.UPDATE_ON_FETCH }}">
-
+
DJDT
diff --git a/debug_toolbar/templates/debug_toolbar/includes/panel_button.html b/debug_toolbar/templates/debug_toolbar/includes/panel_button.html index 344331d8d..bc6f03ad9 100644 --- a/debug_toolbar/templates/debug_toolbar/includes/panel_button.html +++ b/debug_toolbar/templates/debug_toolbar/includes/panel_button.html @@ -1,7 +1,7 @@ {% load i18n %}
  • - + {% if panel.has_content and panel.enabled %} {% else %} @@ -9,7 +9,7 @@ {% endif %} {{ panel.nav_title }} {% if panel.enabled %} - {% with panel.nav_subtitle as subtitle %} + {% with subtitle=panel.nav_subtitle %} {% if subtitle %}
    {{ subtitle }}{% endif %} {% endwith %} {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/includes/panel_content.html b/debug_toolbar/templates/debug_toolbar/includes/panel_content.html index b0f4af704..d797421a5 100644 --- a/debug_toolbar/templates/debug_toolbar/includes/panel_content.html +++ b/debug_toolbar/templates/debug_toolbar/includes/panel_content.html @@ -3,8 +3,8 @@ {% if panel.has_content and panel.enabled %}
    -

    {{ panel.title }}

    +
    {% if toolbar.should_render_panels %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/alerts.html b/debug_toolbar/templates/debug_toolbar/panels/alerts.html index df208836d..6665033fb 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/alerts.html +++ b/debug_toolbar/templates/debug_toolbar/panels/alerts.html @@ -1,12 +1,12 @@ {% load i18n %} {% if alerts %} -

    {% trans "Alerts found" %}

    +

    {% translate "Alerts found" %}

    {% for alert in alerts %}
    • {{ alert.alert }}
    {% endfor %} {% else %} -

    {% trans "No alerts found" %}

    +

    {% translate "No alerts found" %}

    {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/cache.html b/debug_toolbar/templates/debug_toolbar/panels/cache.html index 0e1ec2a4c..fe882750b 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/cache.html +++ b/debug_toolbar/templates/debug_toolbar/panels/cache.html @@ -1,12 +1,12 @@ {% load i18n %} -

    {% trans "Summary" %}

    +

    {% translate "Summary" %}

    - - - - + + + + @@ -18,7 +18,7 @@

    {% trans "Summary" %}

    {% trans "Total calls" %}{% trans "Total time" %}{% trans "Cache hits" %}{% trans "Cache misses" %}{% translate "Total calls" %}{% translate "Total time" %}{% translate "Cache hits" %}{% translate "Cache misses" %}
    -

    {% trans "Commands" %}

    +

    {% translate "Commands" %}

    @@ -36,15 +36,15 @@

    {% trans "Commands" %}

    {% if calls %} -

    {% trans "Calls" %}

    +

    {% translate "Calls" %}

    - - - - - + + + + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/headers.html b/debug_toolbar/templates/debug_toolbar/panels/headers.html index f4146e8dd..db33f1b59 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/headers.html +++ b/debug_toolbar/templates/debug_toolbar/panels/headers.html @@ -1,12 +1,12 @@ {% load i18n %} -

    {% trans "Request headers" %}

    +

    {% translate "Request headers" %}

    {% trans "Time (ms)" %}{% trans "Type" %}{% trans "Arguments" %}{% trans "Keyword arguments" %}{% trans "Backend" %}{% translate "Time (ms)" %}{% translate "Type" %}{% translate "Arguments" %}{% translate "Keyword arguments" %}{% translate "Backend" %}
    - - + + @@ -19,13 +19,13 @@

    {% trans "Request headers" %}

    {% trans "Key" %}{% trans "Value" %}{% translate "Key" %}{% translate "Value" %}
    -

    {% trans "Response headers" %}

    +

    {% translate "Response headers" %}

    - - + + @@ -38,15 +38,15 @@

    {% trans "Response headers" %}

    {% trans "Key" %}{% trans "Value" %}{% translate "Key" %}{% translate "Value" %}
    -

    {% trans "WSGI environ" %}

    +

    {% translate "WSGI environ" %}

    -

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

    +

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

    - - + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/history.html b/debug_toolbar/templates/debug_toolbar/panels/history.html index 840f6c9f4..ba7823d22 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/history.html +++ b/debug_toolbar/templates/debug_toolbar/panels/history.html @@ -1,4 +1,4 @@ -{% load i18n %}{% load static %} +{% load i18n static %} {{ refresh_form.as_div }} @@ -6,12 +6,12 @@
    {% trans "Key" %}{% trans "Value" %}{% translate "Key" %}{% translate "Value" %}
    - - - - - - + + + + + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/history_tr.html b/debug_toolbar/templates/debug_toolbar/panels/history_tr.html index eff544f1a..1642b4a47 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/history_tr.html +++ b/debug_toolbar/templates/debug_toolbar/panels/history_tr.html @@ -19,8 +19,8 @@ - - + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/profiling.html b/debug_toolbar/templates/debug_toolbar/panels/profiling.html index 4c1c3acd3..0c2206a13 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/profiling.html +++ b/debug_toolbar/templates/debug_toolbar/panels/profiling.html @@ -2,12 +2,12 @@
    {% trans "Time" %}{% trans "Method" %}{% trans "Path" %}{% trans "Request Variables" %}{% trans "Status" %}{% trans "Action" %}{% translate "Time" %}{% translate "Method" %}{% translate "Path" %}{% translate "Request Variables" %}{% translate "Status" %}{% translate "Action" %}
    {% trans "Variable" %}{% trans "Value" %}{% translate "Variable" %}{% translate "Value" %}
    - - - - - - + + + + + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/request.html b/debug_toolbar/templates/debug_toolbar/panels/request.html index 076d5f74f..4a16468b5 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/request.html +++ b/debug_toolbar/templates/debug_toolbar/panels/request.html @@ -1,13 +1,13 @@ {% load i18n %} -

    {% trans "View information" %}

    +

    {% translate "View information" %}

    {% trans "Call" %}{% trans "CumTime" %}{% trans "Per" %}{% trans "TotTime" %}{% trans "Per" %}{% trans "Count" %}{% translate "Call" %}{% translate "CumTime" %}{% translate "Per" %}{% translate "TotTime" %}{% translate "Per" %}{% translate "Count" %}
    - - - - + + + + @@ -21,29 +21,29 @@

    {% trans "View information" %}

    {% trans "View function" %}{% trans "Arguments" %}{% trans "Keyword arguments" %}{% trans "URL name" %}{% translate "View function" %}{% translate "Arguments" %}{% translate "Keyword arguments" %}{% translate "URL name" %}
    {% if cookies.list or cookies.raw %} -

    {% trans "Cookies" %}

    +

    {% translate "Cookies" %}

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

    {% trans "No cookies" %}

    +

    {% translate "No cookies" %}

    {% endif %} {% if session.list or session.raw %} -

    {% trans "Session data" %}

    +

    {% translate "Session data" %}

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

    {% trans "No session data" %}

    +

    {% translate "No session data" %}

    {% endif %} {% if get.list or get.raw %} -

    {% trans "GET data" %}

    +

    {% translate "GET data" %}

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

    {% trans "No GET data" %}

    +

    {% translate "No GET data" %}

    {% endif %} {% if post.list or post.raw %} -

    {% trans "POST data" %}

    +

    {% translate "POST data" %}

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

    {% trans "No POST data" %}

    +

    {% translate "No POST data" %}

    {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/request_variables.html b/debug_toolbar/templates/debug_toolbar/panels/request_variables.html index 92200f867..26b487ab0 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/request_variables.html +++ b/debug_toolbar/templates/debug_toolbar/panels/request_variables.html @@ -8,8 +8,8 @@ - {% trans "Variable" %} - {% trans "Value" %} + {% translate "Variable" %} + {% translate "Value" %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/settings.html b/debug_toolbar/templates/debug_toolbar/panels/settings.html index 14763e4e6..5214c1b42 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/settings.html +++ b/debug_toolbar/templates/debug_toolbar/panels/settings.html @@ -2,8 +2,8 @@ - - + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/signals.html b/debug_toolbar/templates/debug_toolbar/panels/signals.html index cd9f42c4a..abd648924 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/signals.html +++ b/debug_toolbar/templates/debug_toolbar/panels/signals.html @@ -2,8 +2,8 @@
    {% trans "Setting" %}{% trans "Value" %}{% translate "Setting" %}{% translate "Value" %}
    - - + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html index e5bf0b7f6..63cf293c1 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html @@ -3,15 +3,15 @@ {% for alias, info in databases %}
  • {{ alias }} - {{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %} + {{ info.time_spent|floatformat:"2" }} ms ({% blocktranslate count num=info.num_queries %}{{ num }} query{% plural %}{{ num }} queries{% endblocktranslate %} {% if info.similar_count %} - {% blocktrans with count=info.similar_count trimmed %} + {% blocktranslate with count=info.similar_count trimmed %} including {{ count }} similar - {% endblocktrans %} + {% endblocktranslate %} {% if info.duplicate_count %} - {% blocktrans with dupes=info.duplicate_count trimmed %} + {% blocktranslate with dupes=info.duplicate_count trimmed %} and {{ dupes }} duplicates - {% endblocktrans %} + {% endblocktranslate %} {% endif %} {% endif %})
  • @@ -31,16 +31,16 @@ - - - - + + + + {% for query in queries %} - + @@ -49,13 +49,13 @@ {% if query.similar_count %} - {% blocktrans with count=query.similar_count %}{{ count }} similar queries.{% endblocktrans %} + {% blocktranslate with count=query.similar_count %}{{ count }} similar queries.{% endblocktranslate %} {% endif %} {% if query.duplicate_count %} - {% blocktrans with dupes=query.duplicate_count %}Duplicated {{ dupes }} times.{% endblocktrans %} + {% blocktranslate with dupes=query.duplicate_count %}Duplicated {{ dupes }} times.{% endblocktranslate %} {% endif %} @@ -92,12 +92,12 @@
    {% trans "Signal" %}{% trans "Receivers" %}{% translate "Signal" %}{% translate "Receivers" %}
    {% trans "Query" %}{% trans "Timeline" %}{% trans "Time (ms)" %}{% trans "Action" %}{% translate "Query" %}{% translate "Timeline" %}{% translate "Time (ms)" %}{% translate "Action" %}
    -

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

    +

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

    {% if query.iso_level %} -

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

    +

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

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

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

    +

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

    {% endif %} {% if query.stacktrace %}
    {{ query.stacktrace }}
    @@ -120,5 +120,5 @@
    {% else %} -

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

    +

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

    {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html index 61dadbda6..b9ff2911d 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html @@ -1,16 +1,16 @@ {% load i18n %}
    +

    {% translate "SQL explained" %}

    -

    {% trans "SQL explained" %}

    -
    {% trans "Executed SQL" %}
    +
    {% translate "Executed SQL" %}
    {{ sql|safe }}
    -
    {% trans "Time" %}
    +
    {% translate "Time" %}
    {{ duration }} ms
    -
    {% trans "Database" %}
    +
    {% translate "Database" %}
    {{ alias }}
    diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html index 57f20b619..d18a309c6 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html @@ -1,17 +1,17 @@ {% load i18n %}
    +

    {% translate "SQL profiled" %}

    -

    {% trans "SQL profiled" %}

    {% if result %}
    -
    {% trans "Executed SQL" %}
    +
    {% translate "Executed SQL" %}
    {{ sql|safe }}
    -
    {% trans "Time" %}
    +
    {% translate "Time" %}
    {{ duration }} ms
    -
    {% trans "Database" %}
    +
    {% translate "Database" %}
    {{ alias }}
    @@ -34,7 +34,7 @@

    {% trans "SQL profiled" %}

    {% else %}
    -
    {% trans "Error" %}
    +
    {% translate "Error" %}
    {{ result_error }}
    {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html index 699c18d87..9360cde05 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html @@ -1,16 +1,16 @@ {% load i18n %}
    +

    {% translate "SQL selected" %}

    -

    {% trans "SQL selected" %}

    -
    {% trans "Executed SQL" %}
    +
    {% translate "Executed SQL" %}
    {{ sql|safe }}
    -
    {% trans "Time" %}
    +
    {% translate "Time" %}
    {{ duration }} ms
    -
    {% trans "Database" %}
    +
    {% translate "Database" %}
    {{ alias }}
    {% if result %} @@ -33,7 +33,7 @@

    {% trans "SQL selected" %}

    {% else %} -

    {% trans "Empty set" %}

    +

    {% translate "Empty set" %}

    {% endif %}
    diff --git a/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html b/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html index 9aa519f67..aaa7c78ab 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html +++ b/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html @@ -1,17 +1,17 @@ {% load i18n %} -

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

    +

    {% blocktranslate count dirs_count=staticfiles_dirs|length %}Static file path{% plural %}Static file paths{% endblocktranslate %}

    {% if staticfiles_dirs %}
      {% for prefix, staticfiles_dir in staticfiles_dirs %} -
    1. {{ staticfiles_dir }}{% if prefix %} {% blocktrans %}(prefix {{ prefix }}){% endblocktrans %}{% endif %}
    2. +
    3. {{ staticfiles_dir }}{% if prefix %} {% blocktranslate %}(prefix {{ prefix }}){% endblocktranslate %}{% endif %}
    4. {% endfor %}
    {% else %} -

    {% trans "None" %}

    +

    {% translate "None" %}

    {% endif %} -

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

    +

    {% blocktranslate count apps_count=staticfiles_apps|length %}Static file app{% plural %}Static file apps{% endblocktranslate %}

    {% if staticfiles_apps %}
      {% for static_app in staticfiles_apps %} @@ -19,10 +19,10 @@

      {% blocktrans count staticfiles_apps|length as apps_count %}Static file app{ {% endfor %}

    {% else %} -

    {% trans "None" %}

    +

    {% translate "None" %}

    {% endif %} -

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

    +

    {% blocktranslate count staticfiles_count=staticfiles|length %}Static file{% plural %}Static files{% endblocktranslate %}

    {% if staticfiles %}
    {% for staticfile in staticfiles %} @@ -31,17 +31,17 @@

    {% blocktrans count staticfiles|length as staticfiles_count %}Static file{% {% endfor %}

    {% else %} -

    {% trans "None" %}

    +

    {% translate "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 %})

    +

    {{ finder }} ({% blocktranslate count payload_count=payload|length %}{{ payload_count }} file{% plural %}{{ payload_count }} files{% endblocktranslate %})

    - - + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/template_source.html b/debug_toolbar/templates/debug_toolbar/panels/template_source.html index 397c44b24..4d47fd3c3 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/template_source.html +++ b/debug_toolbar/templates/debug_toolbar/panels/template_source.html @@ -1,7 +1,7 @@ {% load i18n %}
    +

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

    -

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

    diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html index 121c086a8..4ceae12e7 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/templates.html +++ b/debug_toolbar/templates/debug_toolbar/panels/templates.html @@ -1,5 +1,5 @@ {% load i18n %} -

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

    +

    {% blocktranslate count template_count=template_dirs|length %}Template path{% plural %}Template paths{% endblocktranslate %}

    {% if template_dirs %}
      {% for template in template_dirs %} @@ -7,10 +7,10 @@

      {% blocktrans count template_dirs|length as template_count %}Template path{% {% endfor %}

    {% else %} -

    {% trans "None" %}

    +

    {% translate "None" %}

    {% endif %} -

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

    +

    {% blocktranslate count template_count=templates|length %}Template{% plural %}Templates{% endblocktranslate %}

    {% if templates %}
    {% for template in templates %} @@ -19,7 +19,7 @@

    {% blocktrans count templates|length as template_count %}Template{% plural % {% if template.context %}
    - {% trans "Toggle context" %} + {% translate "Toggle context" %} {{ template.context }}
    @@ -27,22 +27,22 @@

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

    {% else %} -

    {% trans "None" %}

    +

    {% translate "None" %}

    {% endif %} -

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

    +

    {% blocktranslate count context_processors_count=context_processors|length %}Context processor{% plural %}Context processors{% endblocktranslate %}

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

    {% trans "None" %}

    +

    {% translate "None" %}

    {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/timer.html b/debug_toolbar/templates/debug_toolbar/panels/timer.html index 11483c107..b85720483 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/timer.html +++ b/debug_toolbar/templates/debug_toolbar/panels/timer.html @@ -1,5 +1,5 @@ {% load i18n %} -

    {% trans "Resource usage" %}

    +

    {% translate "Resource usage" %}

    {% trans 'Path' %}{% trans 'Location' %}{% translate 'Path' %}{% translate 'Location' %}
    @@ -7,8 +7,8 @@

    {% trans "Resource usage" %}

    - - + + @@ -23,7 +23,7 @@

    {% trans "Resource usage" %}

    -

    {% trans "Browser timing" %}

    +

    {% translate "Browser timing" %}

    {% trans "Resource" %}{% trans "Value" %}{% translate "Resource" %}{% translate "Value" %}
    @@ -32,9 +32,9 @@

    {% trans "Browser timing" %}

    - - - + + + diff --git a/debug_toolbar/templates/debug_toolbar/panels/versions.html b/debug_toolbar/templates/debug_toolbar/panels/versions.html index d0ade6cfb..3428c0561 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/versions.html +++ b/debug_toolbar/templates/debug_toolbar/panels/versions.html @@ -7,9 +7,9 @@ - - - + + + diff --git a/debug_toolbar/templates/debug_toolbar/redirect.html b/debug_toolbar/templates/debug_toolbar/redirect.html index 9d8966ed7..46897846d 100644 --- a/debug_toolbar/templates/debug_toolbar/redirect.html +++ b/debug_toolbar/templates/debug_toolbar/redirect.html @@ -7,9 +7,9 @@

    {{ status_line }}

    -

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

    +

    {% translate "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." %} + {% translate "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." %}

    diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index dc3cc1adc..f4b3eac38 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -14,10 +14,12 @@ from django.template import Node from django.utils.html import format_html from django.utils.safestring import SafeString, mark_safe +from django.views.debug import get_default_exception_reporter_filter from debug_toolbar import _stubs as stubs, settings as dt_settings _local_data = Local() +safe_filter = get_default_exception_reporter_filter() def _is_excluded_frame(frame: Any, excluded_modules: Sequence[str] | None) -> bool: @@ -215,20 +217,50 @@ def getframeinfo(frame: Any, context: int = 1) -> inspect.Traceback: return inspect.Traceback(filename, lineno, frame.f_code.co_name, lines, index) -def get_sorted_request_variable( +def sanitize_and_sort_request_vars( variable: dict[str, Any] | QueryDict, ) -> dict[str, list[tuple[str, Any]] | Any]: """ Get a data structure for showing a sorted list of variables from the - request data. + request data with sensitive values redacted. """ + if not isinstance(variable, (dict, QueryDict)): + return {"raw": variable} + + # Get sorted keys if possible, otherwise just list them + keys = _get_sorted_keys(variable) + + # Process the variable based on its type + if isinstance(variable, QueryDict): + result = _process_query_dict(variable, keys) + else: + result = _process_dict(variable, keys) + + return {"list": result} + + +def _get_sorted_keys(variable): + """Helper function to get sorted keys if possible.""" try: - if isinstance(variable, dict): - return {"list": [(k, variable.get(k)) for k in sorted(variable)]} - else: - return {"list": [(k, variable.getlist(k)) for k in sorted(variable)]} + return sorted(variable) except TypeError: - return {"raw": variable} + return list(variable) + + +def _process_query_dict(query_dict, keys): + """Process a QueryDict into a list of (key, sanitized_value) tuples.""" + result = [] + for k in keys: + values = query_dict.getlist(k) + # Return single value if there's only one, otherwise keep as list + value = values[0] if len(values) == 1 else values + result.append((k, safe_filter.cleanse_setting(k, value))) + return result + + +def _process_dict(dictionary, keys): + """Process a dictionary into a list of (key, sanitized_value) tuples.""" + return [(k, safe_filter.cleanse_setting(k, dictionary.get(k))) for k in keys] def get_stack(context=1) -> list[stubs.InspectStack]: diff --git a/docs/changes.rst b/docs/changes.rst index dd52a09e1..bf1998de8 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -4,8 +4,28 @@ Change log Pending ------- +5.2.0 (2025-04-29) +------------------ + +* Added hook to RedirectsPanel for subclass customization. +* Added feature to sanitize sensitive data in the Request Panel. +* Fixed dark mode conflict in code block toolbar CSS. +* Properly allowed overriding the system theme preference by using the theme + selector. Removed the ``DEFAULT_THEME`` setting, we should always default to + system-level defaults where possible. +* Added support for using django-template-partials with the template panel's + source view functionality. The same change possibly adds support for other + template loaders. +* Introduced `djade `__ to format Django + templates. +* Swapped display order of panel header and close button to prevent style + conflicts +* Added CSS for resetting the height of elements too to avoid problems with + global CSS of a website where the toolbar is used. + 5.1.0 (2025-03-20) ------------------ + * Added Django 5.2 to the tox matrix. * Updated package metadata to include well-known labels. * Added resources section to the documentation. diff --git a/docs/conf.py b/docs/conf.py index 4cb37988e..6e67aac2e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ copyright = copyright.format(datetime.date.today().year) # The full version, including alpha/beta/rc tags -release = "5.1.0" +release = "5.2.0" # -- General configuration --------------------------------------------------- diff --git a/docs/configuration.rst b/docs/configuration.rst index 7cd6bc11b..d9e7ff342 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -201,13 +201,6 @@ Toolbar options request when it occurs. This is especially useful when using htmx boosting or similar JavaScript techniques. -.. _DEFAULT_THEME: - -* ``DEFAULT_THEME`` - - Default: ``"auto"`` - - This controls which theme will use the toolbar by default. Panel options ~~~~~~~~~~~~~ diff --git a/docs/panels.rst b/docs/panels.rst index be481fb6e..a116bff1e 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -122,6 +122,11 @@ Since this behavior is annoying when you aren't debugging a redirect, this panel is included but inactive by default. You can activate it by default with the ``DISABLE_PANELS`` configuration option. +To further customize the behavior, you can subclass the ``RedirectsPanel`` +and override the ``get_interception_response`` method to manipulate the +response directly. To use a custom ``RedirectsPanel``, you need to replace +the original one in ``DEBUG_TOOLBAR_PANELS`` in your ``settings.py``. + .. _profiling-panel: Profiling diff --git a/example/templates/htmx/boost.html b/example/templates/htmx/boost.html index 782303b4e..7153a79ee 100644 --- a/example/templates/htmx/boost.html +++ b/example/templates/htmx/boost.html @@ -7,7 +7,7 @@ -

    Index of Tests (htmx) - Page {{page_num|default:"1"}}

    +

    Index of Tests (htmx) - Page {{ page_num|default:"1" }}

    For the debug panel to remain through page navigation, add the setting: diff --git a/example/templates/turbo/index.html b/example/templates/turbo/index.html index 143054e37..16ca9f2c6 100644 --- a/example/templates/turbo/index.html +++ b/example/templates/turbo/index.html @@ -7,7 +7,7 @@ -

    Turbo Index - Page {{page_num|default:"1"}}

    +

    Turbo Index - Page {{ page_num|default:"1" }}

    For the debug panel to remain through page navigation, add the setting: diff --git a/tests/panels/test_custom.py b/tests/panels/test_custom.py index f13c4ef62..661a5cc53 100644 --- a/tests/panels/test_custom.py +++ b/tests/panels/test_custom.py @@ -33,8 +33,8 @@ def test_escapes_panel_title(self): """

    -

    Title with special chars &"'<>

    +
    diff --git a/tests/panels/test_redirects.py b/tests/panels/test_redirects.py index 2abed9fd0..7d6d5ac06 100644 --- a/tests/panels/test_redirects.py +++ b/tests/panels/test_redirects.py @@ -85,3 +85,16 @@ async def get_response(request): response = await self.panel.process_request(self.request) self.assertIsInstance(response, HttpResponse) self.assertTrue(response is await_response) + + def test_original_response_preserved(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.assertTrue(hasattr(response, "original_response")) + self.assertTrue(response.original_response is redirect) + self.assertIsNone(response.get("Location")) + self.assertEqual( + response.original_response.get("Location"), "http://somewhere/else/" + ) diff --git a/tests/panels/test_request.py b/tests/panels/test_request.py index 707b50bb4..2eb7ba610 100644 --- a/tests/panels/test_request.py +++ b/tests/panels/test_request.py @@ -136,3 +136,76 @@ def test_session_list_sorted_or_not(self): self.panel.generate_stats(self.request, response) panel_stats = self.panel.get_stats() self.assertEqual(panel_stats["session"], data) + + def test_sensitive_post_data_sanitized(self): + """Test that sensitive POST data is redacted.""" + self.request.POST = {"username": "testuser", "password": "secret123"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that password is redacted in panel content + content = self.panel.content + self.assertIn("username", content) + self.assertIn("testuser", content) + self.assertIn("password", content) + self.assertNotIn("secret123", content) + self.assertIn("********************", content) + + def test_sensitive_get_data_sanitized(self): + """Test that sensitive GET data is redacted.""" + self.request.GET = {"api_key": "abc123", "q": "search term"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that api_key is redacted in panel content + content = self.panel.content + self.assertIn("api_key", content) + self.assertNotIn("abc123", content) + self.assertIn("********************", content) + self.assertIn("q", content) + self.assertIn("search term", content) + + def test_sensitive_cookie_data_sanitized(self): + """Test that sensitive cookie data is redacted.""" + self.request.COOKIES = {"session_id": "abc123", "auth_token": "xyz789"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that auth_token is redacted in panel content + content = self.panel.content + self.assertIn("session_id", content) + self.assertIn("abc123", content) + self.assertIn("auth_token", content) + self.assertNotIn("xyz789", content) + self.assertIn("********************", content) + + def test_sensitive_session_data_sanitized(self): + """Test that sensitive session data is redacted.""" + self.request.session = {"user_id": 123, "auth_token": "xyz789"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that auth_token is redacted in panel content + content = self.panel.content + self.assertIn("user_id", content) + self.assertIn("123", content) + self.assertIn("auth_token", content) + self.assertNotIn("xyz789", content) + self.assertIn("********************", content) + + def test_querydict_sanitized(self): + """Test that sensitive data in QueryDict objects is properly redacted.""" + query_dict = QueryDict("username=testuser&password=secret123&token=abc456") + self.request.GET = query_dict + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that sensitive data is redacted in panel content + content = self.panel.content + self.assertIn("username", content) + self.assertIn("testuser", content) + self.assertIn("password", content) + self.assertNotIn("secret123", content) + self.assertIn("token", content) + self.assertNotIn("abc456", content) + self.assertIn("********************", content) diff --git a/tests/panels/test_settings.py b/tests/panels/test_settings.py index 5bf29d322..89b016dc0 100644 --- a/tests/panels/test_settings.py +++ b/tests/panels/test_settings.py @@ -24,8 +24,8 @@ def test_panel_title(self): """
    -

    Settings from None

    +
    diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 8e105657b..a411abb5d 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -655,8 +655,8 @@ def test_flat_template_information(self): template_info = query["template_info"] template_name = os.path.basename(template_info["name"]) self.assertEqual(template_name, "flat.html") - self.assertEqual(template_info["context"][2]["content"].strip(), "{{ users }}") - self.assertEqual(template_info["context"][2]["highlight"], True) + self.assertEqual(template_info["context"][3]["content"].strip(), "{{ users }}") + self.assertEqual(template_info["context"][3]["highlight"], True) @override_settings( DEBUG=True, diff --git a/tests/panels/test_template.py b/tests/panels/test_template.py index 636e88a23..44ac4ff0d 100644 --- a/tests/panels/test_template.py +++ b/tests/panels/test_template.py @@ -132,6 +132,24 @@ def test_lazyobject_eval(self): self.panel.generate_stats(self.request, response) self.assertIn("lazy_value", self.panel.content) + @override_settings( + DEBUG=True, + DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.templates.TemplatesPanel"], + ) + def test_template_source(self): + from django.core import signing + from django.template.loader import get_template + + template = get_template("basic.html") + url = "/__debug__/template_source/" + data = { + "template": template.template.name, + "template_origin": signing.dumps(template.template.origin.name), + } + + response = self.client.get(url, data) + self.assertEqual(response.status_code, 200) + @override_settings( DEBUG=True, DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.templates.TemplatesPanel"] diff --git a/tests/settings.py b/tests/settings.py index 0bf88bec1..12561fb11 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -7,6 +7,7 @@ # Quick-start development settings - unsuitable for production +DEBUG = False SECRET_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" INTERNAL_IPS = ["127.0.0.1"] @@ -27,6 +28,10 @@ "django.contrib.messages", "django.contrib.staticfiles", "debug_toolbar", + # We are not actively using template-partials; we just want more nesting + # in our template loader configuration, see + # https://github.com/django-commons/django-debug-toolbar/issues/2109 + "template_partials", "tests", ] diff --git a/tests/templates/ajax/ajax.html b/tests/templates/ajax/ajax.html index c9de3acb6..7955456de 100644 --- a/tests/templates/ajax/ajax.html +++ b/tests/templates/ajax/ajax.html @@ -1,4 +1,5 @@ {% extends "base.html" %} + {% block content %}
    click for ajax
    @@ -18,4 +19,4 @@ } document.addEventListener("click", (event) => {send_ajax()}); -{% endblock %} +{% endblock content %} diff --git a/tests/templates/basic.html b/tests/templates/basic.html index 46f88e4da..02f87200a 100644 --- a/tests/templates/basic.html +++ b/tests/templates/basic.html @@ -1,2 +1,3 @@ {% extends "base.html" %} + {% block content %}Test for {{ title }}{% endblock %} diff --git a/tests/templates/jinja2/basic.jinja b/tests/templates/jinja2/basic.jinja index e531eee64..1ebced724 100644 --- a/tests/templates/jinja2/basic.jinja +++ b/tests/templates/jinja2/basic.jinja @@ -1,5 +1,6 @@ {% extends 'base.html' %} + {% block content %} Test for {{ title }} (Jinja) {% for i in range(10) %}{{ i }}{% endfor %} {# Jinja2 supports range(), Django templates do not #} -{% endblock %} +{% endblock content %} diff --git a/tests/templates/sql/flat.html b/tests/templates/sql/flat.html index 058dbe043..ee5386c55 100644 --- a/tests/templates/sql/flat.html +++ b/tests/templates/sql/flat.html @@ -1,4 +1,5 @@ {% extends "base.html" %} + {% block content %} {{ users }} -{% endblock %} +{% endblock content %} diff --git a/tests/templates/sql/nested.html b/tests/templates/sql/nested.html index 8558e2d45..e23a53af1 100644 --- a/tests/templates/sql/nested.html +++ b/tests/templates/sql/nested.html @@ -1,4 +1,5 @@ {% extends "base.html" %} + {% block content %} {% include "sql/included.html" %} -{% endblock %} +{% endblock content %} diff --git a/tests/templates/staticfiles/async_static.html b/tests/templates/staticfiles/async_static.html index fc0c9b885..80f636cce 100644 --- a/tests/templates/staticfiles/async_static.html +++ b/tests/templates/staticfiles/async_static.html @@ -3,4 +3,4 @@ {% block head %} -{% endblock %} +{% endblock head %} diff --git a/tests/test_integration.py b/tests/test_integration.py index 3cc0b1420..a431ba29f 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -649,6 +649,9 @@ def setUpClass(cls): options = Options() if os.environ.get("CI"): options.add_argument("-headless") + # Set the browser preference to light mode for consistent testing + options.set_preference("ui.systemUsesDarkTheme", 0) + options.set_preference("ui.prefersReducedMotion", 0) cls.selenium = webdriver.Firefox(options=options) @classmethod @@ -809,7 +812,7 @@ def test_displays_server_error(self): debug_window = self.selenium.find_element(By.ID, "djDebugWindow") self.selenium.find_element(By.CLASS_NAME, "BuggyPanel").click() self.wait.until(EC.visibility_of(debug_window)) - self.assertEqual(debug_window.text, "»\n500: Internal Server Error") + self.assertEqual(debug_window.text, "500: Internal Server Error\n»") def test_toolbar_language_will_render_to_default_language_when_not_set(self): self.get("/regular/basic/") @@ -892,26 +895,35 @@ def test_theme_toggle(self): toolbar = self.selenium.find_element(By.ID, "djDebug") # Check that the default theme is auto - self.assertEqual(toolbar.get_attribute("data-theme"), "auto") + self.assertEqual(toolbar.get_attribute("data-user-theme"), "auto") # The theme toggle button is shown on the toolbar toggle_button = self.selenium.find_element(By.ID, "djToggleThemeButton") self.assertTrue(toggle_button.is_displayed()) - # The theme changes when user clicks the button - toggle_button.click() + # The browser is set to light mode via Firefox preferences + # With light mode system preference, the order is: auto -> dark -> light -> auto + # Check that auto initially uses light theme + self.assertEqual(toolbar.get_attribute("data-user-theme"), "auto") self.assertEqual(toolbar.get_attribute("data-theme"), "light") - toggle_button.click() + + # The theme changes when user clicks the button + toggle_button.click() # auto -> dark + self.assertEqual(toolbar.get_attribute("data-user-theme"), "dark") self.assertEqual(toolbar.get_attribute("data-theme"), "dark") - toggle_button.click() - self.assertEqual(toolbar.get_attribute("data-theme"), "auto") - # Switch back to light. - toggle_button.click() + + toggle_button.click() # dark -> light + self.assertEqual(toolbar.get_attribute("data-user-theme"), "light") + self.assertEqual(toolbar.get_attribute("data-theme"), "light") + + toggle_button.click() # light -> auto + self.assertEqual(toolbar.get_attribute("data-user-theme"), "auto") self.assertEqual(toolbar.get_attribute("data-theme"), "light") # Enter the page again to check that user settings is saved self.get("/regular/basic/") toolbar = self.selenium.find_element(By.ID, "djDebug") + self.assertEqual(toolbar.get_attribute("data-user-theme"), "auto") self.assertEqual(toolbar.get_attribute("data-theme"), "light") def test_async_sql_action(self): diff --git a/tests/test_utils.py b/tests/test_utils.py index 26bfce005..646b6a5ad 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,6 @@ import unittest +from django.http import QueryDict from django.test import override_settings import debug_toolbar.utils @@ -8,6 +9,7 @@ get_stack, get_stack_trace, render_stacktrace, + sanitize_and_sort_request_vars, tidy_stacktrace, ) @@ -109,3 +111,63 @@ def __init__(self, value): rendered_stack_2 = render_stacktrace(stack_2_wrapper.value) self.assertNotIn("test_locals_value_1", rendered_stack_2) self.assertIn("test_locals_value_2", rendered_stack_2) + + +class SanitizeAndSortRequestVarsTestCase(unittest.TestCase): + """Tests for the sanitize_and_sort_request_vars function.""" + + def test_dict_sanitization(self): + """Test sanitization of a regular dictionary.""" + test_dict = { + "username": "testuser", + "password": "secret123", + "api_key": "abc123", + } + result = sanitize_and_sort_request_vars(test_dict) + + # Convert to dict for easier testing + result_dict = dict(result["list"]) + + self.assertEqual(result_dict["username"], "testuser") + self.assertEqual(result_dict["password"], "********************") + self.assertEqual(result_dict["api_key"], "********************") + + def test_querydict_sanitization(self): + """Test sanitization of a QueryDict.""" + query_dict = QueryDict("username=testuser&password=secret123&api_key=abc123") + result = sanitize_and_sort_request_vars(query_dict) + + # Convert to dict for easier testing + result_dict = dict(result["list"]) + + self.assertEqual(result_dict["username"], "testuser") + self.assertEqual(result_dict["password"], "********************") + self.assertEqual(result_dict["api_key"], "********************") + + def test_non_sortable_dict_keys(self): + """Test dictionary with keys that can't be sorted.""" + test_dict = { + 1: "one", + "2": "two", + None: "none", + } + result = sanitize_and_sort_request_vars(test_dict) + self.assertEqual(len(result["list"]), 3) + result_dict = dict(result["list"]) + self.assertEqual(result_dict[1], "one") + self.assertEqual(result_dict["2"], "two") + self.assertEqual(result_dict[None], "none") + + def test_querydict_multiple_values(self): + """Test QueryDict with multiple values for the same key.""" + query_dict = QueryDict("name=bar1&name=bar2&title=value") + result = sanitize_and_sort_request_vars(query_dict) + result_dict = dict(result["list"]) + self.assertEqual(result_dict["name"], ["bar1", "bar2"]) + self.assertEqual(result_dict["title"], "value") + + def test_non_dict_input(self): + """Test handling of non-dict input.""" + test_input = ["not", "a", "dict"] + result = sanitize_and_sort_request_vars(test_input) + self.assertEqual(result["raw"], test_input) diff --git a/tox.ini b/tox.ini index c8f4a6815..e2dcdd6c6 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ deps = selenium>=4.8.0 sqlparse django-csp + django-template-partials passenv= CI COVERAGE_ARGS pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy

    {% trans "Timing attribute" %}{% trans "Timeline" %}{% trans "Milliseconds since navigation start (+length)" %}{% translate "Timing attribute" %}{% translate "Timeline" %}{% translate "Milliseconds since navigation start (+length)" %}
    {% trans "Package" %}{% trans "Name" %}{% trans "Version" %}{% translate "Package" %}{% translate "Name" %}{% translate "Version" %}