From 527dd60380283d8039a367abae07c5ef511e940a Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 18 Feb 2026 16:14:21 +0100 Subject: [PATCH 1/5] test: Assert setup_once() only raises DidNotEnable in integrations with shadowed dependencies --- tests/test_shadowed_module.py | 39 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/test_shadowed_module.py b/tests/test_shadowed_module.py index 10d86f285b..c84bb55312 100644 --- a/tests/test_shadowed_module.py +++ b/tests/test_shadowed_module.py @@ -22,7 +22,7 @@ def pytest_generate_tests(metafunc): submodule_names = { submodule_name for _, submodule_name, _ in pkgutil.walk_packages(integrations.__path__) - } + } - {"spark", "beam"} metafunc.parametrize( "integration_submodule_name", @@ -80,17 +80,19 @@ def test_shadowed_modules_when_importing_integrations( be imported in the environment in which the tests run. """ module_path = f"sentry_sdk.integrations.{integration_submodule_name}" + + spec = importlib.util.find_spec(module_path) + source = pathlib.Path(spec.origin).read_text(encoding="utf-8") + tree = ast.parse(source, filename=spec.origin) + integration_dependencies = find_unrecognized_dependencies(tree) + + mod = None try: # If importing the integration succeeds in the current environment, assume # that the integration has no non-standard imports. - importlib.import_module(module_path) - return - except integrations.DidNotEnable: - spec = importlib.util.find_spec(module_path) - source = pathlib.Path(spec.origin).read_text(encoding="utf-8") - tree = ast.parse(source, filename=spec.origin) - integration_dependencies = find_unrecognized_dependencies(tree) + mod = importlib.import_module(module_path) + except integrations.DidNotEnable: # For each non-standard import, create an empty shadow module to # emulate an empty "agents.py" or analogous local module that # shadows the package. @@ -105,3 +107,24 @@ def test_shadowed_modules_when_importing_integrations( for dependency in integration_dependencies: del sys.modules[dependency] + + # `setup_once()` can also raise when initializing the SDK with a shadowed module. + if mod is not None: + # For each non-standard import, create an empty shadow module to + # emulate an empty "agents.py" or analogous local module that + # shadows the package. + for dependency in integration_dependencies: + sys.modules[dependency] = types.ModuleType(dependency) + + integration_types = [ + v + for v in mod.__dict__.values() + if isinstance(v, type) and issubclass(v, Integration) + ] + + for integration in integration_types: + # The `setup_once()` method should only raise DidNotEnable. + try: + integration.setup_once() + except integrations.DidNotEnable: + pass From dcecb3d88680804b613417acd279d04db220c7cd Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 18 Feb 2026 16:26:53 +0100 Subject: [PATCH 2/5] test cleanup --- tests/test_shadowed_module.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_shadowed_module.py b/tests/test_shadowed_module.py index c84bb55312..d5edda4020 100644 --- a/tests/test_shadowed_module.py +++ b/tests/test_shadowed_module.py @@ -128,3 +128,6 @@ def test_shadowed_modules_when_importing_integrations( integration.setup_once() except integrations.DidNotEnable: pass + + for dependency in integration_dependencies: + del sys.modules[dependency] From e995101097235cb969a4dbca4f409c2f8587ba6f Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 18 Feb 2026 16:36:42 +0100 Subject: [PATCH 3/5] update ignore list --- tests/test_shadowed_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shadowed_module.py b/tests/test_shadowed_module.py index d5edda4020..f0a4960ff4 100644 --- a/tests/test_shadowed_module.py +++ b/tests/test_shadowed_module.py @@ -22,7 +22,7 @@ def pytest_generate_tests(metafunc): submodule_names = { submodule_name for _, submodule_name, _ in pkgutil.walk_packages(integrations.__path__) - } - {"spark", "beam"} + } - {"spark", "beam", "unraisablehook"} metafunc.parametrize( "integration_submodule_name", From de31a873a4d3f74d01f5815185d8cb84a8721ae4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 18 Feb 2026 17:51:41 +0100 Subject: [PATCH 4/5] read all integration files --- tests/test_shadowed_module.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test_shadowed_module.py b/tests/test_shadowed_module.py index f0a4960ff4..1141e6469f 100644 --- a/tests/test_shadowed_module.py +++ b/tests/test_shadowed_module.py @@ -22,7 +22,7 @@ def pytest_generate_tests(metafunc): submodule_names = { submodule_name for _, submodule_name, _ in pkgutil.walk_packages(integrations.__path__) - } - {"spark", "beam", "unraisablehook"} + } - {"beam", "spark", "unraisablehook"} metafunc.parametrize( "integration_submodule_name", @@ -79,18 +79,22 @@ def test_shadowed_modules_when_importing_integrations( An integration is determined to be for a third-party module if it cannot be imported in the environment in which the tests run. """ - module_path = f"sentry_sdk.integrations.{integration_submodule_name}" + module_path = ( + pathlib.Path("sentry_sdk") / "integrations" / integration_submodule_name + ) + import_path = ".".join(module_path.with_suffix("").parts) - spec = importlib.util.find_spec(module_path) - source = pathlib.Path(spec.origin).read_text(encoding="utf-8") - tree = ast.parse(source, filename=spec.origin) - integration_dependencies = find_unrecognized_dependencies(tree) + integration_dependencies = set() + for py_file in pathlib.Path(module_path).rglob("*.py"): + source = py_file.read_text(encoding="utf-8") + tree = ast.parse(source, filename=str(py_file)) + integration_dependencies.update(find_unrecognized_dependencies(tree)) mod = None try: # If importing the integration succeeds in the current environment, assume # that the integration has no non-standard imports. - mod = importlib.import_module(module_path) + mod = importlib.import_module(import_path) except integrations.DidNotEnable: # For each non-standard import, create an empty shadow module to @@ -103,7 +107,7 @@ def test_shadowed_modules_when_importing_integrations( # SDK catches the exception type when attempting to activate # auto-enabling integrations. with pytest.raises(integrations.DidNotEnable): - importlib.import_module(module_path) + importlib.import_module(import_path) for dependency in integration_dependencies: del sys.modules[dependency] From bd4c4ea2ebd4d1de977ed35fcb28baa8931c7ae5 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 18 Feb 2026 22:47:11 +0100 Subject: [PATCH 5/5] . --- tests/test_shadowed_module.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_shadowed_module.py b/tests/test_shadowed_module.py index 1141e6469f..dec2868257 100644 --- a/tests/test_shadowed_module.py +++ b/tests/test_shadowed_module.py @@ -79,10 +79,9 @@ def test_shadowed_modules_when_importing_integrations( An integration is determined to be for a third-party module if it cannot be imported in the environment in which the tests run. """ - module_path = ( - pathlib.Path("sentry_sdk") / "integrations" / integration_submodule_name - ) - import_path = ".".join(module_path.with_suffix("").parts) + parts = integration_submodule_name.split(".") + module_path = pathlib.Path("sentry_sdk") / "integrations" / pathlib.Path(*parts) + import_path = ".".join(("sentry_sdk", "integrations", *parts)) integration_dependencies = set() for py_file in pathlib.Path(module_path).rglob("*.py"):