Skip to content

Commit

Permalink
chore(integrations): avoids using deprecated apis in ``ddtrace.patch(…
Browse files Browse the repository at this point in the history
……)`` [backports 2.20] (#12153)

Backports:  #12107

# Checklist 1
- [x] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

# Checklist 2
- [x] Reviewer has checked that all the criteria below are met
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- 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)
  • Loading branch information
mabdinur authored Jan 31, 2025
1 parent d40de8a commit 22f7d47
Show file tree
Hide file tree
Showing 21 changed files with 48 additions and 62 deletions.
31 changes: 14 additions & 17 deletions ddtrace/_monkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,6 @@
}


DEFAULT_MODULES_PREFIX = "ddtrace.contrib"


class PatchException(Exception):
"""Wraps regular `Exception` class when patching modules"""

Expand All @@ -170,19 +167,14 @@ class ModuleNotFoundException(PatchException):
pass


def _on_import_factory(module, prefix="ddtrace.contrib", raise_errors=True, patch_indicator=True):
def _on_import_factory(module, path_f, raise_errors=True, patch_indicator=True):
# type: (str, str, bool, Union[bool, List[str]]) -> Callable[[Any], None]
"""Factory to create an import hook for the provided module name"""

def on_import(hook):
# Import and patch module
try:
try:
imported_module = importlib.import_module("%s.internal.%s.patch" % (prefix, module))
except ImportError:
# Some integrations do not have an internal patch module, so we use the public one
# FIXME: This is a temporary solution until we refactor the patching logic.
imported_module = importlib.import_module("%s.%s" % (prefix, module))
imported_module = importlib.import_module(path_f % (module,))
imported_module.patch()
if hasattr(imported_module, "patch_submodules"):
imported_module.patch_submodules(patch_indicator)
Expand All @@ -209,7 +201,7 @@ def on_import(hook):
name, True, PATCH_MODULES.get(module) is True, "", version=v
)
elif hasattr(imported_module, "get_version"):
# TODO: Ensure every integration defines either get_version or get_versions in their patch.py module
# Some integrations/iast patchers do not define get_version
version = imported_module.get_version()
telemetry.telemetry_writer.add_integration(
module, True, PATCH_MODULES.get(module) is True, "", version=version
Expand Down Expand Up @@ -258,8 +250,8 @@ def patch_all(**patch_modules):
load_common_appsec_modules()


def patch(raise_errors=True, patch_modules_prefix=DEFAULT_MODULES_PREFIX, **patch_modules):
# type: (bool, str, Union[List[str], bool]) -> None
def patch(raise_errors=True, **patch_modules):
# type: (bool, Union[List[str], bool]) -> None
"""Patch only a set of given modules.
:param bool raise_errors: Raise error if one patch fail.
Expand All @@ -270,17 +262,22 @@ def patch(raise_errors=True, patch_modules_prefix=DEFAULT_MODULES_PREFIX, **patc
contribs = {c: patch_indicator for c, patch_indicator in patch_modules.items() if patch_indicator}
for contrib, patch_indicator in contribs.items():
# Check if we have the requested contrib.
if not os.path.isfile(os.path.join(os.path.dirname(__file__), "contrib", contrib, "__init__.py")):
if not os.path.isfile(os.path.join(os.path.dirname(__file__), "contrib", "internal", contrib, "patch.py")):
if raise_errors:
raise ModuleNotFoundException(
"integration module ddtrace.contrib.%s does not exist, "
"module will not have tracing available" % contrib
"integration module ddtrace.contrib.internal.%s.patch does not exist, "
"automatic instrumentation is disabled for this library" % contrib
)
modules_to_patch = _MODULES_FOR_CONTRIB.get(contrib, (contrib,))
for module in modules_to_patch:
# Use factory to create handler to close over `module` and `raise_errors` values from this loop
when_imported(module)(
_on_import_factory(contrib, raise_errors=raise_errors, patch_indicator=patch_indicator)
_on_import_factory(
contrib,
"ddtrace.contrib.internal.%s.patch",
raise_errors=raise_errors,
patch_indicator=patch_indicator,
)
)

# manually add module to patched modules
Expand Down
8 changes: 2 additions & 6 deletions ddtrace/appsec/_iast/_patch_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ def patch_iast(patch_modules=IAST_PATCH):
from ddtrace._monkey import _on_import_factory

for module in (m for m, e in patch_modules.items() if e):
when_imported("hashlib")(
_on_import_factory(module, prefix="ddtrace.appsec._iast.taint_sinks", raise_errors=False)
)
when_imported("hashlib")(_on_import_factory(module, "ddtrace.appsec._iast.taint_sinks.%s", raise_errors=False))

when_imported("json")(
_on_import_factory("json_tainting", prefix="ddtrace.appsec._iast._patches", raise_errors=False)
)
when_imported("json")(_on_import_factory("json_tainting", "ddtrace.appsec._iast._patches.%s", raise_errors=False))
4 changes: 2 additions & 2 deletions ddtrace/contrib/cherrypy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def index(self):
"""


from ddtrace.contrib.internal.cherrypy.middleware import TraceMiddleware
from ddtrace.contrib.internal.cherrypy.middleware import get_version # noqa: F401
from ddtrace.contrib.internal.cherrypy.patch import TraceMiddleware
from ddtrace.contrib.internal.cherrypy.patch import get_version # noqa: F401
from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning
from ddtrace.vendor.debtcollector import deprecate

Expand Down
4 changes: 2 additions & 2 deletions ddtrace/contrib/flask_cache/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def counter():
"""


from ddtrace.contrib.internal.flask_cache.tracers import get_traced_cache
from ddtrace.contrib.internal.flask_cache.tracers import get_version # noqa: F401
from ddtrace.contrib.internal.flask_cache.patch import get_traced_cache
from ddtrace.contrib.internal.flask_cache.patch import get_version # noqa: F401
from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning
from ddtrace.vendor.debtcollector import deprecate

Expand Down
4 changes: 2 additions & 2 deletions ddtrace/contrib/internal/aioredis/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from ddtrace.constants import _SPAN_MEASURED_KEY
from ddtrace.constants import SPAN_KIND
from ddtrace.contrib import trace_utils
from ddtrace.contrib.internal.redis_utils import ROW_RETURNING_COMMANDS
from ddtrace.contrib.internal.redis_utils import _run_redis_command_async
from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS
from ddtrace.contrib.redis_utils import determine_row_count
from ddtrace.contrib.internal.redis_utils import determine_row_count
from ddtrace.ext import SpanKind
from ddtrace.ext import SpanTypes
from ddtrace.ext import db
Expand Down
12 changes: 0 additions & 12 deletions ddtrace/contrib/internal/cassandra/patch.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,2 @@
from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning
from ddtrace.vendor.debtcollector import deprecate

from .session import patch # noqa: F401
from .session import unpatch # noqa: F401


deprecate(
("%s is deprecated" % (__name__)),
message="Avoid using this package directly. "
"Use ``import ddtrace.auto`` or the ``ddtrace-run`` command to enable and configure this integration.",
category=DDTraceDeprecationWarning,
removal_version="3.0.0",
)
File renamed without changes.
2 changes: 1 addition & 1 deletion ddtrace/contrib/internal/fastapi/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from ddtrace.contrib.internal.asgi.middleware import TraceMiddleware
from ddtrace.contrib.internal.starlette.patch import _trace_background_tasks
from ddtrace.contrib.internal.starlette.patch import traced_handler
from ddtrace.contrib.starlette.patch import traced_route_init
from ddtrace.contrib.internal.starlette.patch import traced_route_init
from ddtrace.internal.logger import get_logger
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.wrappers import unwrap as _u
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/internal/pytest/_plugin_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from ddtrace import DDTraceDeprecationWarning
from ddtrace import config as dd_config
from ddtrace._monkey import patch
from ddtrace.contrib.coverage import patch as patch_coverage
from ddtrace.contrib.internal.coverage.constants import PCT_COVERED_KEY
from ddtrace.contrib.internal.coverage.data import _coverage_data
from ddtrace.contrib.internal.coverage.patch import patch as patch_coverage
from ddtrace.contrib.internal.coverage.patch import run_coverage_report
from ddtrace.contrib.internal.coverage.utils import _is_coverage_invoked_by_coverage_run
from ddtrace.contrib.internal.coverage.utils import _is_coverage_patched
Expand Down
4 changes: 2 additions & 2 deletions ddtrace/contrib/internal/redis/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from ddtrace import config
from ddtrace._trace.utils_redis import _instrument_redis_cmd
from ddtrace._trace.utils_redis import _instrument_redis_execute_pipeline
from ddtrace.contrib.internal.redis_utils import ROW_RETURNING_COMMANDS
from ddtrace.contrib.internal.redis_utils import determine_row_count
from ddtrace.contrib.internal.trace_utils import unwrap
from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS
from ddtrace.contrib.redis_utils import determine_row_count
from ddtrace.internal import core
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.formats import CMD_MAX_LEN
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/llmobs/_llmobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ def _patch_integrations() -> None:
integrations_to_patch.update(
{k: asbool(v) for k, v in dd_patch_modules_to_str.items() if k in SUPPORTED_LLMOBS_INTEGRATIONS.values()}
)
patch(**integrations_to_patch) # type: ignore[arg-type]
patch(**integrations_to_patch)
log.debug("Patched LLM integrations: %s", list(SUPPORTED_LLMOBS_INTEGRATIONS.values()))

@classmethod
Expand Down
3 changes: 2 additions & 1 deletion docs/contributing-integrations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ What does a complete PR look like when adding a new integration?

The following is the check list for ensuring you have all of the components to have a complete PR that is ready for review.

- Patch code for your new integration under ``ddtrace/contrib/your_integration_name``.
- Define `patch` and `unpatch` functions for your new integration under ``ddtrace/contrib/internal/your_integration_name``.
- Document your integration in a ``ddtrace/contrib/_integration_name.py`` module and reference the doc string in ``docs/integrations.rst``.
- Test code for the above in ``tests/contrib/your_integration_name``.
- The virtual environment configurations for your tests in ``riotfile.py``.
- The Circle CI configurations for your tests in ``.circleci/config.templ.yml``.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
other:
- |
cassandra,cherrypy,flask_cache,starlette: Ensures a deprecation warning is not raised when patching these integrations via ``ddtrace-run`` and ``import ddtrace.auto``.
2 changes: 1 addition & 1 deletion tests/contrib/asyncio/test_tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from ddtrace.constants import ERROR_MSG
from ddtrace.contrib.asyncio.compat import asyncio_current_task
from ddtrace.contrib.internal.asyncio.compat import asyncio_current_task
from ddtrace.contrib.internal.asyncio.patch import patch
from ddtrace.contrib.internal.asyncio.patch import unpatch

Expand Down
8 changes: 4 additions & 4 deletions tests/contrib/cherrypy/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ddtrace.constants import ERROR_MSG
from ddtrace.constants import ERROR_STACK
from ddtrace.constants import ERROR_TYPE
from ddtrace.contrib.internal.cherrypy.middleware import TraceMiddleware
from ddtrace.contrib.internal.cherrypy.patch import TraceMiddleware
from ddtrace.ext import http
from tests.contrib.patch import emit_integration_and_version_to_test_agent
from tests.utils import TracerTestCase
Expand Down Expand Up @@ -55,7 +55,7 @@ def setUp(self):
)

def test_and_emit_get_version(self):
from ddtrace.contrib.internal.cherrypy.middleware import get_version
from ddtrace.contrib.internal.cherrypy.patch import get_version

version = get_version()
assert type(version) == str
Expand Down Expand Up @@ -543,7 +543,7 @@ def test_service_name_schema(ddtrace_run_python_code_in_subprocess, schema_versi
from cherrypy.test import helper
from tests.utils import TracerTestCase
from tests.contrib.cherrypy.web import StubApp
from ddtrace.contrib.internal.cherrypy.middleware import TraceMiddleware
from ddtrace.contrib.internal.cherrypy.patch import TraceMiddleware
class TestCherrypy(TracerTestCase, helper.CPWebCase):
@staticmethod
def setup_server():
Expand Down Expand Up @@ -602,7 +602,7 @@ def test_operation_name_schema(ddtrace_run_python_code_in_subprocess, schema_ver
from cherrypy.test import helper
from tests.utils import TracerTestCase
from tests.contrib.cherrypy.web import StubApp
from ddtrace.contrib.internal.cherrypy.middleware import TraceMiddleware
from ddtrace.contrib.internal.cherrypy.patch import TraceMiddleware
class TestCherrypy(TracerTestCase, helper.CPWebCase):
@staticmethod
def setup_server():
Expand Down
4 changes: 2 additions & 2 deletions tests/contrib/flask_cache/test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from flask import Flask

from ddtrace.contrib.internal.flask_cache.tracers import CACHE_BACKEND
from ddtrace.contrib.internal.flask_cache.tracers import get_traced_cache
from ddtrace.contrib.internal.flask_cache.patch import CACHE_BACKEND
from ddtrace.contrib.internal.flask_cache.patch import get_traced_cache
from ddtrace.ext import net
from ddtrace.internal.schema import DEFAULT_SPAN_SERVICE_NAME
from tests.opentracer.utils import init_tracer
Expand Down
2 changes: 1 addition & 1 deletion tests/contrib/flask_cache/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from flask import Flask

from ddtrace._trace.tracer import Tracer
from ddtrace.contrib.internal.flask_cache.tracers import get_traced_cache
from ddtrace.contrib.internal.flask_cache.patch import get_traced_cache
from ddtrace.contrib.internal.flask_cache.utils import _extract_client
from ddtrace.contrib.internal.flask_cache.utils import _extract_conn_tags
from ddtrace.contrib.internal.flask_cache.utils import _resource_from_cache_prefix
Expand Down
4 changes: 2 additions & 2 deletions tests/contrib/flask_cache/test_wrapper_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import pytest
from redis.exceptions import ConnectionError

from ddtrace.contrib.internal.flask_cache.tracers import CACHE_BACKEND
from ddtrace.contrib.internal.flask_cache.tracers import get_traced_cache
from ddtrace.contrib.internal.flask_cache.patch import CACHE_BACKEND
from ddtrace.contrib.internal.flask_cache.patch import get_traced_cache
from ddtrace.ext import net
from tests.utils import TracerTestCase

Expand Down
6 changes: 3 additions & 3 deletions tests/contrib/langgraph/test_langgraph_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import sys
from tempfile import NamedTemporaryFile

from ddtrace.contrib.langgraph import get_version
from ddtrace.contrib.langgraph import patch
from ddtrace.contrib.langgraph import unpatch
from ddtrace.contrib.internal.langgraph.patch import get_version
from ddtrace.contrib.internal.langgraph.patch import patch
from ddtrace.contrib.internal.langgraph.patch import unpatch
from tests.contrib.patch import PatchTestCase
from tests.utils import call_program

Expand Down
4 changes: 2 additions & 2 deletions tests/tracer/test_monkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def test_patch_raise_exception_manual_patch(self):
_monkey.patch(module_dne=True)

assert (
"integration module ddtrace.contrib.module_dne does not exist, module will not have tracing available"
in str(me.exception)
"integration module ddtrace.contrib.internal.module_dne.patch does not exist, "
"automatic instrumentation is disabled for this library" in str(me.exception)
)
assert "module_dne" not in _monkey._PATCHED_MODULES

Expand Down

0 comments on commit 22f7d47

Please sign in to comment.