Skip to content

Commit

Permalink
Fix for partial signals in old Django and old Python versions. (#1641)
Browse files Browse the repository at this point in the history
* Making sure signal names can be retrieved from partials and normal functions in all Python and Django versions. 
* Added test to safeguard the change.
  • Loading branch information
antonpirker authored Sep 28, 2022
1 parent 5348834 commit 77b583a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 13 deletions.
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
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)

0 comments on commit 77b583a

Please sign in to comment.