Skip to content

Commit e2fccd4

Browse files
authored
fix(internal): ignore non-standard modules [backport #7732 to 1.20] (#8469)
Backport of #7732 to 1.20 When mapping imported modules to their origin, we handle non-standard objects to ensure that we do not raise an exception if we find a module that either has no origin, or that cannot be referenced weakly. We expect these cases to be of no importance to the tracking process, as these modules are unlikely to provide useful information via the Python runtime. Fixes #7730. ## 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] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [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)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly 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) - [x] If this PR 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`. - [x] This PR doesn't touch any of that.
1 parent 609e789 commit e2fccd4

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

ddtrace/internal/module.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,16 +250,35 @@ def __init__(self):
250250
@property
251251
def _origin_map(self):
252252
# type: () -> wvdict[str, ModuleType]
253+
def modules_with_origin(modules):
254+
result = wvdict()
255+
256+
for m in modules:
257+
module_origin = origin(m)
258+
if module_origin is None:
259+
continue
260+
261+
try:
262+
result[str(module_origin)] = m
263+
except TypeError:
264+
# This can happen if the module is a special object that
265+
# does not allow for weak references. Quite likely this is
266+
# an object created by a native extension. We make the
267+
# assumption that this module does not contain valuable
268+
# information that can be used at the Python runtime level.
269+
pass
270+
271+
return result
272+
253273
if self._om is None:
254274
try:
255-
self._om = wvdict({origin(module): module for module in sys.modules.values()})
275+
self._om = modules_with_origin(sys.modules.values())
256276
except RuntimeError:
257277
# The state of sys.modules might have been mutated by another
258278
# thread. We try to build the full mapping at the next occasion.
259279
# For now we take the more expensive route of building a list of
260280
# the current values, which might be incomplete.
261-
return wvdict({origin(module): module for module in list(sys.modules.values())})
262-
281+
return modules_with_origin(list(sys.modules.values()))
263282
return self._om
264283

265284
def _add_to_meta_path(self):
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
dynamic instrumentation: Fix an issue that caused the instrumented
5+
application to fail to start if a non-standard module was imported.

tests/internal/test_module.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,17 @@ def lazy_import(name):
400400
lazy.__spec__
401401
except AttributeError:
402402
pass
403+
404+
405+
@pytest.mark.subprocess(ddtrace_run=True)
406+
def test_module_watchdog_weakref():
407+
"""Check that we can ignore entries in sys.modules that cannot be weakref'ed."""
408+
import sys
409+
410+
sys.modules["bogus"] = str.__init__ # Cannot create weak ref to method_descriptor
411+
412+
from ddtrace.internal.module import ModuleWatchdog
413+
414+
instance = ModuleWatchdog._instance
415+
instance._om = None
416+
assert ModuleWatchdog._instance._origin_map

0 commit comments

Comments
 (0)