aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py')
-rw-r--r--.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py
new file mode 100644
index 00000000..24527656
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/middleware.py
@@ -0,0 +1,187 @@
+"""
+Create spans from Django middleware invocations
+"""
+
+from functools import wraps
+
+from django import VERSION as DJANGO_VERSION
+
+import sentry_sdk
+from sentry_sdk.consts import OP
+from sentry_sdk.utils import (
+ ContextVar,
+ transaction_from_function,
+ capture_internal_exceptions,
+)
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from typing import Any
+ from typing import Callable
+ from typing import Optional
+ from typing import TypeVar
+
+ from sentry_sdk.tracing import Span
+
+ F = TypeVar("F", bound=Callable[..., Any])
+
+_import_string_should_wrap_middleware = ContextVar(
+ "import_string_should_wrap_middleware"
+)
+
+DJANGO_SUPPORTS_ASYNC_MIDDLEWARE = DJANGO_VERSION >= (3, 1)
+
+if not DJANGO_SUPPORTS_ASYNC_MIDDLEWARE:
+ _asgi_middleware_mixin_factory = lambda _: object
+else:
+ from .asgi import _asgi_middleware_mixin_factory
+
+
+def patch_django_middlewares():
+ # type: () -> None
+ from django.core.handlers import base
+
+ old_import_string = base.import_string
+
+ def sentry_patched_import_string(dotted_path):
+ # type: (str) -> Any
+ rv = old_import_string(dotted_path)
+
+ if _import_string_should_wrap_middleware.get(None):
+ rv = _wrap_middleware(rv, dotted_path)
+
+ return rv
+
+ base.import_string = sentry_patched_import_string
+
+ old_load_middleware = base.BaseHandler.load_middleware
+
+ def sentry_patched_load_middleware(*args, **kwargs):
+ # type: (Any, Any) -> Any
+ _import_string_should_wrap_middleware.set(True)
+ try:
+ return old_load_middleware(*args, **kwargs)
+ finally:
+ _import_string_should_wrap_middleware.set(False)
+
+ base.BaseHandler.load_middleware = sentry_patched_load_middleware
+
+
+def _wrap_middleware(middleware, middleware_name):
+ # type: (Any, str) -> Any
+ from sentry_sdk.integrations.django import DjangoIntegration
+
+ def _check_middleware_span(old_method):
+ # type: (Callable[..., Any]) -> Optional[Span]
+ integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
+ if integration is None or not integration.middleware_spans:
+ return None
+
+ function_name = transaction_from_function(old_method)
+
+ description = middleware_name
+ function_basename = getattr(old_method, "__name__", None)
+ if function_basename:
+ description = "{}.{}".format(description, function_basename)
+
+ middleware_span = sentry_sdk.start_span(
+ op=OP.MIDDLEWARE_DJANGO,
+ name=description,
+ origin=DjangoIntegration.origin,
+ )
+ middleware_span.set_tag("django.function_name", function_name)
+ middleware_span.set_tag("django.middleware_name", middleware_name)
+
+ return middleware_span
+
+ def _get_wrapped_method(old_method):
+ # type: (F) -> F
+ with capture_internal_exceptions():
+
+ def sentry_wrapped_method(*args, **kwargs):
+ # type: (*Any, **Any) -> Any
+ middleware_span = _check_middleware_span(old_method)
+
+ if middleware_span is None:
+ return old_method(*args, **kwargs)
+
+ with middleware_span:
+ return old_method(*args, **kwargs)
+
+ try:
+ # fails for __call__ of function on Python 2 (see py2.7-django-1.11)
+ sentry_wrapped_method = wraps(old_method)(sentry_wrapped_method)
+
+ # Necessary for Django 3.1
+ sentry_wrapped_method.__self__ = old_method.__self__ # type: ignore
+ except Exception:
+ pass
+
+ return sentry_wrapped_method # type: ignore
+
+ return old_method
+
+ class SentryWrappingMiddleware(
+ _asgi_middleware_mixin_factory(_check_middleware_span) # type: ignore
+ ):
+ sync_capable = getattr(middleware, "sync_capable", True)
+ async_capable = DJANGO_SUPPORTS_ASYNC_MIDDLEWARE and getattr(
+ middleware, "async_capable", False
+ )
+
+ def __init__(self, get_response=None, *args, **kwargs):
+ # type: (Optional[Callable[..., Any]], *Any, **Any) -> None
+ if get_response:
+ self._inner = middleware(get_response, *args, **kwargs)
+ else:
+ self._inner = middleware(*args, **kwargs)
+ self.get_response = get_response
+ self._call_method = None
+ if self.async_capable:
+ super().__init__(get_response)
+
+ # We need correct behavior for `hasattr()`, which we can only determine
+ # when we have an instance of the middleware we're wrapping.
+ def __getattr__(self, method_name):
+ # type: (str) -> Any
+ if method_name not in (
+ "process_request",
+ "process_view",
+ "process_template_response",
+ "process_response",
+ "process_exception",
+ ):
+ raise AttributeError()
+
+ old_method = getattr(self._inner, method_name)
+ rv = _get_wrapped_method(old_method)
+ self.__dict__[method_name] = rv
+ return rv
+
+ def __call__(self, *args, **kwargs):
+ # type: (*Any, **Any) -> Any
+ if hasattr(self, "async_route_check") and self.async_route_check():
+ return self.__acall__(*args, **kwargs)
+
+ f = self._call_method
+ if f is None:
+ self._call_method = f = self._inner.__call__
+
+ middleware_span = _check_middleware_span(old_method=f)
+
+ if middleware_span is None:
+ return f(*args, **kwargs)
+
+ with middleware_span:
+ return f(*args, **kwargs)
+
+ for attr in (
+ "__name__",
+ "__module__",
+ "__qualname__",
+ ):
+ if hasattr(middleware, attr):
+ setattr(SentryWrappingMiddleware, attr, getattr(middleware, attr))
+
+ return SentryWrappingMiddleware