diff --git a/ddtrace/bootstrap/sitecustomize.py b/ddtrace/bootstrap/sitecustomize.py index 6c02b57e94e..28b2268afb9 100644 --- a/ddtrace/bootstrap/sitecustomize.py +++ b/ddtrace/bootstrap/sitecustomize.py @@ -6,7 +6,7 @@ import os import logging -from ddtrace.util import asbool +from ddtrace.utils.formats import asbool debug = os.environ.get("DATADOG_TRACE_DEBUG") diff --git a/ddtrace/contrib/__init__.py b/ddtrace/contrib/__init__.py index c71f26f589f..6a35d31c95d 100644 --- a/ddtrace/contrib/__init__.py +++ b/ddtrace/contrib/__init__.py @@ -1 +1 @@ -from .util import func_name, module_name, require_modules # noqa +from ..utils.importlib import func_name, module_name, require_modules # noqa diff --git a/ddtrace/contrib/aiobotocore/__init__.py b/ddtrace/contrib/aiobotocore/__init__.py index af891e0f02c..b2e37635f83 100644 --- a/ddtrace/contrib/aiobotocore/__init__.py +++ b/ddtrace/contrib/aiobotocore/__init__.py @@ -18,7 +18,7 @@ # This query generates a trace lambda_client.list_functions() """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['aiobotocore.client'] diff --git a/ddtrace/contrib/aiobotocore/patch.py b/ddtrace/contrib/aiobotocore/patch.py index 3b960ba0fd9..bcd543b8ddf 100644 --- a/ddtrace/contrib/aiobotocore/patch.py +++ b/ddtrace/contrib/aiobotocore/patch.py @@ -2,13 +2,13 @@ import wrapt import aiobotocore.client -from ddtrace import Pin -from ddtrace.util import deep_getattr, unwrap - from aiobotocore.endpoint import ClientResponseContentProxy +from ...pin import Pin from ...ext import http, aws from ...compat import PYTHON_VERSION_INFO +from ...utils.formats import deep_getattr +from ...utils.wrappers import unwrap ARGS_NAME = ('action', 'params', 'path', 'verb') diff --git a/ddtrace/contrib/aiohttp/__init__.py b/ddtrace/contrib/aiohttp/__init__.py index 9aabcdde862..b9416055a68 100644 --- a/ddtrace/contrib/aiohttp/__init__.py +++ b/ddtrace/contrib/aiohttp/__init__.py @@ -44,7 +44,7 @@ async def home_handler(request): ctx = request['datadog_context'] # do something with the tracing Context """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['aiohttp'] diff --git a/ddtrace/contrib/aiohttp/patch.py b/ddtrace/contrib/aiohttp/patch.py index 4a422338390..53c5455f344 100644 --- a/ddtrace/contrib/aiohttp/patch.py +++ b/ddtrace/contrib/aiohttp/patch.py @@ -1,7 +1,7 @@ import wrapt from ...pin import Pin -from ddtrace.util import unwrap +from ...utils.wrappers import unwrap try: diff --git a/ddtrace/contrib/aiopg/__init__.py b/ddtrace/contrib/aiopg/__init__.py index 461e33464bc..ab4553e6c3e 100644 --- a/ddtrace/contrib/aiopg/__init__.py +++ b/ddtrace/contrib/aiopg/__init__.py @@ -15,7 +15,7 @@ # Use a pin to specify metadata related to this connection Pin.override(db, service='postgres-users') """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['aiopg'] diff --git a/ddtrace/contrib/aiopg/connection.py b/ddtrace/contrib/aiopg/connection.py index d481f1e4551..8a7b5e5b118 100644 --- a/ddtrace/contrib/aiopg/connection.py +++ b/ddtrace/contrib/aiopg/connection.py @@ -5,8 +5,7 @@ from .. import dbapi from ...ext import sql - -from ddtrace import Pin +from ...pin import Pin class AIOTracedCursor(wrapt.ObjectProxy): diff --git a/ddtrace/contrib/aiopg/patch.py b/ddtrace/contrib/aiopg/patch.py index 994abee0528..76bc65e0271 100644 --- a/ddtrace/contrib/aiopg/patch.py +++ b/ddtrace/contrib/aiopg/patch.py @@ -8,7 +8,7 @@ from .connection import AIOTracedConnection from ..psycopg.patch import _patch_extensions, \ _unpatch_extensions, patch_conn as psycppg_patch_conn -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u def patch(): diff --git a/ddtrace/contrib/asyncio/__init__.py b/ddtrace/contrib/asyncio/__init__.py index d48c480aed3..24f4a5ee0b0 100644 --- a/ddtrace/contrib/asyncio/__init__.py +++ b/ddtrace/contrib/asyncio/__init__.py @@ -39,7 +39,7 @@ async def some_work(): wrappers without changing your code. In that case, the patch method **must be called before** importing stdlib functions. """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['asyncio'] diff --git a/ddtrace/contrib/asyncio/patch.py b/ddtrace/contrib/asyncio/patch.py index 040b67733f4..e48c57dd581 100644 --- a/ddtrace/contrib/asyncio/patch.py +++ b/ddtrace/contrib/asyncio/patch.py @@ -3,7 +3,7 @@ from wrapt import wrap_function_wrapper as _w from .helpers import _wrapped_create_task -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u def patch(): diff --git a/ddtrace/contrib/boto/__init__.py b/ddtrace/contrib/boto/__init__.py index 447574b607c..252a814c1d3 100644 --- a/ddtrace/contrib/boto/__init__.py +++ b/ddtrace/contrib/boto/__init__.py @@ -17,7 +17,7 @@ ec2.get_all_instances() """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['boto.connection'] diff --git a/ddtrace/contrib/boto/patch.py b/ddtrace/contrib/boto/patch.py index 61dc3f5f9aa..27ae9e469e3 100644 --- a/ddtrace/contrib/boto/patch.py +++ b/ddtrace/contrib/boto/patch.py @@ -2,13 +2,11 @@ import wrapt import inspect -from ddtrace import Pin -from ddtrace.util import unwrap +from ...pin import Pin +from ...ext import http, aws +from ...utils.wrappers import unwrap -from ...ext import http -from ...ext import aws - # Original boto client class _Boto_client = boto.connection.AWSQueryConnection diff --git a/ddtrace/contrib/botocore/__init__.py b/ddtrace/contrib/botocore/__init__.py index d6b4edf1cb6..c54852d7160 100644 --- a/ddtrace/contrib/botocore/__init__.py +++ b/ddtrace/contrib/botocore/__init__.py @@ -20,7 +20,7 @@ """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['botocore.client'] diff --git a/ddtrace/contrib/botocore/patch.py b/ddtrace/contrib/botocore/patch.py index f0f05a7d618..748cc6aae9d 100644 --- a/ddtrace/contrib/botocore/patch.py +++ b/ddtrace/contrib/botocore/patch.py @@ -1,17 +1,15 @@ """ Trace queries to aws api done via botocore client """ - -# project -from ddtrace import Pin -from ddtrace.util import deep_getattr, unwrap - # 3p import wrapt import botocore.client -from ...ext import http -from ...ext import aws +# project +from ...pin import Pin +from ...ext import http, aws +from ...utils.formats import deep_getattr +from ...utils.wrappers import unwrap # Original botocore client class diff --git a/ddtrace/contrib/bottle/__init__.py b/ddtrace/contrib/bottle/__init__.py index 4e76c59e439..4bf6f8ea2c9 100644 --- a/ddtrace/contrib/bottle/__init__.py +++ b/ddtrace/contrib/bottle/__init__.py @@ -15,7 +15,7 @@ plugin = TracePlugin(service="my-web-app", distributed_tracing=True) """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['bottle'] diff --git a/ddtrace/contrib/cassandra/__init__.py b/ddtrace/contrib/cassandra/__init__.py index b45f05f38be..bc2bf63814b 100644 --- a/ddtrace/contrib/cassandra/__init__.py +++ b/ddtrace/contrib/cassandra/__init__.py @@ -21,7 +21,8 @@ session = cluster.connect("my_keyspace") session.execute("select id from my_table limit 10;") """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['cassandra.cluster'] diff --git a/ddtrace/contrib/cassandra/session.py b/ddtrace/contrib/cassandra/session.py index 7d7b9795757..39271057ff7 100644 --- a/ddtrace/contrib/cassandra/session.py +++ b/ddtrace/contrib/cassandra/session.py @@ -10,7 +10,9 @@ # project from ddtrace import Pin from ddtrace.compat import stringify -from ...util import deep_getattr, deprecated + +from ...utils.formats import deep_getattr +from ...utils.deprecation import deprecated from ...ext import net, cassandra as cassx, errors log = logging.getLogger(__name__) @@ -257,7 +259,7 @@ def _sanitize_query(span, query): # DEPRECATED # -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_cassandra(*args, **kwargs): return _get_traced_cluster(*args, **kwargs) diff --git a/ddtrace/contrib/celery/__init__.py b/ddtrace/contrib/celery/__init__.py index dbf8d5a16ec..f21de1e4e1a 100644 --- a/ddtrace/contrib/celery/__init__.py +++ b/ddtrace/contrib/celery/__init__.py @@ -43,7 +43,7 @@ def run(self): BaseClassTask = patch_task(BaseClassTask) fn_task = patch_task(fn_task) """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['celery'] diff --git a/ddtrace/contrib/django/__init__.py b/ddtrace/contrib/django/__init__.py index 1b7e0204af0..170b8843148 100644 --- a/ddtrace/contrib/django/__init__.py +++ b/ddtrace/contrib/django/__init__.py @@ -65,7 +65,7 @@ rendering will not be instrumented. Only configurable when ``AUTO_INSTRUMENT`` is set to ``True``. """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['django'] diff --git a/ddtrace/contrib/django/restframework.py b/ddtrace/contrib/django/restframework.py index 84a71d3254a..24289359afe 100644 --- a/ddtrace/contrib/django/restframework.py +++ b/ddtrace/contrib/django/restframework.py @@ -2,7 +2,7 @@ from rest_framework.views import APIView -from ddtrace.util import unwrap +from ...utils.wrappers import unwrap def patch_restframework(tracer): diff --git a/ddtrace/contrib/elasticsearch/__init__.py b/ddtrace/contrib/elasticsearch/__init__.py index 47e0a32fc5d..a0c006bedbe 100644 --- a/ddtrace/contrib/elasticsearch/__init__.py +++ b/ddtrace/contrib/elasticsearch/__init__.py @@ -19,7 +19,7 @@ Pin.override(es.transport, service='elasticsearch-videos') es.indices.create(index='videos', ignore=400) """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['elasticsearch'] diff --git a/ddtrace/contrib/elasticsearch/patch.py b/ddtrace/contrib/elasticsearch/patch.py index a0064e65a67..fdc43066518 100644 --- a/ddtrace/contrib/elasticsearch/patch.py +++ b/ddtrace/contrib/elasticsearch/patch.py @@ -5,7 +5,7 @@ from . import metadata from .quantize import quantize -from ddtrace.util import unwrap +from ...utils.wrappers import unwrap from ...compat import urlencode from ...pin import Pin from ...ext import http diff --git a/ddtrace/contrib/elasticsearch/transport.py b/ddtrace/contrib/elasticsearch/transport.py index bf5295a5bce..c8f0ddf9633 100644 --- a/ddtrace/contrib/elasticsearch/transport.py +++ b/ddtrace/contrib/elasticsearch/transport.py @@ -1,17 +1,18 @@ from elasticsearch import Transport from elasticsearch.exceptions import TransportError -from .quantize import quantize from . import metadata +from .quantize import quantize + +from ...utils.deprecation import deprecated from ...compat import urlencode from ...ext import AppTypes, http -from ...util import deprecated DEFAULT_SERVICE = 'elasticsearch' SPAN_TYPE = 'elasticsearch' -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_transport(datadog_tracer, datadog_service=DEFAULT_SERVICE): datadog_tracer.set_service_info( diff --git a/ddtrace/contrib/falcon/__init__.py b/ddtrace/contrib/falcon/__init__.py index 4a7112b85ea..695f4036364 100644 --- a/ddtrace/contrib/falcon/__init__.py +++ b/ddtrace/contrib/falcon/__init__.py @@ -20,7 +20,7 @@ To enable distributed tracing when using autopatching, set the DATADOG_FALCON_DISTRIBUTED_TRACING environment variable to true. """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['falcon'] diff --git a/ddtrace/contrib/falcon/patch.py b/ddtrace/contrib/falcon/patch.py index 55f892b0aa1..95f7a18d1c9 100644 --- a/ddtrace/contrib/falcon/patch.py +++ b/ddtrace/contrib/falcon/patch.py @@ -5,7 +5,7 @@ from ddtrace import tracer from .middleware import TraceMiddleware -from ...util import asbool +from ...utils.formats import asbool def patch(): diff --git a/ddtrace/contrib/flask/__init__.py b/ddtrace/contrib/flask/__init__.py index 44c671cba9c..2852edc5c71 100644 --- a/ddtrace/contrib/flask/__init__.py +++ b/ddtrace/contrib/flask/__init__.py @@ -32,7 +32,8 @@ def home(): We suggest to enable it only for internal services where headers are under your control. """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['flask'] diff --git a/ddtrace/contrib/flask_cache/__init__.py b/ddtrace/contrib/flask_cache/__init__.py index 8ce17521359..d8cfe3036ff 100644 --- a/ddtrace/contrib/flask_cache/__init__.py +++ b/ddtrace/contrib/flask_cache/__init__.py @@ -32,7 +32,8 @@ def counter(): """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['flask_cache'] diff --git a/ddtrace/contrib/futures/__init__.py b/ddtrace/contrib/futures/__init__.py index 3fb3f29e054..99b2f9160e9 100644 --- a/ddtrace/contrib/futures/__init__.py +++ b/ddtrace/contrib/futures/__init__.py @@ -15,12 +15,11 @@ # or, when instrumenting all libraries patch_all(futures=True) """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['concurrent.futures'] - with require_modules(required_modules) as missing_modules: if not missing_modules: from .patch import patch, unpatch diff --git a/ddtrace/contrib/futures/patch.py b/ddtrace/contrib/futures/patch.py index 38c050bcf29..079311760fc 100644 --- a/ddtrace/contrib/futures/patch.py +++ b/ddtrace/contrib/futures/patch.py @@ -3,7 +3,7 @@ from wrapt import wrap_function_wrapper as _w from .threading import _wrap_submit -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u def patch(): diff --git a/ddtrace/contrib/gevent/__init__.py b/ddtrace/contrib/gevent/__init__.py index 253e6a4583f..5628e737a37 100644 --- a/ddtrace/contrib/gevent/__init__.py +++ b/ddtrace/contrib/gevent/__init__.py @@ -29,7 +29,7 @@ def worker_function(): with tracer.trace("greenlet.child_call") as child: ... """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['gevent'] diff --git a/ddtrace/contrib/httplib/patch.py b/ddtrace/contrib/httplib/patch.py index c7c9728811d..d87dc7ce7f5 100644 --- a/ddtrace/contrib/httplib/patch.py +++ b/ddtrace/contrib/httplib/patch.py @@ -8,7 +8,7 @@ from ...compat import httplib, PY2 from ...ext import http as ext_http from ...pin import Pin -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u span_name = 'httplib.request' if PY2 else 'http.client.request' diff --git a/ddtrace/contrib/mongoengine/__init__.py b/ddtrace/contrib/mongoengine/__init__.py index 36ef36b4fb3..554802f0877 100644 --- a/ddtrace/contrib/mongoengine/__init__.py +++ b/ddtrace/contrib/mongoengine/__init__.py @@ -17,7 +17,7 @@ Pin.override(client, service="mongo-master") """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['mongoengine'] diff --git a/ddtrace/contrib/mongoengine/patch.py b/ddtrace/contrib/mongoengine/patch.py index f305a38edd9..a623b806159 100644 --- a/ddtrace/contrib/mongoengine/patch.py +++ b/ddtrace/contrib/mongoengine/patch.py @@ -1,7 +1,7 @@ import mongoengine from .trace import WrappedConnect -from ddtrace.util import deprecated +from ...utils.deprecation import deprecated # Original connect function _connect = mongoengine.connect @@ -13,7 +13,7 @@ def patch(): def unpatch(): setattr(mongoengine, 'connect', _connect) -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def trace_mongoengine(*args, **kwargs): return _connect diff --git a/ddtrace/contrib/mysql/__init__.py b/ddtrace/contrib/mysql/__init__.py index 145aba85d8e..154c376ddc2 100644 --- a/ddtrace/contrib/mysql/__init__.py +++ b/ddtrace/contrib/mysql/__init__.py @@ -24,7 +24,7 @@ Help on mysql.connector can be found on: https://dev.mysql.com/doc/connector-python/en/ """ -from ..util import require_modules +from ...utils.importlib import require_modules # check `mysql-connector` availability required_modules = ['mysql.connector'] diff --git a/ddtrace/contrib/mysql/tracers.py b/ddtrace/contrib/mysql/tracers.py index e98d2800ca1..14640210bf2 100644 --- a/ddtrace/contrib/mysql/tracers.py +++ b/ddtrace/contrib/mysql/tracers.py @@ -1,7 +1,8 @@ import mysql.connector -from ddtrace.util import deprecated +from ...utils.deprecation import deprecated -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') + +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_mysql_connection(*args, **kwargs): return mysql.connector.MySQLConnection diff --git a/ddtrace/contrib/mysqldb/__init__.py b/ddtrace/contrib/mysqldb/__init__.py index a0bef757ee9..a75321fd756 100644 --- a/ddtrace/contrib/mysqldb/__init__.py +++ b/ddtrace/contrib/mysqldb/__init__.py @@ -24,7 +24,7 @@ Help on mysqlclient can be found on: https://mysqlclient.readthedocs.io/ """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['MySQLdb'] diff --git a/ddtrace/contrib/mysqldb/patch.py b/ddtrace/contrib/mysqldb/patch.py index 1828ce940e2..25996d3e45d 100644 --- a/ddtrace/contrib/mysqldb/patch.py +++ b/ddtrace/contrib/mysqldb/patch.py @@ -8,7 +8,7 @@ from ddtrace.contrib.dbapi import TracedConnection from ...ext import net, db -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u KWPOS_BY_TAG = { diff --git a/ddtrace/contrib/psycopg/__init__.py b/ddtrace/contrib/psycopg/__init__.py index c3bf80d27d0..7ff699636d7 100644 --- a/ddtrace/contrib/psycopg/__init__.py +++ b/ddtrace/contrib/psycopg/__init__.py @@ -17,7 +17,8 @@ # Use a pin to specify metadata related to this connection Pin.override(db, service='postgres-users') """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['psycopg2'] diff --git a/ddtrace/contrib/psycopg/connection.py b/ddtrace/contrib/psycopg/connection.py index 0edfaaf60f9..09550c58342 100644 --- a/ddtrace/contrib/psycopg/connection.py +++ b/ddtrace/contrib/psycopg/connection.py @@ -9,13 +9,13 @@ from ...ext import net from ...ext import sql from ...ext import AppTypes -from ...util import deprecated +from ...utils.deprecation import deprecated # 3p from psycopg2.extensions import connection, cursor -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def connection_factory(tracer, service="postgres"): """ Return a connection factory class that will can be used to trace postgres queries. diff --git a/ddtrace/contrib/pylibmc/__init__.py b/ddtrace/contrib/pylibmc/__init__.py index 0c44d1ee36e..798faffbc5d 100644 --- a/ddtrace/contrib/pylibmc/__init__.py +++ b/ddtrace/contrib/pylibmc/__init__.py @@ -19,7 +19,7 @@ Pin.override(client, service="memcached-sessions") """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['pylibmc'] diff --git a/ddtrace/contrib/pylons/__init__.py b/ddtrace/contrib/pylons/__init__.py index dc845bb3a3e..1023d69f372 100644 --- a/ddtrace/contrib/pylons/__init__.py +++ b/ddtrace/contrib/pylons/__init__.py @@ -18,7 +18,8 @@ traced_app = PylonsTraceMiddleware(app, tracer, service='my-pylons-app', distributed_tracing=True) """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['pylons.wsgiapp'] diff --git a/ddtrace/contrib/pylons/patch.py b/ddtrace/contrib/pylons/patch.py index 8fe50fd6709..88b6ca73903 100644 --- a/ddtrace/contrib/pylons/patch.py +++ b/ddtrace/contrib/pylons/patch.py @@ -5,7 +5,7 @@ from ddtrace import tracer, Pin from .middleware import PylonsTraceMiddleware -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u def patch(): diff --git a/ddtrace/contrib/pymongo/__init__.py b/ddtrace/contrib/pymongo/__init__.py index 957c23c699b..7c67dc7c8fb 100644 --- a/ddtrace/contrib/pymongo/__init__.py +++ b/ddtrace/contrib/pymongo/__init__.py @@ -21,7 +21,8 @@ client = pymongo.MongoClient() pin = Pin.override(client, service="mongo-master") """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['pymongo'] diff --git a/ddtrace/contrib/pymongo/client.py b/ddtrace/contrib/pymongo/client.py index cfacb435eed..e3d1811f599 100644 --- a/ddtrace/contrib/pymongo/client.py +++ b/ddtrace/contrib/pymongo/client.py @@ -9,11 +9,11 @@ # project import ddtrace +from ...utils.deprecation import deprecated from ...compat import iteritems from ...ext import AppTypes from ...ext import mongo as mongox from ...ext import net as netx -from ...util import deprecated from .parse import parse_spec, parse_query, parse_msg # Original Client class @@ -22,7 +22,7 @@ log = logging.getLogger(__name__) -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def trace_mongo_client(client, tracer, service=mongox.TYPE): tracer.set_service_info( service=service, diff --git a/ddtrace/contrib/pymysql/__init__.py b/ddtrace/contrib/pymysql/__init__.py index 6f5ca695eb1..0904e1e4c0c 100644 --- a/ddtrace/contrib/pymysql/__init__.py +++ b/ddtrace/contrib/pymysql/__init__.py @@ -18,7 +18,8 @@ Pin.override(conn, service='pymysql-users') """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['pymysql'] diff --git a/ddtrace/contrib/pymysql/tracers.py b/ddtrace/contrib/pymysql/tracers.py index 11aed7c0525..d4d95bec557 100644 --- a/ddtrace/contrib/pymysql/tracers.py +++ b/ddtrace/contrib/pymysql/tracers.py @@ -1,7 +1,8 @@ import pymysql.connections -from ddtrace.util import deprecated +from ...utils.deprecation import deprecated -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') + +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_pymysql_connection(*args, **kwargs): return pymysql.connections.Connection diff --git a/ddtrace/contrib/pyramid/__init__.py b/ddtrace/contrib/pyramid/__init__.py index 9c70a9bd5af..9dfaae1cbf3 100644 --- a/ddtrace/contrib/pyramid/__init__.py +++ b/ddtrace/contrib/pyramid/__init__.py @@ -38,7 +38,8 @@ """ -from ..util import require_modules +from ...utils.importlib import require_modules + required_modules = ['pyramid'] diff --git a/ddtrace/contrib/pyramid/patch.py b/ddtrace/contrib/pyramid/patch.py index fd77a7f8eba..4a9b7b72b2f 100644 --- a/ddtrace/contrib/pyramid/patch.py +++ b/ddtrace/contrib/pyramid/patch.py @@ -2,7 +2,7 @@ from .trace import trace_pyramid, DD_TWEEN_NAME from .constants import SETTINGS_SERVICE, SETTINGS_DISTRIBUTED_TRACING -from ...util import asbool +from ...utils.formats import asbool import pyramid.config from pyramid.path import caller_package diff --git a/ddtrace/contrib/redis/__init__.py b/ddtrace/contrib/redis/__init__.py index 84cc430321b..50622016fb0 100644 --- a/ddtrace/contrib/redis/__init__.py +++ b/ddtrace/contrib/redis/__init__.py @@ -17,7 +17,7 @@ Pin.override(client, service='redis-queue') """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['redis', 'redis.client'] diff --git a/ddtrace/contrib/redis/patch.py b/ddtrace/contrib/redis/patch.py index a35ac0fffd5..37f0fa1cc78 100644 --- a/ddtrace/contrib/redis/patch.py +++ b/ddtrace/contrib/redis/patch.py @@ -1,12 +1,11 @@ - # 3p import redis import wrapt # project -from ddtrace import Pin -from ddtrace.ext import redis as redisx -from ddtrace.util import unwrap +from ...pin import Pin +from ...ext import redis as redisx +from ...utils.wrappers import unwrap from .util import format_command_args, _extract_conn_tags diff --git a/ddtrace/contrib/redis/tracers.py b/ddtrace/contrib/redis/tracers.py index 737bdc2c642..62912ce06cd 100644 --- a/ddtrace/contrib/redis/tracers.py +++ b/ddtrace/contrib/redis/tracers.py @@ -1,18 +1,20 @@ from redis import StrictRedis -from ...util import deprecated +from ...utils.deprecation import deprecated + DEFAULT_SERVICE = 'redis' -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_redis(ddtracer, service=DEFAULT_SERVICE, meta=None): return _get_traced_redis(ddtracer, StrictRedis, service, meta) -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') + +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def get_traced_redis_from(ddtracer, baseclass, service=DEFAULT_SERVICE, meta=None): return _get_traced_redis(ddtracer, baseclass, service, meta) + def _get_traced_redis(ddtracer, baseclass, service, meta): return baseclass - diff --git a/ddtrace/contrib/requests/__init__.py b/ddtrace/contrib/requests/__init__.py index 753494a1085..8720d43232a 100644 --- a/ddtrace/contrib/requests/__init__.py +++ b/ddtrace/contrib/requests/__init__.py @@ -25,10 +25,9 @@ session.distributed_tracing = True session.get("http://host.lan/webservice") """ +from ...utils.importlib import require_modules -from ..util import require_modules - required_modules = ['requests'] with require_modules(required_modules) as missing_modules: diff --git a/ddtrace/contrib/requests/patch.py b/ddtrace/contrib/requests/patch.py index 5139a5a8673..73eaa5cc0c0 100644 --- a/ddtrace/contrib/requests/patch.py +++ b/ddtrace/contrib/requests/patch.py @@ -2,13 +2,13 @@ import logging import wrapt -import requests - import ddtrace +import requests from ...ext import http from ...propagation.http import HTTPPropagator -from ...util import asbool, unwrap as _u +from ...utils.formats import asbool +from ...utils.wrappers import unwrap as _u log = logging.getLogger(__name__) diff --git a/ddtrace/contrib/sqlalchemy/__init__.py b/ddtrace/contrib/sqlalchemy/__init__.py index 99096f90f7e..b47586040ca 100644 --- a/ddtrace/contrib/sqlalchemy/__init__.py +++ b/ddtrace/contrib/sqlalchemy/__init__.py @@ -15,10 +15,9 @@ # Use a PIN to specify metadata related to this engine Pin.override(engine, service='replica-db') """ +from ...utils.importlib import require_modules -from ..util import require_modules - required_modules = ['sqlalchemy', 'sqlalchemy.event'] with require_modules(required_modules) as missing_modules: diff --git a/ddtrace/contrib/sqlalchemy/patch.py b/ddtrace/contrib/sqlalchemy/patch.py index 63d34a4bef6..db33ce11aa5 100644 --- a/ddtrace/contrib/sqlalchemy/patch.py +++ b/ddtrace/contrib/sqlalchemy/patch.py @@ -1,9 +1,9 @@ import sqlalchemy from wrapt import wrap_function_wrapper as _w -from ddtrace.util import unwrap from .engine import _wrap_create_engine +from ...utils.wrappers import unwrap def patch(): diff --git a/ddtrace/contrib/sqlite3/connection.py b/ddtrace/contrib/sqlite3/connection.py index f26f70f686d..8088ab2c3df 100644 --- a/ddtrace/contrib/sqlite3/connection.py +++ b/ddtrace/contrib/sqlite3/connection.py @@ -1,7 +1,8 @@ from sqlite3 import Connection -from ddtrace.util import deprecated +from ...utils.deprecation import deprecated -@deprecated(message='Use patching instead (see the docs).', version='0.6.0') + +@deprecated(message='Use patching instead (see the docs).', version='1.0.0') def connection_factory(*args, **kwargs): return Connection diff --git a/ddtrace/contrib/tornado/__init__.py b/ddtrace/contrib/tornado/__init__.py index e2160f2da56..406847bdc03 100644 --- a/ddtrace/contrib/tornado/__init__.py +++ b/ddtrace/contrib/tornado/__init__.py @@ -75,7 +75,7 @@ def notify(self): * ``agent_hostname`` (default: `localhost`): define the hostname of the APM agent. * ``agent_port`` (default: `8126`): define the port of the APM agent. """ -from ..util import require_modules +from ...utils.importlib import require_modules required_modules = ['tornado'] diff --git a/ddtrace/contrib/tornado/patch.py b/ddtrace/contrib/tornado/patch.py index 85a11fa8187..2a48ed2bb2e 100644 --- a/ddtrace/contrib/tornado/patch.py +++ b/ddtrace/contrib/tornado/patch.py @@ -4,7 +4,7 @@ from wrapt import wrap_function_wrapper as _w from . import handlers, application, decorators, template, compat, context_provider -from ...util import unwrap as _u +from ...utils.wrappers import unwrap as _u def patch(): diff --git a/ddtrace/contrib/util.py b/ddtrace/contrib/util.py index b2c689daebd..ae96cc4c12d 100644 --- a/ddtrace/contrib/util.py +++ b/ddtrace/contrib/util.py @@ -1,33 +1,16 @@ -from importlib import import_module - - -class require_modules(object): - """ - Context manager to check the availability of required modules. - """ - def __init__(self, modules): - self._missing_modules = [] - for module in modules: - try: - import_module(module) - except ImportError: - self._missing_modules.append(module) - - def __enter__(self): - return self._missing_modules - - def __exit__(self, exc_type, exc_value, traceback): - return False - - -def func_name(f): - """ - Return a human readable version of the function's name. - """ - if hasattr(f, '__module__'): - return "%s.%s" % (f.__module__, getattr(f, '__name__', f.__class__.__name__)) - return getattr(f, '__name__', f.__class__.__name__) - - -def module_name(instance): - return instance.__class__.__module__.split('.')[0] +# [Backward compatibility]: keep importing modules functions +from ..utils.deprecation import deprecation +from ..utils.importlib import require_modules, func_name, module_name + + +deprecation( + name='ddtrace.contrib.util', + message='Use `ddtrace.utils.importlib` module instead', + version='1.0.0', +) + +__all__ = [ + 'require_modules', + 'func_name', + 'module_name', +] diff --git a/ddtrace/util.py b/ddtrace/util.py index 58e430e8a47..5151769877e 100644 --- a/ddtrace/util.py +++ b/ddtrace/util.py @@ -1,137 +1,20 @@ -import os -import inspect -import logging -import wrapt - -from functools import wraps - - -def deprecated(message='', version=None): - """Function decorator to report a deprecated function""" - def decorator(func): - @wraps(func) - def wrapper(*args, **kwargs): - logger = logging.getLogger(func.__module__) - logger.warning("%s is deprecated and will be remove in future versions%s. %s", - func.__name__, - ' (%s)' % version if version else '', - message) - return func(*args, **kwargs) - return wrapper - return decorator - - -def deep_getattr(obj, attr_string, default=None): - """ - Returns the attribute of `obj` at the dotted path given by `attr_string` - If no such attribute is reachable, returns `default` - - >>> deep_getattr(cass, "cluster") - >> deep_getattr(cass, "cluster.metadata.partitioner") - u'org.apache.cassandra.dht.Murmur3Partitioner' - - >>> deep_getattr(cass, "i.dont.exist", default="default") - 'default' - """ - attrs = attr_string.split('.') - for attr in attrs: - try: - obj = getattr(obj, attr) - except AttributeError: - return default - - return obj - - -def safe_patch(patchable, key, patch_func, service, meta, tracer): - """ takes patch_func (signature: takes the orig_method that is - wrapped in the monkey patch == UNBOUND + service and meta) and - attach the patched result to patchable at patchable.key - - - - if this is the module/class we can rely on methods being unbound, and just have to - update the __dict__ - - - if this is an instance, we have to unbind the current and rebind our - patched method - - - If patchable is an instance and if we've already patched at the module/class level - then patchable[key] contains an already patched command! - To workaround this, check if patchable or patchable.__class__ are _dogtraced - If is isn't, nothing to worry about, patch the key as usual - But if it is, search for a "__dd_orig_{key}" method on the class, which is - the original unpatched method we wish to trace. - - """ - def _get_original_method(thing, key): - orig = None - if hasattr(thing, '_dogtraced'): - # Search for original method - orig = getattr(thing, "__dd_orig_{}".format(key), None) - else: - orig = getattr(thing, key) - # Set it for the next time we attempt to patch `thing` - setattr(thing, "__dd_orig_{}".format(key), orig) - - return orig - - if inspect.isclass(patchable) or inspect.ismodule(patchable): - orig = _get_original_method(patchable, key) - if not orig: - # Should never happen - return - elif hasattr(patchable, '__class__'): - orig = _get_original_method(patchable.__class__, key) - if not orig: - # Should never happen - return - else: - return - - dest = patch_func(orig, service, meta, tracer) - - if inspect.isclass(patchable) or inspect.ismodule(patchable): - setattr(patchable, key, dest) - elif hasattr(patchable, '__class__'): - setattr(patchable, key, dest.__get__(patchable, patchable.__class__)) - - -def asbool(value): - """Convert the given String to a boolean object. Accepted - values are `True` and `1`.""" - if value is None: - return False - - if isinstance(value, bool): - return value - - return value.lower() in ("true", "1") - - -def get_env(integration, variable, default=None): - """Retrieves environment variables value for the given integration. It must be used - for consistency between integrations. The implementation is backward compatible - with legacy nomenclature: - * `DATADOG_` is a legacy prefix with lower priority - * `DD_` environment variables have the highest priority - * the environment variable is built concatenating `integration` and `variable` - arguments - * return `default` otherwise - """ - key = '{}_{}'.format(integration, variable).upper() - legacy_env = 'DATADOG_{}'.format(key) - env = 'DD_{}'.format(key) - - # [Backward compatibility]: `DATADOG_` variables should be supported; - # add a deprecation warning later if it's used, so that we can drop the key - # in newer releases. - value = os.getenv(env) or os.getenv(legacy_env) - return value if value else default - - -def unwrap(obj, attr): - f = getattr(obj, attr, None) - if f and isinstance(f, wrapt.ObjectProxy) and hasattr(f, '__wrapped__'): - setattr(obj, attr, f.__wrapped__) +# [Backward compatibility]: keep importing modules functions +from .utils.deprecation import deprecated, deprecation +from .utils.formats import asbool, deep_getattr, get_env +from .utils.wrappers import safe_patch, unwrap + + +deprecation( + name='ddtrace.util', + message='Use `ddtrace.utils` package instead', + version='1.0.0', +) + +__all__ = [ + 'deprecated', + 'asbool', + 'deep_getattr', + 'get_env', + 'safe_patch', + 'unwrap', +] diff --git a/ddtrace/utils/__init__.py b/ddtrace/utils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ddtrace/utils/deprecation.py b/ddtrace/utils/deprecation.py new file mode 100644 index 00000000000..ea852cc76a4 --- /dev/null +++ b/ddtrace/utils/deprecation.py @@ -0,0 +1,60 @@ +import warnings + +from functools import wraps + + +class RemovedInDDTrace10Warning(DeprecationWarning): + pass + + +def format_message(name, message, version): + """Message formatter to create `DeprecationWarning` messages + such as: + + 'fn' is deprecated and will be remove in future versions (1.0). + """ + return "'{}' is deprecated and will be remove in future versions{}. {}".format( + name, + ' ({})'.format(version) if version else '', + message, + ) + + +def warn(message, stacklevel=2): + """Helper function used as a ``DeprecationWarning``.""" + warnings.warn(message, RemovedInDDTrace10Warning, stacklevel=stacklevel) + + +def deprecation(name='', message='', version=None): + """Function to report a ``DeprecationWarning``. Bear in mind that `DeprecationWarning` + are ignored by default so they're not available in user logs. To show them, + the application must be launched with a special flag: + + $ python -Wall script.py + + This approach is used by most of the frameworks, including Django + (ref: https://docs.djangoproject.com/en/2.0/howto/upgrade-version/#resolving-deprecation-warnings) + """ + msg = format_message(name, message, version) + warn(msg, stacklevel=4) + + +def deprecated(message='', version=None): + """Decorator function to report a ``DeprecationWarning``. Bear + in mind that `DeprecationWarning` are ignored by default so they're + not available in user logs. To show them, the application must be launched + with a special flag: + + $ python -Wall script.py + + This approach is used by most of the frameworks, including Django + (ref: https://docs.djangoproject.com/en/2.0/howto/upgrade-version/#resolving-deprecation-warnings) + """ + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + msg = format_message(func.__name__, message, version) + warn(msg, stacklevel=3) + return func(*args, **kwargs) + return wrapper + return decorator diff --git a/ddtrace/utils/formats.py b/ddtrace/utils/formats.py new file mode 100644 index 00000000000..84ef093ca84 --- /dev/null +++ b/ddtrace/utils/formats.py @@ -0,0 +1,58 @@ +import os + + +def get_env(integration, variable, default=None): + """Retrieves environment variables value for the given integration. It must be used + for consistency between integrations. The implementation is backward compatible + with legacy nomenclature: + * `DATADOG_` is a legacy prefix with lower priority + * `DD_` environment variables have the highest priority + * the environment variable is built concatenating `integration` and `variable` + arguments + * return `default` otherwise + """ + key = '{}_{}'.format(integration, variable).upper() + legacy_env = 'DATADOG_{}'.format(key) + env = 'DD_{}'.format(key) + + # [Backward compatibility]: `DATADOG_` variables should be supported; + # add a deprecation warning later if it's used, so that we can drop the key + # in newer releases. + value = os.getenv(env) or os.getenv(legacy_env) + return value if value else default + + +def deep_getattr(obj, attr_string, default=None): + """ + Returns the attribute of `obj` at the dotted path given by `attr_string` + If no such attribute is reachable, returns `default` + + >>> deep_getattr(cass, "cluster") + >> deep_getattr(cass, "cluster.metadata.partitioner") + u'org.apache.cassandra.dht.Murmur3Partitioner' + + >>> deep_getattr(cass, "i.dont.exist", default="default") + 'default' + """ + attrs = attr_string.split('.') + for attr in attrs: + try: + obj = getattr(obj, attr) + except AttributeError: + return default + + return obj + + +def asbool(value): + """Convert the given String to a boolean object. Accepted + values are `True` and `1`.""" + if value is None: + return False + + if isinstance(value, bool): + return value + + return value.lower() in ("true", "1") diff --git a/ddtrace/utils/importlib.py b/ddtrace/utils/importlib.py new file mode 100644 index 00000000000..34bcbb2da7e --- /dev/null +++ b/ddtrace/utils/importlib.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import + +from importlib import import_module + + +class require_modules(object): + """Context manager to check the availability of required modules.""" + def __init__(self, modules): + self._missing_modules = [] + for module in modules: + try: + import_module(module) + except ImportError: + self._missing_modules.append(module) + + def __enter__(self): + return self._missing_modules + + def __exit__(self, exc_type, exc_value, traceback): + return False + + +def func_name(f): + """Return a human readable version of the function's name.""" + if hasattr(f, '__module__'): + return "%s.%s" % (f.__module__, getattr(f, '__name__', f.__class__.__name__)) + return getattr(f, '__name__', f.__class__.__name__) + + +def module_name(instance): + """Return the instance module name.""" + return instance.__class__.__module__.split('.')[0] diff --git a/ddtrace/utils/wrappers.py b/ddtrace/utils/wrappers.py new file mode 100644 index 00000000000..ce543800069 --- /dev/null +++ b/ddtrace/utils/wrappers.py @@ -0,0 +1,65 @@ +import wrapt +import inspect + +from .deprecation import deprecated + + +def unwrap(obj, attr): + f = getattr(obj, attr, None) + if f and isinstance(f, wrapt.ObjectProxy) and hasattr(f, '__wrapped__'): + setattr(obj, attr, f.__wrapped__) + + +@deprecated('`wrapt` library is used instead', version='1.0.0') +def safe_patch(patchable, key, patch_func, service, meta, tracer): + """ takes patch_func (signature: takes the orig_method that is + wrapped in the monkey patch == UNBOUND + service and meta) and + attach the patched result to patchable at patchable.key + + + - if this is the module/class we can rely on methods being unbound, and just have to + update the __dict__ + + - if this is an instance, we have to unbind the current and rebind our + patched method + + - If patchable is an instance and if we've already patched at the module/class level + then patchable[key] contains an already patched command! + To workaround this, check if patchable or patchable.__class__ are _dogtraced + If is isn't, nothing to worry about, patch the key as usual + But if it is, search for a "__dd_orig_{key}" method on the class, which is + the original unpatched method we wish to trace. + + """ + def _get_original_method(thing, key): + orig = None + if hasattr(thing, '_dogtraced'): + # Search for original method + orig = getattr(thing, "__dd_orig_{}".format(key), None) + else: + orig = getattr(thing, key) + # Set it for the next time we attempt to patch `thing` + setattr(thing, "__dd_orig_{}".format(key), orig) + + return orig + + if inspect.isclass(patchable) or inspect.ismodule(patchable): + orig = _get_original_method(patchable, key) + if not orig: + # Should never happen + return + elif hasattr(patchable, '__class__'): + orig = _get_original_method(patchable.__class__, key) + if not orig: + # Should never happen + return + else: + return + + dest = patch_func(orig, service, meta, tracer) + + if inspect.isclass(patchable) or inspect.ismodule(patchable): + setattr(patchable, key, dest) + elif hasattr(patchable, '__class__'): + setattr(patchable, key, dest.__get__(patchable, patchable.__class__)) + diff --git a/docs/index.rst b/docs/index.rst index eb7f592eb5b..6efce09e8f1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -421,6 +421,22 @@ Information will be lost but it allows to control any potential performance impa tracer.sampler = RateSampler(sample_rate) +Resolving deprecation warnings +------------------------------ +Before upgrading, it’s a good idea to resolve any deprecation warnings raised by your project. +These warnings must be fixed before upgrading, otherwise ``ddtrace`` library will not work +as expected. Our deprecation messages include the version where the behavior is altered or +removed. + +In Python, deprecation warnings are silenced by default, and to turn them on you may add the +following flag or environment variable:: + + $ python -Wall app.py + + # or + + $ PYTHONWARNINGS=all python app.py + Advanced Usage -------------- diff --git a/tests/contrib/test_utils.py b/tests/contrib/test_utils.py index 91c6610d7c6..3bbda22de2c 100644 --- a/tests/contrib/test_utils.py +++ b/tests/contrib/test_utils.py @@ -1,8 +1,9 @@ from nose.tools import eq_ -from ddtrace.contrib.util import func_name -from ddtrace.util import asbool from functools import partial +from ddtrace.utils.importlib import func_name +from ddtrace.utils.formats import asbool + class SomethingCallable(object): """ diff --git a/tests/test_utils.py b/tests/test_utils.py index 4f312164216..4c2e19c468b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,9 +1,11 @@ import os import unittest +import warnings from nose.tools import eq_, ok_ -from ddtrace.util import asbool, get_env +from ddtrace.utils.deprecation import deprecation, deprecated, format_message +from ddtrace.utils.formats import asbool, get_env class TestUtilities(unittest.TestCase): @@ -46,3 +48,39 @@ def test_get_env_key_priority(self): os.environ['DATADOG_REQUESTS_DISTRIBUTED_TRACING'] = 'lowest' value = get_env('requests', 'distributed_tracing') eq_(value, 'highest') + + def test_deprecation_formatter(self): + # ensure the formatter returns the proper message + msg = format_message( + 'deprecated_function', + 'use something else instead', + '1.0.0', + ) + expected = "'deprecated_function' is deprecated and will be remove in future versions (1.0.0). use something else instead" + eq_(msg, expected) + + def test_deprecation(self): + # ensure `deprecation` properly raise a DeprecationWarning + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + deprecation( + name='fn', + message='message', + version='1.0.0' + ) + ok_(len(w) == 1) + ok_(issubclass(w[-1].category, DeprecationWarning)) + ok_('message' in str(w[-1].message)) + + def test_deprecated_decorator(self): + # ensure `deprecated` decorator properly raise a DeprecationWarning + @deprecated('decorator', version='1.0.0') + def fxn(): + pass + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + fxn() + ok_(len(w) == 1) + ok_(issubclass(w[-1].category, DeprecationWarning)) + ok_('decorator' in str(w[-1].message))