impuls/lib/python3.11/site-packages/debug_toolbar/panels/__init__.py

266 lines
7.9 KiB
Python

from django.template.loader import render_to_string
from debug_toolbar import settings as dt_settings
from debug_toolbar.utils import get_name_from_obj
class Panel:
"""
Base class for panels.
"""
def __init__(self, toolbar, get_response):
self.toolbar = toolbar
self.get_response = get_response
# Private panel properties
@property
def panel_id(self):
return self.__class__.__name__
@property
def enabled(self) -> bool:
# The user's cookies should override the default value
cookie_value = self.toolbar.request.COOKIES.get("djdt" + self.panel_id)
if cookie_value is not None:
return cookie_value == "on"
# Check to see if settings has a default value for it
disabled_panels = dt_settings.get_config()["DISABLE_PANELS"]
panel_path = get_name_from_obj(self)
# Some panels such as the SQLPanel and TemplatesPanel exist in a
# panel module, but can be disabled without panel in the path.
# For that reason, replace .panel. in the path and check for that
# value in the disabled panels as well.
return (
panel_path not in disabled_panels
and panel_path.replace(".panel.", ".") not in disabled_panels
)
# Titles and content
@property
def nav_title(self):
"""
Title shown in the side bar. Defaults to :attr:`title`.
"""
return self.title
@property
def nav_subtitle(self):
"""
Subtitle shown in the side bar. Defaults to the empty string.
"""
return ""
@property
def has_content(self):
"""
``True`` if the panel can be displayed in full screen, ``False`` if
it's only shown in the side bar. Defaults to ``True``.
"""
return True
@property
def is_historical(self):
"""
Panel supports rendering historical values.
Defaults to :attr:`has_content`.
"""
return self.has_content
@property
def title(self):
"""
Title shown in the panel when it's displayed in full screen.
Mandatory, unless the panel sets :attr:`has_content` to ``False``.
"""
raise NotImplementedError
@property
def template(self):
"""
Template used to render :attr:`content`.
Mandatory, unless the panel sets :attr:`has_content` to ``False`` or
overrides :attr:`content`.
"""
raise NotImplementedError
@property
def content(self):
"""
Content of the panel when it's displayed in full screen.
By default this renders the template defined by :attr:`template`.
Statistics stored with :meth:`record_stats` are available in the
template's context.
"""
if self.has_content:
return render_to_string(self.template, self.get_stats())
@property
def scripts(self):
"""
Scripts used by the HTML content of the panel when it's displayed.
When a panel is rendered on the frontend, the ``djdt.panel.render``
JavaScript event will be dispatched. The scripts can listen for
this event to support dynamic functionality.
"""
return []
# Panel early initialization
@classmethod
def ready(cls):
"""
Perform early initialization for the panel.
This should only include initialization or instrumentation that needs to
be done unconditionally for the panel regardless of whether it is
enabled for a particular request. It should be idempotent.
"""
pass
# URLs for panel-specific views
@classmethod
def get_urls(cls):
"""
Return URLpatterns, if the panel has its own views.
"""
return []
# Enable and disable (expensive) instrumentation, must be idempotent
def enable_instrumentation(self):
"""
Enable instrumentation to gather data for this panel.
This usually means monkey-patching (!) or registering signal
receivers. Any instrumentation with a non-negligible effect on
performance should be installed by this method rather than at import
time.
Unless the toolbar or this panel is disabled, this method will be
called early in ``DebugToolbarMiddleware``. It should be idempotent.
"""
def disable_instrumentation(self):
"""
Disable instrumentation to gather data for this panel.
This is the opposite of :meth:`enable_instrumentation`.
Unless the toolbar or this panel is disabled, this method will be
called late in the middleware. It should be idempotent.
"""
# Store and retrieve stats (shared between panels for no good reason)
def record_stats(self, stats):
"""
Store data gathered by the panel. ``stats`` is a :class:`dict`.
Each call to ``record_stats`` updates the statistics dictionary.
"""
self.toolbar.stats.setdefault(self.panel_id, {}).update(stats)
def get_stats(self):
"""
Access data stored by the panel. Returns a :class:`dict`.
"""
return self.toolbar.stats.get(self.panel_id, {})
def record_server_timing(self, key, title, value):
"""
Store data gathered by the panel. ``stats`` is a :class:`dict`.
Each call to ``record_stats`` updates the statistics dictionary.
"""
data = {key: {"title": title, "value": value}}
self.toolbar.server_timing_stats.setdefault(self.panel_id, {}).update(data)
def get_server_timing_stats(self):
"""
Access data stored by the panel. Returns a :class:`dict`.
"""
return self.toolbar.server_timing_stats.get(self.panel_id, {})
# Standard middleware methods
def process_request(self, request):
"""
Like __call__ in Django's middleware.
Write panel logic related to the request there. Save data with
:meth:`record_stats`.
Return the existing response or overwrite it.
"""
return self.get_response(request)
def get_headers(self, request):
"""
Get headers the panel needs to set.
Called after :meth:`process_request
<debug_toolbar.panels.Panel.generate_stats>` and
:meth:`process_request<debug_toolbar.panels.Panel.generate_stats>`
Header values will be appended if multiple panels need to set it.
By default it sets the Server-Timing header.
Return dict of headers to be appended.
"""
headers = {}
stats = self.get_server_timing_stats()
if stats:
headers["Server-Timing"] = ", ".join(
# example: `SQLPanel_sql_time;dur=0;desc="SQL 0 queries"`
'{}_{};dur={};desc="{}"'.format(
self.panel_id, key, record.get("value"), record.get("title")
)
for key, record in stats.items()
)
return headers
def generate_stats(self, request, response):
"""
Write panel logic related to the response there. Post-process data
gathered while the view executed. Save data with :meth:`record_stats`.
Called after :meth:`process_request
<debug_toolbar.panels.Panel.process_request>`.
Does not return a value.
"""
def generate_server_timing(self, request, response):
"""
Similar to :meth:`generate_stats
<debug_toolbar.panels.Panel.generate_stats>`,
Generate stats for Server Timing https://w3c.github.io/server-timing/
Does not return a value.
"""
@classmethod
def run_checks(cls):
"""
Check that the integration is configured correctly for the panel.
This will be called as a part of the Django checks system when the
application is being setup.
Return a list of :class:`django.core.checks.CheckMessage` instances.
"""
return []