From 64ba71c4ec35cc23edbfd7a360fd53d6807ce133 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:15:53 +0000 Subject: [PATCH] fix: incomplete pkg_resources support [backport 2.5] (#8180) Backport cc0a79883b39b23e0b2c6d63d756ffa0579b3bf0 from #8176 to 2.5. We fix an incomplete support for pkg_resources. This change covers for the case where pkg_resources was somehow already imported before ddtrace. Follow-up on #8113. ## Checklist - [x] Change(s) are motivated and described in the PR description - [x] Testing strategy is described if automated tests are not included in the PR - [x] Risks are described (performance impact, potential for breakage, maintainability) - [x] Change is maintainable (easy to change, telemetry, documentation) - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed or label `changelog/no-changelog` is set - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)) - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) - [x] If this PR changes the public interface, I've notified `@DataDog/apm-tees`. - [x] If change touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. ## Reviewer Checklist - [x] Title is accurate - [x] All changes are related to the pull request's stated goal - [x] Description motivates each change - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - [x] Testing strategy adequately addresses listed risks - [x] Change is maintainable (easy to change, telemetry, documentation) - [x] Release note makes sense to a user of the library - [x] Author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) Co-authored-by: Gabriele N. Tornetta --- ddtrace/internal/module.py | 9 +++++- ...kg_resources-support-58baf819ea8705ef.yaml | 5 +++ tests/internal/test_module.py | 31 ++++++++++++++++--- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/fix-incomplete-pkg_resources-support-58baf819ea8705ef.yaml diff --git a/ddtrace/internal/module.py b/ddtrace/internal/module.py index 47fe7de3633..cbdfaab8d6a 100644 --- a/ddtrace/internal/module.py +++ b/ddtrace/internal/module.py @@ -238,6 +238,12 @@ def __init__(self): # type: () -> None self._finding = set() # type: Set[str] + # DEV: pkg_resources support to prevent errors such as + # NotImplementedError: Can't perform this operation for unregistered + pkg_resources = sys.modules.get("pkg_resources") + if pkg_resources is not None: + pkg_resources.register_loader_type(_ImportHookChainedLoader, pkg_resources.DefaultProvider) + def _add_to_meta_path(self): # type: () -> None sys.meta_path.insert(0, self) # type: ignore[arg-type] @@ -369,9 +375,10 @@ class ModuleWatchdog(BaseModuleWatchdog): def __init__(self): # type: () -> None + super().__init__() + self._hook_map = defaultdict(list) # type: DefaultDict[str, List[ModuleHookType]] self._om = None # type: Optional[Dict[str, ModuleType]] - self._finding = set() # type: Set[str] self._pre_exec_module_hooks = [] # type: List[Tuple[PreExecHookCond, PreExecHookType]] @property diff --git a/releasenotes/notes/fix-incomplete-pkg_resources-support-58baf819ea8705ef.yaml b/releasenotes/notes/fix-incomplete-pkg_resources-support-58baf819ea8705ef.yaml new file mode 100644 index 00000000000..77d85f86f23 --- /dev/null +++ b/releasenotes/notes/fix-incomplete-pkg_resources-support-58baf819ea8705ef.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix an incomplete support for pkg_resouces that could have caused an + exception on start-up. diff --git a/tests/internal/test_module.py b/tests/internal/test_module.py index c434024b9ae..0a3831ee03d 100644 --- a/tests/internal/test_module.py +++ b/tests/internal/test_module.py @@ -453,15 +453,36 @@ def test_module_watchdog_namespace_import_no_warnings(): import namespace_test.ns_module # noqa:F401 +@pytest.mark.subprocess(ddtrace_run=True, env=dict(NSPATH=str(Path(__file__).parent))) +def test_module_watchdog_pkg_resources_support(): + # Test that we can access resource files with pkg_resources without raising + # an exception. + import os + import sys + + sys.path.insert(0, os.getenv("NSPATH")) + + import pkg_resources as p + + p.resource_listdir("namespace_test.ns_module", ".") + + @pytest.mark.subprocess( - ddtrace_run=True, - env=dict( - PYTHONPATH=os.pathsep.join((str(Path(__file__).parent), os.environ.get("PYTHONPATH", ""))), - ), + env=dict(NSPATH=str(Path(__file__).parent)), + err=lambda _: "Can't perform this operation for unregistered loader type" not in _, ) -def test_module_watchdog_pkg_resources_support(): +def test_module_watchdog_pkg_resources_support_already_imported(): # Test that we can access resource files with pkg_resources without raising # an exception. + import os + import sys + + assert "ddtrace" not in sys.modules + + sys.path.insert(0, os.getenv("NSPATH")) + import pkg_resources as p + import ddtrace # noqa + p.resource_listdir("namespace_test.ns_module", ".")