Skip to content

Commit 4d10edf

Browse files
authored
Make sure get_dsn_parameters is an actual function (#2441)
Some non-standard DB backends have their own `__getattr__`, which renders our check for attributes useless.
1 parent d8634d0 commit 4d10edf

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

sentry_sdk/integrations/django/__init__.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import
33

4+
import inspect
45
import sys
56
import threading
67
import weakref
@@ -665,12 +666,21 @@ def _set_db_data(span, cursor_or_db):
665666
vendor = db.vendor
666667
span.set_data(SPANDATA.DB_SYSTEM, vendor)
667668

668-
connection_params = (
669-
cursor_or_db.connection.get_dsn_parameters()
670-
if hasattr(cursor_or_db, "connection")
669+
if (
670+
hasattr(cursor_or_db, "connection")
671671
and hasattr(cursor_or_db.connection, "get_dsn_parameters")
672-
else db.get_connection_params()
673-
)
672+
and inspect.isfunction(cursor_or_db.connection.get_dsn_parameters)
673+
):
674+
# Some custom backends override `__getattr__`, making it look like `cursor_or_db`
675+
# actually has a `connection` and the `connection` has a `get_dsn_parameters`
676+
# attribute, only to throw an error once you actually want to call it.
677+
# Hence the `inspect` check whether `get_dsn_parameters` is an actual callable
678+
# function.
679+
connection_params = cursor_or_db.connection.get_dsn_parameters()
680+
681+
else:
682+
connection_params = db.get_connection_params()
683+
674684
db_name = connection_params.get("dbname") or connection_params.get("database")
675685
if db_name is not None:
676686
span.set_data(SPANDATA.DB_NAME, db_name)

tests/integrations/django/test_basic.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
from sentry_sdk._compat import PY2, PY310
2323
from sentry_sdk import capture_message, capture_exception, configure_scope
2424
from sentry_sdk.consts import SPANDATA
25-
from sentry_sdk.integrations.django import DjangoIntegration
25+
from sentry_sdk.integrations.django import DjangoIntegration, _set_db_data
2626
from sentry_sdk.integrations.django.signals_handlers import _get_receiver_name
2727
from sentry_sdk.integrations.django.caching import _get_span_description
2828
from sentry_sdk.integrations.executing import ExecutingIntegration
29+
from sentry_sdk.tracing import Span
2930
from tests.integrations.django.myapp.wsgi import application
3031
from tests.integrations.django.utils import pytest_mark_django_db_decorator
3132

@@ -656,6 +657,24 @@ def test_db_connection_span_data(sentry_init, client, capture_events):
656657
assert data.get(SPANDATA.SERVER_PORT) == "5432"
657658

658659

660+
def test_set_db_data_custom_backend():
661+
class DummyBackend(object):
662+
# https://github.com/mongodb/mongo-python-driver/blob/6ffae5522c960252b8c9adfe2a19b29ff28187cb/pymongo/collection.py#L126
663+
def __getattr__(self, attr):
664+
return self
665+
666+
def __call__(self):
667+
raise TypeError
668+
669+
def get_connection_params(self):
670+
return {}
671+
672+
try:
673+
_set_db_data(Span(), DummyBackend())
674+
except TypeError:
675+
pytest.fail("A TypeError was raised")
676+
677+
659678
@pytest.mark.parametrize(
660679
"transaction_style,client_url,expected_transaction,expected_source,expected_response",
661680
[

0 commit comments

Comments
 (0)