├── requirements ├── build.in ├── tests.in ├── docs.in ├── dev.in ├── typing.in ├── build.txt ├── tests.txt ├── docs.txt └── typing.txt ├── src └── flask_debugtoolbar │ ├── py.typed │ ├── static │ ├── img │ │ ├── asc.gif │ │ ├── bg.gif │ │ ├── back.png │ │ ├── close.png │ │ ├── desc.gif │ │ ├── tick.png │ │ ├── panel_bg.png │ │ ├── tick-red.png │ │ ├── back_hover.png │ │ ├── indicator.png │ │ ├── close_hover.png │ │ └── djdt_vertical.png │ └── codemirror │ │ ├── mode │ │ ├── diff │ │ │ ├── diff.css │ │ │ └── diff.js │ │ ├── rpm │ │ │ ├── spec │ │ │ │ ├── spec.css │ │ │ │ ├── spec.js │ │ │ │ └── index.html │ │ │ └── changes │ │ │ │ ├── changes.js │ │ │ │ └── index.html │ │ ├── xquery │ │ │ ├── test │ │ │ │ ├── testEmptySequenceKeyword.js │ │ │ │ ├── testProcessingInstructions.js │ │ │ │ ├── testMultiAttr.js │ │ │ │ ├── testQuotes.js │ │ │ │ └── index.html │ │ │ └── LICENSE │ │ ├── tiddlywiki │ │ │ └── tiddlywiki.css │ │ ├── vbscript │ │ │ ├── vbscript.js │ │ │ └── index.html │ │ ├── pascal │ │ │ ├── LICENSE │ │ │ ├── index.html │ │ │ └── pascal.js │ │ ├── ntriples │ │ │ └── index.html │ │ ├── python │ │ │ ├── LICENSE.txt │ │ │ └── index.html │ │ ├── perl │ │ │ ├── LICENSE │ │ │ └── index.html │ │ ├── jinja2 │ │ │ ├── index.html │ │ │ └── jinja2.js │ │ ├── coffeescript │ │ │ └── LICENSE │ │ ├── rust │ │ │ └── index.html │ │ ├── css │ │ │ └── index.html │ │ ├── mysql │ │ │ └── index.html │ │ ├── ecl │ │ │ └── index.html │ │ ├── properties │ │ │ ├── index.html │ │ │ └── properties.js │ │ ├── sparql │ │ │ └── index.html │ │ ├── gfm │ │ │ ├── index.html │ │ │ └── gfm.js │ │ ├── r │ │ │ ├── LICENSE │ │ │ └── index.html │ │ ├── ruby │ │ │ └── LICENSE │ │ ├── php │ │ │ └── index.html │ │ ├── smalltalk │ │ │ └── index.html │ │ ├── htmlmixed │ │ │ ├── index.html │ │ │ └── htmlmixed.js │ │ ├── xml │ │ │ └── index.html │ │ ├── plsql │ │ │ └── index.html │ │ ├── htmlembedded │ │ │ ├── index.html │ │ │ └── htmlembedded.js │ │ ├── lua │ │ │ └── index.html │ │ ├── yaml │ │ │ ├── index.html │ │ │ └── yaml.js │ │ ├── haskell │ │ │ └── index.html │ │ ├── groovy │ │ │ └── index.html │ │ ├── go │ │ │ └── index.html │ │ ├── clojure │ │ │ └── index.html │ │ ├── scheme │ │ │ └── index.html │ │ ├── xmlpure │ │ │ └── index.html │ │ ├── smarty │ │ │ └── index.html │ │ ├── javascript │ │ │ └── index.html │ │ ├── velocity │ │ │ └── index.html │ │ └── clike │ │ │ └── index.html │ │ ├── util │ │ ├── simple-hint.css │ │ ├── dialog.css │ │ ├── runmode.js │ │ ├── match-highlighter.js │ │ ├── overlay.js │ │ ├── dialog.js │ │ └── simple-hint.js │ │ ├── theme │ │ ├── neat.css │ │ ├── elegant.css │ │ ├── cobalt.css │ │ ├── eclipse.css │ │ ├── night.css │ │ ├── monokai.css │ │ ├── rubyblue.css │ │ ├── lesser-dark.css │ │ └── xq-dark.css │ │ └── codemirror.css │ ├── templates │ ├── panels │ │ ├── g.html │ │ ├── headers.html │ │ ├── config_vars.html │ │ ├── timer.html │ │ ├── logger.html │ │ ├── versions.html │ │ ├── route_list.html │ │ ├── template.html │ │ ├── profiler.html │ │ ├── sqlalchemy_select.html │ │ ├── sqlalchemy.html │ │ ├── sqlalchemy_error.html │ │ └── request_vars.html │ ├── redirect.html │ └── base.html │ ├── panels │ ├── g.py │ ├── config_vars.py │ ├── versions.py │ ├── route_list.py │ ├── headers.py │ ├── request_vars.py │ ├── __init__.py │ ├── timer.py │ └── logger.py │ ├── toolbar.py │ └── utils.py ├── tests ├── templates │ └── basic_app.html ├── conftest.py └── basic_app.py ├── docs ├── _static │ ├── example.gif │ ├── screenshot-time-panel.png │ ├── screenshot-config-panel.png │ ├── screenshot-headers-panel.png │ ├── screenshot-logger-panel.png │ ├── screenshot-profiler-panel.png │ ├── screenshot-template-panel.png │ ├── screenshot-request-vars-panel.png │ └── screenshot-sqlalchemy-panel.png ├── license.rst ├── conf.py └── panels.rst ├── .gitignore ├── .readthedocs.yaml ├── example ├── templates │ └── index.html └── app.py ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.md │ └── bug-report.md ├── dependabot.yml ├── workflows │ ├── lock.yaml │ ├── tests.yaml │ └── publish.yaml └── pull_request_template.md ├── .pre-commit-config.yaml ├── README.md ├── tox.ini ├── LICENSE.txt └── pyproject.toml /requirements/build.in: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/tests.in: -------------------------------------------------------------------------------- 1 | pytest 2 | flask-sqlalchemy 3 | pygments 4 | -------------------------------------------------------------------------------- /requirements/docs.in: -------------------------------------------------------------------------------- 1 | pallets-sphinx-themes 2 | sphinx 3 | sphinxcontrib-log-cabinet 4 | -------------------------------------------------------------------------------- /requirements/dev.in: -------------------------------------------------------------------------------- 1 | -r docs.txt 2 | -r tests.txt 3 | -r typing.txt 4 | pre-commit 5 | tox 6 | -------------------------------------------------------------------------------- /requirements/typing.in: -------------------------------------------------------------------------------- 1 | mypy 2 | pyright 3 | pytest 4 | types-pygments 5 | flask-sqlalchemy 6 | -------------------------------------------------------------------------------- /tests/templates/basic_app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello world 4 | 5 | -------------------------------------------------------------------------------- /docs/_static/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/example.gif -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | BSD-3-Clause License 2 | ==================== 3 | 4 | .. literalinclude:: ../LICENSE.txt 5 | :language: text 6 | -------------------------------------------------------------------------------- /docs/_static/screenshot-time-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-time-panel.png -------------------------------------------------------------------------------- /docs/_static/screenshot-config-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-config-panel.png -------------------------------------------------------------------------------- /docs/_static/screenshot-headers-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-headers-panel.png -------------------------------------------------------------------------------- /docs/_static/screenshot-logger-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-logger-panel.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/asc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/asc.gif -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/bg.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | .venv*/ 4 | venv*/ 5 | __pycache__/ 6 | dist/ 7 | .coverage* 8 | htmlcov/ 9 | .tox/ 10 | docs/_build/ 11 | -------------------------------------------------------------------------------- /docs/_static/screenshot-profiler-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-profiler-panel.png -------------------------------------------------------------------------------- /docs/_static/screenshot-template-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-template-panel.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/back.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/close.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/desc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/desc.gif -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/tick.png -------------------------------------------------------------------------------- /docs/_static/screenshot-request-vars-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-request-vars-panel.png -------------------------------------------------------------------------------- /docs/_static/screenshot-sqlalchemy-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/docs/_static/screenshot-sqlalchemy-panel.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/panel_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/panel_bg.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/tick-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/tick-red.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/diff/diff.css: -------------------------------------------------------------------------------- 1 | span.cm-rangeinfo {color: #a0b;} 2 | span.cm-minus {color: red;} 3 | span.cm-plus {color: #2b2;} 4 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/back_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/back_hover.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/indicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/indicator.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/close_hover.png -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/img/djdt_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/HEAD/src/flask_debugtoolbar/static/img/djdt_vertical.png -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def mock_env_development(monkeypatch: pytest.MonkeyPatch) -> None: 8 | monkeypatch.setenv("FLASK_ENV", "development") 9 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-22.04 4 | tools: 5 | python: '3.12' 6 | python: 7 | install: 8 | - requirements: requirements/docs.txt 9 | - method: pip 10 | path: . 11 | sphinx: 12 | builder: dirhtml 13 | fail_on_warning: true 14 | -------------------------------------------------------------------------------- /example/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Flask-debug-toolbar 4 | 5 | 6 |

Flask-debug-toolbar

7 | Redirect example 8 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | max_line_length = 88 11 | 12 | [*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rpm/spec/spec.css: -------------------------------------------------------------------------------- 1 | .cm-s-default span.cm-preamble {color: #b26818; font-weight: bold;} 2 | .cm-s-default span.cm-macro {color: #b218b2;} 3 | .cm-s-default span.cm-section {color: green; font-weight: bold;} 4 | .cm-s-default span.cm-script {color: red;} 5 | .cm-s-default span.cm-issue {color: yellow;} 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions on Discussions 4 | url: https://github.com/pallets-eco/flask-debugtoolbar/discussions/ 5 | about: Ask questions about your own code on the Discussions tab. 6 | - name: Questions on Chat 7 | url: https://discord.gg/pallets 8 | about: Ask questions about your own code on our Discord chat. 9 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/diff/diff.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("diff", function() { 2 | return { 3 | token: function(stream) { 4 | var ch = stream.next(); 5 | stream.skipToEnd(); 6 | if (ch == "+") return "plus"; 7 | if (ch == "-") return "minus"; 8 | if (ch == "@") return "rangeinfo"; 9 | } 10 | }; 11 | }); 12 | 13 | CodeMirror.defineMIME("text/x-diff", "diff"); 14 | -------------------------------------------------------------------------------- /requirements/build.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.8 3 | # by the following command: 4 | # 5 | # pip-compile build.in 6 | # 7 | build==1.2.2 8 | # via -r build.in 9 | importlib-metadata==7.1.0 10 | # via build 11 | packaging==24.0 12 | # via build 13 | pyproject-hooks==1.1.0 14 | # via build 15 | tomli==2.0.1 16 | # via build 17 | zipp==3.19.1 18 | # via importlib-metadata 19 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/g.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% for key, value in g_content|dictsort %} 10 | 11 | 12 | 13 | 14 | {% endfor %} 15 | 16 |
KeyValue
{{ key|escape }}{{ value|escape }}
17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | groups: 8 | github-actions: 9 | patterns: 10 | - '*' 11 | - package-ecosystem: pip 12 | directory: /requirements/ 13 | schedule: 14 | interval: monthly 15 | groups: 16 | python-requirements: 17 | patterns: 18 | - '*' 19 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/headers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% for key, value in headers|dictsort %} 10 | 11 | 12 | 13 | 14 | {% endfor %} 15 | 16 |
KeyValue
{{ key|escape }}{{ value|escape }}
17 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/simple-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-completions { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 6 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 7 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 8 | } 9 | .CodeMirror-completions select { 10 | background: #fafafa; 11 | outline: none; 12 | border: none; 13 | padding: 0; 14 | margin: 0; 15 | font-family: monospace; 16 | } 17 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/config_vars.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% for key, value in config|dictsort %} 10 | 11 | 12 | 13 | 14 | {% endfor %} 15 | 16 |
KeyValue
{{ key }}{{ value|printable }}
17 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/timer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for key, value in rows %} 14 | 15 | 16 | 17 | 18 | {% endfor %} 19 | 20 |
ResourceValue
{{ key|escape }}{{ value|escape }}
21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature for Flask-DebugToolbar 4 | --- 5 | 6 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: relative; 3 | } 4 | 5 | .CodeMirror-dialog > div { 6 | position: absolute; 7 | top: 0; left: 0; right: 0; 8 | background: white; 9 | border-bottom: 1px solid #eee; 10 | z-index: 15; 11 | padding: .1em .8em; 12 | overflow: hidden; 13 | color: #333; 14 | } 15 | 16 | .CodeMirror-dialog input { 17 | border: none; 18 | outline: none; 19 | background: transparent; 20 | width: 20em; 21 | color: inherit; 22 | font-family: monospace; 23 | } 24 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/neat.css: -------------------------------------------------------------------------------- 1 | .cm-s-neat span.cm-comment { color: #a86; } 2 | .cm-s-neat span.cm-keyword { line-height: 1em; font-weight: bold; color: blue; } 3 | .cm-s-neat span.cm-string { color: #a22; } 4 | .cm-s-neat span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; } 5 | .cm-s-neat span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } 6 | .cm-s-neat span.cm-variable { color: black; } 7 | .cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; } 8 | .cm-s-neat span.cm-meta {color: #555;} 9 | .cm-s-neat span.cm-link { color: #3a3; } 10 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Redirect intercepted 4 | 5 | 6 |

Redirect ({{ redirect_code }})

7 |

Location: {{ redirect_to }}

8 |

9 | The Flask Debug Toolbar has intercepted a redirect to the above URL for 10 | debug viewing purposes. You can click the above link to continue with the 11 | redirect as normal. If you'd like to disable this feature, you can set the 12 | config variable DEBUG_TB_INTERCEPT_REDIRECTS to False. 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/elegant.css: -------------------------------------------------------------------------------- 1 | .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;} 2 | .cm-s-elegant span.cm-comment {color: #262; font-style: italic; line-height: 1em;} 3 | .cm-s-elegant span.cm-meta {color: #555; font-style: italic; line-height: 1em;} 4 | .cm-s-elegant span.cm-variable {color: black;} 5 | .cm-s-elegant span.cm-variable-2 {color: #b11;} 6 | .cm-s-elegant span.cm-qualifier {color: #555;} 7 | .cm-s-elegant span.cm-keyword {color: #730;} 8 | .cm-s-elegant span.cm-builtin {color: #30a;} 9 | .cm-s-elegant span.cm-error {background-color: #fdd;} 10 | .cm-s-elegant span.cm-link {color: #762;} 11 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/g.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from flask import g 4 | 5 | from . import DebugPanel 6 | 7 | 8 | class GDebugPanel(DebugPanel): 9 | """A panel to display ``flask.g`` content.""" 10 | 11 | name = "g" 12 | has_content = True 13 | 14 | def nav_title(self) -> str: 15 | return "flask.g" 16 | 17 | def title(self) -> str: 18 | return "flask.g content" 19 | 20 | def url(self) -> str: 21 | return "" 22 | 23 | def content(self) -> str: 24 | context = self.context.copy() 25 | context.update({"g_content": g.__dict__}) 26 | return self.render("panels/g.html", context) 27 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autoupdate_schedule: monthly 3 | repos: 4 | - repo: https://github.com/astral-sh/ruff-pre-commit 5 | rev: v0.6.3 6 | hooks: 7 | - id: ruff 8 | - id: ruff-format 9 | - repo: https://github.com/pre-commit/pre-commit-hooks 10 | rev: v4.6.0 11 | hooks: 12 | - id: check-merge-conflict 13 | exclude: "(codemirror|jquery)" 14 | - id: debug-statements 15 | exclude: "(codemirror|jquery)" 16 | - id: fix-byte-order-marker 17 | exclude: "(codemirror|jquery)" 18 | - id: trailing-whitespace 19 | exclude: "(codemirror|jquery)" 20 | - id: end-of-file-fixer 21 | exclude: "(codemirror|jquery)" 22 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/logger.html: -------------------------------------------------------------------------------- 1 | {% if records %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% for record in records %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% endfor %} 20 | 21 |
LevelTimeMessageLocation
{{ record.level }}{{ record.time }}{{ record.message }}{{ record.file }}:{{ record.line }}
22 | {% else %} 23 |

No messages logged.

24 | {% endif %} 25 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/versions.html: -------------------------------------------------------------------------------- 1 |

Installed Packages

2 | 3 |

4 | Current Site Packages Directory: 5 |

6 |
 7 | {{ python_lib_dir }}
 8 | 
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for package in packages %} 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% endfor %} 28 | 29 |
PackageVersionHomepageSummary
{{ package.get('Name') }}{{ package.get('Version') }}{{ package.get('Home-page') }}{{ package.get('Summary') }}
30 | -------------------------------------------------------------------------------- /.github/workflows/lock.yaml: -------------------------------------------------------------------------------- 1 | name: Lock inactive closed issues 2 | # Lock closed issues that have not received any further activity for two weeks. 3 | # This does not close open issues, only humans may do that. It is easier to 4 | # respond to new issues with fresh examples rather than continuing discussions 5 | # on old issues. 6 | 7 | on: 8 | schedule: 9 | - cron: '0 0 * * *' 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | concurrency: 14 | group: lock 15 | jobs: 16 | lock: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 20 | with: 21 | issue-inactive-days: 14 22 | pr-inactive-days: 14 23 | discussion-inactive-days: 14 24 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/test/testEmptySequenceKeyword.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | module("testEmptySequenceKeyword"); 3 | test("testEmptySequenceKeyword", function() { 4 | expect(1); 5 | 6 | var input = '"foo" instance of empty-sequence()'; 7 | var expected = '"foo" instance of empty-sequence()'; 8 | 9 | $("#sandbox").html(''); 10 | var editor = CodeMirror.fromTextArea($("#editor")[0]); 11 | var result = $(".CodeMirror-lines div div pre")[0].innerHTML; 12 | 13 | equal(result, expected); 14 | $("#editor").html(""); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rpm/changes/changes.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("changes", function(config, modeConfig) { 2 | var headerSeperator = /^-+$/; 3 | var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /; 4 | var simpleEmail = /^[\w+.-]+@[\w.-]+/; 5 | 6 | return { 7 | token: function(stream) { 8 | if (stream.sol()) { 9 | if (stream.match(headerSeperator)) { return 'tag'; } 10 | if (stream.match(headerLine)) { return 'tag'; } 11 | } 12 | if (stream.match(simpleEmail)) { return 'string'; } 13 | stream.next(); 14 | return null; 15 | } 16 | }; 17 | }); 18 | 19 | CodeMirror.defineMIME("text/x-rpm-changes", "changes"); 20 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/config_vars.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from flask import current_app 4 | 5 | from . import DebugPanel 6 | 7 | 8 | class ConfigVarsDebugPanel(DebugPanel): 9 | """A panel to display all variables from Flask configuration.""" 10 | 11 | name = "ConfigVars" 12 | has_content = True 13 | 14 | def nav_title(self) -> str: 15 | return "Config" 16 | 17 | def title(self) -> str: 18 | return "Config" 19 | 20 | def url(self) -> str: 21 | return "" 22 | 23 | def content(self) -> str: 24 | context = self.context.copy() 25 | context.update( 26 | { 27 | "config": current_app.config, 28 | } 29 | ) 30 | return self.render("panels/config_vars.html", context) 31 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/route_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% if routes %} 13 | {% for route in routes|sort(attribute='rule') %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% endfor %} 22 | {% else %} 23 | 24 | 25 | 26 | {% endif %} 27 | 28 |
URL routeEndpoint nameHTTP methodsIs aliasRedirect to
{{ route.rule }}{{ route.endpoint }}{{ route.methods|sort|join(', ') }}{{ route.alias }}{{ route.redirect_to }}
No routes have been configured.
29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug in Flask-DebugToolbar (not other projects which depend on Flask-DebugToolbar) 4 | --- 5 | 6 | 13 | 14 | 20 | 21 | 24 | 25 | Environment: 26 | 27 | - Python version: 28 | - Flask-DebugToolbar version: 29 | - Flask version: 30 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/template.html: -------------------------------------------------------------------------------- 1 | {% if templates %} 2 | {% if editable %} 3 | Edit templates 4 | {% endif %} 5 | {% for template in templates %} 6 |

