diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index f93aa935c2..1b5d8b7696 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -271,7 +271,6 @@ def _setup_instrumentation(self, functions_to_trace): function_obj = getattr(module_obj, function_name) setattr(module_obj, function_name, trace(function_obj)) logger.debug("Enabled tracing for %s", function_qualname) - except module_not_found_error: try: # Try to import a class @@ -372,6 +371,7 @@ def _capture_envelope(envelope): with_auto_enabling_integrations=self.options[ "auto_enabling_integrations" ], + disabled_integrations=self.options["disabled_integrations"], ) self.spotlight = None diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index b4d30cd24a..d09802bdd6 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -514,6 +514,7 @@ def __init__( profiles_sampler=None, # type: Optional[TracesSampler] profiler_mode=None, # type: Optional[ProfilerMode] auto_enabling_integrations=True, # type: bool + disabled_integrations=None, # type: Optional[Sequence[Integration]] auto_session_tracking=True, # type: bool send_client_reports=True, # type: bool _experiments={}, # type: Experiments # noqa: B006 diff --git a/sentry_sdk/integrations/__init__.py b/sentry_sdk/integrations/__init__.py index 9e3b11f318..3c43ed5472 100644 --- a/sentry_sdk/integrations/__init__.py +++ b/sentry_sdk/integrations/__init__.py @@ -6,10 +6,12 @@ if TYPE_CHECKING: + from collections.abc import Sequence from typing import Callable from typing import Dict from typing import Iterator from typing import List + from typing import Optional from typing import Set from typing import Type @@ -114,14 +116,20 @@ def iter_default_integrations(with_auto_enabling_integrations): def setup_integrations( - integrations, with_defaults=True, with_auto_enabling_integrations=False + integrations, + with_defaults=True, + with_auto_enabling_integrations=False, + disabled_integrations=None, ): - # type: (List[Integration], bool, bool) -> Dict[str, Integration] + # type: (Sequence[Integration], bool, bool, Optional[Sequence[Integration]]) -> Dict[str, Integration] """ Given a list of integration instances, this installs them all. When `with_defaults` is set to `True` all default integrations are added unless they were already provided before. + + `disabled_integrations` takes precedence over `with_defaults` and + `with_auto_enabling_integrations`. """ integrations = dict( (integration.identifier, integration) for integration in integrations or () @@ -129,6 +137,12 @@ def setup_integrations( logger.debug("Setting up integrations (with default = %s)", with_defaults) + # Integrations that will not be enabled + disabled_integrations = [ + integration if isinstance(integration, type) else type(integration) + for integration in disabled_integrations or [] + ] + # Integrations that are not explicitly set up by the user. used_as_default_integration = set() @@ -144,20 +158,23 @@ def setup_integrations( for identifier, integration in integrations.items(): with _installer_lock: if identifier not in _processed_integrations: - logger.debug( - "Setting up previously not enabled integration %s", identifier - ) - try: - type(integration).setup_once() - except DidNotEnable as e: - if identifier not in used_as_default_integration: - raise - + if type(integration) in disabled_integrations: + logger.debug("Ignoring integration %s", identifier) + else: logger.debug( - "Did not enable default integration %s: %s", identifier, e + "Setting up previously not enabled integration %s", identifier ) - else: - _installed_integrations.add(identifier) + try: + type(integration).setup_once() + except DidNotEnable as e: + if identifier not in used_as_default_integration: + raise + + logger.debug( + "Did not enable default integration %s: %s", identifier, e + ) + else: + _installed_integrations.add(identifier) _processed_integrations.add(identifier) diff --git a/tests/conftest.py b/tests/conftest.py index 52e0c75c5c..3c5e444f6a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ from sentry_sdk.envelope import Envelope from sentry_sdk.integrations import ( # noqa: F401 _DEFAULT_INTEGRATIONS, + _installed_integrations, _processed_integrations, ) from sentry_sdk.profiler import teardown_profiler @@ -182,6 +183,7 @@ def reset_integrations(): except ValueError: pass _processed_integrations.clear() + _installed_integrations.clear() @pytest.fixture diff --git a/tests/test_basics.py b/tests/test_basics.py index 2c31cfa3ae..3a801c5785 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1,4 +1,5 @@ import datetime +import importlib import logging import os import sys @@ -7,12 +8,12 @@ import pytest from sentry_sdk.client import Client - from tests.conftest import patch_start_tracing_child import sentry_sdk import sentry_sdk.scope from sentry_sdk import ( + get_client, push_scope, configure_scope, capture_event, @@ -27,11 +28,13 @@ ) from sentry_sdk.integrations import ( _AUTO_ENABLING_INTEGRATIONS, + _DEFAULT_INTEGRATIONS, Integration, setup_integrations, ) from sentry_sdk.integrations.logging import LoggingIntegration from sentry_sdk.integrations.redis import RedisIntegration +from sentry_sdk.integrations.stdlib import StdlibIntegration from sentry_sdk.scope import add_global_event_processor from sentry_sdk.utils import get_sdk_name, reraise from sentry_sdk.tracing_utils import has_tracing_enabled @@ -473,6 +476,51 @@ def test_integration_scoping(sentry_init, capture_events): assert not events +default_integrations = [ + getattr( + importlib.import_module(integration.rsplit(".", 1)[0]), + integration.rsplit(".", 1)[1], + ) + for integration in _DEFAULT_INTEGRATIONS +] + + +@pytest.mark.forked +@pytest.mark.parametrize( + "provided_integrations,default_integrations,disabled_integrations,expected_integrations", + [ + ([], False, None, set()), + ([], False, [], set()), + ([LoggingIntegration()], False, None, {LoggingIntegration}), + ([], True, None, set(default_integrations)), + ( + [], + True, + [LoggingIntegration(), StdlibIntegration], + set(default_integrations) - {LoggingIntegration, StdlibIntegration}, + ), + ], +) +def test_integrations( + sentry_init, + provided_integrations, + default_integrations, + disabled_integrations, + expected_integrations, + reset_integrations, +): + sentry_init( + integrations=provided_integrations, + default_integrations=default_integrations, + disabled_integrations=disabled_integrations, + auto_enabling_integrations=False, + debug=True, + ) + assert { + type(integration) for integration in get_client().integrations.values() + } == expected_integrations + + @pytest.mark.skip( reason="This test is not valid anymore, because with the new Scopes calling bind_client on the Hub sets the client on the global scope. This test should be removed once the Hub is removed" )