-
Notifications
You must be signed in to change notification settings - Fork 609
Expand file tree
/
Copy pathtest_shadowed_module.py
More file actions
111 lines (92 loc) · 3.72 KB
/
test_shadowed_module.py
File metadata and controls
111 lines (92 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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 pytest_generate_tests(metafunc):
"""
All submodules of sentry_sdk.integrations are picked up, so modules
without a subclass of sentry_sdk.integrations.Integration are also tested
for poorly gated imports.
This approach was chosen to keep the implementation simple.
"""
if "integration_submodule_name" in metafunc.fixturenames:
submodule_names = {
submodule_name
for _, submodule_name, _ in pkgutil.walk_packages(integrations.__path__)
}
metafunc.parametrize(
"integration_submodule_name",
# Temporarily skip some integrations
submodule_names
- {
"litellm",
},
)
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, integration_submodule_name
):
"""
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.
"""
module_path = f"sentry_sdk.integrations.{integration_submodule_name}"
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)
# 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)
# Importing the integration must raise DidNotEnable, since the
# SDK catches the exception type when attempting to activate
# auto-enabling integrations.
with pytest.raises(integrations.DidNotEnable):
importlib.import_module(module_path)
for dependency in integration_dependencies:
del sys.modules[dependency]