diff --git a/debug_toolbar/_stubs.py b/debug_toolbar/_stubs.py index 01d0b8637..ca69ecedd 100644 --- a/debug_toolbar/_stubs.py +++ b/debug_toolbar/_stubs.py @@ -1,6 +1,7 @@ -from typing import Any, List, NamedTuple, Optional, Tuple +from typing import Any, List, NamedTuple, Optional, Protocol, Tuple from django import template as dj_template +from django.http import HttpRequest, HttpResponse class InspectStack(NamedTuple): @@ -22,3 +23,8 @@ class RenderContext(dj_template.context.RenderContext): class RequestContext(dj_template.RequestContext): template: dj_template.Template render_context: RenderContext + + +class GetResponse(Protocol): + def __call__(self, request: HttpRequest) -> HttpResponse: + ... diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index b5e5d0827..8c6c8a491 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -6,16 +6,19 @@ from functools import lru_cache from django.conf import settings +from django.http import HttpRequest from django.utils.module_loading import import_string from debug_toolbar import settings as dt_settings from debug_toolbar.toolbar import DebugToolbar from debug_toolbar.utils import clear_stack_trace_caches +from ._stubs import GetResponse + _HTML_TYPES = ("text/html", "application/xhtml+xml") -def show_toolbar(request): +def show_toolbar(request: HttpRequest): """ Default function to determine whether to show the toolbar on a given page. """ @@ -39,7 +42,7 @@ class DebugToolbarMiddleware: on outgoing response. """ - def __init__(self, get_response): + def __init__(self, get_response: GetResponse): self.get_response = get_response def __call__(self, request): diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py index 57f385a5e..8f38c08f3 100644 --- a/debug_toolbar/panels/__init__.py +++ b/debug_toolbar/panels/__init__.py @@ -1,6 +1,7 @@ from django.template.loader import render_to_string from debug_toolbar import settings as dt_settings +from debug_toolbar._stubs import GetResponse from debug_toolbar.utils import get_name_from_obj @@ -9,7 +10,7 @@ class Panel: Base class for panels. """ - def __init__(self, toolbar, get_response): + def __init__(self, toolbar, get_response: GetResponse): self.toolbar = toolbar self.get_response = get_response diff --git a/debug_toolbar/toolbar.py b/debug_toolbar/toolbar.py index 11f8a1daa..df6ee0ad3 100644 --- a/debug_toolbar/toolbar.py +++ b/debug_toolbar/toolbar.py @@ -5,25 +5,32 @@ import uuid from collections import OrderedDict from functools import lru_cache +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type from django.apps import apps from django.core.exceptions import ImproperlyConfigured from django.dispatch import Signal +from django.http import HttpRequest from django.template import TemplateSyntaxError from django.template.loader import render_to_string -from django.urls import path, resolve +from django.urls import URLPattern, path, resolve from django.urls.exceptions import Resolver404 from django.utils.module_loading import import_string from django.utils.translation import get_language, override as lang_override from debug_toolbar import APP_NAME, settings as dt_settings +from ._stubs import GetResponse + +if TYPE_CHECKING: + from .panels import Panel + class DebugToolbar: # for internal testing use only _created = Signal() - def __init__(self, request, get_response): + def __init__(self, request: HttpRequest, get_response: GetResponse): self.request = request self.config = dt_settings.get_config().copy() panels = [] @@ -36,32 +43,32 @@ def __init__(self, request, get_response): # Use OrderedDict for the _panels attribute so that items can be efficiently # removed using FIFO order in the DebugToolbar.store() method. The .popitem() # method of Python's built-in dict only supports LIFO removal. - self._panels = OrderedDict() + self._panels = OrderedDict() # type: ignore[var-annotated] while panels: panel = panels.pop() self._panels[panel.panel_id] = panel - self.stats = {} - self.server_timing_stats = {} - self.store_id = None + self.stats: Dict[str, Any] = {} + self.server_timing_stats: Dict[str, Any] = {} + self.store_id: Optional[str] = None self._created.send(request, toolbar=self) # Manage panels @property - def panels(self): + def panels(self) -> List["Panel"]: """ Get a list of all available panels. """ return list(self._panels.values()) @property - def enabled_panels(self): + def enabled_panels(self) -> List["Panel"]: """ Get a list of panels enabled for the current request. """ return [panel for panel in self._panels.values() if panel.enabled] - def get_panel_by_id(self, panel_id): + def get_panel_by_id(self, panel_id: str) -> "Panel": """ Get the panel with the given id, which is the class name by default. """ @@ -69,7 +76,7 @@ def get_panel_by_id(self, panel_id): # Handle rendering the toolbar in HTML - def render_toolbar(self): + def render_toolbar(self) -> str: """ Renders the overall Toolbar with panels inside. """ @@ -90,7 +97,7 @@ def render_toolbar(self): else: raise - def should_render_panels(self): + def should_render_panels(self) -> bool: """Determine whether the panels should be rendered during the request If False, the panels will be loaded via Ajax. @@ -106,7 +113,7 @@ def should_render_panels(self): # Handle storing toolbars in memory and fetching them later on - _store = OrderedDict() + _store = OrderedDict() # type: ignore[var-annotated] def store(self): # Store already exists. @@ -124,7 +131,7 @@ def fetch(cls, store_id): # Manually implement class-level caching of panel classes and url patterns # because it's more obvious than going through an abstraction. - _panel_classes = None + _panel_classes: Optional[List[Type["Panel"]]] = None @classmethod def get_panel_classes(cls): @@ -136,10 +143,10 @@ def get_panel_classes(cls): cls._panel_classes = panel_classes return cls._panel_classes - _urlpatterns = None + _urlpatterns: Optional[List[URLPattern]] = None @classmethod - def get_urls(cls): + def get_urls(cls) -> List[URLPattern]: if cls._urlpatterns is None: from . import views @@ -155,7 +162,7 @@ def get_urls(cls): return cls._urlpatterns @classmethod - def is_toolbar_request(cls, request): + def is_toolbar_request(cls, request) -> bool: """ Determine if the request is for a DebugToolbar view. """ @@ -167,7 +174,10 @@ def is_toolbar_request(cls, request): ) except Resolver404: return False - return resolver_match.namespaces and resolver_match.namespaces[-1] == APP_NAME + return ( + bool(resolver_match.namespaces) + and resolver_match.namespaces[-1] == APP_NAME + ) @staticmethod @lru_cache(maxsize=None) @@ -181,7 +191,7 @@ def get_observe_request(): return func_or_path -def observe_request(request): +def observe_request(request: HttpRequest): """ Determine whether to update the toolbar from a client side request. """