-
Notifications
You must be signed in to change notification settings - Fork 571
test: Import integrations with empty shadow modules #5150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
f02d43a
3055936
e5bbbc2
f417060
0ea8f7e
5e6878e
535991e
18bf400
5a1459e
e68637d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import sys | ||
| import ast | ||
| import types | ||
| import pkgutil | ||
| import importlib | ||
| import pathlib | ||
| import pytest | ||
|
|
||
| from sentry_sdk import integrations | ||
| from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, Integration | ||
|
|
||
|
|
||
| def find_unrecognized_dependencies(tree): | ||
| """ | ||
| Finds unrecognized imports in the AST for a Python module. In an empty | ||
| environment the set of non-standard library modules is returned. | ||
| """ | ||
| unrecognized_dependencies = set() | ||
| package_name = lambda name: name.split(".")[0] | ||
|
|
||
| for node in ast.walk(tree): | ||
| if isinstance(node, ast.Import): | ||
| for alias in node.names: | ||
| root = package_name(alias.name) | ||
|
|
||
| try: | ||
| if not importlib.util.find_spec(root): | ||
| unrecognized_dependencies.add(root) | ||
| except ValueError: | ||
| continue | ||
|
|
||
| elif isinstance(node, ast.ImportFrom): | ||
| # if node.level is not 0 the import is relative | ||
| if node.level > 0 or node.module is None: | ||
| continue | ||
|
|
||
| root = package_name(node.module) | ||
|
|
||
| try: | ||
| if not importlib.util.find_spec(root): | ||
| unrecognized_dependencies.add(root) | ||
| except ValueError: | ||
| continue | ||
|
|
||
| return unrecognized_dependencies | ||
|
|
||
|
|
||
| @pytest.mark.skipif( | ||
| sys.version_info < (3, 7), reason="asyncpg imports __future__.annotations" | ||
| ) | ||
| def test_shadowed_modules_when_importing_integrations(sentry_init): | ||
| """ | ||
| Check that importing integrations for third-party module raises an | ||
| DidNotEnable exception when the associated module is shadowed by an empty | ||
| module. | ||
|
|
||
| An integration is determined to be for a third-party module if it cannot | ||
| be imported in the environment in which the tests run. | ||
| """ | ||
| for _, submodule_name, _ in pkgutil.walk_packages(integrations.__path__): | ||
| module_path = f"sentry_sdk.integrations.{submodule_name}" | ||
|
|
||
| # Temporary skip list | ||
| if submodule_name in ( | ||
| "clickhouse_driver", | ||
| "grpc", | ||
| "litellm", | ||
| "opentelemetry", | ||
| "pure_eval", | ||
| "ray", | ||
| "trytond", | ||
| "typer", | ||
| ): | ||
| continue | ||
|
|
||
| try: | ||
| importlib.import_module(module_path) | ||
| continue | ||
| 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) | ||
| for dependency in integration_dependencies: | ||
| sys.modules[dependency] = types.ModuleType(dependency) | ||
| with pytest.raises(integrations.DidNotEnable): | ||
alexander-alderman-webb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| importlib.import_module(module_path) | ||
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we merge this test, my plan is to remove integrations from the skip list one-by-one.