Skip to content

Commit

Permalink
feat(integrations): Add disabled_integrations (#3328)
Browse files Browse the repository at this point in the history
Add a new init option called disabled_integrations, which is a sequence of integrations that will not be enabled regardless of what auto_enabling_integrations and default_integrations is set to.
  • Loading branch information
sentrivana authored Jul 23, 2024
1 parent c81c175 commit 357d6f5
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 16 deletions.
2 changes: 1 addition & 1 deletion sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 31 additions & 14 deletions sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -114,21 +116,33 @@ 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 ()
)

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()

Expand All @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -182,6 +183,7 @@ def reset_integrations():
except ValueError:
pass
_processed_integrations.clear()
_installed_integrations.clear()


@pytest.fixture
Expand Down
50 changes: 49 additions & 1 deletion tests/test_basics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import importlib
import logging
import os
import sys
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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"
)
Expand Down

0 comments on commit 357d6f5

Please sign in to comment.