Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions ddtrace/_trace/pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from typing import Dict
from typing import Optional

import wrapt

import ddtrace
from ddtrace.internal.compat import is_wrapted
from ddtrace.settings.asm import config as asm_config

from ..internal.logger import get_logger
Expand Down Expand Up @@ -104,7 +103,7 @@ def get_from(obj: Any) -> Optional["Pin"]:
if hasattr(obj, "__getddpin__"):
return obj.__getddpin__()

pin_name = _DD_PIN_PROXY_NAME if isinstance(obj, wrapt.ObjectProxy) else _DD_PIN_NAME
pin_name = _DD_PIN_PROXY_NAME if is_wrapted(obj) else _DD_PIN_NAME
pin = getattr(obj, pin_name, None)
# detect if the PIN has been inherited from a class
if pin is not None and pin._target != id(obj):
Expand Down Expand Up @@ -167,7 +166,7 @@ def onto(self, obj: Any, send: bool = True) -> None:
if hasattr(obj, "__setddpin__"):
return obj.__setddpin__(self)

pin_name = _DD_PIN_PROXY_NAME if isinstance(obj, wrapt.ObjectProxy) else _DD_PIN_NAME
pin_name = _DD_PIN_PROXY_NAME if is_wrapted(obj) else _DD_PIN_NAME

# set the target reference; any get_from, clones and retarget the new PIN
self._target = id(obj)
Expand All @@ -180,7 +179,7 @@ def onto(self, obj: Any, send: bool = True) -> None:
def remove_from(self, obj: Any) -> None:
# Remove pin from the object.
try:
pin_name = _DD_PIN_PROXY_NAME if isinstance(obj, wrapt.ObjectProxy) else _DD_PIN_NAME
pin_name = _DD_PIN_PROXY_NAME if is_wrapted(obj) else _DD_PIN_NAME

pin = Pin.get_from(obj)
if pin is not None:
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/internal/aiopg/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ddtrace.contrib.internal.psycopg.connection import patch_conn as psycopg_patch_conn
from ddtrace.contrib.internal.psycopg.extensions import _patch_extensions
from ddtrace.contrib.internal.psycopg.extensions import _unpatch_extensions
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.wrappers import unwrap as _u

Expand Down Expand Up @@ -63,7 +64,7 @@ def _unroll_args(obj, scope=None):

# register_type performs a c-level check of the object
# type so we must be sure to pass in the actual db connection
if scope and isinstance(scope, wrapt.ObjectProxy):
if scope and is_wrapted(scope):
scope = scope.__wrapped__._conn

return func(obj, scope) if scope else func(obj)
Expand Down
5 changes: 2 additions & 3 deletions ddtrace/contrib/internal/django/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
from typing import Type
from typing import cast

import wrapt

