about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py')
-rw-r--r--.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py188
1 files changed, 188 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py
new file mode 100644
index 00000000..10e8a924
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sentry_sdk/integrations/django/templates.py
@@ -0,0 +1,188 @@
+import functools
+
+from django.template import TemplateSyntaxError
+from django.utils.safestring import mark_safe
+from django import VERSION as DJANGO_VERSION
+
+import sentry_sdk
+from sentry_sdk.consts import OP
+from sentry_sdk.utils import ensure_integration_enabled
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from typing import Any
+    from typing import Dict
+    from typing import Optional
+    from typing import Iterator
+    from typing import Tuple
+
+try:
+    # support Django 1.9
+    from django.template.base import Origin
+except ImportError:
+    # backward compatibility
+    from django.template.loader import LoaderOrigin as Origin
+
+
+def get_template_frame_from_exception(exc_value):
+    # type: (Optional[BaseException]) -> Optional[Dict[str, Any]]
+
+    # As of Django 1.9 or so the new template debug thing showed up.
+    if hasattr(exc_value, "template_debug"):
+        return _get_template_frame_from_debug(exc_value.template_debug)  # type: ignore
+
+    # As of r16833 (Django) all exceptions may contain a
+    # ``django_template_source`` attribute (rather than the legacy
+    # ``TemplateSyntaxError.source`` check)
+    if hasattr(exc_value, "django_template_source"):
+        return _get_template_frame_from_source(
+            exc_value.django_template_source  # type: ignore
+        )
+
+    if isinstance(exc_value, TemplateSyntaxError) and hasattr(exc_value, "source"):
+        source = exc_value.source
+        if isinstance(source, (tuple, list)) and isinstance(source[0], Origin):
+            return _get_template_frame_from_source(source)  # type: ignore
+
+    return None
+
+
+def _get_template_name_description(template_name):
+    # type: (str) -> str
+    if isinstance(template_name, (list, tuple)):
+        if template_name:
+            return "[{}, ...]".format(template_name[0])
+    else:
+        return template_name
+
+
+def patch_templates():
+    # type: () -> None
+    from django.template.response import SimpleTemplateResponse
+    from sentry_sdk.integrations.django import DjangoIntegration
+
+    real_rendered_content = SimpleTemplateResponse.rendered_content
+
+    @property  # type: ignore
+    @ensure_integration_enabled(DjangoIntegration, real_rendered_content.fget)
+    def rendered_content(self):
+        # type: (SimpleTemplateResponse) -> str
+        with sentry_sdk.start_span(
+            op=OP.TEMPLATE_RENDER,
+            name=_get_template_name_description(self.template_name),
+            origin=DjangoIntegration.origin,
+        ) as span:
+            span.set_data("context", self.context_data)
+            return real_rendered_content.fget(self)
+
+    SimpleTemplateResponse.rendered_content = rendered_content
+
+    if DJANGO_VERSION < (1, 7):
+        return
+    import django.shortcuts
+
+    real_render = django.shortcuts.render
+
+    @functools.wraps(real_render)
+    @ensure_integration_enabled(DjangoIntegration, real_render)
+    def render(request, template_name, context=None, *args, **kwargs):
+        # type: (django.http.HttpRequest, str, Optional[Dict[str, Any]], *Any, **Any) -> django.http.HttpResponse
+
+        # Inject trace meta tags into template context
+        context = context or {}
+        if "sentry_trace_meta" not in context:
+            context["sentry_trace_meta"] = mark_safe(
+                sentry_sdk.get_current_scope().trace_propagation_meta()
+            )
+
+        with sentry_sdk.start_span(
+            op=OP.TEMPLATE_RENDER,
+            name=_get_template_name_description(template_name),
+            origin=DjangoIntegration.origin,
+        ) as span:
+            span.set_data("context", context)
+            return real_render(request, template_name, context, *args, **kwargs)
+
+    django.shortcuts.render = render
+
+
+def _get_template_frame_from_debug(debug):
+    # type: (Dict[str, Any]) -> Dict[str, Any]
+    if debug is None:
+        return None
+
+    lineno = debug["line"]
+    filename = debug["name"]
+    if filename is None:
+        filename = "<django template>"
+
+    pre_context = []
+    post_context = []
+    context_line = None
+
+    for i, line in debug["source_lines"]:
+        if i < lineno:
+            pre_context.append(line)
+        elif i > lineno:
+            post_context.append(line)
+        else:
+            context_line = line
+
+    return {
+        "filename": filename,
+        "lineno": lineno,
+        "pre_context": pre_context[-5:],
+        "post_context": post_context[:5],
+        "context_line": context_line,
+        "in_app": True,
+    }
+
+
+def _linebreak_iter(template_source):
+    # type: (str) -> Iterator[int]
+    yield 0
+    p = template_source.find("\n")
+    while p >= 0:
+        yield p + 1
+        p = template_source.find("\n", p + 1)
+
+
+def _get_template_frame_from_source(source):
+    # type: (Tuple[Origin, Tuple[int, int]]) -> Optional[Dict[str, Any]]
+    if not source:
+        return None
+
+    origin, (start, end) = source
+    filename = getattr(origin, "loadname", None)
+    if filename is None:
+        filename = "<django template>"
+    template_source = origin.reload()
+    lineno = None
+    upto = 0
+    pre_context = []
+    post_context = []
+    context_line = None
+
+    for num, next in enumerate(_linebreak_iter(template_source)):
+        line = template_source[upto:next]
+        if start >= upto and end <= next:
+            lineno = num
+            context_line = line
+        elif lineno is None:
+            pre_context.append(line)
+        else:
+            post_context.append(line)
+
+        upto = next
+
+    if context_line is None or lineno is None:
+        return None
+
+    return {
+        "filename": filename,
+        "lineno": lineno,
+        "pre_context": pre_context[-5:],
+        "post_context": post_context[:5],
+        "context_line": context_line,
+    }