Skip to content

Commit b3ccf96

Browse files
Ensure RedisIntegration is disabled, unless redis is installed (#2504)
* Add test to ensure redis integration disabled unless installed * Integrations added to enabled list if actually installed * Move test to test_basics.py * Code review suggestions * Fixed test failures * Add unit test to check multiple `setup_integrations` calls * fix type hint for 2.7 * Added staticmethod * Move test to `test_basics`
1 parent 9bf6c13 commit b3ccf96

File tree

3 files changed

+71
-8
lines changed

3 files changed

+71
-8
lines changed

sentry_sdk/integrations/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717

1818
_installer_lock = Lock()
19+
20+
# Set of all integration identifiers we have attempted to install
21+
_processed_integrations = set() # type: Set[str]
22+
23+
# Set of all integration identifiers we have actually installed
1924
_installed_integrations = set() # type: Set[str]
2025

2126

@@ -121,7 +126,7 @@ def setup_integrations(
121126

122127
for identifier, integration in iteritems(integrations):
123128
with _installer_lock:
124-
if identifier not in _installed_integrations:
129+
if identifier not in _processed_integrations:
125130
logger.debug(
126131
"Setting up previously not enabled integration %s", identifier
127132
)
@@ -144,8 +149,16 @@ def setup_integrations(
144149
logger.debug(
145150
"Did not enable default integration %s: %s", identifier, e
146151
)
152+
else:
153+
_installed_integrations.add(identifier)
154+
155+
_processed_integrations.add(identifier)
147156

148-
_installed_integrations.add(identifier)
157+
integrations = {
158+
identifier: integration
159+
for identifier, integration in iteritems(integrations)
160+
if identifier in _installed_integrations
161+
}
149162

150163
for identifier in integrations:
151164
logger.debug("Enabling integration %s", identifier)

tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import sentry_sdk
3131
from sentry_sdk._compat import iteritems, reraise, string_types
3232
from sentry_sdk.envelope import Envelope
33-
from sentry_sdk.integrations import _installed_integrations # noqa: F401
33+
from sentry_sdk.integrations import _processed_integrations # noqa: F401
3434
from sentry_sdk.profiler import teardown_profiler
3535
from sentry_sdk.transport import Transport
3636
from sentry_sdk.utils import capture_internal_exceptions
@@ -187,8 +187,8 @@ def reset_integrations():
187187
with a clean slate to ensure monkeypatching works well,
188188
but this also means some other stuff will be monkeypatched twice.
189189
"""
190-
global _installed_integrations
191-
_installed_integrations.clear()
190+
global _processed_integrations
191+
_processed_integrations.clear()
192192

193193

194194
@pytest.fixture

tests/test_basics.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
Hub,
1919
)
2020
from sentry_sdk._compat import reraise
21-
from sentry_sdk.integrations import _AUTO_ENABLING_INTEGRATIONS
21+
from sentry_sdk.integrations import (
22+
_AUTO_ENABLING_INTEGRATIONS,
23+
Integration,
24+
setup_integrations,
25+
)
2226
from sentry_sdk.integrations.logging import LoggingIntegration
27+
from sentry_sdk.integrations.redis import RedisIntegration
2328
from sentry_sdk.scope import ( # noqa: F401
2429
add_global_event_processor,
2530
global_event_processors,
@@ -28,6 +33,36 @@
2833
from sentry_sdk.tracing_utils import has_tracing_enabled
2934

3035

36+
def _redis_installed(): # type: () -> bool
37+
"""
38+
Determines whether Redis is installed.
39+
"""
40+
try:
41+
import redis # noqa: F401
42+
except ImportError:
43+
return False
44+
45+
return True
46+
47+
48+
class NoOpIntegration(Integration):
49+
"""
50+
A simple no-op integration for testing purposes.
51+
"""
52+
53+
identifier = "noop"
54+
55+
@staticmethod
56+
def setup_once(): # type: () -> None
57+
pass
58+
59+
def __eq__(self, __value): # type: (object) -> bool
60+
"""
61+
All instances of NoOpIntegration should be considered equal to each other.
62+
"""
63+
return type(__value) == type(self)
64+
65+
3166
def test_processors(sentry_init, capture_events):
3267
sentry_init()
3368
events = capture_events()
@@ -59,8 +94,8 @@ def test_auto_enabling_integrations_catches_import_error(sentry_init, caplog):
5994
sentry_init(auto_enabling_integrations=True, debug=True)
6095

6196
for import_string in _AUTO_ENABLING_INTEGRATIONS:
62-
# Ignore redis in the test case, because it is installed as a
63-
# dependency for running tests, and therefore always enabled.
97+
# Ignore redis in the test case, because it does not raise a DidNotEnable
98+
# exception on import; rather, it raises the exception upon enabling.
6499
if _AUTO_ENABLING_INTEGRATIONS[redis_index] == import_string:
65100
continue
66101

@@ -686,3 +721,18 @@ def test_functions_to_trace_with_class(sentry_init, capture_events):
686721
assert len(event["spans"]) == 2
687722
assert event["spans"][0]["description"] == "tests.test_basics.WorldGreeter.greet"
688723
assert event["spans"][1]["description"] == "tests.test_basics.WorldGreeter.greet"
724+
725+
726+
@pytest.mark.skipif(_redis_installed(), reason="skipping because redis is installed")
727+
def test_redis_disabled_when_not_installed(sentry_init):
728+
sentry_init()
729+
730+
assert Hub.current.get_integration(RedisIntegration) is None
731+
732+
733+
def test_multiple_setup_integrations_calls():
734+
first_call_return = setup_integrations([NoOpIntegration()], with_defaults=False)
735+
assert first_call_return == {NoOpIntegration.identifier: NoOpIntegration()}
736+
737+
second_call_return = setup_integrations([NoOpIntegration()], with_defaults=False)
738+
assert second_call_return == {NoOpIntegration.identifier: NoOpIntegration()}

0 commit comments

Comments
 (0)