import ddtrace
from ddtrace import config
from ddtrace._trace.pin import Pin
Expand All @@ -18,6 +16,7 @@
from ddtrace.ext import db
from ddtrace.ext import net
from ddtrace.ext import sql as sqlx
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.logger import get_logger
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.cache import cached
Expand Down Expand Up @@ -68,7 +67,7 @@ def cursor(func: FunctionType, args: Tuple[Any], kwargs: Dict[str, Any]) -> Any:
# Don't double wrap Django database cursors:
# If the underlying cursor is already wrapped (e.g. by another library),
# we just add the Django tags to the existing Pin (if any) and return
if isinstance(cursor.cursor, wrapt.ObjectProxy) and not config_django.always_create_database_spans:
if is_wrapted(cursor.cursor) and not config_django.always_create_database_spans:
instance = args[0]
tags = {
"django.db.vendor": getattr(instance, "vendor", "db"),
Expand Down
7 changes: 4 additions & 3 deletions ddtrace/contrib/internal/django/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from ddtrace.contrib import trace_utils
from ddtrace.contrib.internal.django.user import _DjangoUserInfoRetriever
from ddtrace.internal import core
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.constants import COMPONENT
from ddtrace.internal.core.event_hub import ResultType
from ddtrace.internal.endpoints import endpoint_collection
Expand Down Expand Up @@ -252,7 +253,7 @@ def _instrument_view(django, view, path=None):
for name in list(request_method_list or _DEFAULT_METHODS) + list(lifecycle_methods):
try:
func = getattr(view, name, None)
if not func or isinstance(func, wrapt.ObjectProxy):
if not func or is_wrapted(func):
continue

resource = "{0}.{1}".format(func_name(view), name)
Expand All @@ -269,7 +270,7 @@ def _instrument_view(django, view, path=None):
try:
func = getattr(response_cls, name, None)
# Do not wrap if the method does not exist or is already wrapped
if not func or isinstance(func, wrapt.ObjectProxy):
if not func or is_wrapted(func):
continue

resource = "{0}.{1}".format(func_name(response_cls), name)
Expand All @@ -279,7 +280,7 @@ def _instrument_view(django, view, path=None):
log.debug("Failed to instrument Django response %r function %s", response_cls, name, exc_info=True)

# If the view itself is not wrapped, wrap it
if not isinstance(view, wrapt.ObjectProxy):
if not is_wrapted(view):
view = utils.DjangoViewProxy(
view, traced_func(django, "django.view", resource=func_name(view), ignored_excs=[django.http.Http404])
)
Expand Down
8 changes: 4 additions & 4 deletions ddtrace/contrib/internal/langchain/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from typing import Tuple

import langchain_core
import wrapt

from ddtrace import config
from ddtrace._trace.pin import Pin
Expand All @@ -14,6 +13,7 @@
from ddtrace.contrib.internal.trace_utils import with_traced_module
from ddtrace.contrib.internal.trace_utils import wrap
from ddtrace.internal import core
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.logger import get_logger
from ddtrace.internal.utils import ArgumentError
from ddtrace.internal.utils import get_argument_value
Expand Down Expand Up @@ -587,11 +587,11 @@ def patched_embeddings_init_subclass(func, instance, args, kwargs):

try:
embed_documents = getattr(cls, "embed_documents", None)
if embed_documents and not isinstance(embed_documents, wrapt.ObjectProxy):
if embed_documents and not is_wrapted(embed_documents):
wrap(cls, "embed_documents", traced_embedding(langchain_core))

embed_query = getattr(cls, "embed_query", None)
if embed_query and not isinstance(embed_query, wrapt.ObjectProxy):
if embed_query and not is_wrapted(embed_query):
wrap(cls, "embed_query", traced_embedding(langchain_core))
except Exception:
log.warning("Unable to patch LangChain Embeddings class %s", str(cls))
Expand All @@ -603,7 +603,7 @@ def patched_vectorstore_init_subclass(func, instance, args, kwargs):

try:
method = getattr(cls, "similarity_search", None)
if method and not isinstance(method, wrapt.ObjectProxy):
if method and not is_wrapted(method):
wrap(cls, "similarity_search", traced_similarity_search(langchain_core))
except Exception:
log.warning("Unable to patch LangChain VectorStore class %s", str(cls))
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/internal/mysql/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ddtrace.contrib.internal.trace_utils import _convert_to_string
from ddtrace.ext import db
from ddtrace.ext import net
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.schema import schematize_database_operation
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.formats import asbool
Expand Down Expand Up @@ -62,7 +63,7 @@ def patch():


def unpatch():
if isinstance(mysql.connector.connect, wrapt.ObjectProxy):
if is_wrapted(mysql.connector.connect):
mysql.connector.connect = mysql.connector.connect.__wrapped__
if hasattr(mysql.connector, "Connect"):
mysql.connector.Connect = mysql.connector.connect
Expand Down
10 changes: 6 additions & 4 deletions ddtrace/contrib/internal/psycopg/extensions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import wrapt

from ddtrace.internal.compat import is_wrapted


def get_psycopg2_extensions(psycopg_module):
_extensions = [
Expand Down Expand Up @@ -36,7 +38,7 @@ def _unroll_args(obj, scope=None):

# register_type performs a c-level check of the object
# type so we must be sure to pass in the actual db connection
if scope and isinstance(scope, wrapt.ObjectProxy):
if scope and is_wrapted(scope):
scope = scope.__wrapped__

return func(obj, scope) if scope else func(obj)
Expand All @@ -50,7 +52,7 @@ def _unroll_args(obj, scope=None):

# register_type performs a c-level check of the object
# type so we must be sure to pass in the actual db connection
if scope and isinstance(scope, wrapt.ObjectProxy):
if scope and is_wrapted(scope):
scope = scope.__wrapped__

return func(obj, scope) if scope else func(obj)
Expand All @@ -72,7 +74,7 @@ def prepare(self, *args, **kwargs):

# prepare performs a c-level check of the object type so
# we must be sure to pass in the actual db connection
if isinstance(conn, wrapt.ObjectProxy):
if is_wrapted(conn):
conn = conn.__wrapped__

return func(conn, *args[1:], **kwargs)
Expand All @@ -82,7 +84,7 @@ def _patch_extensions(_extensions):
# we must patch extensions all the time (it's pretty harmless) so split
# from global patching of connections. must be idempotent.
for _, module, func, wrapper in _extensions:
if not hasattr(module, func) or isinstance(getattr(module, func), wrapt.ObjectProxy):
if not hasattr(module, func) or is_wrapted(getattr(module, func)):
continue
wrapt.wrap_function_wrapper(module, func, wrapper)

Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/internal/pymysql/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ddtrace.contrib.internal.trace_utils import _convert_to_string
from ddtrace.ext import db
from ddtrace.ext import net
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.schema import schematize_database_operation
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.utils.formats import asbool
Expand Down Expand Up @@ -52,7 +53,7 @@ def patch():


def unpatch():
if isinstance(pymysql.connect, wrapt.ObjectProxy):
if is_wrapted(pymysql.connect):
pymysql.connect = pymysql.connect.__wrapped__
pymysql._datadog_patch = False

Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/internal/pyramid/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ddtrace import config
from ddtrace.ext import SpanTypes
from ddtrace.internal import core
from ddtrace.internal.compat import is_wrapted
from ddtrace.internal.constants import COMPONENT
from ddtrace.internal.logger import get_logger
from ddtrace.internal.schema import schematize_service_name
Expand All @@ -34,7 +35,7 @@ def includeme(config):
# Add our tween just before the default exception handler
config.add_tween(DD_TWEEN_NAME, over=pyramid.tweens.EXCVIEW)
# ensure we only patch the renderer once.
if not isinstance(pyramid.renderers.RendererHelper.render, wrapt.ObjectProxy):
if not is_wrapted(pyramid.renderers.RendererHelper.render):
wrapt.wrap_function_wrapper("pyramid.renderers", "RendererHelper.render", trace_render)


Expand Down
13 changes: 13 additions & 0 deletions ddtrace/internal/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from typing import Type # noqa:F401
from typing import Union # noqa:F401

import wrapt


__all__ = [
"maybe_stringify",
Expand Down Expand Up @@ -126,3 +128,14 @@ def __getattr__(name: str) -> Any:
return globals()[name]
except KeyError:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


if hasattr(wrapt, "BaseObjectProxy"):
# This must be used for wrapt version >= 2.0.0
wrapt_class: type = wrapt.BaseObjectProxy
else:
wrapt_class = wrapt.ObjectProxy


def is_wrapted(o: object) -> bool:
return isinstance(o, wrapt_class)
4 changes: 2 additions & 2 deletions ddtrace/internal/utils/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Optional # noqa:F401
from typing import TypeVar # noqa:F401

import wrapt
from ddtrace.internal.compat import is_wrapted


F = TypeVar("F", bound=Callable[..., Any])
Expand All @@ -14,7 +14,7 @@ def iswrapped(obj, attr=None):
"""Returns whether an attribute is wrapped or not."""
if attr is not None:
obj = getattr(obj, attr, None)
return (hasattr(obj, "__wrapped__") and isinstance(obj, wrapt.ObjectProxy)) or hasattr(obj, "__dd_wrapped__")
return (hasattr(obj, "__wrapped__") and is_wrapted(obj)) or hasattr(obj, "__dd_wrapped__")


def unwrap(obj, attr):
Expand Down
4 changes: 4 additions & 0 deletions releasenotes/notes/wrapt_2-42f935eac8adaac8.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
tracer: This fix ensures compatibility with wrapt 2.0.0
4 changes: 2 additions & 2 deletions tests/contrib/dramatiq/autopatch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import wrapt
from ddtrace.internal.compat import is_wrapted


if __name__ == "__main__":
Expand All @@ -15,5 +15,5 @@ def add_numbers(a: int, b: int):

# now dramatiq should be patched
actor = broker.get_actor("add_numbers")
assert isinstance(dramatiq.Actor.send_with_options, wrapt.ObjectProxy)
assert is_wrapted(dramatiq.Actor.send_with_options)
print("Test success")
10 changes: 5 additions & 5 deletions tests/contrib/dramatiq/test_patch_manual.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest

import wrapt
from ddtrace.internal.compat import is_wrapted


class DramatiqPatchTest(unittest.TestCase):
Expand All @@ -27,9 +27,9 @@ def add_numbers(a: int, b: int):
assert msg.args == (1, 2)

# Check patch/unpatch outcome
assert isinstance(dramatiq.Actor.send_with_options, wrapt.ObjectProxy)
assert is_wrapted(dramatiq.Actor.send_with_options)
unpatch()
assert not isinstance(dramatiq.Actor.send_with_options, wrapt.ObjectProxy)
assert not is_wrapted(dramatiq.Actor.send_with_options)

def test_patch_after_import(self):
import dramatiq
Expand All @@ -55,6 +55,6 @@ def custom_function_max_power(base: int, exp: int):
assert msg.args == (3, 4)

# Check patch/unpatch behavior
assert isinstance(dramatiq.Actor.send_with_options, wrapt.ObjectProxy)
assert is_wrapted(dramatiq.Actor.send_with_options)
unpatch()
assert not isinstance(dramatiq.Actor.send_with_options, wrapt.ObjectProxy)
assert not is_wrapted(dramatiq.Actor.send_with_options)
6 changes: 3 additions & 3 deletions tests/contrib/flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import flask
from flask.testing import FlaskClient
import wrapt

from ddtrace._trace.pin import Pin
from ddtrace.contrib.internal.flask.patch import patch
from ddtrace.contrib.internal.flask.patch import unpatch
from ddtrace.internal.compat import is_wrapted
from tests.utils import TracerTestCase


Expand Down Expand Up @@ -47,10 +47,10 @@ def get_spans(self):
return self.tracer.pop()

def assert_is_wrapped(self, obj):
self.assertTrue(isinstance(obj, wrapt.ObjectProxy), "{} is not wrapped".format(obj))
self.assertTrue(is_wrapted(obj), "{} is not wrapped".format(obj))

def assert_is_not_wrapped(self, obj):
self.assertFalse(isinstance(obj, wrapt.ObjectProxy), "{} is wrapped".format(obj))
self.assertFalse(is_wrapted(obj), "{} is wrapped".format(obj))

def find_span_by_name(self, spans, name, required=True):
"""Helper to find the first span with a given name from a list"""
Expand Down
6 changes: 3 additions & 3 deletions tests/contrib/flask/test_idempotency.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import flask
import mock
import wrapt

from ddtrace.contrib.internal.flask.patch import _u
from ddtrace.contrib.internal.flask.patch import _w
from ddtrace.contrib.internal.flask.patch import patch
from ddtrace.contrib.internal.flask.patch import unpatch
from ddtrace.internal.compat import is_wrapted


class FlaskIdempotencyTestCase(unittest.TestCase):
Expand All @@ -17,11 +17,11 @@ def tearDown(self):

def assert_is_patched(self):
self.assertTrue(flask._datadog_patch)
self.assertTrue(isinstance(flask.render_template, wrapt.ObjectProxy))
self.assertTrue(is_wrapted(flask.render_template))

def assert_is_not_patched(self):
self.assertFalse(flask._datadog_patch)
self.assertFalse(isinstance(flask.render_template, wrapt.ObjectProxy))
self.assertFalse(is_wrapted(flask.render_template))

def test_datadog_patch(self):
# If we have been patching/testing in other files,
Expand Down
Loading
Loading