{{ template.template.name }}

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% for k, v in template.context|dictsort %} 16 | 17 | 18 | 19 | 20 | {% endfor %} 21 | 22 |
VariableValue
{{ k }}{{ v|printable }}
23 | {% endfor %} 24 | {% else %} 25 |

No template rendered

26 | {% endif %} 27 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/test/testProcessingInstructions.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | module("testProcessingInstructions"); 3 | test("testProcessingInstructions", function() { 4 | expect(1); 5 | 6 | var input = 'data() instance of xs:string'; 7 | var expected = 'data(<?target content?>) instance of xs:string'; 8 | 9 | $("#sandbox").html(''); 10 | var editor = CodeMirror.fromTextArea($("#editor")[0]); 11 | var result = $(".CodeMirror-lines div div pre")[0].innerHTML; 12 | 13 | equal(result, expected); 14 | $("#editor").html(""); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/profiler.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for row in function_calls %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | 24 |
CallsTotal Time (ms)Per Call (ms)Cumulative Time (ms)Per Call (ms)Function
{{ row.ncalls }}{{ row.tottime }}{{ '%.4f'|format(row.percall) }}{{ row.cumtime }}{{ '%.4f'|format(row.percall_cum) }}{{ row.filename|escape }}
25 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/test/testMultiAttr.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | module("testMultiAttr"); 3 | test("test1", function() { 4 | expect(1); 5 | 6 | var expected = '<p a1="foo" a2="bar">hello world</p>'; 7 | 8 | $("#sandbox").html(''); 9 | $("#editor").html('

hello world

'); 10 | var editor = CodeMirror.fromTextArea($("#editor")[0]); 11 | var result = $(".CodeMirror-lines div div pre")[0].innerHTML; 12 | 13 | equal(result, expected); 14 | $("#editor").html(""); 15 | }); 16 | }); -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 26 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/tiddlywiki/tiddlywiki.css: -------------------------------------------------------------------------------- 1 | .cm-s-default span.cm-header {color: blue; font-weight:bold;} 2 | .cm-s-default span.cm-code {color: #a50;} 3 | .cm-s-default span.cm-code-inline {color: #660;} 4 | 5 | .cm-s-default span.cm-quote {color: #555;} 6 | .cm-s-default span.cm-list {color: #c60;} 7 | .cm-s-default span.cm-hr {color: #999;} 8 | .cm-s-default span.cm-em {font-style: italic;} 9 | .cm-s-default span.cm-strong {font-weight: bold;} 10 | 11 | .cm-s-default span.cm-link-external {color: blue;} 12 | .cm-s-default span.cm-brace {color: #170; font-weight: bold;} 13 | .cm-s-default span.cm-macro {color: #9E3825;} 14 | .cm-s-default span.cm-table {color: blue; font-weight: bold;} 15 | .cm-s-default span.cm-warning {color: red; font-weight: bold;} 16 | 17 | .cm-s-default span.cm-underlined {text-decoration: underline;} 18 | .cm-s-default span.cm-line-through {text-decoration: line-through;} 19 | 20 | .cm-s-default span.cm-comment {color: #666;} 21 | 22 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/vbscript/vbscript.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("vbscript", function() { 2 | var regexVBScriptKeyword = /^(?:Call|Case|CDate|Clear|CInt|CLng|Const|CStr|Description|Dim|Do|Each|Else|ElseIf|End|Err|Error|Exit|False|For|Function|If|LCase|Loop|LTrim|Next|Nothing|Now|Number|On|Preserve|Quit|ReDim|Resume|RTrim|Select|Set|Sub|Then|To|Trim|True|UBound|UCase|Until|VbCr|VbCrLf|VbLf|VbTab)$/im; 3 | 4 | return { 5 | token: function(stream) { 6 | if (stream.eatSpace()) return null; 7 | var ch = stream.next(); 8 | if (ch == "'") { 9 | stream.skipToEnd(); 10 | return "comment"; 11 | } 12 | if (ch == '"') { 13 | stream.skipTo('"'); 14 | return "string"; 15 | } 16 | 17 | if (/\w/.test(ch)) { 18 | stream.eatWhile(/\w/); 19 | if (regexVBScriptKeyword.test(stream.current())) return "keyword"; 20 | } 21 | return null; 22 | } 23 | }; 24 | }); 25 | 26 | CodeMirror.defineMIME("text/vbscript", "vbscript"); 27 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/sqlalchemy_select.html: -------------------------------------------------------------------------------- 1 |
2 | Back 3 |

SQL Details

4 |
5 |
6 |
7 |
8 |
Executed SQL
9 |
{{ sql }}
10 |
Original query duration
11 |
{{ '%.4f'|format(duration * 1000) }} ms
12 |
13 | {% if result %} 14 | 15 | 16 | 17 | {% for h in headers %} 18 | 19 | {% endfor %} 20 | 21 | 22 | 23 | {% for row in result %} 24 | 25 | {% for column in row %} 26 | 27 | {% endfor %} 28 | 29 | {% endfor %} 30 | 31 |
{{ h|upper }}
{{ column }}
32 | {% else %} 33 |

Empty set

34 | {% endif %} 35 |
36 |
37 | -------------------------------------------------------------------------------- /requirements/tests.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.8 3 | # by the following command: 4 | # 5 | # pip-compile tests.in 6 | # 7 | blinker==1.8.1 8 | # via flask 9 | click==8.1.7 10 | # via flask 11 | exceptiongroup==1.2.1 12 | # via pytest 13 | flask==3.0.3 14 | # via flask-sqlalchemy 15 | flask-sqlalchemy==3.1.1 16 | # via -r tests.in 17 | greenlet==3.0.3 18 | # via sqlalchemy 19 | importlib-metadata==7.1.0 20 | # via flask 21 | iniconfig==2.0.0 22 | # via pytest 23 | itsdangerous==2.2.0 24 | # via flask 25 | jinja2==3.1.4 26 | # via flask 27 | markupsafe==2.1.5 28 | # via 29 | # jinja2 30 | # werkzeug 31 | packaging==24.0 32 | # via pytest 33 | pluggy==1.5.0 34 | # via pytest 35 | pygments==2.18.0 36 | # via -r tests.in 37 | pytest==8.3.3 38 | # via -r tests.in 39 | sqlalchemy==2.0.29 40 | # via flask-sqlalchemy 41 | tomli==2.0.1 42 | # via pytest 43 | typing-extensions==4.11.0 44 | # via sqlalchemy 45 | werkzeug==3.0.3 46 | # via flask 47 | zipp==3.19.1 48 | # via importlib-metadata 49 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/pascal/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 souceLair 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/cobalt.css: -------------------------------------------------------------------------------- 1 | .cm-s-cobalt { background: #002240; color: white; } 2 | .cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; } 3 | .cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } 4 | .cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; } 5 | .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; } 6 | 7 | .cm-s-cobalt span.cm-comment { color: #08f; } 8 | .cm-s-cobalt span.cm-atom { color: #845dc4; } 9 | .cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; } 10 | .cm-s-cobalt span.cm-keyword { color: #ffee80; } 11 | .cm-s-cobalt span.cm-string { color: #3ad900; } 12 | .cm-s-cobalt span.cm-meta { color: #ff9d00; } 13 | .cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; } 14 | .cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def { color: white; } 15 | .cm-s-cobalt span.cm-error { color: #9d1e15; } 16 | .cm-s-cobalt span.cm-bracket { color: #d8d8d8; } 17 | .cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; } 18 | .cm-s-cobalt span.cm-link { color: #845dc4; } 19 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/versions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import importlib.metadata 4 | import os 5 | from sysconfig import get_path 6 | 7 | from . import DebugPanel 8 | 9 | flask_version: str = importlib.metadata.version("flask") 10 | 11 | 12 | class VersionDebugPanel(DebugPanel): 13 | """Panel that displays the Flask version.""" 14 | 15 | name = "Version" 16 | has_content = True 17 | 18 | def nav_title(self) -> str: 19 | return "Versions" 20 | 21 | def nav_subtitle(self) -> str: 22 | return f"Flask {flask_version}" 23 | 24 | def url(self) -> str: 25 | return "" 26 | 27 | def title(self) -> str: 28 | return "Versions" 29 | 30 | def content(self) -> str: 31 | packages_metadata = [p.metadata for p in importlib.metadata.distributions()] 32 | packages = sorted(packages_metadata, key=lambda p: p["Name"].lower()) 33 | return self.render( 34 | "panels/versions.html", 35 | { 36 | "packages": packages, 37 | "python_lib_dir": os.path.normpath(get_path("platlib")), 38 | }, 39 | ) 40 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/ntriples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: NTriples mode 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |

CodeMirror: NTriples mode

17 |
18 | 25 |
26 | 27 | 30 |

MIME types defined: text/n-triples.

31 | 32 | 33 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/test/testQuotes.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | module("testQuoteEscape"); 3 | test("testQuoteEscapeDouble", function() { 4 | expect(1); 5 | 6 | var input = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\ 7 | let $keysfolder := concat($rootfolder, "keys\\")\ 8 | return\ 9 | $keysfolder'; 10 | var expected = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"let $keysfolder := concat($rootfolder, "keys\\")return$keysfolder'; 11 | 12 | $("#sandbox").html(''); 13 | var editor = CodeMirror.fromTextArea($("#editor")[0]); 14 | var result = $(".CodeMirror-lines div div pre")[0].innerHTML; 15 | 16 | equal(result, expected); 17 | $("#editor").html(""); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/eclipse.css: -------------------------------------------------------------------------------- 1 | .cm-s-eclipse span.cm-meta {color: #FF1717;} 2 | .cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; } 3 | .cm-s-eclipse span.cm-atom {color: #219;} 4 | .cm-s-eclipse span.cm-number {color: #164;} 5 | .cm-s-eclipse span.cm-def {color: #00f;} 6 | .cm-s-eclipse span.cm-variable {color: black;} 7 | .cm-s-eclipse span.cm-variable-2 {color: #0000C0;} 8 | .cm-s-eclipse span.cm-variable-3 {color: #0000C0;} 9 | .cm-s-eclipse span.cm-property {color: black;} 10 | .cm-s-eclipse span.cm-operator {color: black;} 11 | .cm-s-eclipse span.cm-comment {color: #3F7F5F;} 12 | .cm-s-eclipse span.cm-string {color: #2A00FF;} 13 | .cm-s-eclipse span.cm-string-2 {color: #f50;} 14 | .cm-s-eclipse span.cm-error {color: #f00;} 15 | .cm-s-eclipse span.cm-qualifier {color: #555;} 16 | .cm-s-eclipse span.cm-builtin {color: #30a;} 17 | .cm-s-eclipse span.cm-bracket {color: #cc7;} 18 | .cm-s-eclipse span.cm-tag {color: #170;} 19 | .cm-s-eclipse span.cm-attribute {color: #00c;} 20 | .cm-s-eclipse span.cm-link {color: #219;} 21 | 22 | .cm-s-eclipse .CodeMirror-matchingbracket { 23 | border:1px solid grey; 24 | color:black !important;; 25 | } 26 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/python/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010 Timothy Farrell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/basic_app.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from flask import Flask 4 | from flask import render_template 5 | from flask_sqlalchemy import SQLAlchemy 6 | 7 | from flask_debugtoolbar import DebugToolbarExtension 8 | 9 | app = Flask("basic_app") 10 | app.config["DEBUG"] = True 11 | app.config["SECRET_KEY"] = "abc123" 12 | app.config["SQLALCHEMY_RECORD_QUERIES"] = True 13 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" 14 | # This is no longer needed for Flask-SQLAlchemy 3.0+, 15 | # if you're using 2.X you'll want to define this: 16 | # app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 17 | 18 | # make sure these are printable in the config panel 19 | app.config["BYTES_VALUE"] = b"\x00" 20 | app.config["UNICODE_VALUE"] = "\uffff" 21 | 22 | toolbar = DebugToolbarExtension(app) 23 | db = SQLAlchemy(app) 24 | 25 | 26 | class Foo(db.Model): # type: ignore[name-defined, misc] 27 | __tablename__ = "foo" 28 | id = db.Column(db.Integer, primary_key=True) 29 | 30 | 31 | @app.route("/") 32 | def index() -> str: 33 | Foo.query.filter_by(id=1).all() 34 | return render_template("basic_app.html") 35 | 36 | 37 | with app.app_context(): 38 | db.create_all() 39 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/perl/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Sabaca under the MIT license. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by MarkLogic Corporation 2 | Author: Mike Brevoort 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/night.css: -------------------------------------------------------------------------------- 1 | /* Loosely based on the Midnight Textmate theme */ 2 | 3 | .cm-s-night { background: #0a001f; color: #f8f8f8; } 4 | .cm-s-night div.CodeMirror-selected { background: #a8f !important; } 5 | .cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } 6 | .cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; } 7 | .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; } 8 | 9 | .cm-s-night span.cm-comment { color: #6900a1; } 10 | .cm-s-night span.cm-atom { color: #845dc4; } 11 | .cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; } 12 | .cm-s-night span.cm-keyword { color: #599eff; } 13 | .cm-s-night span.cm-string { color: #37f14a; } 14 | .cm-s-night span.cm-meta { color: #7678e2; } 15 | .cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; } 16 | .cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; } 17 | .cm-s-night span.cm-error { color: #9d1e15; } 18 | .cm-s-night span.cm-bracket { color: #8da6ce; } 19 | .cm-s-night span.cm-comment { color: #6900a1; } 20 | .cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; } 21 | .cm-s-night span.cm-link { color: #845dc4; } 22 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/sqlalchemy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% for query in queries %} 12 | 13 | 14 | 20 | 23 | 28 | 29 | {% endfor %} 30 | 31 |
 (ms)ActionContextQuery
{{ '%.4f'|format(query.duration * 1000) }} 15 | {% if query.signed_query %} 16 | SELECT
17 | EXPLAIN
18 | {% endif %} 19 |
21 | {{ query.location }} 22 | 24 |
25 |
{{ query.sql }}
26 |
27 |
32 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/jinja2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Jinja2 mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Jinja2 mode

13 |
31 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/route_list.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from flask import current_app 4 | from werkzeug import Request 5 | from werkzeug.routing import Rule 6 | 7 | from . import DebugPanel 8 | 9 | 10 | class RouteListDebugPanel(DebugPanel): 11 | """Panel that displays the URL routing rules.""" 12 | 13 | name = "RouteList" 14 | has_content = True 15 | routes: list[Rule] = [] 16 | 17 | def nav_title(self) -> str: 18 | return "Route List" 19 | 20 | def title(self) -> str: 21 | return "Route List" 22 | 23 | def url(self) -> str: 24 | return "" 25 | 26 | def nav_subtitle(self) -> str: 27 | count = len(self.routes) 28 | plural = "route" if count == 1 else "routes" 29 | return f"{count} {plural}" 30 | 31 | def process_request(self, request: Request) -> None: 32 | self.routes = [ 33 | rule 34 | for rule in current_app.url_map.iter_rules() 35 | if not rule.rule.startswith("/_debug_toolbar") 36 | ] 37 | 38 | def content(self) -> str: 39 | return self.render( 40 | "panels/route_list.html", 41 | { 42 | "routes": self.routes, 43 | }, 44 | ) 45 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/coffeescript/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Jeff Pickhardt 4 | Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/monokai.css: -------------------------------------------------------------------------------- 1 | /* Based on Sublime Text's Monokai theme */ 2 | 3 | .cm-s-monokai {background: #272822; color: #f8f8f2;} 4 | .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} 5 | .cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;} 6 | .cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;} 7 | .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;} 8 | 9 | .cm-s-monokai span.cm-comment {color: #75715e;} 10 | .cm-s-monokai span.cm-atom {color: #ae81ff;} 11 | .cm-s-monokai span.cm-number {color: #ae81ff;} 12 | 13 | .cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {color: #a6e22e;} 14 | .cm-s-monokai span.cm-keyword {color: #f92672;} 15 | .cm-s-monokai span.cm-string {color: #e6db74;} 16 | 17 | .cm-s-monokai span.cm-variable {color: #a6e22e;} 18 | .cm-s-monokai span.cm-variable-2 {color: #9effff;} 19 | .cm-s-monokai span.cm-def {color: #fd971f;} 20 | .cm-s-monokai span.cm-error {background: #f92672; color: #f8f8f0;} 21 | .cm-s-monokai span.cm-bracket {color: #f8f8f2;} 22 | .cm-s-monokai span.cm-tag {color: #f92672;} 23 | .cm-s-monokai span.cm-link {color: #ae81ff;} 24 | 25 | .cm-s-monokai .CodeMirror-matchingbracket { 26 | text-decoration: underline; 27 | color: white !important; 28 | } 29 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | 3 | # Project -------------------------------------------------------------- 4 | 5 | project = "Flask-DebugToolbar" 6 | version = release = importlib.metadata.version("flask-debugtoolbar").partition(".dev")[ 7 | 0 8 | ] 9 | 10 | # General -------------------------------------------------------------- 11 | 12 | default_role = "code" 13 | extensions = [ 14 | "sphinx.ext.autodoc", 15 | "sphinx.ext.extlinks", 16 | "sphinx.ext.intersphinx", 17 | "sphinxcontrib.log_cabinet", 18 | "pallets_sphinx_themes", 19 | ] 20 | autodoc_member_order = "bysource" 21 | autodoc_typehints = "description" 22 | autodoc_preserve_defaults = True 23 | extlinks = { 24 | "issue": ("https://github.com/pallets-eco/flask-debugtoolbar/issues/%s", "#%s"), 25 | "pr": ("https://github.com/pallets-eco/flask-debugtoolbar/pull/%s", "#%s"), 26 | } 27 | intersphinx_mapping = { 28 | "python": ("https://docs.python.org/3/", None), 29 | "flasksqlalchemy": ("https://flask-sqlalchemy.palletsprojects.com", None), 30 | } 31 | 32 | # HTML ----------------------------------------------------------------- 33 | 34 | html_theme = "flask" 35 | html_static_path = ["_static"] 36 | html_copy_source = False 37 | html_show_copyright = False 38 | html_use_index = False 39 | html_domain_indices = False 40 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rust/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Rust mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Rust mode

13 | 14 |
37 | 38 | 45 | 46 |

MIME types defined: text/x-rustsrc.

47 | 48 | 49 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xquery/test/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |

XQuery CodeMirror Mode

21 |

22 |

23 |
    24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/css/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: CSS mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: CSS mode

13 |
48 | 51 | 52 |

MIME types defined: text/css.

53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/mysql/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: MySQL mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: MySQL mode

13 |
30 | 37 | 38 |

MIME types defined: text/x-mysql.

39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/pascal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Pascal mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Pascal mode

13 | 14 |
37 | 38 | 45 | 46 |

MIME types defined: text/x-pascal.

47 | 48 | 49 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/ecl/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: ECL mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: ECL mode

13 |
31 | 37 | 38 |

Based on CodeMirror's clike mode. For more information see HPCC Systems web site.

39 |

MIME types defined: text/x-ecl.

40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/vbscript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: VBScript mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: VBScript mode

13 | 14 |
31 | 32 | 38 | 39 |

MIME types defined: text/vbscript.

40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask-DebugToolbar 2 | 3 | A [Flask][] extension that injects debugging information into rendered HTML 4 | pages. Presented as a sidebar with configurable panels of information. 5 | 6 | This is a port of the excellent [django-debug-toolbar][ddt]. 7 | 8 | [Flask]: https://flask.palletsprojects.com 9 | [ddt]: https://github.com/jazzband/django-debug-toolbar/ 10 | 11 | 12 | ## Pallets Community Ecosystem 13 | 14 | > [!IMPORTANT]\ 15 | > This project is part of the Pallets Community Ecosystem. Pallets is the open 16 | > source organization that maintains Flask; Pallets-Eco enables community 17 | > maintenance of related projects. If you are interested in helping maintain 18 | > this project, please reach out on [the Pallets Discord server][discord]. 19 | 20 | [discord]: https://discord.gg/pallets 21 | 22 | 23 | ## Example 24 | 25 | Setting up the debug toolbar is simple: 26 | 27 | ```python 28 | from flask import Flask 29 | from flask_debugtoolbar import DebugToolbarExtension 30 | 31 | app = Flask(__name__) 32 | app.config["SECRET_KEY"] = "" 33 | 34 | toolbar = DebugToolbarExtension(app) 35 | ``` 36 | 37 | The toolbar will automatically be injected into Jinja templates when debug 38 | mode is enabled. 39 | 40 | ``` 41 | $ flask -A my_app run --debug 42 | ``` 43 | 44 | ![](https://raw.githubusercontent.com/pallets-eco/flask-debugtoolbar/main/docs/_static/example.gif) 45 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/properties/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Properties files mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Properties files mode

13 |
32 | 35 | 36 |

MIME types defined: text/x-properties, 37 | text/x-ini.

38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/sparql/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: SPARQL mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: SPARQL mode

13 |
29 | 36 | 37 |

MIME types defined: application/x-sparql-query.

38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/rubyblue.css: -------------------------------------------------------------------------------- 1 | .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */ 2 | 3 | .cm-s-rubyblue { background: #112435; color: white; } 4 | .cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } 5 | .cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; } 6 | .cm-s-rubyblue .CodeMirror-gutter-text { color: white; } 7 | .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; } 8 | 9 | .cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; } 10 | .cm-s-rubyblue span.cm-atom { color: #F4C20B; } 11 | .cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; } 12 | .cm-s-rubyblue span.cm-keyword { color: #F0F; } 13 | .cm-s-rubyblue span.cm-string { color: #F08047; } 14 | .cm-s-rubyblue span.cm-meta { color: #F0F; } 15 | .cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; } 16 | .cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def { color: white; } 17 | .cm-s-rubyblue span.cm-error { color: #AF2018; } 18 | .cm-s-rubyblue span.cm-bracket { color: #F0F; } 19 | .cm-s-rubyblue span.cm-link { color: #F4C20B; } 20 | .cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; } 21 | .cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; } 22 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py3{12,11,10,9,8} 4 | minimal 5 | style 6 | typing 7 | docs 8 | skip_missing_interpreters = true 9 | 10 | [testenv] 11 | package = wheel 12 | wheel_build_env = .pkg 13 | constrain_package_deps = true 14 | use_frozen_constraints = true 15 | deps = -r requirements/tests.txt 16 | commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs} 17 | 18 | [testenv:minimal] 19 | deps = 20 | commands = python -c "from flask_debugtoolbar import DebugToolbarExtension" 21 | 22 | [testenv:style] 23 | deps = pre-commit 24 | skip_install = true 25 | commands = pre-commit run --all-files 26 | 27 | [testenv:typing] 28 | deps = -r requirements/typing.txt 29 | commands = 30 | mypy 31 | pyright 32 | pyright --verifytypes flask_debugtoolbar --ignoreexternal 33 | 34 | [testenv:docs] 35 | deps = -r requirements/docs.txt 36 | commands = sphinx-build -E -W -b dirhtml docs docs/_build/dirhtml 37 | 38 | [testenv:update-pre_commit] 39 | labels = update 40 | deps = pre-commit 41 | skip_install = true 42 | commands = pre-commit autoupdate -j4 43 | 44 | [testenv:update-requirements] 45 | base_python = 3.8 46 | labels = update 47 | deps = pip-tools 48 | skip_install = true 49 | change_dir = requirements 50 | commands = 51 | pip-compile build.in -q {posargs:-U} 52 | pip-compile docs.in -q {posargs:-U} 53 | pip-compile tests.in -q {posargs:-U} 54 | pip-compile typing.in -q {posargs:-U} 55 | pip-compile dev.in -q {posargs:-U} 56 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/perl/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Perl mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Perl mode

13 | 14 |
52 | 53 | 59 | 60 |

MIME types defined: text/x-perl.

61 | 62 | 63 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.8 3 | # by the following command: 4 | # 5 | # pip-compile docs.in 6 | # 7 | alabaster==0.7.13 8 | # via sphinx 9 | babel==2.14.0 10 | # via sphinx 11 | certifi==2024.7.4 12 | # via requests 13 | charset-normalizer==3.3.2 14 | # via requests 15 | docutils==0.20.1 16 | # via sphinx 17 | idna==3.7 18 | # via requests 19 | imagesize==1.4.1 20 | # via sphinx 21 | importlib-metadata==7.1.0 22 | # via sphinx 23 | jinja2==3.1.4 24 | # via sphinx 25 | markupsafe==2.1.5 26 | # via jinja2 27 | packaging==24.0 28 | # via 29 | # pallets-sphinx-themes 30 | # sphinx 31 | pallets-sphinx-themes==2.1.3 32 | # via -r docs.in 33 | pygments==2.18.0 34 | # via sphinx 35 | pytz==2024.1 36 | # via babel 37 | requests==2.32.0 38 | # via sphinx 39 | snowballstemmer==2.2.0 40 | # via sphinx 41 | sphinx==7.1.2 42 | # via 43 | # -r docs.in 44 | # pallets-sphinx-themes 45 | # sphinxcontrib-log-cabinet 46 | sphinxcontrib-applehelp==1.0.4 47 | # via sphinx 48 | sphinxcontrib-devhelp==1.0.2 49 | # via sphinx 50 | sphinxcontrib-htmlhelp==2.0.1 51 | # via sphinx 52 | sphinxcontrib-jsmath==1.0.1 53 | # via sphinx 54 | sphinxcontrib-log-cabinet==1.0.1 55 | # via -r docs.in 56 | sphinxcontrib-qthelp==1.0.3 57 | # via sphinx 58 | sphinxcontrib-serializinghtml==1.1.5 59 | # via sphinx 60 | urllib3==2.2.2 61 | # via requests 62 | zipp==3.19.1 63 | # via importlib-metadata 64 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/gfm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: GFM mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

CodeMirror: GFM mode

17 | 18 | 19 |
36 | 37 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/sqlalchemy_error.html: -------------------------------------------------------------------------------- 1 |

Queries Unavailable

2 | 3 |

4 | The toolbar was unable to fetch the SQLAlchemy queries for this request. 5 | To enable the SQLAlchemy query display, please: 6 |

7 | 8 |
    9 | {% if not sqlalchemy_available %} 10 |
  1. 11 |
    Install required libraries:
    12 |
      13 |
    • Flask-SQLAlchemy
    • 14 |
    15 |
  2. 16 | {% endif %} 17 | 18 | {% if not extension_used %} 19 |
  3. 20 |
    Configure Flask-SQLAlchemy:
    21 |

    22 | The Flask-SQLAlchemy extension needs to be configured for this application. 23 | Please see the 24 | Flask-SQLAlchemy documentation for details. 25 |

    26 |
  4. 27 | {% endif %} 28 | 29 | {% if not recording_enabled %} 30 |
  5. 31 |
    Enable query recording:
    32 |

    33 | Since this app is not currently running in DEBUG mode, Flask-SQLAlchemy will not record queries by default. To enable query recording in non-debug mode, set the following configuration value: 34 |

    35 |
    app.config['SQLALCHEMY_RECORD_QUERIES'] = True
    36 |

    37 | See the 38 | 39 | documention of Flask-SQLAlchemy's get_debug_queries() 40 | for additional details. 41 |

    42 |
  6. 43 | {% endif %} 44 |
45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2011 Pallets Community Ecosystem 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 3. Neither the name of the copyright holder nor the names of its contributors 12 | may be used to endorse or promote products derived from this software 13 | without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/r/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Ubalo, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Ubalo, Inc nor the names of its 12 | contributors may be used to endorse or promote products derived 13 | from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL UBALO, INC BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/ruby/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Ubalo, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Ubalo, Inc. nor the names of its 12 | contributors may be used to endorse or promote products derived 13 | from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL UBALO, INC BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /example/app.py: -------------------------------------------------------------------------------- 1 | # Run using: `FLASK_DEBUG=True flask run` 2 | 3 | from flask import Flask 4 | from flask import redirect 5 | from flask import render_template 6 | from flask import url_for 7 | from flask_sqlalchemy import SQLAlchemy 8 | 9 | from flask_debugtoolbar import DebugToolbarExtension 10 | 11 | app = Flask(__name__) 12 | app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = True 13 | # app.config['DEBUG_TB_PANELS'] = ( 14 | # 'flask_debugtoolbar.panels.headers.HeaderDebugPanel', 15 | # 'flask_debugtoolbar.panels.logger.LoggingPanel', 16 | # 'flask_debugtoolbar.panels.timer.TimerDebugPanel', 17 | # ) 18 | # app.config['DEBUG_TB_HOSTS'] = ('127.0.0.1', '::1' ) 19 | app.config["SECRET_KEY"] = "asd" 20 | app.config["SQLALCHEMY_RECORD_QUERIES"] = True 21 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/test.db" 22 | # This is no longer needed for Flask-SQLAlchemy 3.0+, if you're using 2.X you'll 23 | # want to define this: 24 | # app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 25 | 26 | db = SQLAlchemy(app) 27 | toolbar = DebugToolbarExtension(app) 28 | 29 | 30 | class ExampleModel(db.Model): 31 | __tablename__ = "examples" 32 | value = db.Column(db.String(100), primary_key=True) 33 | 34 | 35 | @app.route("/") 36 | def index(): 37 | app.logger.info("Hello there") 38 | ExampleModel.query.get(1) 39 | return render_template("index.html") 40 | 41 | 42 | @app.route("/redirect") 43 | def redirect_example(): 44 | response = redirect(url_for("index")) 45 | response.set_cookie("test_cookie", "1") 46 | return response 47 | 48 | 49 | with app.app_context(): 50 | db.create_all() 51 | -------------------------------------------------------------------------------- /requirements/typing.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.8 3 | # by the following command: 4 | # 5 | # pip-compile typing.in 6 | # 7 | blinker==1.8.1 8 | # via flask 9 | click==8.1.7 10 | # via flask 11 | exceptiongroup==1.2.1 12 | # via pytest 13 | flask==3.0.3 14 | # via flask-sqlalchemy 15 | flask-sqlalchemy==3.1.1 16 | # via -r typing.in 17 | greenlet==3.0.3 18 | # via sqlalchemy 19 | importlib-metadata==7.1.0 20 | # via flask 21 | iniconfig==2.0.0 22 | # via pytest 23 | itsdangerous==2.2.0 24 | # via flask 25 | jinja2==3.1.4 26 | # via flask 27 | markupsafe==2.1.5 28 | # via 29 | # jinja2 30 | # werkzeug 31 | mypy==1.11.2 32 | # via -r typing.in 33 | mypy-extensions==1.0.0 34 | # via mypy 35 | nodeenv==1.8.0 36 | # via pyright 37 | packaging==24.0 38 | # via pytest 39 | pluggy==1.5.0 40 | # via pytest 41 | pyright==1.1.382.post1 42 | # via -r typing.in 43 | pytest==8.3.3 44 | # via -r typing.in 45 | sqlalchemy==2.0.29 46 | # via flask-sqlalchemy 47 | tomli==2.0.1 48 | # via 49 | # mypy 50 | # pytest 51 | types-docutils==0.21.0.20240423 52 | # via types-pygments 53 | types-pygments==2.18.0.20240506 54 | # via -r typing.in 55 | types-setuptools==69.5.0.20240423 56 | # via types-pygments 57 | typing-extensions==4.11.0 58 | # via 59 | # mypy 60 | # pyright 61 | # sqlalchemy 62 | werkzeug==3.0.3 63 | # via flask 64 | zipp==3.19.1 65 | # via importlib-metadata 66 | 67 | # The following packages are considered to be unsafe in a requirements file: 68 | # setuptools 69 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/headers.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing as t 4 | 5 | from werkzeug import Request 6 | 7 | from . import DebugPanel 8 | 9 | 10 | class HeaderDebugPanel(DebugPanel): 11 | """A panel to display HTTP headers.""" 12 | 13 | name = "Header" 14 | has_content = True 15 | # List of headers we want to display 16 | header_filter: tuple[str, ...] = ( 17 | "CONTENT_TYPE", 18 | "HTTP_ACCEPT", 19 | "HTTP_ACCEPT_CHARSET", 20 | "HTTP_ACCEPT_ENCODING", 21 | "HTTP_ACCEPT_LANGUAGE", 22 | "HTTP_CACHE_CONTROL", 23 | "HTTP_CONNECTION", 24 | "HTTP_HOST", 25 | "HTTP_KEEP_ALIVE", 26 | "HTTP_REFERER", 27 | "HTTP_USER_AGENT", 28 | "QUERY_STRING", 29 | "REMOTE_ADDR", 30 | "REMOTE_HOST", 31 | "REQUEST_METHOD", 32 | "SCRIPT_NAME", 33 | "SERVER_NAME", 34 | "SERVER_PORT", 35 | "SERVER_PROTOCOL", 36 | "SERVER_SOFTWARE", 37 | ) 38 | 39 | def nav_title(self) -> str: 40 | return "HTTP Headers" 41 | 42 | def title(self) -> str: 43 | return "HTTP Headers" 44 | 45 | def url(self) -> str: 46 | return "" 47 | 48 | def process_request(self, request: Request) -> None: 49 | self.headers: dict[str, t.Any] = { 50 | k: request.environ[k] for k in self.header_filter if k in request.environ 51 | } 52 | 53 | def content(self) -> str: 54 | context = self.context.copy() 55 | context.update({"headers": self.headers}) 56 | return self.render("panels/headers.html", context) 57 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/php/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: PHP mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

CodeMirror: PHP mode

17 | 18 |
29 | 30 | 41 | 42 |

Simple HTML/PHP mode based on 43 | the C-like mode. Depends on XML, 44 | JavaScript, CSS, and C-like modes.

45 | 46 |

MIME types defined: application/x-httpd-php (HTML with PHP code), text/x-php (plain, non-wrapped PHP code).

47 | 48 | 49 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/smalltalk/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Smalltalk mode 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |

CodeMirror: Smalltalk mode

17 | 18 |
41 | 42 | 50 | 51 |

Simple Smalltalk mode.

52 | 53 |

MIME types defined: text/x-stsrc.

54 | 55 | 56 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/htmlmixed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: HTML mixed mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

CodeMirror: HTML mixed mode

16 |
40 | 43 | 44 |

The HTML mixed mode depends on the XML, JavaScript, and CSS modes.

45 | 46 |

MIME types defined: text/html 47 | (redefined, only takes effect if you load this parser after the 48 | XML parser).

49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xml/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: XML mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: XML mode

13 |
25 | 31 |

The XML mode supports two configuration parameters:

32 |
33 |
htmlMode (boolean)
34 |
This switches the mode to parse HTML instead of XML. This 35 | means attributes do not have to be quoted, and some elements 36 | (such as br) do not require a closing tag.
37 |
alignCDATA (boolean)
38 |
Setting this to true will force the opening tag of CDATA 39 | blocks to not be indented.
40 |
41 | 42 |

MIME types defined: application/xml, text/html.

43 | 44 | 45 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/runmode.js: -------------------------------------------------------------------------------- 1 | CodeMirror.runMode = function(string, modespec, callback, options) { 2 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 3 | var isNode = callback.nodeType == 1; 4 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 5 | if (isNode) { 6 | var node = callback, accum = [], col = 0; 7 | callback = function(text, style) { 8 | if (text == "\n") { 9 | accum.push("
"); 10 | col = 0; 11 | return; 12 | } 13 | var escaped = ""; 14 | // HTML-escape and replace tabs 15 | for (var pos = 0;;) { 16 | var idx = text.indexOf("\t", pos); 17 | if (idx == -1) { 18 | escaped += CodeMirror.htmlEscape(text.slice(pos)); 19 | col += text.length - pos; 20 | break; 21 | } else { 22 | col += idx - pos; 23 | escaped += CodeMirror.htmlEscape(text.slice(pos, idx)); 24 | var size = tabSize - col % tabSize; 25 | col += size; 26 | for (var i = 0; i < size; ++i) escaped += " "; 27 | pos = idx + 1; 28 | } 29 | } 30 | 31 | if (style) 32 | accum.push("" + escaped + ""); 33 | else 34 | accum.push(escaped); 35 | } 36 | } 37 | var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); 38 | for (var i = 0, e = lines.length; i < e; ++i) { 39 | if (i) callback("\n"); 40 | var stream = new CodeMirror.StringStream(lines[i]); 41 | while (!stream.eol()) { 42 | var style = mode.token(stream, state); 43 | callback(stream.current(), style, i, stream.start); 44 | stream.start = stream.pos; 45 | } 46 | } 47 | if (isNode) 48 | node.innerHTML = accum.join(""); 49 | }; 50 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/plsql/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Oracle PL/SQL mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Oracle PL/SQL mode

13 | 14 |
46 | 47 | 55 | 56 |

57 | Simple mode that handles Oracle PL/SQL language (and Oracle SQL, of course). 58 |

59 | 60 |

MIME type defined: text/x-plsql 61 | (PLSQL code) 62 | 63 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/match-highlighter.js: -------------------------------------------------------------------------------- 1 | // Define match-highlighter commands. Depends on searchcursor.js 2 | // Use by attaching the following function call to the onCursorActivity event: 3 | //myCodeMirror.matchHighlight(minChars); 4 | // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html) 5 | 6 | (function() { 7 | var DEFAULT_MIN_CHARS = 2; 8 | 9 | function MatchHighlightState() { 10 | this.marked = []; 11 | } 12 | function getMatchHighlightState(cm) { 13 | return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState()); 14 | } 15 | 16 | function clearMarks(cm) { 17 | var state = getMatchHighlightState(cm); 18 | for (var i = 0; i < state.marked.length; ++i) 19 | state.marked[i].clear(); 20 | state.marked = []; 21 | } 22 | 23 | function markDocument(cm, className, minChars) { 24 | clearMarks(cm); 25 | minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS); 26 | if (cm.somethingSelected() && cm.getSelection().length >= minChars) { 27 | var state = getMatchHighlightState(cm); 28 | var query = cm.getSelection(); 29 | cm.operation(function() { 30 | if (cm.lineCount() < 2000) { // This is too expensive on big documents. 31 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { 32 | //Only apply matchhighlight to the matches other than the one actually selected 33 | if (!(cursor.from().line === cm.getCursor(true).line && cursor.from().ch === cm.getCursor(true).ch)) 34 | state.marked.push(cm.markText(cursor.from(), cursor.to(), className)); 35 | } 36 | } 37 | }); 38 | } 39 | } 40 | 41 | CodeMirror.defineExtension("matchHighlight", function(className, minChars) { 42 | markDocument(this, className, minChars); 43 | }); 44 | })(); 45 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - '*.x' 7 | paths-ignore: 8 | - 'docs/**' 9 | - '*.md' 10 | - '*.rst' 11 | pull_request: 12 | paths-ignore: 13 | - 'docs/**' 14 | - '*.md' 15 | - '*.rst' 16 | jobs: 17 | tests: 18 | name: ${{ matrix.name || matrix.python }} 19 | runs-on: ${{ matrix.os || 'ubuntu-latest' }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | include: 24 | - {python: '3.12'} 25 | - {python: '3.11'} 26 | - {python: '3.10'} 27 | - {python: '3.9'} 28 | - {python: '3.8'} 29 | - {name: Minimal, python: '3.12', tox: minimal} 30 | steps: 31 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 32 | - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 33 | with: 34 | python-version: ${{ matrix.python }} 35 | allow-prereleases: true 36 | cache: pip 37 | cache-dependency-path: requirements*/*.txt 38 | - run: pip install tox 39 | - run: tox run -e ${{ matrix.tox || format('py{0}', matrix.python) }} 40 | typing: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 44 | - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 45 | with: 46 | python-version: '3.x' 47 | cache: pip 48 | cache-dependency-path: requirements*/*.txt 49 | - name: cache mypy 50 | uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 51 | with: 52 | path: ./.mypy_cache 53 | key: mypy|${{ hashFiles('pyproject.toml') }} 54 | - run: pip install tox 55 | - run: tox run -e typing 56 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/request_vars.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import collections.abc as c 4 | import typing as t 5 | 6 | from flask import session 7 | from werkzeug import Request 8 | 9 | from . import DebugPanel 10 | 11 | 12 | class RequestVarsDebugPanel(DebugPanel): 13 | """A panel to display request variables (POST/GET, session, cookies).""" 14 | 15 | name = "RequestVars" 16 | has_content = True 17 | 18 | def nav_title(self) -> str: 19 | return "Request Vars" 20 | 21 | def title(self) -> str: 22 | return "Request Vars" 23 | 24 | def url(self) -> str: 25 | return "" 26 | 27 | def process_request(self, request: Request) -> None: 28 | self.request = request 29 | self.session = session 30 | self.view_func: c.Callable[..., t.Any] | None = None 31 | self.view_kwargs: dict[str, t.Any] = {} 32 | 33 | def process_view( 34 | self, 35 | request: Request, 36 | view_func: c.Callable[..., t.Any], 37 | view_kwargs: dict[str, t.Any], 38 | ) -> None: 39 | self.view_func = view_func 40 | self.view_kwargs = view_kwargs 41 | 42 | def content(self) -> str: 43 | context = self.context.copy() 44 | context.update( 45 | { 46 | "get": self.request.args.lists(), 47 | "post": self.request.form.lists(), 48 | "cookies": self.request.cookies.items(), 49 | "view_func": ( 50 | f"{self.view_func.__module__}.{self.view_func.__name__}" 51 | if self.view_func 52 | else "[unknown]" 53 | ), 54 | "view_kwargs": self.view_kwargs or {}, 55 | "session": self.session.items(), 56 | } 57 | ) 58 | 59 | return self.render("panels/request_vars.html", context) 60 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/htmlembedded/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Html Embedded Scripts mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

CodeMirror: Html Embedded Scripts mode

17 | 18 |
30 | 31 | 42 | 43 |

Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on 44 | JavaScript, CSS and XML.
Other dependancies include those of the scriping language chosen.

45 | 46 |

MIME types defined: application/x-aspx (ASP.NET), 47 | application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)

48 | 49 | 50 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/lua/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Lua mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

CodeMirror: Lua mode

14 |
55 | 62 | 63 |

Loosely based on Franciszek 64 | Wawrzak's CodeMirror 65 | 1 mode. One configuration parameter is 66 | supported, specials, to which you can provide an 67 | array of strings to have those identifiers highlighted with 68 | the lua-special style.

69 |

MIME types defined: text/x-lua.

70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/properties/properties.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("properties", function() { 2 | return { 3 | token: function(stream, state) { 4 | var sol = stream.sol() || state.afterSection; 5 | var eol = stream.eol(); 6 | 7 | state.afterSection = false; 8 | 9 | if (sol) { 10 | if (state.nextMultiline) { 11 | state.inMultiline = true; 12 | state.nextMultiline = false; 13 | } else { 14 | state.position = "def"; 15 | } 16 | } 17 | 18 | if (eol && ! state.nextMultiline) { 19 | state.inMultiline = false; 20 | state.position = "def"; 21 | } 22 | 23 | if (sol) { 24 | while(stream.eatSpace()); 25 | } 26 | 27 | var ch = stream.next(); 28 | 29 | if (sol && (ch === "#" || ch === "!" || ch === ";")) { 30 | state.position = "comment"; 31 | stream.skipToEnd(); 32 | return "comment"; 33 | } else if (sol && ch === "[") { 34 | state.afterSection = true; 35 | stream.skipTo("]"); stream.eat("]"); 36 | return "header"; 37 | } else if (ch === "=" || ch === ":") { 38 | state.position = "quote"; 39 | return null; 40 | } else if (ch === "\\" && state.position === "quote") { 41 | if (stream.next() !== "u") { // u = Unicode sequence \u1234 42 | // Multiline value 43 | state.nextMultiline = true; 44 | } 45 | } 46 | 47 | return state.position; 48 | }, 49 | 50 | startState: function() { 51 | return { 52 | position : "def", // Current position, "def", "quote" or "comment" 53 | nextMultiline : false, // Is the next line multiline value 54 | inMultiline : false, // Is the current line a multiline value 55 | afterSection : false // Did we just open a section 56 | }; 57 | } 58 | 59 | }; 60 | }); 61 | 62 | CodeMirror.defineMIME("text/x-properties", "properties"); 63 | CodeMirror.defineMIME("text/x-ini", "properties"); 64 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rpm/changes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: RPM changes mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: RPM changes mode

13 | 14 |
41 | 50 | 51 |

MIME types defined: text/x-rpm-changes.

52 | 53 | 54 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/yaml/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: YAML mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: YAML mode

13 |
60 | 63 | 64 |

MIME types defined: text/x-yaml.

65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/haskell/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Haskell mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

CodeMirror: Haskell mode

14 | 15 |
49 | 50 | 57 | 58 |

MIME types defined: text/x-haskell.

59 | 60 | 61 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/overlay.js: -------------------------------------------------------------------------------- 1 | // Utility function that allows modes to be combined. The mode given 2 | // as the base argument takes care of most of the normal mode 3 | // functionality, but a second (typically simple) mode is used, which 4 | // can override the style of text. Both modes get to parse all of the 5 | // text, but when both assign a non-null style to a piece of code, the 6 | // overlay wins, unless the combine argument was true, in which case 7 | // the styles are combined. 8 | 9 | CodeMirror.overlayParser = function(base, overlay, combine) { 10 | return { 11 | startState: function() { 12 | return { 13 | base: CodeMirror.startState(base), 14 | overlay: CodeMirror.startState(overlay), 15 | basePos: 0, baseCur: null, 16 | overlayPos: 0, overlayCur: null 17 | }; 18 | }, 19 | copyState: function(state) { 20 | return { 21 | base: CodeMirror.copyState(base, state.base), 22 | overlay: CodeMirror.copyState(overlay, state.overlay), 23 | basePos: state.basePos, baseCur: null, 24 | overlayPos: state.overlayPos, overlayCur: null 25 | }; 26 | }, 27 | 28 | token: function(stream, state) { 29 | if (stream.start == state.basePos) { 30 | state.baseCur = base.token(stream, state.base); 31 | state.basePos = stream.pos; 32 | } 33 | if (stream.start == state.overlayPos) { 34 | stream.pos = stream.start; 35 | state.overlayCur = overlay.token(stream, state.overlay); 36 | state.overlayPos = stream.pos; 37 | } 38 | stream.pos = Math.min(state.basePos, state.overlayPos); 39 | if (stream.eol()) state.basePos = state.overlayPos = 0; 40 | 41 | if (state.overlayCur == null) return state.baseCur; 42 | if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; 43 | else return state.overlayCur; 44 | }, 45 | 46 | indent: function(state, textAfter) { 47 | return base.indent(state.base, textAfter); 48 | }, 49 | electricChars: base.electricChars 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/groovy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Groovy mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Groovy mode

13 | 14 |
60 | 61 | 68 | 69 |

MIME types defined: text/x-groovy

70 | 71 | 72 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/go/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Go mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

CodeMirror: Go mode

14 | 15 |
58 | 59 | 69 | 70 |

MIME type: text/x-go

71 | 72 | 73 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/jinja2/jinja2.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("jinja2", function(config, parserConf) { 2 | var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", 3 | "loop", "none", "self", "super", "if", "as", "not", "and", 4 | "else", "import", "with", "without", "context", 'extends', 'macro', 'endmacro']; 5 | 6 | var blocks = ['block', 'for', 'if', 'import', 'with', 'macro', 'raw', 'call', 'filter']; 7 | // TODO raw should stop highlighting until the endraw 8 | var standalone = ['extends', 'import', 'else', 'elif', 'from', 'include', 'set']; 9 | var special = ['in', 'is', 'true', 'false', 'loop', 'none', 'self', 'super', 10 | 'as', 'not', 'and', 'if', 'else', 'without', 'with', 11 | 'context', 'ignore', 'missing', 'import']; 12 | // TODO list builtin filters and tests 13 | 14 | keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); 15 | 16 | function tokenBase (stream, state) { 17 | var ch = stream.next(); 18 | if (ch == "{") { 19 | if (ch = stream.eat(/\{|%|#/)) { 20 | stream.eat("-"); 21 | state.tokenize = inTag(ch); 22 | return "tag"; 23 | } 24 | } 25 | } 26 | function inTag (close) { 27 | if (close == "{") { 28 | close = "}"; 29 | } 30 | return function (stream, state) { 31 | var ch = stream.next(); 32 | if ((ch == close || (ch == "-" && stream.eat(close))) 33 | && stream.eat("}")) { 34 | state.tokenize = tokenBase; 35 | return "tag"; 36 | } 37 | if (stream.match(keywords)) { 38 | return "keyword"; 39 | } 40 | return close == "#" ? "comment" : "string"; 41 | }; 42 | } 43 | return { 44 | startState: function () { 45 | return {tokenize: tokenBase}; 46 | }, 47 | token: function (stream, state) { 48 | return state.tokenize(stream, state); 49 | } 50 | }; 51 | }); 52 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 11 | - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 12 | with: 13 | python-version: '3.x' 14 | cache: pip 15 | cache-dependency-path: requirements*/*.txt 16 | - run: pip install -r requirements/build.txt 17 | # Use the commit date instead of the current date during the build. 18 | - run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV 19 | - run: python -m build 20 | - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 21 | with: 22 | path: ./dist 23 | create-release: 24 | needs: [build] 25 | runs-on: ubuntu-latest 26 | permissions: 27 | contents: write 28 | steps: 29 | - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 30 | - name: create release 31 | run: > 32 | gh release create --draft --repo ${{ github.repository }} 33 | ${{ github.ref_name }} artifact/* 34 | env: 35 | GH_TOKEN: ${{ github.token }} 36 | publish-pypi: 37 | needs: [build] 38 | # Wait for approval before attempting to upload to PyPI. This allows reviewing the 39 | # files in the draft release. 40 | environment: 41 | name: publish 42 | url: https://pypi.org/project/Flask-DebugToolbar/${{ github.ref_name }} 43 | runs-on: ubuntu-latest 44 | permissions: 45 | id-token: write 46 | steps: 47 | - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 48 | - uses: pypa/gh-action-pypi-publish@8a08d616893759ef8e1aa1f2785787c0b97e20d6 # v1.10.0 49 | with: 50 | repository-url: https://test.pypi.org/legacy/ 51 | packages-dir: artifact/ 52 | - uses: pypa/gh-action-pypi-publish@8a08d616893759ef8e1aa1f2785787c0b97e20d6 # v1.10.0 53 | with: 54 | packages-dir: artifact/ 55 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "Flask-DebugToolbar" 3 | version = "0.16.0" 4 | description = "A toolbar overlay for debugging Flask applications." 5 | readme = "README.md" 6 | license = { file = "LICENSE.txt" } 7 | author = [{ name = "Michael van Tellingen" }] 8 | maintainers = [{ name = "Pallets Ecosystem", email = "contact@palletsprojects.com" }] 9 | classifiers = [ 10 | "Development Status :: 4 - Beta", 11 | "Framework :: Flask", 12 | "License :: OSI Approved :: BSD License", 13 | "Programming Language :: Python", 14 | "Typing :: Typed", 15 | ] 16 | requires-python = ">=3.8" 17 | dependencies = [ 18 | "flask>=2.3.0", 19 | ] 20 | 21 | [project.urls] 22 | Documentation = "https://flask-debugtoolbar.readthedocs.io" 23 | Changes = "https://github.com/pallets-eco/flask-debugtoolbar/releases/" 24 | Source = "https://github.com/pallets-eco/flask-debugtoolbar/" 25 | Chat = "https://discord.gg/pallets" 26 | 27 | [build-system] 28 | requires = ["flit_core<4"] 29 | build-backend = "flit_core.buildapi" 30 | 31 | [tool.flit.module] 32 | name = "flask_debugtoolbar" 33 | 34 | [tool.pytest.ini_options] 35 | testpaths = ["tests"] 36 | filterwarnings = [ 37 | "error", 38 | ] 39 | 40 | [tool.coverage.run] 41 | branch = true 42 | source = ["flask_debugtoolbar", "tests"] 43 | 44 | [tool.coverage.paths] 45 | source = ["src", "*/site-packages"] 46 | 47 | [tool.mypy] 48 | python_version = "3.8" 49 | files = ["src/flask_debugtoolbar", "tests"] 50 | show_error_codes = true 51 | pretty = true 52 | strict = true 53 | 54 | [[tool.mypy.overrides]] 55 | module = [ 56 | "sqlparse.*" 57 | ] 58 | ignore_missing_imports = true 59 | 60 | [tool.pyright] 61 | pythonVersion = "3.8" 62 | include = ["src/flask_debugtoolbar", "tests"] 63 | typeCheckingMode = "basic" 64 | 65 | [tool.ruff] 66 | src = ["src"] 67 | fix = true 68 | show-fixes = true 69 | output-format = "full" 70 | 71 | [tool.ruff.lint] 72 | select = [ 73 | "B", # flake8-bugbear 74 | "E", # pycodestyle error 75 | "F", # pyflakes 76 | "I", # isort 77 | "UP", # pyupgrade 78 | "W", # pycodestyle warning 79 | ] 80 | ignore-init-module-imports = true 81 | 82 | [tool.ruff.lint.isort] 83 | force-single-line = true 84 | order-by-type = false 85 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/dialog.js: -------------------------------------------------------------------------------- 1 | // Open simple dialogs on top of an editor. Relies on dialog.css. 2 | 3 | (function() { 4 | function dialogDiv(cm, template) { 5 | var wrap = cm.getWrapperElement(); 6 | var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); 7 | dialog.className = "CodeMirror-dialog"; 8 | dialog.innerHTML = '
' + template + '
'; 9 | return dialog; 10 | } 11 | 12 | CodeMirror.defineExtension("openDialog", function(template, callback) { 13 | var dialog = dialogDiv(this, template); 14 | var closed = false, me = this; 15 | function close() { 16 | if (closed) return; 17 | closed = true; 18 | dialog.parentNode.removeChild(dialog); 19 | } 20 | var inp = dialog.getElementsByTagName("input")[0]; 21 | if (inp) { 22 | CodeMirror.connect(inp, "keydown", function(e) { 23 | if (e.keyCode == 13 || e.keyCode == 27) { 24 | CodeMirror.e_stop(e); 25 | close(); 26 | me.focus(); 27 | if (e.keyCode == 13) callback(inp.value); 28 | } 29 | }); 30 | inp.focus(); 31 | CodeMirror.connect(inp, "blur", close); 32 | } 33 | return close; 34 | }); 35 | 36 | CodeMirror.defineExtension("openConfirm", function(template, callbacks) { 37 | var dialog = dialogDiv(this, template); 38 | var buttons = dialog.getElementsByTagName("button"); 39 | var closed = false, me = this, blurring = 1; 40 | function close() { 41 | if (closed) return; 42 | closed = true; 43 | dialog.parentNode.removeChild(dialog); 44 | me.focus(); 45 | } 46 | buttons[0].focus(); 47 | for (var i = 0; i < buttons.length; ++i) { 48 | var b = buttons[i]; 49 | (function(callback) { 50 | CodeMirror.connect(b, "click", function(e) { 51 | CodeMirror.e_preventDefault(e); 52 | close(); 53 | if (callback) callback(me); 54 | }); 55 | })(callbacks[i]); 56 | CodeMirror.connect(b, "blur", function() { 57 | --blurring; 58 | setTimeout(function() { if (blurring <= 0) close(); }, 200); 59 | }); 60 | CodeMirror.connect(b, "focus", function() { ++blurring; }); 61 | } 62 | }); 63 | })(); -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/lesser-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | http://lesscss.org/ dark theme 3 | Ported to CodeMirror by Peter Kroon 4 | */ 5 | .CodeMirror{ 6 | line-height: 15px; 7 | } 8 | .cm-s-lesser-dark { 9 | font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Monaco', Courier, monospace !important; 10 | font-size:12px; 11 | } 12 | 13 | .cm-s-lesser-dark { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } 14 | .cm-s-lesser-dark div.CodeMirror-selected {background: #45443B !important;} /* 33322B*/ 15 | .cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white !important; } 16 | .cm-s-lesser-dark .CodeMirror-lines { margin-left:3px; margin-right:3px; }/*editable code holder*/ 17 | 18 | div.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ 19 | 20 | .cm-s-lesser-dark .CodeMirror-gutter { background: #262626; border-right:1px solid #aaa; padding-right:3px; min-width:2.5em; } 21 | .cm-s-lesser-dark .CodeMirror-gutter-text { color: #777; } 22 | 23 | .cm-s-lesser-dark span.cm-keyword { color: #599eff; } 24 | .cm-s-lesser-dark span.cm-atom { color: #C2B470; } 25 | .cm-s-lesser-dark span.cm-number { color: #B35E4D; } 26 | .cm-s-lesser-dark span.cm-def {color: color: white;} 27 | .cm-s-lesser-dark span.cm-variable { color:#D9BF8C; } 28 | .cm-s-lesser-dark span.cm-variable-2 { color: #669199; } 29 | .cm-s-lesser-dark span.cm-variable-3 { color: white; } 30 | .cm-s-lesser-dark span.cm-property {color: #92A75C;} 31 | .cm-s-lesser-dark span.cm-operator {color: #92A75C;} 32 | .cm-s-lesser-dark span.cm-comment { color: #666; } 33 | .cm-s-lesser-dark span.cm-string { color: #BCD279; } 34 | .cm-s-lesser-dark span.cm-string-2 {color: #f50;} 35 | .cm-s-lesser-dark span.cm-meta { color: #738C73; } 36 | .cm-s-lesser-dark span.cm-error { color: #9d1e15; } 37 | .cm-s-lesser-dark span.cm-qualifier {color: #555;} 38 | .cm-s-lesser-dark span.cm-builtin { color: #ff9e59; } 39 | .cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; } 40 | .cm-s-lesser-dark span.cm-tag { color: #669199; } 41 | .cm-s-lesser-dark span.cm-attribute {color: #00c;} 42 | .cm-s-lesser-dark span.cm-header {color: #a0a;} 43 | .cm-s-lesser-dark span.cm-quote {color: #090;} 44 | .cm-s-lesser-dark span.cm-hr {color: #999;} 45 | .cm-s-lesser-dark span.cm-link {color: #00c;} 46 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/clojure/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Clojure mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Clojure mode

13 |
59 | 62 | 63 |

MIME types defined: text/x-clojure.

64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/panels/request_vars.html: -------------------------------------------------------------------------------- 1 | 2 |

View information

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 | 23 | 24 |
View Functionkwargs
{{ view_func }} 14 | {% if view_kwargs.items() %} 15 | {% for k, v in view_kwargs.items() %} 16 | {{ k }}={{ v }}{% if not loop.last %}, {% endif %} 17 | {% endfor %} 18 | {% else %} 19 | None 20 | {% endif %} 21 |
25 | 26 | {% macro show_map(map) %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {% for key, value in map %} 40 | 41 | 42 | 43 | 44 | {% endfor %} 45 | 46 |
VariableValue
{{ key|printable }}{{ value|printable }}
47 | {% endmacro %} 48 | 49 | 50 |

COOKIES Variables

51 | {% if cookies %} 52 | {{ show_map(cookies) }} 53 | {% else %} 54 |

No COOKIE data

55 | {% endif %} 56 | 57 |

SESSION Variables

58 | {% if session %} 59 | {{ show_map(session) }} 60 | {% else %} 61 |

No SESSION data

62 | {% endif %} 63 | 64 | 65 | {% macro show_multi_map(map) %} 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {% for key, value in map %} 75 | 76 | 77 | 83 | 84 | {% endfor %} 85 | 86 |
VariableValue
{{ key|printable }} 78 | {%- set sep = joiner() -%} 79 | {%- for v in value -%} 80 | {{ sep() }}{{ v|printable }} 81 | {%- endfor -%} 82 |
87 | {% endmacro %} 88 | 89 | 90 |

GET Variables

91 | {% if get %} 92 | {{ show_multi_map(get) }} 93 | {% else %} 94 |

No GET data

95 | {% endif %} 96 | 97 |

POST Variables

98 | {% if post %} 99 | {{ show_multi_map(post) }} 100 | {% else %} 101 |

No POST data

102 | {% endif %} 103 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/scheme/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Scheme mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Scheme mode

13 |
57 | 60 | 61 |

MIME types defined: text/x-scheme.

62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/theme/xq-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by MarkLogic Corporation 3 | Author: Mike Brevoort 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | .cm-s-xq-dark { background: #0a001f; color: #f8f8f8; } 24 | .cm-s-xq-dark span.CodeMirror-selected { background: #a8f !important; } 25 | .cm-s-xq-dark .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } 26 | .cm-s-xq-dark .CodeMirror-gutter-text { color: #f8f8f8; } 27 | .cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white !important; } 28 | 29 | .cm-s-xq-dark span.cm-keyword {color: #FFBD40;} 30 | .cm-s-xq-dark span.cm-atom {color: #6C8CD5;} 31 | .cm-s-xq-dark span.cm-number {color: #164;} 32 | .cm-s-xq-dark span.cm-def {color: #FFF; text-decoration:underline;} 33 | .cm-s-xq-dark span.cm-variable {color: #FFF;} 34 | .cm-s-xq-dark span.cm-variable-2 {color: #EEE;} 35 | .cm-s-xq-dark span.cm-variable-3 {color: #DDD;} 36 | .cm-s-xq-dark span.cm-property {} 37 | .cm-s-xq-dark span.cm-operator {} 38 | .cm-s-xq-dark span.cm-comment {color: gray;} 39 | .cm-s-xq-dark span.cm-string {color: #9FEE00;} 40 | .cm-s-xq-dark span.cm-meta {color: yellow;} 41 | .cm-s-xq-dark span.cm-error {color: #f00;} 42 | .cm-s-xq-dark span.cm-qualifier {color: #FFF700;} 43 | .cm-s-xq-dark span.cm-builtin {color: #30a;} 44 | .cm-s-xq-dark span.cm-bracket {color: #cc7;} 45 | .cm-s-xq-dark span.cm-tag {color: #FFBD40;} 46 | .cm-s-xq-dark span.cm-attribute {color: #FFF700;} 47 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/templates/base.html: -------------------------------------------------------------------------------- 1 | 60 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/r/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: R mode 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 |

CodeMirror: R mode

19 |
62 | 65 | 66 |

MIME types defined: text/x-rsrc.

67 | 68 |

Development of the CodeMirror R mode was kindly sponsored 69 | by Ubalo, who hold 70 | the license.

71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/xmlpure/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Pure XML mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: XML mode

13 |
30 | 33 | 34 |

This is my XML parser, based on the original:

35 |
    36 |
  • No html mode - this is pure xml
  • 37 |
  • Illegal attributes and element names are errors
  • 38 |
  • Attributes must have a value
  • 39 |
  • XML declaration supported (e.g.: <?xml version="1.0" encoding="utf-8" standalone="no" ?>)
  • 40 |
  • CDATA and comment blocks are not indented (except for their start-tag)
  • 41 |
  • Better handling of errors per line with the state object - provides good infrastructure for extending it
  • 42 |
43 | 44 |

What's missing:

45 |
    46 |
  • Make sure only a single root element exists at the document level
  • 47 |
  • Multi-line attributes should NOT indent
  • 48 |
  • Start tags are not painted red when they have no matching end tags (is this really wrong?)
  • 49 |
50 | 51 |

MIME types defined: application/xml, text/xml.

52 | 53 |

@author: Dror BG (deebug.dev[at]gmail.com)
54 |

@date: August, 2011
55 |

@github: https://github.com/deebugger/CodeMirror2

56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/htmlembedded/htmlembedded.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { 2 | 3 | //config settings 4 | var scriptStartRegex = parserConfig.scriptStartRegex || /^<%/i, 5 | scriptEndRegex = parserConfig.scriptEndRegex || /^%>/i; 6 | 7 | //inner modes 8 | var scriptingMode, htmlMixedMode; 9 | 10 | //tokenizer when in html mode 11 | function htmlDispatch(stream, state) { 12 | if (stream.match(scriptStartRegex, false)) { 13 | state.token=scriptingDispatch; 14 | return scriptingMode.token(stream, state.scriptState); 15 | } 16 | else 17 | return htmlMixedMode.token(stream, state.htmlState); 18 | } 19 | 20 | //tokenizer when in scripting mode 21 | function scriptingDispatch(stream, state) { 22 | if (stream.match(scriptEndRegex, false)) { 23 | state.token=htmlDispatch; 24 | return htmlMixedMode.token(stream, state.htmlState); 25 | } 26 | else 27 | return scriptingMode.token(stream, state.scriptState); 28 | } 29 | 30 | 31 | return { 32 | startState: function() { 33 | scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec); 34 | htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed"); 35 | return { 36 | token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, 37 | htmlState : htmlMixedMode.startState(), 38 | scriptState : scriptingMode.startState() 39 | } 40 | }, 41 | 42 | token: function(stream, state) { 43 | return state.token(stream, state); 44 | }, 45 | 46 | indent: function(state, textAfter) { 47 | if (state.token == htmlDispatch) 48 | return htmlMixedMode.indent(state.htmlState, textAfter); 49 | else 50 | return scriptingMode.indent(state.scriptState, textAfter); 51 | }, 52 | 53 | copyState: function(state) { 54 | return { 55 | token : state.token, 56 | htmlState : CodeMirror.copyState(htmlMixedMode, state.htmlState), 57 | scriptState : CodeMirror.copyState(scriptingMode, state.scriptState) 58 | } 59 | }, 60 | 61 | 62 | electricChars: "/{}:" 63 | } 64 | }); 65 | 66 | CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"}); 67 | CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"}); 68 | CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"}); 69 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/smarty/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Smarty mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Smarty mode

13 | 14 |
35 | 36 | 42 | 43 |
44 | 45 |
66 | 67 | 77 | 78 |

A plain text/Smarty mode which allows for custom delimiter tags (defaults to { and }).

79 | 80 |

MIME types defined: text/x-smarty

81 | 82 | 83 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/toolbar.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import collections.abc as c 4 | import typing as t 5 | from urllib.parse import unquote 6 | 7 | from flask import current_app 8 | from flask import Flask 9 | from flask import url_for 10 | from jinja2 import Environment 11 | from werkzeug import Request 12 | from werkzeug.utils import import_string 13 | 14 | from .panels import DebugPanel 15 | 16 | 17 | class DebugToolbar: 18 | _cached_panel_classes: t.ClassVar[dict[str, type[DebugPanel] | None]] = {} 19 | 20 | def __init__(self, request: Request, jinja_env: Environment) -> None: 21 | self.jinja_env = jinja_env 22 | self.request = request 23 | self.panels: list[DebugPanel] = [] 24 | self.template_context: dict[str, t.Any] = { 25 | "static_path": url_for("_debug_toolbar.static", filename="") 26 | } 27 | self.create_panels() 28 | 29 | def create_panels(self) -> None: 30 | """Populate debug panels""" 31 | activated_str = self.request.cookies.get("fldt_active", "") 32 | activated = unquote(activated_str).split(";") 33 | 34 | for panel_class in self._iter_panels(current_app): 35 | panel_instance = panel_class( 36 | jinja_env=self.jinja_env, context=self.template_context 37 | ) 38 | 39 | if panel_instance.dom_id() in activated: 40 | panel_instance.is_active = True 41 | 42 | self.panels.append(panel_instance) 43 | 44 | def render_toolbar(self) -> str: 45 | context = self.template_context.copy() 46 | context.update({"panels": self.panels}) 47 | template = self.jinja_env.get_template("base.html") 48 | return template.render(**context) 49 | 50 | @classmethod 51 | def load_panels(cls, app: Flask) -> None: 52 | for panel_class in cls._iter_panels(app): 53 | # Call `.init_app()` on panels 54 | panel_class.init_app(app) 55 | 56 | @classmethod 57 | def _iter_panels(cls, app: Flask) -> c.Iterator[type[DebugPanel]]: 58 | for panel_path in app.config["DEBUG_TB_PANELS"]: 59 | panel_class = cls._import_panel(app, panel_path) 60 | 61 | if panel_class is not None: 62 | yield panel_class 63 | 64 | @classmethod 65 | def _import_panel(cls, app: Flask, path: str) -> type[DebugPanel] | None: 66 | cache = cls._cached_panel_classes 67 | 68 | try: 69 | return cache[path] 70 | except KeyError: 71 | pass 72 | 73 | try: 74 | panel_class: type[DebugPanel] | None = import_string(path) 75 | except ImportError as e: 76 | app.logger.warning("Disabled %s due to ImportError: %s", path, e) 77 | panel_class = None 78 | 79 | cache[path] = panel_class 80 | return panel_class 81 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: JavaScript mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: JavaScript mode

13 | 14 |
63 | 64 | 70 | 71 |

JavaScript mode supports a single configuration 72 | option, json, which will set the mode to expect JSON 73 | data rather than a JavaScript program.

74 | 75 |

MIME types defined: text/javascript, application/json.

76 | 77 | 78 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rpm/spec/spec.js: -------------------------------------------------------------------------------- 1 | // Quick and dirty spec file highlighting 2 | 3 | CodeMirror.defineMode("spec", function(config, modeConfig) { 4 | var arch = /^(i386|i586|i686|x86_64|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/; 5 | 6 | var preamble = /^(Name|Version|Release|License|Summary|Url|Group|Source|BuildArch|BuildRequires|BuildRoot|AutoReqProv|Provides|Requires(\(\w+\))?|Obsoletes|Conflicts|Recommends|Source\d*|Patch\d*|ExclusiveArch|NoSource|Supplements):/; 7 | var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preun|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/; 8 | var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros 9 | var control_flow_simple = /^%(else|endif)/; // rpm control flow macros 10 | var operators = /^(\!|\?|\<\=|\<|\>\=|\>|\=\=|\&\&|\|\|)/; // operators in control flow macros 11 | 12 | return { 13 | startState: function () { 14 | return { 15 | controlFlow: false, 16 | macroParameters: false, 17 | section: false 18 | }; 19 | }, 20 | token: function (stream, state) { 21 | var ch = stream.peek(); 22 | if (ch == "#") { stream.skipToEnd(); return "comment"; } 23 | 24 | if (stream.sol()) { 25 | if (stream.match(preamble)) { return "preamble"; } 26 | if (stream.match(section)) { return "section"; } 27 | } 28 | 29 | if (stream.match(/^\$\w+/)) { return "def"; } // Variables like '$RPM_BUILD_ROOT' 30 | if (stream.match(/^\$\{\w+\}/)) { return "def"; } // Variables like '${RPM_BUILD_ROOT}' 31 | 32 | if (stream.match(control_flow_simple)) { return "keyword"; } 33 | if (stream.match(control_flow_complex)) { 34 | state.controlFlow = true; 35 | return "keyword"; 36 | } 37 | if (state.controlFlow) { 38 | if (stream.match(operators)) { return "operator"; } 39 | if (stream.match(/^(\d+)/)) { return "number"; } 40 | if (stream.eol()) { state.controlFlow = false; } 41 | } 42 | 43 | if (stream.match(arch)) { return "number"; } 44 | 45 | // Macros like '%make_install' or '%attr(0775,root,root)' 46 | if (stream.match(/^%[\w]+/)) { 47 | if (stream.match(/^\(/)) { state.macroParameters = true; } 48 | return "macro"; 49 | } 50 | if (state.macroParameters) { 51 | if (stream.match(/^\d+/)) { return "number";} 52 | if (stream.match(/^\)/)) { 53 | state.macroParameters = false; 54 | return "macro"; 55 | } 56 | } 57 | if (stream.match(/^%\{\??[\w \-]+\}/)) { return "macro"; } // Macros like '%{defined fedora}' 58 | 59 | //TODO: Include bash script sub-parser (CodeMirror supports that) 60 | stream.next(); 61 | return null; 62 | } 63 | }; 64 | }); 65 | 66 | CodeMirror.defineMIME("text/x-rpm-spec", "spec"); 67 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/pascal/pascal.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("pascal", function(config) { 2 | function words(str) { 3 | var obj = {}, words = str.split(" "); 4 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 5 | return obj; 6 | } 7 | var keywords = words("and array begin case const div do downto else end file for forward integer " + 8 | "boolean char function goto if in label mod nil not of or packed procedure " + 9 | "program record repeat set string then to type until var while with"); 10 | var atoms = {"null": true}; 11 | 12 | var isOperatorChar = /[+\-*&%=<>!?|\/]/; 13 | 14 | function tokenBase(stream, state) { 15 | var ch = stream.next(); 16 | if (ch == "#" && state.startOfLine) { 17 | stream.skipToEnd(); 18 | return "meta"; 19 | } 20 | if (ch == '"' || ch == "'") { 21 | state.tokenize = tokenString(ch); 22 | return state.tokenize(stream, state); 23 | } 24 | if (ch == "(" && stream.eat("*")) { 25 | state.tokenize = tokenComment; 26 | return tokenComment(stream, state); 27 | } 28 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 29 | return null 30 | } 31 | if (/\d/.test(ch)) { 32 | stream.eatWhile(/[\w\.]/); 33 | return "number"; 34 | } 35 | if (ch == "/") { 36 | if (stream.eat("/")) { 37 | stream.skipToEnd(); 38 | return "comment"; 39 | } 40 | } 41 | if (isOperatorChar.test(ch)) { 42 | stream.eatWhile(isOperatorChar); 43 | return "operator"; 44 | } 45 | stream.eatWhile(/[\w\$_]/); 46 | var cur = stream.current(); 47 | if (keywords.propertyIsEnumerable(cur)) return "keyword"; 48 | if (atoms.propertyIsEnumerable(cur)) return "atom"; 49 | return "word"; 50 | } 51 | 52 | function tokenString(quote) { 53 | return function(stream, state) { 54 | var escaped = false, next, end = false; 55 | while ((next = stream.next()) != null) { 56 | if (next == quote && !escaped) {end = true; break;} 57 | escaped = !escaped && next == "\\"; 58 | } 59 | if (end || !escaped) state.tokenize = null; 60 | return "string"; 61 | }; 62 | } 63 | 64 | function tokenComment(stream, state) { 65 | var maybeEnd = false, ch; 66 | while (ch = stream.next()) { 67 | if (ch == ")" && maybeEnd) { 68 | state.tokenize = null; 69 | break; 70 | } 71 | maybeEnd = (ch == "*"); 72 | } 73 | return "comment"; 74 | } 75 | 76 | // Interface 77 | 78 | return { 79 | startState: function(basecolumn) { 80 | return {tokenize: null}; 81 | }, 82 | 83 | token: function(stream, state) { 84 | if (stream.eatSpace()) return null; 85 | var style = (state.tokenize || tokenBase)(stream, state); 86 | if (style == "comment" || style == "meta") return style; 87 | return style; 88 | }, 89 | 90 | electricChars: "{}" 91 | }; 92 | }); 93 | 94 | CodeMirror.defineMIME("text/x-pascal", "pascal"); 95 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/yaml/yaml.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("yaml", function() { 2 | 3 | var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; 4 | var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); 5 | 6 | return { 7 | token: function(stream, state) { 8 | var ch = stream.peek(); 9 | var esc = state.escaped; 10 | state.escaped = false; 11 | /* comments */ 12 | if (ch == "#") { stream.skipToEnd(); return "comment"; } 13 | if (state.literal && stream.indentation() > state.keyCol) { 14 | stream.skipToEnd(); return "string"; 15 | } else if (state.literal) { state.literal = false; } 16 | if (stream.sol()) { 17 | state.keyCol = 0; 18 | state.pair = false; 19 | state.pairStart = false; 20 | /* document start */ 21 | if(stream.match(/---/)) { return "def"; } 22 | /* document end */ 23 | if (stream.match(/\.\.\./)) { return "def"; } 24 | /* array list item */ 25 | if (stream.match(/\s*-\s+/)) { return 'meta'; } 26 | } 27 | /* pairs (associative arrays) -> key */ 28 | if (!state.pair && stream.match(/^\s*([a-z0-9\._-])+(?=\s*:)/i)) { 29 | state.pair = true; 30 | state.keyCol = stream.indentation(); 31 | return "atom"; 32 | } 33 | if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } 34 | 35 | /* inline pairs/lists */ 36 | if (stream.match(/^(\{|\}|\[|\])/)) { 37 | if (ch == '{') 38 | state.inlinePairs++; 39 | else if (ch == '}') 40 | state.inlinePairs--; 41 | else if (ch == '[') 42 | state.inlineList++; 43 | else 44 | state.inlineList--; 45 | return 'meta'; 46 | } 47 | 48 | /* list seperator */ 49 | if (state.inlineList > 0 && !esc && ch == ',') { 50 | stream.next(); 51 | return 'meta'; 52 | } 53 | /* pairs seperator */ 54 | if (state.inlinePairs > 0 && !esc && ch == ',') { 55 | state.keyCol = 0; 56 | state.pair = false; 57 | state.pairStart = false; 58 | stream.next(); 59 | return 'meta'; 60 | } 61 | 62 | /* start of value of a pair */ 63 | if (state.pairStart) { 64 | /* block literals */ 65 | if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; 66 | /* references */ 67 | if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } 68 | /* numbers */ 69 | if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } 70 | if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } 71 | /* keywords */ 72 | if (stream.match(keywordRegex)) { return 'keyword'; } 73 | } 74 | 75 | /* nothing found, continue */ 76 | state.pairStart = false; 77 | state.escaped = (ch == '\\'); 78 | stream.next(); 79 | return null; 80 | }, 81 | startState: function() { 82 | return { 83 | pair: false, 84 | pairStart: false, 85 | keyCol: 0, 86 | inlinePairs: 0, 87 | inlineList: 0, 88 | literal: false, 89 | escaped: false 90 | }; 91 | } 92 | }; 93 | }); 94 | 95 | CodeMirror.defineMIME("text/x-yaml", "yaml"); 96 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/htmlmixed/htmlmixed.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { 2 | var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); 3 | var jsMode = CodeMirror.getMode(config, "javascript"); 4 | var cssMode = CodeMirror.getMode(config, "css"); 5 | 6 | function html(stream, state) { 7 | var style = htmlMode.token(stream, state.htmlState); 8 | if (style == "tag" && stream.current() == ">" && state.htmlState.context) { 9 | if (/^script$/i.test(state.htmlState.context.tagName)) { 10 | state.token = javascript; 11 | state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); 12 | state.mode = "javascript"; 13 | } 14 | else if (/^style$/i.test(state.htmlState.context.tagName)) { 15 | state.token = css; 16 | state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); 17 | state.mode = "css"; 18 | } 19 | } 20 | return style; 21 | } 22 | function maybeBackup(stream, pat, style) { 23 | var cur = stream.current(); 24 | var close = cur.search(pat); 25 | if (close > -1) stream.backUp(cur.length - close); 26 | return style; 27 | } 28 | function javascript(stream, state) { 29 | if (stream.match(/^<\/\s*script\s*>/i, false)) { 30 | state.token = html; 31 | state.localState = null; 32 | state.mode = "html"; 33 | return html(stream, state); 34 | } 35 | return maybeBackup(stream, /<\/\s*script\s*>/, 36 | jsMode.token(stream, state.localState)); 37 | } 38 | function css(stream, state) { 39 | if (stream.match(/^<\/\s*style\s*>/i, false)) { 40 | state.token = html; 41 | state.localState = null; 42 | state.mode = "html"; 43 | return html(stream, state); 44 | } 45 | return maybeBackup(stream, /<\/\s*style\s*>/, 46 | cssMode.token(stream, state.localState)); 47 | } 48 | 49 | return { 50 | startState: function() { 51 | var state = htmlMode.startState(); 52 | return {token: html, localState: null, mode: "html", htmlState: state}; 53 | }, 54 | 55 | copyState: function(state) { 56 | if (state.localState) 57 | var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState); 58 | return {token: state.token, localState: local, mode: state.mode, 59 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; 60 | }, 61 | 62 | token: function(stream, state) { 63 | return state.token(stream, state); 64 | }, 65 | 66 | indent: function(state, textAfter) { 67 | if (state.token == html || /^\s*<\//.test(textAfter)) 68 | return htmlMode.indent(state.htmlState, textAfter); 69 | else if (state.token == javascript) 70 | return jsMode.indent(state.localState, textAfter); 71 | else 72 | return cssMode.indent(state.localState, textAfter); 73 | }, 74 | 75 | compareStates: function(a, b) { 76 | return htmlMode.compareStates(a.htmlState, b.htmlState); 77 | }, 78 | 79 | electricChars: "/{}:" 80 | } 81 | }); 82 | 83 | CodeMirror.defineMIME("text/html", "htmlmixed"); 84 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/util/simple-hint.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | CodeMirror.simpleHint = function(editor, getHints) { 3 | // We want a single cursor position. 4 | if (editor.somethingSelected()) return; 5 | var result = getHints(editor); 6 | if (!result || !result.list.length) return; 7 | var completions = result.list; 8 | function insert(str) { 9 | editor.replaceRange(str, result.from, result.to); 10 | } 11 | // When there is only one completion, use it directly. 12 | if (completions.length == 1) {insert(completions[0]); return true;} 13 | 14 | // Build the select widget 15 | var complete = document.createElement("div"); 16 | complete.className = "CodeMirror-completions"; 17 | var sel = complete.appendChild(document.createElement("select")); 18 | // Opera doesn't move the selection when pressing up/down in a 19 | // multi-select, but it does properly support the size property on 20 | // single-selects, so no multi-select is necessary. 21 | if (!window.opera) sel.multiple = true; 22 | for (var i = 0; i < completions.length; ++i) { 23 | var opt = sel.appendChild(document.createElement("option")); 24 | opt.appendChild(document.createTextNode(completions[i])); 25 | } 26 | sel.firstChild.selected = true; 27 | sel.size = Math.min(10, completions.length); 28 | var pos = editor.cursorCoords(); 29 | complete.style.left = pos.x + "px"; 30 | complete.style.top = pos.yBot + "px"; 31 | document.body.appendChild(complete); 32 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 33 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 34 | if(winW - pos.x < sel.clientWidth) 35 | complete.style.left = (pos.x - sel.clientWidth) + "px"; 36 | // Hack to hide the scrollbar. 37 | if (completions.length <= 10) 38 | complete.style.width = (sel.clientWidth - 1) + "px"; 39 | 40 | var done = false; 41 | function close() { 42 | if (done) return; 43 | done = true; 44 | complete.parentNode.removeChild(complete); 45 | } 46 | function pick() { 47 | insert(completions[sel.selectedIndex]); 48 | close(); 49 | setTimeout(function(){editor.focus();}, 50); 50 | } 51 | CodeMirror.connect(sel, "blur", close); 52 | CodeMirror.connect(sel, "keydown", function(event) { 53 | var code = event.keyCode; 54 | // Enter 55 | if (code == 13) {CodeMirror.e_stop(event); pick();} 56 | // Escape 57 | else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} 58 | else if (code != 38 && code != 40) { 59 | close(); editor.focus(); 60 | // Pass the event to the CodeMirror instance so that it can handle things like backspace properly. 61 | editor.triggerOnKeyDown(event); 62 | setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50); 63 | } 64 | }); 65 | CodeMirror.connect(sel, "dblclick", pick); 66 | 67 | sel.focus(); 68 | // Opera sometimes ignores focusing a freshly created node 69 | if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); 70 | return true; 71 | }; 72 | })(); 73 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import collections.abc as c 4 | import typing as t 5 | 6 | from flask import Flask 7 | from jinja2 import Environment 8 | from werkzeug import Request 9 | from werkzeug import Response 10 | 11 | 12 | class DebugPanel: 13 | """Base class for debug panels.""" 14 | 15 | name: str 16 | 17 | # If content returns something, set to true in subclass 18 | has_content = False 19 | 20 | # If the client is able to activate/de-activate the panel 21 | user_enable = False 22 | 23 | # We'll maintain a local context instance so we can expose our template 24 | # context variables to panels which need them: 25 | context: dict[str, t.Any] = {} 26 | 27 | # Panel methods 28 | def __init__( 29 | self, jinja_env: Environment, context: dict[str, t.Any] | None = None 30 | ) -> None: 31 | if context is not None: 32 | self.context.update(context) 33 | 34 | self.jinja_env = jinja_env 35 | # If the client enabled the panel 36 | self.is_active = False 37 | 38 | @classmethod 39 | def init_app(cls, app: Flask) -> None: 40 | """Method that can be overridden by child classes. 41 | Can be used for setting up additional URL-rules/routes. 42 | 43 | Example:: 44 | 45 | class UMLDiagramPanel(DebugPanel): 46 | 47 | @classmethod 48 | def init_app(cls, app): 49 | app.add_url_rule( 50 | '/_flask_debugtoolbar_umldiagram/', 51 | '_flask_debugtoolbar_umldiagram.serve_generated_image', 52 | cls.serve_generated_image 53 | ) 54 | 55 | @classmethod 56 | def serve_generated_image(cls, app): 57 | return Response(...) 58 | """ 59 | pass 60 | 61 | def render(self, template_name: str, context: dict[str, t.Any]) -> str: 62 | template = self.jinja_env.get_template(template_name) 63 | return template.render(**context) 64 | 65 | def dom_id(self) -> str: 66 | return f"flDebug{self.name.replace(' ', '')}Panel" 67 | 68 | def nav_title(self) -> str: 69 | """Title showing in toolbar""" 70 | raise NotImplementedError 71 | 72 | def nav_subtitle(self) -> str: 73 | """Subtitle showing until title in toolbar""" 74 | return "" 75 | 76 | def title(self) -> str: 77 | """Title showing in panel""" 78 | raise NotImplementedError 79 | 80 | def url(self) -> str: 81 | raise NotImplementedError 82 | 83 | def content(self) -> str: 84 | raise NotImplementedError 85 | 86 | # Standard middleware methods 87 | def process_request(self, request: Request) -> None: 88 | pass 89 | 90 | def process_view( 91 | self, 92 | request: Request, 93 | view_func: c.Callable[..., t.Any], 94 | view_kwargs: dict[str, t.Any], 95 | ) -> c.Callable[..., t.Any] | None: 96 | pass 97 | 98 | def process_response(self, request: Request, response: Response) -> None: 99 | pass 100 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/velocity/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Velocity mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

CodeMirror: Velocity mode

14 |
89 | 99 | 100 |

MIME types defined: text/velocity.

101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /docs/panels.rst: -------------------------------------------------------------------------------- 1 | Built-In Panels 2 | =============== 3 | 4 | Versions 5 | -------- 6 | flask_debugtoolbar.panels.versions.VersionDebugPanel 7 | 8 | Shows the installed Flask version. The expanded view displays all installed packages and their versions as detected by ``setuptools``. 9 | 10 | 11 | Time 12 | ---- 13 | 14 | flask_debugtoolbar.panels.timer.TimerDebugPanel 15 | 16 | Shows the time taken to process the current request. The expanded view includes the breakdown of CPU time, by user and system, wall clock time, and context switches. 17 | 18 | .. image:: _static/screenshot-time-panel.png 19 | 20 | 21 | HTTP Headers 22 | ------------ 23 | 24 | flask_debugtoolbar.panels.headers.HeaderDebugPanel 25 | 26 | Displays the HTTP headers for the current request. 27 | 28 | .. image:: _static/screenshot-headers-panel.png 29 | 30 | 31 | Request Vars 32 | ------------ 33 | 34 | flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel 35 | 36 | Displays details of the Flask request-related variables, including the view function parameters, cookies, session variables, and GET and POST variables. 37 | 38 | .. image:: _static/screenshot-request-vars-panel.png 39 | 40 | 41 | Config 42 | ------ 43 | 44 | flask_debugtoolbar.panels.config_vars.ConfigVarsDebugPanel 45 | 46 | Shows the contents of the Flask application's config dict ``app.config``. 47 | 48 | .. image:: _static/screenshot-config-panel.png 49 | 50 | 51 | Templates 52 | --------- 53 | 54 | flask_debugtoolbar.panels.template.TemplateDebugPanel 55 | 56 | Shows information about the templates rendered for this request, and the value of the template parameters provided. 57 | 58 | .. image:: _static/screenshot-template-panel.png 59 | 60 | 61 | SQLAlchemy 62 | ---------- 63 | 64 | flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel 65 | 66 | Shows SQL queries run during the current request. 67 | 68 | .. note:: This panel requires using the `Flask-SQLAlchemy`_ extension in order 69 | to record the queries. See the Flask-SQLAlchemy 70 | :ref:`flasksqlalchemy:quickstart` section to configure it. 71 | 72 | For additional details on query recording see the 73 | :py:func:`~flask_sqlalchemy.get_debug_queries` documentation. 74 | 75 | .. note:: SQL syntax highlighting requires `Pygments`_ to be installed. 76 | 77 | .. image:: _static/screenshot-sqlalchemy-panel.png 78 | 79 | .. _Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/ 80 | 81 | .. _Pygments: https://pygments.org/ 82 | 83 | 84 | Logging 85 | ------- 86 | 87 | flask_debugtoolbar.panels.logger.LoggingPanel 88 | 89 | Displays log messages recorded during the current request. 90 | 91 | .. image:: _static/screenshot-logger-panel.png 92 | 93 | 94 | Route List 95 | ---------- 96 | 97 | flask_debugtoolbar.panels.route_list.RouteListDebugPanel 98 | 99 | 100 | Displays the Flask URL routing rules. 101 | 102 | 103 | Profiler 104 | -------- 105 | 106 | flask_debugtoolbar.panels.profiler.ProfilerDebugPanel 107 | 108 | Reports profiling data for the current request. Due to the performance overhead, profiling is disabled by default. Click the checkmark to toggle profiling on or off. After enabling the profiler, refresh the page to re-run it with profiling. 109 | 110 | .. image:: _static/screenshot-profiler-panel.png 111 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/python/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: Python mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: Python mode

13 | 14 |
103 | 114 |

Configuration Options:

115 |
    116 |
  • version - 2/3 - The version of Python to recognize. Default is 2.
  • 117 |
  • singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.
  • 118 |
119 | 120 |

MIME types defined: text/x-python.

121 | 122 | 123 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/rpm/spec/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: RPM spec mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

CodeMirror: RPM spec mode

14 | 15 |
88 | 96 | 97 |

MIME types defined: text/x-rpm-spec.

98 | 99 | 100 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | line-height: 1em; 3 | font-family: monospace; 4 | } 5 | 6 | .CodeMirror-scroll { 7 | overflow: auto; 8 | height: 300px; 9 | /* This is needed to prevent an IE[67] bug where the scrolled content 10 | is visible outside of the scrolling box. */ 11 | position: relative; 12 | outline: none; 13 | } 14 | 15 | .CodeMirror-gutter { 16 | position: absolute; left: 0; top: 0; 17 | z-index: 10; 18 | background-color: #f7f7f7; 19 | border-right: 1px solid #eee; 20 | min-width: 2em; 21 | height: 100%; 22 | } 23 | .CodeMirror-gutter-text { 24 | color: #aaa; 25 | text-align: right; 26 | padding: .4em .2em .4em .4em; 27 | white-space: pre !important; 28 | } 29 | .CodeMirror-lines { 30 | padding: .4em; 31 | white-space: pre; 32 | } 33 | 34 | .CodeMirror pre { 35 | -moz-border-radius: 0; 36 | -webkit-border-radius: 0; 37 | -o-border-radius: 0; 38 | border-radius: 0; 39 | border-width: 0; margin: 0; padding: 0; background: transparent; 40 | font-family: inherit; 41 | font-size: inherit; 42 | padding: 0; margin: 0; 43 | white-space: pre; 44 | word-wrap: normal; 45 | } 46 | 47 | .CodeMirror-wrap pre { 48 | word-wrap: break-word; 49 | white-space: pre-wrap; 50 | } 51 | .CodeMirror-wrap .CodeMirror-scroll { 52 | overflow-x: hidden; 53 | } 54 | 55 | .CodeMirror textarea { 56 | outline: none !important; 57 | } 58 | 59 | .CodeMirror pre.CodeMirror-cursor { 60 | z-index: 10; 61 | position: absolute; 62 | visibility: hidden; 63 | border-left: 1px solid black; 64 | border-right:none; 65 | width:0; 66 | } 67 | .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} 68 | .CodeMirror-focused pre.CodeMirror-cursor { 69 | visibility: visible; 70 | } 71 | 72 | div.CodeMirror-selected { background: #d9d9d9; } 73 | .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } 74 | 75 | .CodeMirror-searching { 76 | background: #ffa; 77 | background: rgba(255, 255, 0, .4); 78 | } 79 | 80 | /* Default theme */ 81 | 82 | .cm-s-default span.cm-keyword {color: #708;} 83 | .cm-s-default span.cm-atom {color: #219;} 84 | .cm-s-default span.cm-number {color: #164;} 85 | .cm-s-default span.cm-def {color: #00f;} 86 | .cm-s-default span.cm-variable {color: black;} 87 | .cm-s-default span.cm-variable-2 {color: #05a;} 88 | .cm-s-default span.cm-variable-3 {color: #085;} 89 | .cm-s-default span.cm-property {color: black;} 90 | .cm-s-default span.cm-operator {color: black;} 91 | .cm-s-default span.cm-comment {color: #a50;} 92 | .cm-s-default span.cm-string {color: #a11;} 93 | .cm-s-default span.cm-string-2 {color: #f50;} 94 | .cm-s-default span.cm-meta {color: #555;} 95 | .cm-s-default span.cm-error {color: #f00;} 96 | .cm-s-default span.cm-qualifier {color: #555;} 97 | .cm-s-default span.cm-builtin {color: #30a;} 98 | .cm-s-default span.cm-bracket {color: #cc7;} 99 | .cm-s-default span.cm-tag {color: #170;} 100 | .cm-s-default span.cm-attribute {color: #00c;} 101 | .cm-s-default span.cm-header {color: #a0a;} 102 | .cm-s-default span.cm-quote {color: #090;} 103 | .cm-s-default span.cm-hr {color: #999;} 104 | .cm-s-default span.cm-link {color: #00c;} 105 | 106 | span.cm-header, span.cm-strong {font-weight: bold;} 107 | span.cm-em {font-style: italic;} 108 | span.cm-emstrong {font-style: italic; font-weight: bold;} 109 | span.cm-link {text-decoration: underline;} 110 | 111 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 112 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 113 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import collections.abc as c 4 | import gzip 5 | import io 6 | import itertools 7 | import os.path 8 | import sys 9 | from types import ModuleType 10 | 11 | from flask import current_app 12 | from markupsafe import Markup 13 | 14 | try: 15 | from pygments import highlight 16 | from pygments.formatters import HtmlFormatter 17 | from pygments.lexers import SqlLexer 18 | from pygments.styles import get_style_by_name 19 | 20 | PYGMENT_STYLE = get_style_by_name("colorful") 21 | HAVE_PYGMENTS = True 22 | except ImportError: 23 | HAVE_PYGMENTS = False 24 | 25 | try: 26 | import sqlparse # pyright: ignore 27 | 28 | HAVE_SQLPARSE = True 29 | except ImportError: 30 | HAVE_SQLPARSE = False 31 | 32 | 33 | def format_fname(value: str) -> str: 34 | # If the value has a builtin prefix, return it unchanged 35 | if value.startswith(("{", "<")): 36 | return value 37 | 38 | value = os.path.normpath(value) 39 | 40 | # If the file is absolute, try normalizing it relative to the project root 41 | # to handle it as a project file 42 | if os.path.isabs(value): 43 | value = _shortest_relative_path(value, [current_app.root_path], os.path) 44 | 45 | # If the value is a relative path, it is a project file 46 | if not os.path.isabs(value): 47 | return os.path.join(".", value) 48 | 49 | # Otherwise, normalize other paths relative to sys.path 50 | return f"<{_shortest_relative_path(value, sys.path, os.path)}>" 51 | 52 | 53 | def _shortest_relative_path( 54 | value: str, paths: list[str], path_module: ModuleType 55 | ) -> str: 56 | relpaths = _relative_paths(value, paths, path_module) 57 | return min(itertools.chain(relpaths, [value]), key=len) 58 | 59 | 60 | def _relative_paths( 61 | value: str, paths: list[str], path_module: ModuleType 62 | ) -> c.Iterator[str]: 63 | for path in paths: 64 | try: 65 | relval = path_module.relpath(value, path) 66 | except ValueError: 67 | # on Windows, relpath throws a ValueError for 68 | # paths with different drives 69 | continue 70 | 71 | if not relval.startswith(path_module.pardir): 72 | yield relval 73 | 74 | 75 | def decode_text(value: str | bytes) -> str: 76 | """ 77 | Decode a text-like value for display. 78 | 79 | Unicode values are returned unchanged. Byte strings will be decoded 80 | with a text-safe replacement for unrecognized characters. 81 | """ 82 | if isinstance(value, bytes): 83 | return value.decode("ascii", "replace") 84 | 85 | return value # pyright: ignore 86 | 87 | 88 | def format_sql(query: str | bytes, args: object) -> str: 89 | if HAVE_SQLPARSE: 90 | query = sqlparse.format(query, reindent=True, keyword_case="upper") 91 | 92 | if not HAVE_PYGMENTS: 93 | return decode_text(query) 94 | 95 | return Markup( 96 | highlight(query, SqlLexer(), HtmlFormatter(noclasses=True, style=PYGMENT_STYLE)) 97 | ) 98 | 99 | 100 | def gzip_compress(data: bytes, compresslevel: int = 6) -> bytes: 101 | buff = io.BytesIO() 102 | 103 | with gzip.GzipFile(fileobj=buff, mode="wb", compresslevel=compresslevel) as f: 104 | f.write(data) 105 | 106 | return buff.getvalue() 107 | 108 | 109 | def gzip_decompress(data: bytes) -> bytes: 110 | with gzip.GzipFile(fileobj=io.BytesIO(data), mode="rb") as f: 111 | return f.read() 112 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/clike/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodeMirror: C-like mode 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

CodeMirror: C-like mode

13 | 14 |
81 | 82 | 89 | 90 |

Simple mode that tries to handle C-like languages as well as it 91 | can. Takes two configuration parameters: keywords, an 92 | object whose property names are the keywords in the language, 93 | and useCPP, which determines whether C preprocessor 94 | directives are recognized.

95 | 96 |

MIME types defined: text/x-csrc 97 | (C code), text/x-c++src (C++ 98 | code), text/x-java (Java 99 | code), text/x-csharp (C#).

100 | 101 | 102 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/static/codemirror/mode/gfm/gfm.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("gfm", function(config, parserConfig) { 2 | var mdMode = CodeMirror.getMode(config, "markdown"); 3 | var aliases = { 4 | html: "htmlmixed", 5 | js: "javascript", 6 | json: "application/json", 7 | c: "text/x-csrc", 8 | "c++": "text/x-c++src", 9 | java: "text/x-java", 10 | csharp: "text/x-csharp", 11 | "c#": "text/x-csharp" 12 | }; 13 | 14 | // make this lazy so that we don't need to load GFM last 15 | var getMode = (function () { 16 | var i, modes = {}, mimes = {}, mime; 17 | 18 | var list = CodeMirror.listModes(); 19 | for (i = 0; i < list.length; i++) { 20 | modes[list[i]] = list[i]; 21 | } 22 | var mimesList = CodeMirror.listMIMEs(); 23 | for (i = 0; i < mimesList.length; i++) { 24 | mime = mimesList[i].mime; 25 | mimes[mime] = mimesList[i].mime; 26 | } 27 | 28 | for (var a in aliases) { 29 | if (aliases[a] in modes || aliases[a] in mimes) 30 | modes[a] = aliases[a]; 31 | } 32 | 33 | return function (lang) { 34 | return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null; 35 | } 36 | }()); 37 | 38 | function markdown(stream, state) { 39 | // intercept fenced code blocks 40 | if (stream.sol() && stream.match(/^```([\w+#]*)/)) { 41 | // try switching mode 42 | state.localMode = getMode(RegExp.$1) 43 | if (state.localMode) 44 | state.localState = state.localMode.startState(); 45 | 46 | state.token = local; 47 | return 'code'; 48 | } 49 | 50 | return mdMode.token(stream, state.mdState); 51 | } 52 | 53 | function local(stream, state) { 54 | if (stream.sol() && stream.match(/^```/)) { 55 | state.localMode = state.localState = null; 56 | state.token = markdown; 57 | return 'code'; 58 | } 59 | else if (state.localMode) { 60 | return state.localMode.token(stream, state.localState); 61 | } else { 62 | stream.skipToEnd(); 63 | return 'code'; 64 | } 65 | } 66 | 67 | // custom handleText to prevent emphasis in the middle of a word 68 | // and add autolinking 69 | function handleText(stream, mdState) { 70 | var match; 71 | if (stream.match(/^\w+:\/\/\S+/)) { 72 | return 'linkhref'; 73 | } 74 | if (stream.match(/^[^\[*\\<>` _][^\[*\\<>` ]*[^\[*\\<>` _]/)) { 75 | return mdMode.getType(mdState); 76 | } 77 | if (match = stream.match(/^[^\[*\\<>` ]+/)) { 78 | var word = match[0]; 79 | if (word[0] === '_' && word[word.length-1] === '_') { 80 | stream.backUp(word.length); 81 | return undefined; 82 | } 83 | return mdMode.getType(mdState); 84 | } 85 | if (stream.eatSpace()) { 86 | return null; 87 | } 88 | } 89 | 90 | return { 91 | startState: function() { 92 | var mdState = mdMode.startState(); 93 | mdState.text = handleText; 94 | return {token: markdown, mode: "markdown", mdState: mdState, 95 | localMode: null, localState: null}; 96 | }, 97 | 98 | copyState: function(state) { 99 | return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState), 100 | localMode: state.localMode, 101 | localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null}; 102 | }, 103 | 104 | token: function(stream, state) { 105 | return state.token(stream, state); 106 | } 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/timer.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import time 4 | 5 | from werkzeug import Request 6 | from werkzeug import Response 7 | 8 | from . import DebugPanel 9 | 10 | try: 11 | import resource 12 | 13 | HAVE_RESOURCE = True 14 | except ImportError: 15 | HAVE_RESOURCE = False 16 | 17 | 18 | class TimerDebugPanel(DebugPanel): 19 | """Panel that displays the time a response took in milliseconds.""" 20 | 21 | name = "Timer" 22 | has_content = HAVE_RESOURCE 23 | 24 | def process_request(self, request: Request) -> None: 25 | self._start_time = time.time() 26 | 27 | if HAVE_RESOURCE: 28 | self._start_rusage = resource.getrusage(resource.RUSAGE_SELF) 29 | 30 | def process_response(self, request: Request, response: Response) -> None: 31 | self.total_time: float = (time.time() - self._start_time) * 1000 32 | 33 | if HAVE_RESOURCE: 34 | self._end_rusage = resource.getrusage(resource.RUSAGE_SELF) 35 | 36 | def nav_title(self) -> str: 37 | return "Time" 38 | 39 | def nav_subtitle(self) -> str: 40 | if not HAVE_RESOURCE: 41 | return f"TOTAL: {self.total_time:0.2f}ms" 42 | 43 | utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime 44 | stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime 45 | return f"CPU: {(utime + stime) * 1000.0:0.2f}ms ({self.total_time:0.2f}ms)" 46 | 47 | def title(self) -> str: 48 | return "Resource Usage" 49 | 50 | def url(self) -> str: 51 | return "" 52 | 53 | def _elapsed_ru(self, name: str) -> float: 54 | return getattr(self._end_rusage, name) - getattr(self._start_rusage, name) # type: ignore[no-any-return] 55 | 56 | def content(self) -> str: 57 | utime = 1000 * self._elapsed_ru("ru_utime") 58 | stime = 1000 * self._elapsed_ru("ru_stime") 59 | vcsw = self._elapsed_ru("ru_nvcsw") 60 | ivcsw = self._elapsed_ru("ru_nivcsw") 61 | # minflt = self._elapsed_ru("ru_minflt") 62 | # majflt = self._elapsed_ru("ru_majflt") 63 | 64 | # these are documented as not meaningful under Linux. If you're running BSD 65 | # feel free to enable them, and add any others that I hadn't gotten to before 66 | # I noticed that I was getting nothing but zeroes and that the docs agreed. :-( 67 | # blkin = self._elapsed_ru("ru_inblock") 68 | # blkout = self._elapsed_ru("ru_oublock") 69 | # swap = self._elapsed_ru("ru_nswap") 70 | # rss = self._end_rusage.ru_maxrss 71 | # srss = self._end_rusage.ru_ixrss 72 | # urss = self._end_rusage.ru_idrss 73 | # usrss = self._end_rusage.ru_isrss 74 | rows = ( 75 | ("User CPU time", f"{utime:0.3f} msec"), 76 | ("System CPU time", f"{stime:0.3f} msec"), 77 | ("Total CPU time", f"{(utime + stime):0.3f} msec"), 78 | ("Elapsed time", f"{self.total_time:0.3f} msec"), 79 | ("Context switches", f"{vcsw} voluntary, {ivcsw} involuntary"), 80 | # ( 81 | # "Memory use", 82 | # f"{rss} max RSS, {srss} shared, {urss + usrss} unshared", 83 | # ), 84 | # ("Page faults", f"{minflt} no i/o, {majflt} requiring i/o"), 85 | # ("Disk operations", f"{blkin} in, {blkout} out, {swap} swapout"), 86 | ) 87 | context = self.context.copy() 88 | context.update( 89 | { 90 | "rows": rows, 91 | } 92 | ) 93 | return self.render("panels/timer.html", context) 94 | -------------------------------------------------------------------------------- /src/flask_debugtoolbar/panels/logger.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime 4 | import logging 5 | import threading 6 | 7 | from werkzeug import Request 8 | 9 | from ..utils import format_fname 10 | from . import DebugPanel 11 | 12 | 13 | class ThreadTrackingHandler(logging.Handler): 14 | def __init__(self) -> None: 15 | super().__init__() 16 | # a dictionary that maps threads to log records 17 | self.records: dict[threading.Thread, list[logging.LogRecord]] = {} 18 | 19 | def emit(self, record: logging.LogRecord) -> None: 20 | self.get_records().append(record) 21 | 22 | def get_records( 23 | self, thread: threading.Thread | None = None 24 | ) -> list[logging.LogRecord]: 25 | """ 26 | Returns a list of records for the provided thread, of if none is 27 | provided, returns a list for the current thread. 28 | """ 29 | if thread is None: 30 | thread = threading.current_thread() 31 | 32 | if thread not in self.records: 33 | self.records[thread] = [] 34 | 35 | return self.records[thread] 36 | 37 | def clear_records(self, thread: threading.Thread | None = None) -> None: 38 | if thread is None: 39 | thread = threading.current_thread() 40 | 41 | if thread in self.records: 42 | del self.records[thread] 43 | 44 | 45 | handler: ThreadTrackingHandler = None # type: ignore[assignment] 46 | _init_lock = threading.Lock() 47 | 48 | 49 | def _init_once() -> None: 50 | global handler 51 | 52 | if handler is not None: 53 | return 54 | 55 | with _init_lock: 56 | if handler is not None: 57 | return 58 | 59 | # Call werkzeug's internal logging to make sure it gets configured 60 | # before we add our handler. Otherwise werkzeug will see our handler 61 | # and not configure console logging for the request log. 62 | # Werkzeug's default log level is INFO so this message probably won't 63 | # be seen. 64 | from werkzeug._internal import _log 65 | 66 | _log("debug", "Initializing Flask-DebugToolbar log handler") 67 | handler = ThreadTrackingHandler() 68 | logging.root.addHandler(handler) 69 | 70 | 71 | class LoggingPanel(DebugPanel): 72 | name = "Logging" 73 | has_content = True 74 | 75 | def process_request(self, request: Request) -> None: 76 | _init_once() 77 | handler.clear_records() 78 | 79 | def get_and_delete(self) -> list[logging.LogRecord]: 80 | records = handler.get_records() 81 | handler.clear_records() 82 | return records 83 | 84 | def nav_title(self) -> str: 85 | return "Logging" 86 | 87 | def nav_subtitle(self) -> str: 88 | num_records = len(handler.get_records()) 89 | plural = "message" if num_records == 1 else "messages" 90 | return f"{num_records} {plural}" 91 | 92 | def title(self) -> str: 93 | return "Log Messages" 94 | 95 | def url(self) -> str: 96 | return "" 97 | 98 | def content(self) -> str: 99 | records = [] 100 | 101 | for record in self.get_and_delete(): 102 | records.append( 103 | { 104 | "message": record.getMessage(), 105 | "time": datetime.datetime.fromtimestamp(record.created), 106 | "level": record.levelname, 107 | "file": format_fname(record.pathname), 108 | "file_long": record.pathname, 109 | "line": record.lineno, 110 | } 111 | ) 112 | 113 | context = self.context.copy() 114 | context.update({"records": records}) 115 | return self.render("panels/logger.html", context) 116 | --------------------------------------------------------------------------------