Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for partial signals in old Django and old Python versions. #1641

Merged
merged 10 commits into from
Sep 28, 2022
32 changes: 23 additions & 9 deletions sentry_sdk/integrations/django/signals_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,32 @@
from typing import List


def _get_receiver_name(receiver):
# type: (Callable[..., Any]) -> str
name = ""

if hasattr(receiver, "__qualname__"):
name += receiver.__qualname__
elif hasattr(receiver, "__name__"): # Python 2.7 has no __qualname__
name += receiver.__name__

if (
name == ""
): # certain functions (like partials) dont have a name so return the string representation
return str(receiver)

if hasattr(receiver, "__module__"): # prepend with module, if there is one
antonpirker marked this conversation as resolved.
Show resolved Hide resolved
name = receiver.__module__ + "." + name

return name


def patch_signals():
# type: () -> None
"""Patch django signal receivers to create a span"""

old_live_receivers = Signal._live_receivers

def _get_receiver_name(receiver):
# type: (Callable[..., Any]) -> str
name = receiver.__module__ + "."
if hasattr(receiver, "__name__"):
return name + receiver.__name__
return name + str(receiver)

def _sentry_live_receivers(self, sender):
# type: (Signal, Any) -> List[Callable[..., Any]]
hub = Hub.current
Expand All @@ -35,11 +48,12 @@ def sentry_receiver_wrapper(receiver):
# type: (Callable[..., Any]) -> Callable[..., Any]
def wrapper(*args, **kwargs):
# type: (Any, Any) -> Any
signal_name = _get_receiver_name(receiver)
with hub.start_span(
op="django.signals",
description=_get_receiver_name(receiver),
description=signal_name,
) as span:
span.set_data("signal", _get_receiver_name(receiver))
span.set_data("signal", signal_name)
return receiver(*args, **kwargs)

return wrapper
Expand Down
28 changes: 24 additions & 4 deletions tests/integrations/django/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
from __future__ import absolute_import

import json
import pytest
import pytest_django
import json
from functools import partial

from werkzeug.test import Client
from django import VERSION as DJANGO_VERSION
from django.contrib.auth.models import User
from django.core.management import execute_from_command_line
from django.db.utils import OperationalError, ProgrammingError, DataError

from sentry_sdk.integrations.executing import ExecutingIntegration

try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse

from sentry_sdk._compat import PY2
from sentry_sdk import capture_message, capture_exception, configure_scope
from sentry_sdk.integrations.django import DjangoIntegration
from functools import partial
from sentry_sdk.integrations.django.signals_handlers import _get_receiver_name
from sentry_sdk.integrations.executing import ExecutingIntegration

from tests.integrations.django.myapp.wsgi import application

Expand Down Expand Up @@ -816,3 +817,22 @@ def test_custom_urlconf_middleware(
assert "custom_urlconf_middleware" in render_span_tree(transaction_event)

settings.MIDDLEWARE.pop(0)


def test_get_receiver_name():
def dummy(a, b):
return a + b

name = _get_receiver_name(dummy)

if PY2:
assert name == "tests.integrations.django.test_basic.dummy"
else:
assert (
name
== "tests.integrations.django.test_basic.test_get_receiver_name.<locals>.dummy"
)

a_partial = partial(dummy)
name = _get_receiver_name(a_partial)
assert name == str(a_partial)