diff --git a/.circleci/config.yml b/.circleci/config.yml index 09150d9cd..1858bc398 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -202,17 +202,6 @@ jobs: - *persist_to_workspace_step - *save_cache_step - pylons: - docker: - - *test_runner - resource_class: *resource_class - steps: - - checkout - - *restore_cache_step - - run: scripts/run-tox-scenario '^pylons_contrib-' - - *persist_to_workspace_step - - *save_cache_step - aiohttp: docker: - *test_runner @@ -318,7 +307,7 @@ jobs: steps: - checkout - *restore_cache_step - - run: tox -e 'falcon_contrib{,_autopatch}-{py27,py34,py35,py36}-falcon{10,11,12,13,14}' --result-json /tmp/falcon.results + - run: tox -e 'falcon_contrib{,_autopatch}-{py34,py35,py36}-falcon{10,11,12,13,14}' --result-json /tmp/falcon.results - *persist_to_workspace_step - *save_cache_step @@ -720,7 +709,7 @@ jobs: - run: command: | mkdir -p /tmp/test-reports - tox -e 'benchmarks-{py27,py34,py35,py36,py37}' --result-json /tmp/benchmarks.results -- --benchmark-storage=file:///tmp/test-reports/ --benchmark-autosave + tox -e 'benchmarks-{py34,py35,py36,py37}' --result-json /tmp/benchmarks.results -- --benchmark-storage=file:///tmp/test-reports/ --benchmark-autosave - store_test_results: path: /tmp/test-reports - store_artifacts: @@ -985,10 +974,6 @@ workflows: requires: - flake8 - black - - pylons: - requires: - - flake8 - - black - pymemcache: requires: - flake8 @@ -1097,7 +1082,6 @@ workflows: - opentracer - psycopg - pylibmc - - pylons - pymemcache - pymongo - pymysql diff --git a/conftest.py b/conftest.py index 9e3fef991..34e2d520d 100644 --- a/conftest.py +++ b/conftest.py @@ -11,7 +11,7 @@ import sys import pytest -PY_DIR_PATTERN = re.compile(r"^py[23][0-9]$") +PY_DIR_PATTERN = re.compile(r"^py3[0-9]$") # Determine if the folder should be ignored @@ -27,7 +27,6 @@ def pytest_ignore_collect(path, config): Example:: File: tests/contrib/vertica/py35/test.py - Python 2.7: Skip Python 3.4: Skip Python 3.5: Collect Python 3.6: Collect diff --git a/ddtrace/bootstrap/sitecustomize.py b/ddtrace/bootstrap/sitecustomize.py index cd3fecc36..029f63e68 100644 --- a/ddtrace/bootstrap/sitecustomize.py +++ b/ddtrace/bootstrap/sitecustomize.py @@ -43,7 +43,6 @@ EXTRA_PATCHED_MODULES = { "django": True, "falcon": True, "flask": True, - "pylons": True, "pyramid": True, } diff --git a/ddtrace/commands/ddtrace_run.py b/ddtrace/commands/ddtrace_run.py index a13bdec3c..53ed8311c 100755 --- a/ddtrace/commands/ddtrace_run.py +++ b/ddtrace/commands/ddtrace_run.py @@ -31,7 +31,7 @@ Available environment variables: DATADOG_TRACE_AGENT_PORT=8126: override the port that the default tracer will submit to (default: 8126) DATADOG_SERVICE_NAME : override the service name to be used for this program (no default) This value is passed through when setting up middleware for web framework integrations. - (e.g. pylons, flask, django) + (e.g. flask, django) For tracing without a web integration, prefer setting the service name in code. DATADOG_PRIORITY_SAMPLING=true|false : (default: false): enables Priority Sampling. """ # noqa: E501 diff --git a/ddtrace/contrib/flask/patch.py b/ddtrace/contrib/flask/patch.py index 243a8cc56..da9ca6ce0 100644 --- a/ddtrace/contrib/flask/patch.py +++ b/ddtrace/contrib/flask/patch.py @@ -156,13 +156,6 @@ def patch(): for signal in signals: module = 'flask' - # v0.9 missed importing `appcontext_tearing_down` in `flask/__init__.py` - # https://github.com/pallets/flask/blob/0.9/flask/__init__.py#L35-L37 - # https://github.com/pallets/flask/blob/0.9/flask/signals.py#L52 - # DEV: Version 0.9 doesn't have a patch version - if flask_version <= (0, 9) and signal == 'appcontext_tearing_down': - module = 'flask.signals' - # DEV: Patch `receivers_for` instead of `connect` to ensure we don't mess with `disconnect` _w(module, '{}.receivers_for'.format(signal), traced_signal_receivers_for(signal)) @@ -241,13 +234,6 @@ def unpatch(): # Handle 'flask.request_started.receivers_for' obj = flask - # v0.9.0 missed importing `appcontext_tearing_down` in `flask/__init__.py` - # https://github.com/pallets/flask/blob/0.9/flask/__init__.py#L35-L37 - # https://github.com/pallets/flask/blob/0.9/flask/signals.py#L52 - # DEV: Version 0.9 doesn't have a patch version - if flask_version <= (0, 9) and prop == 'appcontext_tearing_down.receivers_for': - obj = flask.signals - if '.' in prop: attr, _, prop = prop.partition('.') obj = getattr(obj, attr, object()) diff --git a/ddtrace/contrib/pylons/__init__.py b/ddtrace/contrib/pylons/__init__.py deleted file mode 100644 index 88339224d..000000000 --- a/ddtrace/contrib/pylons/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -The pylons trace middleware will track request timings. To -install the middleware, prepare your WSGI application and do -the following:: - - from pylons.wsgiapp import PylonsApp - - from ddtrace import tracer - from ddtrace.contrib.pylons import PylonsTraceMiddleware - - app = PylonsApp(...) - - traced_app = PylonsTraceMiddleware(app, tracer, service='my-pylons-app') - -Then you can define your routes and views as usual. -""" - -from ...utils.importlib import require_modules - - -required_modules = ['pylons.wsgiapp'] - -with require_modules(required_modules) as missing_modules: - if not missing_modules: - from .middleware import PylonsTraceMiddleware - from .patch import patch, unpatch - - __all__ = [ - 'patch', - 'unpatch', - 'PylonsTraceMiddleware', - ] diff --git a/ddtrace/contrib/pylons/compat.py b/ddtrace/contrib/pylons/compat.py deleted file mode 100644 index f49480d05..000000000 --- a/ddtrace/contrib/pylons/compat.py +++ /dev/null @@ -1,8 +0,0 @@ -try: - from pylons.templating import render_mako # noqa - - # Pylons > 0.9.7 - legacy_pylons = False -except ImportError: - # Pylons <= 0.9.7 - legacy_pylons = True diff --git a/ddtrace/contrib/pylons/constants.py b/ddtrace/contrib/pylons/constants.py deleted file mode 100644 index ae0fb4249..000000000 --- a/ddtrace/contrib/pylons/constants.py +++ /dev/null @@ -1 +0,0 @@ -CONFIG_MIDDLEWARE = '__datadog_middleware' diff --git a/ddtrace/contrib/pylons/middleware.py b/ddtrace/contrib/pylons/middleware.py deleted file mode 100644 index 2d2d5df35..000000000 --- a/ddtrace/contrib/pylons/middleware.py +++ /dev/null @@ -1,110 +0,0 @@ -import sys - -from webob import Request -from pylons import config - -from .renderer import trace_rendering -from .constants import CONFIG_MIDDLEWARE - -from ...compat import reraise -from ...constants import ANALYTICS_SAMPLE_RATE_KEY -from ...ext import SpanTypes, http -from ...internal.logger import get_logger -from ...propagation.http import HTTPPropagator -from ...settings import config as ddconfig - - -log = get_logger(__name__) - - -class PylonsTraceMiddleware(object): - - def __init__(self, app, tracer, service='pylons', distributed_tracing=True): - self.app = app - self._service = service - self._distributed_tracing = distributed_tracing - self._tracer = tracer - - # register middleware reference - config[CONFIG_MIDDLEWARE] = self - - # add template tracing - trace_rendering() - - def __call__(self, environ, start_response): - if self._distributed_tracing: - # retrieve distributed tracing headers - request = Request(environ) - propagator = HTTPPropagator() - context = propagator.extract(request.headers) - # only need to active the new context if something was propagated - if context.trace_id: - self._tracer.context_provider.activate(context) - - with self._tracer.trace('pylons.request', service=self._service, span_type=SpanTypes.WEB) as span: - # Set the service in tracer.trace() as priority sampling requires it to be - # set as early as possible when different services share one single agent. - - # set analytics sample rate with global config enabled - span.set_tag( - ANALYTICS_SAMPLE_RATE_KEY, - ddconfig.pylons.get_analytics_sample_rate(use_global_config=True) - ) - - if not span.sampled: - return self.app(environ, start_response) - - # tentative on status code, otherwise will be caught by except below - def _start_response(status, *args, **kwargs): - """ a patched response callback which will pluck some metadata. """ - http_code = int(status.split()[0]) - span.set_tag(http.STATUS_CODE, http_code) - if http_code >= 500: - span.error = 1 - return start_response(status, *args, **kwargs) - - try: - return self.app(environ, _start_response) - except Exception as e: - # store current exceptions info so we can re-raise it later - (typ, val, tb) = sys.exc_info() - - # e.code can either be a string or an int - code = getattr(e, 'code', 500) - try: - code = int(code) - if not 100 <= code < 600: - code = 500 - except Exception: - code = 500 - span.set_tag(http.STATUS_CODE, code) - span.error = 1 - - # re-raise the original exception with its original traceback - reraise(typ, val, tb=tb) - except SystemExit: - span.set_tag(http.STATUS_CODE, 500) - span.error = 1 - raise - finally: - controller = environ.get('pylons.routes_dict', {}).get('controller') - action = environ.get('pylons.routes_dict', {}).get('action') - - # There are cases where users re-route requests and manually - # set resources. If this is so, don't do anything, otherwise - # set the resource to the controller / action that handled it. - if span.resource == span.name: - span.resource = '%s.%s' % (controller, action) - - span.set_tags({ - http.METHOD: environ.get('REQUEST_METHOD'), - http.URL: '%s://%s:%s%s' % (environ.get('wsgi.url_scheme'), - environ.get('SERVER_NAME'), - environ.get('SERVER_PORT'), - environ.get('PATH_INFO')), - 'pylons.user': environ.get('REMOTE_USER', ''), - 'pylons.route.controller': controller, - 'pylons.route.action': action, - }) - if ddconfig.pylons.trace_query_string: - span.set_tag(http.QUERY_STRING, environ.get('QUERY_STRING')) diff --git a/ddtrace/contrib/pylons/patch.py b/ddtrace/contrib/pylons/patch.py deleted file mode 100644 index ad437d8c2..000000000 --- a/ddtrace/contrib/pylons/patch.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -from ddtrace.vendor import wrapt -import pylons.wsgiapp - -from ddtrace import tracer, Pin - -from .middleware import PylonsTraceMiddleware -from ...utils.formats import asbool, get_env -from ...utils.wrappers import unwrap as _u - - -def patch(): - """Instrument Pylons applications""" - if getattr(pylons.wsgiapp, '_datadog_patch', False): - return - - setattr(pylons.wsgiapp, '_datadog_patch', True) - wrapt.wrap_function_wrapper('pylons.wsgiapp', 'PylonsApp.__init__', traced_init) - - -def unpatch(): - """Disable Pylons tracing""" - if not getattr(pylons.wsgiapp, '__datadog_patch', False): - return - setattr(pylons.wsgiapp, '__datadog_patch', False) - - _u(pylons.wsgiapp.PylonsApp, '__init__') - - -def traced_init(wrapped, instance, args, kwargs): - wrapped(*args, **kwargs) - - # set tracing options and create the TraceMiddleware - service = os.environ.get('DATADOG_SERVICE_NAME', 'pylons') - distributed_tracing = asbool(get_env('pylons', 'distributed_tracing', True)) - Pin(service=service, tracer=tracer).onto(instance) - traced_app = PylonsTraceMiddleware(instance, tracer, service=service, distributed_tracing=distributed_tracing) - - # re-order the middleware stack so that the first middleware is ours - traced_app.app = instance.app - instance.app = traced_app diff --git a/ddtrace/contrib/pylons/renderer.py b/ddtrace/contrib/pylons/renderer.py deleted file mode 100644 index 45ae49c80..000000000 --- a/ddtrace/contrib/pylons/renderer.py +++ /dev/null @@ -1,36 +0,0 @@ -import pylons - -from pylons import config - -from ddtrace.vendor.wrapt import wrap_function_wrapper as _w - -from .compat import legacy_pylons -from .constants import CONFIG_MIDDLEWARE - - -def trace_rendering(): - """Patch all Pylons renderers. It supports multiple versions - of Pylons and multiple renderers. - """ - # patch only once - if getattr(pylons.templating, '__datadog_patch', False): - return - setattr(pylons.templating, '__datadog_patch', True) - - if legacy_pylons: - # Pylons <= 0.9.7 - _w('pylons.templating', 'render', _traced_renderer) - else: - # Pylons > 0.9.7 - _w('pylons.templating', 'render_mako', _traced_renderer) - _w('pylons.templating', 'render_mako_def', _traced_renderer) - _w('pylons.templating', 'render_genshi', _traced_renderer) - _w('pylons.templating', 'render_jinja2', _traced_renderer) - - -def _traced_renderer(wrapped, instance, args, kwargs): - """Traced renderer""" - tracer = config[CONFIG_MIDDLEWARE]._tracer - with tracer.trace('pylons.render') as span: - span.set_tag('template.name', args[0]) - return wrapped(*args, **kwargs) diff --git a/ddtrace/monkey.py b/ddtrace/monkey.py index 88b9fe984..7f5c1e72b 100644 --- a/ddtrace/monkey.py +++ b/ddtrace/monkey.py @@ -57,7 +57,6 @@ PATCH_MODULES = { # Ignore some web framework integrations that might be configured explicitly in code 'django': False, 'falcon': False, - 'pylons': False, 'pyramid': False, # Standard library modules off by default diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index 10671be58..3d65dcf43 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -52,8 +52,6 @@ Supported web frameworks: +-------------------+---------+ | :ref:`flask` | True | +-------------------+---------+ -| :ref:`pylons` | True | -+-------------------+---------+ | :ref:`pyramid` | True | +-------------------+---------+ | :ref:`requests` | True | @@ -197,7 +195,6 @@ Enabling APM events for all web frameworks can be accomplished by setting the en * :ref:`falcon` * :ref:`flask` * :ref:`molten` -* :ref:`pylons` * :ref:`pyramid` * :ref:`requests` * :ref:`tornado` @@ -238,8 +235,6 @@ For most libraries, APM events can be enabled with the environment variable ``DD +----------------------+----------------------------------------+ | :ref:`pylibmc` | ``DD_PYLIBMC_ANALYTICS_ENABLED`` | +----------------------+----------------------------------------+ -| :ref:`pylons` | ``DD_PYLONS_ANALYTICS_ENABLED`` | -+----------------------+----------------------------------------+ | :ref:`pymemcache` | ``DD_PYMEMCACHE_ANALYTICS_ENABLED`` | +----------------------+----------------------------------------+ | :ref:`pymongo` | ``DD_PYMONGO_ANALYTICS_ENABLED`` | @@ -564,7 +559,7 @@ The available environment variables for ``ddtrace-run`` are: the tracer * ``DATADOG_SERVICE_NAME`` (no default): override the service name to be used for this program. This value is passed through when setting up middleware for - web framework integrations (e.g. pylons, flask, django). For tracing without a + web framework integrations (e.g. flask, django). For tracing without a web integration, prefer setting the service name in code. * ``DATADOG_PATCH_MODULES=module:patch,module:patch...`` e.g. ``boto:true,redis:false``: override the modules patched for this execution of diff --git a/docs/index.rst b/docs/index.rst index e3166228e..002ad2b78 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,7 +25,7 @@ documentation`_. Supported Libraries ------------------- -We officially support Python 2.7, 3.4 and above. +We officially support Python 3.4 and above. The versions listed are the versions that we have tested, but ``ddtrace`` can still be compatible with other versions of these libraries. If a version of a @@ -72,7 +72,7 @@ contacting support. +--------------------------------------------------+---------------+----------------+ | :ref:`flask_cache` | >= 0.12 | No | +--------------------------------------------------+---------------+----------------+ -| :ref:`gevent` | >= 1.0 | No | +| :ref:`gevent` | >= 1.1 | No | +--------------------------------------------------+---------------+----------------+ | :ref:`grpc` | >= 1.8.0 | Yes | +--------------------------------------------------+---------------+----------------+ @@ -96,8 +96,6 @@ contacting support. +--------------------------------------------------+---------------+----------------+ | :ref:`pylibmc` | >= 1.4 | Yes | +--------------------------------------------------+---------------+----------------+ -| :ref:`pylons` | >= 0.9.6 | No | -+--------------------------------------------------+---------------+----------------+ | :ref:`pymemcache` | >= 1.3 | Yes | +--------------------------------------------------+---------------+----------------+ | :ref:`pymongo` | >= 3.0 | Yes | diff --git a/docs/web_integrations.rst b/docs/web_integrations.rst index 4145558cc..23cc1e220 100644 --- a/docs/web_integrations.rst +++ b/docs/web_integrations.rst @@ -59,14 +59,6 @@ Molten .. automodule:: ddtrace.contrib.molten -.. _pylons: - -Pylons -^^^^^^ - -.. automodule:: ddtrace.contrib.pylons - - .. _pyramid: Pyramid diff --git a/pyproject.toml b/pyproject.toml index 8790a1e7b..e784ad60b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length = 120 -target_version = ['py27', 'py34', 'py35', 'py36', 'py37', 'py38'] +target_version = ['py34', 'py35', 'py36', 'py37', 'py38'] exclude = ''' ( \.eggs @@ -70,7 +70,6 @@ exclude = ''' | mysqldb | psycopg | pylibmc - | pylons | pymemcache | pymongo | pymysql @@ -140,7 +139,6 @@ exclude = ''' | patch.py | psycopg | pylibmc - | pylons | pymemcache | pymongo | pymysql diff --git a/setup.py b/setup.py index a21be5dd9..da2407f99 100644 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ documentation][visualization docs]. # enum34 is an enum backport for earlier versions of python # funcsigs backport required for vendored debtcollector -install_requires = ["psutil>=5.0.0", "enum34; python_version<'3.4'", "funcsigs>=1.0.0;python_version=='2.7'"] +install_requires = ["psutil>=5.0.0", "enum34; python_version<'3.4'"] # Base `setup()` kwargs without any C-extension registering setup_kwargs = dict( @@ -113,7 +113,6 @@ setup_kwargs = dict( entry_points={"console_scripts": ["ddtrace-run = ddtrace.commands.ddtrace_run:main"]}, classifiers=[ "Programming Language :: Python", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", diff --git a/tests/contrib/flask/test_signals.py b/tests/contrib/flask/test_signals.py index fc6e7b120..98b973fcf 100644 --- a/tests/contrib/flask/test_signals.py +++ b/tests/contrib/flask/test_signals.py @@ -11,12 +11,6 @@ from . import BaseFlaskTestCase class FlaskSignalsTestCase(BaseFlaskTestCase): def get_signal(self, signal_name): - # v0.9 missed importing `appcontext_tearing_down` in `flask/__init__.py` - # https://github.com/pallets/flask/blob/0.9/flask/__init__.py#L35-L37 - # https://github.com/pallets/flask/blob/0.9/flask/signals.py#L52 - # DEV: Version 0.9 doesn't have a patch version - if flask_version <= (0, 9) and signal_name == 'appcontext_tearing_down': - return getattr(flask.signals, signal_name) return getattr(flask, signal_name) def signal_function(self, name): diff --git a/tests/contrib/pylons/__init__.py b/tests/contrib/pylons/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/contrib/pylons/app/__init__.py b/tests/contrib/pylons/app/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/contrib/pylons/app/controllers/__init__.py b/tests/contrib/pylons/app/controllers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/contrib/pylons/app/controllers/root.py b/tests/contrib/pylons/app/controllers/root.py deleted file mode 100644 index f0fb71c24..000000000 --- a/tests/contrib/pylons/app/controllers/root.py +++ /dev/null @@ -1,44 +0,0 @@ -from pylons.controllers import WSGIController - -from ..lib.helpers import ExceptionWithCodeMethod, get_render_fn - - -class BaseController(WSGIController): - - def __call__(self, environ, start_response): - """Invoke the Controller""" - # WSGIController.__call__ dispatches to the Controller method - # the request is routed to. This routing information is - # available in environ['pylons.routes_dict'] - return WSGIController.__call__(self, environ, start_response) - - -class RootController(BaseController): - """Controller used for most tests""" - - def index(self): - return 'Hello World' - - def raise_exception(self): - raise Exception('Ouch!') - - def raise_wrong_code(self): - e = Exception('Ouch!') - e.code = 'wrong formatted code' - raise e - - def raise_code_method(self): - raise ExceptionWithCodeMethod('Ouch!') - - def raise_custom_code(self): - e = Exception('Ouch!') - e.code = '512' - raise e - - def render(self): - render = get_render_fn() - return render('/template.mako') - - def render_exception(self): - render = get_render_fn() - return render('/exception.mako') diff --git a/tests/contrib/pylons/app/lib/__init__.py b/tests/contrib/pylons/app/lib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/contrib/pylons/app/lib/base.py b/tests/contrib/pylons/app/lib/base.py deleted file mode 100644 index 0e9d09cfd..000000000 --- a/tests/contrib/pylons/app/lib/base.py +++ /dev/null @@ -1 +0,0 @@ -# this file is required when Pylons calls the `legacy` module diff --git a/tests/contrib/pylons/app/lib/helpers.py b/tests/contrib/pylons/app/lib/helpers.py deleted file mode 100644 index 769c2be94..000000000 --- a/tests/contrib/pylons/app/lib/helpers.py +++ /dev/null @@ -1,29 +0,0 @@ -from webhelpers import * # noqa - - -class ExceptionWithCodeMethod(Exception): - """Use case where the status code is defined by - the `code()` method. - """ - def __init__(self, message): - super(ExceptionWithCodeMethod, self).__init__(message) - - def code(): - pass - - -class AppGlobals(object): - """Object used to store application globals.""" - pass - - -def get_render_fn(): - """Re-import the function everytime so that double-patching - is correctly tested. - """ - try: - from pylons.templating import render_mako as render - except ImportError: - from pylons.templating import render - - return render diff --git a/tests/contrib/pylons/app/middleware.py b/tests/contrib/pylons/app/middleware.py deleted file mode 100644 index 13fd8c6fb..000000000 --- a/tests/contrib/pylons/app/middleware.py +++ /dev/null @@ -1,43 +0,0 @@ -from webob import Request, Response - - -class ExceptionMiddleware(object): - """A middleware which raises an exception.""" - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - raise Exception('Middleware exception') - - -class ExceptionToSuccessMiddleware(object): - """A middleware which catches any exceptions that occur in a later - middleware and returns a successful request. - """ - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - req = Request(environ) - try: - response = req.get_response(self.app) - except Exception: - response = Response() - response.status_int = 200 - response.body = 'An error has been handled appropriately' - return response(environ, start_response) - - -class ExceptionToClientErrorMiddleware(object): - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - req = Request(environ) - try: - response = req.get_response(self.app) - except Exception: - response = Response() - response.status_int = 404 - response.body = 'An error has occured with proper client error handling' - return response(environ, start_response) diff --git a/tests/contrib/pylons/app/router.py b/tests/contrib/pylons/app/router.py deleted file mode 100644 index 54ebd7e9e..000000000 --- a/tests/contrib/pylons/app/router.py +++ /dev/null @@ -1,20 +0,0 @@ -import os - -from routes import Mapper - - -def create_routes(): - """Change this function if you need to add more routes - to your Pylons test app. - """ - app_dir = os.path.dirname(os.path.abspath(__file__)) - controller_dir = os.path.join(app_dir, 'controllers') - routes = Mapper(directory=controller_dir) - routes.connect('/', controller='root', action='index') - routes.connect('/raise_exception', controller='root', action='raise_exception') - routes.connect('/raise_wrong_code', controller='root', action='raise_wrong_code') - routes.connect('/raise_custom_code', controller='root', action='raise_custom_code') - routes.connect('/raise_code_method', controller='root', action='raise_code_method') - routes.connect('/render', controller='root', action='render') - routes.connect('/render_exception', controller='root', action='render_exception') - return routes diff --git a/tests/contrib/pylons/app/templates/exception.mako b/tests/contrib/pylons/app/templates/exception.mako deleted file mode 100644 index 370df1da3..000000000 --- a/tests/contrib/pylons/app/templates/exception.mako +++ /dev/null @@ -1 +0,0 @@ -${1/0} diff --git a/tests/contrib/pylons/app/templates/template.mako b/tests/contrib/pylons/app/templates/template.mako deleted file mode 100644 index cd0875583..000000000 --- a/tests/contrib/pylons/app/templates/template.mako +++ /dev/null @@ -1 +0,0 @@ -Hello world! diff --git a/tests/contrib/pylons/app/web.py b/tests/contrib/pylons/app/web.py deleted file mode 100644 index 5e98f10ff..000000000 --- a/tests/contrib/pylons/app/web.py +++ /dev/null @@ -1,41 +0,0 @@ -import os - -from mako.lookup import TemplateLookup - -from pylons import config -from pylons.wsgiapp import PylonsApp - -from routes.middleware import RoutesMiddleware -from beaker.middleware import SessionMiddleware, CacheMiddleware - -from paste.registry import RegistryManager - -from .router import create_routes -from .lib.helpers import AppGlobals - - -def make_app(global_conf, full_stack=True, **app_conf): - # load Pylons environment - root = os.path.dirname(os.path.abspath(__file__)) - paths = dict( - templates=[os.path.join(root, 'templates')], - ) - config.init_app(global_conf, app_conf, paths=paths) - config['pylons.package'] = 'tests.contrib.pylons.app' - config['pylons.app_globals'] = AppGlobals() - - # set Pylons routes - config['routes.map'] = create_routes() - - # Create the Mako TemplateLookup, with the default auto-escaping - config['pylons.app_globals'].mako_lookup = TemplateLookup( - directories=paths['templates'], - ) - - # define a default middleware stack - app = PylonsApp() - app = RoutesMiddleware(app, config['routes.map']) - app = SessionMiddleware(app, config) - app = CacheMiddleware(app, config) - app = RegistryManager(app) - return app diff --git a/tests/contrib/pylons/test.ini b/tests/contrib/pylons/test.ini deleted file mode 100644 index ea5a165ce..000000000 --- a/tests/contrib/pylons/test.ini +++ /dev/null @@ -1,9 +0,0 @@ -[DEFAULT] -debug = false - -[app:main] -use = call:tests.contrib.pylons.app.web:make_app -full_stack = true -cache_dir = %(here)s/.cache -beaker.session.key = helloworld -beaker.session.secret = somesecret diff --git a/tests/contrib/pylons/test_pylons.py b/tests/contrib/pylons/test_pylons.py deleted file mode 100644 index 3bf903e01..000000000 --- a/tests/contrib/pylons/test_pylons.py +++ /dev/null @@ -1,429 +0,0 @@ -import os - -from routes import url_for -from paste import fixture -from paste.deploy import loadapp -import pytest - -from ddtrace import config -from ddtrace.ext import http, errors -from ddtrace.constants import SAMPLING_PRIORITY_KEY, ANALYTICS_SAMPLE_RATE_KEY -from ddtrace.contrib.pylons import PylonsTraceMiddleware - -from tests.opentracer.utils import init_tracer -from ...base import BaseTracerTestCase -from ...utils import assert_span_http_status_code - - -class PylonsTestCase(BaseTracerTestCase): - """Pylons Test Controller that is used to test specific - cases defined in the Pylons controller. To test a new behavior, - add a new action in the `app.controllers.root` module. - """ - conf_dir = os.path.dirname(os.path.abspath(__file__)) - - def setUp(self): - super(PylonsTestCase, self).setUp() - # initialize a real traced Pylons app - wsgiapp = loadapp('config:test.ini', relative_to=PylonsTestCase.conf_dir) - self._wsgiapp = wsgiapp - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - self.app = fixture.TestApp(app) - - def test_controller_exception(self): - """Ensure exceptions thrown in controllers can be handled. - - No error tags should be set in the span. - """ - from .app.middleware import ExceptionToSuccessMiddleware - wsgiapp = ExceptionToSuccessMiddleware(self._wsgiapp) - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - - app = fixture.TestApp(app) - app.get(url_for(controller='root', action='raise_exception')) - - spans = self.tracer.writer.pop() - - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_exception' - assert span.error == 0 - assert span.get_tag(http.URL) == 'http://localhost:80/raise_exception' - assert_span_http_status_code(span, 200) - assert http.QUERY_STRING not in span.meta - assert span.get_tag(errors.ERROR_MSG) is None - assert span.get_tag(errors.ERROR_TYPE) is None - assert span.get_tag(errors.ERROR_STACK) is None - assert span.span_type == 'web' - - def test_mw_exc_success(self): - """Ensure exceptions can be properly handled by other middleware. - - No error should be reported in the span. - """ - from .app.middleware import ExceptionMiddleware, ExceptionToSuccessMiddleware - wsgiapp = ExceptionMiddleware(self._wsgiapp) - wsgiapp = ExceptionToSuccessMiddleware(wsgiapp) - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - app = fixture.TestApp(app) - - app.get(url_for(controller='root', action='index')) - - spans = self.tracer.writer.pop() - - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'None.None' - assert span.error == 0 - assert span.get_tag(http.URL) == 'http://localhost:80/' - assert_span_http_status_code(span, 200) - assert span.get_tag(errors.ERROR_MSG) is None - assert span.get_tag(errors.ERROR_TYPE) is None - assert span.get_tag(errors.ERROR_STACK) is None - - def test_middleware_exception(self): - """Ensure exceptions raised in middleware are properly handled. - - Uncaught exceptions should result in error tagged spans. - """ - from .app.middleware import ExceptionMiddleware - wsgiapp = ExceptionMiddleware(self._wsgiapp) - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - app = fixture.TestApp(app) - - with pytest.raises(Exception): - app.get(url_for(controller='root', action='index')) - - spans = self.tracer.writer.pop() - - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'None.None' - assert span.error == 1 - assert span.get_tag(http.URL) == 'http://localhost:80/' - assert_span_http_status_code(span, 500) - assert span.get_tag(errors.ERROR_MSG) == 'Middleware exception' - assert span.get_tag(errors.ERROR_TYPE) == 'exceptions.Exception' - assert span.get_tag(errors.ERROR_STACK) - - def test_exc_success(self): - from .app.middleware import ExceptionToSuccessMiddleware - wsgiapp = ExceptionToSuccessMiddleware(self._wsgiapp) - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - app = fixture.TestApp(app) - - app.get(url_for(controller='root', action='raise_exception')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_exception' - assert span.error == 0 - assert span.get_tag(http.URL) == 'http://localhost:80/raise_exception' - assert_span_http_status_code(span, 200) - assert span.get_tag(errors.ERROR_MSG) is None - assert span.get_tag(errors.ERROR_TYPE) is None - assert span.get_tag(errors.ERROR_STACK) is None - - def test_exc_client_failure(self): - from .app.middleware import ExceptionToClientErrorMiddleware - wsgiapp = ExceptionToClientErrorMiddleware(self._wsgiapp) - app = PylonsTraceMiddleware(wsgiapp, self.tracer, service='web') - app = fixture.TestApp(app) - - app.get(url_for(controller='root', action='raise_exception'), status=404) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_exception' - assert span.error == 0 - assert span.get_tag(http.URL) == 'http://localhost:80/raise_exception' - assert_span_http_status_code(span, 404) - assert span.get_tag(errors.ERROR_MSG) is None - assert span.get_tag(errors.ERROR_TYPE) is None - assert span.get_tag(errors.ERROR_STACK) is None - - def test_success_200(self, query_string=''): - if query_string: - fqs = '?' + query_string - else: - fqs = '' - res = self.app.get(url_for(controller='root', action='index') + fqs) - assert res.status == 200 - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.index' - assert_span_http_status_code(span, 200) - if config.pylons.trace_query_string: - assert span.meta.get(http.QUERY_STRING) == query_string - else: - assert http.QUERY_STRING not in span.meta - assert span.error == 0 - - def test_query_string(self): - return self.test_success_200('foo=bar') - - def test_multi_query_string(self): - return self.test_success_200('foo=bar&foo=baz&x=y') - - def test_query_string_trace(self): - with self.override_http_config('pylons', dict(trace_query_string=True)): - return self.test_success_200('foo=bar') - - def test_multi_query_string_trace(self): - with self.override_http_config('pylons', dict(trace_query_string=True)): - return self.test_success_200('foo=bar&foo=baz&x=y') - - def test_analytics_global_on_integration_default(self): - """ - When making a request - When an integration trace search is not event sample rate is not set and globally trace search is enabled - We expect the root span to have the appropriate tag - """ - with self.override_global_config(dict(analytics_enabled=True)): - res = self.app.get(url_for(controller='root', action='index')) - self.assertEqual(res.status, 200) - - self.assert_structure( - dict(name='pylons.request', metrics={ANALYTICS_SAMPLE_RATE_KEY: 1.0}) - ) - - def test_analytics_global_on_integration_on(self): - """ - When making a request - When an integration trace search is enabled and sample rate is set and globally trace search is enabled - We expect the root span to have the appropriate tag - """ - with self.override_global_config(dict(analytics_enabled=True)): - with self.override_config('pylons', dict(analytics_enabled=True, analytics_sample_rate=0.5)): - res = self.app.get(url_for(controller='root', action='index')) - self.assertEqual(res.status, 200) - - self.assert_structure( - dict(name='pylons.request', metrics={ANALYTICS_SAMPLE_RATE_KEY: 0.5}) - ) - - def test_analytics_global_off_integration_default(self): - """ - When making a request - When an integration trace search is not set and sample rate is set and globally trace search is disabled - We expect the root span to not include tag - """ - with self.override_global_config(dict(analytics_enabled=False)): - res = self.app.get(url_for(controller='root', action='index')) - self.assertEqual(res.status, 200) - - root = self.get_root_span() - self.assertIsNone(root.get_metric(ANALYTICS_SAMPLE_RATE_KEY)) - - def test_analytics_global_off_integration_on(self): - """ - When making a request - When an integration trace search is enabled and sample rate is set and globally trace search is disabled - We expect the root span to have the appropriate tag - """ - with self.override_global_config(dict(analytics_enabled=False)): - with self.override_config('pylons', dict(analytics_enabled=True, analytics_sample_rate=0.5)): - res = self.app.get(url_for(controller='root', action='index')) - self.assertEqual(res.status, 200) - - self.assert_structure( - dict(name='pylons.request', metrics={ANALYTICS_SAMPLE_RATE_KEY: 0.5}) - ) - - def test_template_render(self): - res = self.app.get(url_for(controller='root', action='render')) - assert res.status == 200 - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 2 - request = spans[0] - template = spans[1] - - assert request.service == 'web' - assert request.resource == 'root.render' - assert_span_http_status_code(request, 200) - assert request.error == 0 - - assert template.service == 'web' - assert template.resource == 'pylons.render' - assert template.meta.get('template.name') == '/template.mako' - assert template.error == 0 - - def test_template_render_exception(self): - with pytest.raises(Exception): - self.app.get(url_for(controller='root', action='render_exception')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 2 - request = spans[0] - template = spans[1] - - assert request.service == 'web' - assert request.resource == 'root.render_exception' - assert_span_http_status_code(request, 500) - assert request.error == 1 - - assert template.service == 'web' - assert template.resource == 'pylons.render' - assert template.meta.get('template.name') == '/exception.mako' - assert template.error == 1 - assert template.get_tag('error.msg') == 'integer division or modulo by zero' - assert 'ZeroDivisionError: integer division or modulo by zero' in template.get_tag('error.stack') - - def test_failure_500(self): - with pytest.raises(Exception): - self.app.get(url_for(controller='root', action='raise_exception')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_exception' - assert span.error == 1 - assert_span_http_status_code(span, 500) - assert span.get_tag('error.msg') == 'Ouch!' - assert span.get_tag(http.URL) == 'http://localhost:80/raise_exception' - assert 'Exception: Ouch!' in span.get_tag('error.stack') - - def test_failure_500_with_wrong_code(self): - with pytest.raises(Exception): - self.app.get(url_for(controller='root', action='raise_wrong_code')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_wrong_code' - assert span.error == 1 - assert_span_http_status_code(span, 500) - assert span.meta.get(http.URL) == 'http://localhost:80/raise_wrong_code' - assert span.get_tag('error.msg') == 'Ouch!' - assert 'Exception: Ouch!' in span.get_tag('error.stack') - - def test_failure_500_with_custom_code(self): - with pytest.raises(Exception): - self.app.get(url_for(controller='root', action='raise_custom_code')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_custom_code' - assert span.error == 1 - assert_span_http_status_code(span, 512) - assert span.meta.get(http.URL) == 'http://localhost:80/raise_custom_code' - assert span.get_tag('error.msg') == 'Ouch!' - assert 'Exception: Ouch!' in span.get_tag('error.stack') - - def test_failure_500_with_code_method(self): - with pytest.raises(Exception): - self.app.get(url_for(controller='root', action='raise_code_method')) - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.service == 'web' - assert span.resource == 'root.raise_code_method' - assert span.error == 1 - assert_span_http_status_code(span, 500) - assert span.meta.get(http.URL) == 'http://localhost:80/raise_code_method' - assert span.get_tag('error.msg') == 'Ouch!' - - def test_distributed_tracing_default(self): - # ensure by default, distributed tracing is not enabled - headers = { - 'x-datadog-trace-id': '100', - 'x-datadog-parent-id': '42', - 'x-datadog-sampling-priority': '2', - } - res = self.app.get(url_for(controller='root', action='index'), headers=headers) - assert res.status == 200 - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.trace_id == 100 - assert span.parent_id == 42 - assert span.get_metric(SAMPLING_PRIORITY_KEY) == 2 - - def test_distributed_tracing_disabled(self): - # ensure distributed tracing propagator is working - middleware = self.app.app - middleware._distributed_tracing = False - headers = { - 'x-datadog-trace-id': '100', - 'x-datadog-parent-id': '42', - 'x-datadog-sampling-priority': '2', - } - - res = self.app.get(url_for(controller='root', action='index'), headers=headers) - assert res.status == 200 - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 1 - span = spans[0] - - assert span.trace_id != 100 - assert span.parent_id != 42 - assert span.get_metric(SAMPLING_PRIORITY_KEY) != 2 - - def test_success_200_ot(self): - """OpenTracing version of test_success_200.""" - ot_tracer = init_tracer('pylons_svc', self.tracer) - - with ot_tracer.start_active_span('pylons_get'): - res = self.app.get(url_for(controller='root', action='index')) - assert res.status == 200 - - spans = self.tracer.writer.pop() - assert spans, spans - assert len(spans) == 2 - ot_span, dd_span = spans - - # confirm the parenting - assert ot_span.parent_id is None - assert dd_span.parent_id == ot_span.span_id - - assert ot_span.name == 'pylons_get' - assert ot_span.service == 'pylons_svc' - - assert dd_span.service == 'web' - assert dd_span.resource == 'root.index' - assert_span_http_status_code(dd_span, 200) - assert dd_span.meta.get(http.URL) == 'http://localhost:80/' - assert dd_span.error == 0 diff --git a/tox.ini b/tox.ini index 5e40dd2a2..911d61bbc 100644 --- a/tox.ini +++ b/tox.ini @@ -20,12 +20,12 @@ envlist = flake8 black wait - {py27,py34,py35,py36,py37}-tracer - {py27,py34,py35,py36,py37}-internal - {py27,py34,py35,py36,py37}-integration - {py27,py34,py35,py36,py37}-ddtracerun - {py27,py34,py35,py36,py37}-test_utils - {py27,py34,py35,py36,py37}-test_logging + {py34,py35,py36,py37}-tracer + {py34,py35,py36,py37}-internal + {py34,py35,py36,py37}-integration + {py34,py35,py36,py37}-ddtracerun + {py34,py35,py36,py37}-test_utils + {py34,py35,py36,py37}-test_logging # Integrations environments aiobotocore_contrib-py34-aiobotocore{02,03,04} aiobotocore_contrib-{py35,py36}-aiobotocore{02,03,04,05,07,08,09,010} @@ -37,70 +37,63 @@ envlist = aiohttp_contrib-{py35,py36,py37}-aiohttp{30,31,32,33,34,35}-aiohttp_jinja{015}-yarl10 aiopg_contrib-{py34,py35,py36}-aiopg{012,015} aiopg_contrib-py37-aiopg015 - algoliasearch_contrib-{py27,py34,py35,py36,py37}-algoliasearch{1,2} + algoliasearch_contrib-{py34,py35,py36,py37}-algoliasearch{1,2} asyncio_contrib-{py34,py35,py36,py37} # boto needs moto<1 and moto<1 does not support Python >= 3.7 - boto_contrib-{py27,py34,py35,py36}-boto - botocore_contrib-{py27,py34,py35,py36,py37}-botocore - bottle_contrib{,_autopatch}-{py27,py34,py35,py36,py37}-bottle{11,12}-webtest - cassandra_contrib-{py27,py34,py35,py36,py37}-cassandra{35,36,37,38,315} + boto_contrib-{py34,py35,py36}-boto + botocore_contrib-{py34,py35,py36,py37}-botocore + bottle_contrib{,_autopatch}-{py34,py35,py36,py37}-bottle{11,12}-webtest + cassandra_contrib-{py34,py35,py36,py37}-cassandra{35,36,37,38,315} # Non-4.x celery should be able to use the older redis lib, since it locks to an older kombu - celery_contrib-{py27,py34,py35,py36}-celery{31}-redis{210} + celery_contrib-{py34,py35,py36}-celery{31}-redis{210} # 4.x celery bumps kombu to 4.4+, which requires redis 3.2 or later, this tests against # older redis with an older kombu, and newer kombu/newer redis. # https://github.com/celery/kombu/blob/3e60e6503a77b9b1a987cf7954659929abac9bac/Changelog#L35 - celery_contrib-{py27,py34,py35,py36}-celery{40,41}-{redis210-kombu43,redis320-kombu44} + celery_contrib-{py34,py35,py36}-celery{40,41}-{redis210-kombu43,redis320-kombu44} # Celery 4.2 is now limited to Kombu 4.3 # https://github.com/celery/celery/commit/1571d414461f01ae55be63a03e2adaa94dbcb15d - celery_contrib-{py27,py34,py35,py36}-celery42-redis210-kombu43 + celery_contrib-{py34,py35,py36}-celery42-redis210-kombu43 # Celery 4.3 wants Kombu >= 4.4 and Redis >= 3.2 # Python 3.7 needs Celery 4.3 - celery_contrib-{py27,py34,py35,py36,py37}-celery43-redis320-kombu44 - consul_contrib-py{27,34,35,36,37}-consul{07,10,11} - dbapi_contrib-{py27,py34,py35,py36} - django_contrib{,_autopatch}-{py27,py34,py35,py36}-django{18,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached + celery_contrib-{py34,py35,py36,py37}-celery43-redis320-kombu44 + consul_contrib-py{34,35,36,37}-consul{07,10,11} + dbapi_contrib-{py34,py35,py36} + django_contrib{,_autopatch}-{py34,py35,py36}-django{18,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached django_contrib{,_autopatch}-{py34,py35,py36}-django{200}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached - django_drf_contrib-{py27,py34,py35,py36}-django{111}-djangorestframework{34,37,38} + django_drf_contrib-{py34,py35,py36}-django{111}-djangorestframework{34,37,38} django_drf_contrib-{py34,py35,py36}-django{200}-djangorestframework{37,38} - dogpile_contrib-{py27,py35,py36,py37}-dogpilecache{06,07,08,latest} - elasticsearch_contrib-{py27,py34,py35,py36}-elasticsearch{16,17,18,23,24,51,52,53,54,63,64} - elasticsearch_contrib-{py27,py34,py35,py36}-elasticsearch1{100} - elasticsearch_contrib-{py27,py34,py35,py36}-elasticsearch2{50} - elasticsearch_contrib-{py27,py34,py35,py36}-elasticsearch5{50} - elasticsearch_contrib-{py27,py34,py35,py36}-elasticsearch6{40} - falcon_contrib{,_autopatch}-{py27,py34,py35,py36}-falcon{10,11,12,13,14} - flask_contrib{,_autopatch}-{py27,py34,py35,py36}-flask{010,011,012,10}-blinker -# Flask <=0.9 does not support Python 3 - flask_contrib{,_autopatch}-{py27}-flask{09}-blinker - flask_cache_contrib{,_autopatch}-{py27,py34,py35,py36,py37}-flask{010,011,012}-flaskcache{013}-memcached-redis{210}-blinker - flask_cache_contrib{,_autopatch}-{py27}-flask{010,011}-flaskcache{012}-memcached-redis{210}-blinker - futures_contrib-{py27}-futures{30,31,32} + dogpile_contrib-{py35,py36,py37}-dogpilecache{06,07,08,latest} + elasticsearch_contrib-{py34,py35,py36}-elasticsearch{16,17,18,23,24,51,52,53,54,63,64} + elasticsearch_contrib-{py34,py35,py36}-elasticsearch1{100} + elasticsearch_contrib-{py34,py35,py36}-elasticsearch2{50} + elasticsearch_contrib-{py34,py35,py36}-elasticsearch5{50} + elasticsearch_contrib-{py34,py35,py36}-elasticsearch6{40} + falcon_contrib{,_autopatch}-{py34,py35,py36}-falcon{10,11,12,13,14} + flask_contrib{,_autopatch}-{py34,py35,py36}-flask{010,011,012,10}-blinker + flask_cache_contrib{,_autopatch}-{py34,py35,py36,py37}-flask{010,011,012}-flaskcache{013}-memcached-redis{210}-blinker futures_contrib-{py34,py35,py36,py37} - gevent_contrib-{py27,py34,py35,py36}-gevent{11,12,13} + gevent_contrib-{py34,py35,py36}-gevent{11,12,13} gevent_contrib-py37-gevent{13,14} -# gevent 1.0 is not python 3 compatible - gevent_contrib-{py27}-gevent{10} - grpc_contrib-{py27,py34,py35,py36,py37}-grpc{112,113,114,115,116,117,118,119,120,121,122} - httplib_contrib-{py27,py34,py35,py36,py37} - jinja2_contrib-{py27,py34,py35,py36,py37}-jinja{27,28,29,210} - mako_contrib-{py27,py34,py35,py36,py37}-mako{010,100} + grpc_contrib-{py34,py35,py36,py37}-grpc{112,113,114,115,116,117,118,119,120,121,122} + httplib_contrib-{py34,py35,py36,py37} + jinja2_contrib-{py34,py35,py36,py37}-jinja{27,28,29,210} + mako_contrib-{py34,py35,py36,py37}-mako{010,100} molten_contrib-py{36,37}-molten{070,072} - mongoengine_contrib-{py27,py34,py35,py36,py37}-mongoengine{015,016,017,018,latest}-pymongo{latest} - mysql_contrib-{py27,py34,py35,py36,py37}-mysqlconnector + mongoengine_contrib-{py34,py35,py36,py37}-mongoengine{015,016,017,018,latest}-pymongo{latest} + mysql_contrib-{py34,py35,py36,py37}-mysqlconnector mysqldb_contrib-{py27}-mysqldb{12} - mysqldb_contrib-{py27,py34,py35,py36,py37}-mysqlclient{13} - psycopg_contrib-{py27,py34,py35,py36}-psycopg2{24,25,26,27,28} + mysqldb_contrib-{py34,py35,py36,py37}-mysqlclient{13} + psycopg_contrib-{py34,py35,py36}-psycopg2{24,25,26,27,28} psycopg_contrib-py37-psycopg2{27,28} - pylibmc_contrib-{py27,py34,py35,py36,py37}-pylibmc{140,150} - pylons_contrib-{py27}-pylons{096,097,010,10} - pymemcache_contrib{,_autopatch}-{py27,py34,py35,py36,py37}-pymemcache{130,140} - pymongo_contrib-{py27,py34,py35,py36,py37}-pymongo{30,31,32,33,34,35,36,37,38,39,latest}-mongoengine{latest} - pymysql_contrib-{py27,py34,py35,py36,py37}-pymysql{07,08,09} - pyramid_contrib{,_autopatch}-{py27,py34,py35,py36,py37}-pyramid{17,18,19}-webtest - redis_contrib-{py27,py34,py35,py36,py37}-redis{26,27,28,29,210,300} - rediscluster_contrib-{py27,py34,py35,py36,py37}-rediscluster{135,136}-redis210 - requests_contrib{,_autopatch}-{py27,py34,py35,py36,py37}-requests{208,209,210,211,212,213,219} - kombu_contrib-{py27,py34,py35,py36}-kombu{40,41,42} + pylibmc_contrib-{py34,py35,py36,py37}-pylibmc{140,150} + pymemcache_contrib{,_autopatch}-{py34,py35,py36,py37}-pymemcache{130,140} + pymongo_contrib-{py34,py35,py36,py37}-pymongo{30,31,32,33,34,35,36,37,38,39,latest}-mongoengine{latest} + pymysql_contrib-{py34,py35,py36,py37}-pymysql{07,08,09} + pyramid_contrib{,_autopatch}-{py34,py35,py36,py37}-pyramid{17,18,19}-webtest + redis_contrib-{py34,py35,py36,py37}-redis{26,27,28,29,210,300} + rediscluster_contrib-{py34,py35,py36,py37}-rediscluster{135,136}-redis210 + requests_contrib{,_autopatch}-{py34,py35,py36,py37}-requests{208,209,210,211,212,213,219} + kombu_contrib-{py34,py35,py36}-kombu{40,41,42} # Python 3.7 needs Kombu >= 4.2 kombu_contrib-py37-kombu42 # python 3.6 requests + gevent regression test @@ -108,22 +101,20 @@ envlist = # https://github.com/gevent/gevent/issues/903 requests_gevent_contrib-{py36}-requests{208,209,210,211,212,213,219}-gevent{12,13} requests_gevent_contrib-py37-requests{208,209,210,211,212,213,219}-gevent13 - sqlalchemy_contrib-{py27,py34,py35,py36,py37}-sqlalchemy{10,11,12}-psycopg228-mysqlconnector - sqlite3_contrib-{py27,py34,py35,py36,py37}-sqlite3 - tornado_contrib-{py27,py34,py35,py36,py37}-tornado{40,41,42,43,44,45} + sqlalchemy_contrib-{py34,py35,py36,py37}-sqlalchemy{10,11,12}-psycopg228-mysqlconnector + sqlite3_contrib-{py34,py35,py36,py37}-sqlite3 + tornado_contrib-{py34,py35,py36,py37}-tornado{40,41,42,43,44,45} tornado_contrib-{py37}-tornado{50,51,60} - tornado_contrib-{py27}-tornado{40,41,42,43,44,45}-futures{30,31,32} - vertica_contrib-{py27,py34,py35,py36,py37}-vertica{060,070} + vertica_contrib-{py34,py35,py36,py37}-vertica{060,070} # Opentracer - {py27,py34,py35,py36,py37}-opentracer + {py34,py35,py36,py37}-opentracer {py34,py35,py36,py37}-opentracer_asyncio {py34,py35,py36,py37}-opentracer_tornado-tornado{40,41,42,43,44} - {py27}-opentracer_gevent-gevent{10} - {py27,py34,py35,py36}-opentracer_gevent-gevent{11,12} + {py34,py35,py36}-opentracer_gevent-gevent{11,12} py37-opentracer_gevent-gevent{13,14} # Unit tests: pytest based test suite that do not require any additional dependency - unit_tests-{py27,py34,py35,py36,py37} - benchmarks-{py27,py34,py35,py36,py37} + unit_tests-{py34,py35,py36,py37} + benchmarks-{py34,py35,py36,py37} [testenv] # Always re-run `setup.py develop` to ensure the proper C-extension .so files are created @@ -133,7 +124,6 @@ envlist = commands_pre={envpython} {toxinidir}/setup.py develop usedevelop = True basepython = - py27: python2.7 py34: python3.4 py35: python3.5 py36: python3.6 @@ -153,8 +143,6 @@ deps = # https://github.com/aio-libs/aiohttp/issues/2662 yarl: yarl==0.18.0 yarl10: yarl>=1.0,<1.1 -# backports - py27: enum34 # integrations aiobotocore010: aiobotocore>=0.10,<0.11 aiobotocore09: aiobotocore>=0.9,<0.10 @@ -246,7 +234,6 @@ deps = falcon12: falcon>=1.2,<1.3 falcon13: falcon>=1.3,<1.4 falcon14: falcon>=1.4,<1.5 - flask09: flask>=0.9,<0.10 flask010: flask>=0.10,<0.11 flask011: flask>=0.11,<0.12 flask012: flask>=0.12,<0.13 @@ -257,7 +244,6 @@ deps = futures30: futures>=3.0,<3.1 futures31: futures>=3.1,<3.2 futures32: futures>=3.2,<3.3 - gevent10: gevent>=1.0,<1.1 gevent11: gevent>=1.1,<1.2 gevent12: gevent>=1.2,<1.3 gevent13: gevent>=1.3,<1.4 @@ -301,14 +287,6 @@ deps = mysqlconnector: mysql-connector-python!=8.0.18 mysqldb12: mysql-python>=1.2,<1.3 mysqlclient13: mysqlclient>=1.3,<1.4 -# webob is required for Pylons < 1.0 - pylons096: pylons>=0.9.6,<0.9.7 - pylons096: webob<1.1 - pylons097: pylons>=0.9.7,<0.9.8 - pylons097: webob<1.1 - pylons010: pylons>=0.10,<0.11 - pylons010: webob<1.1 - pylons10: pylons>=1.0,<1.1 pylibmc: pylibmc pylibmc140: pylibmc>=1.4.0,<1.5.0 pylibmc150: pylibmc>=1.5.0,<1.6.0 @@ -426,7 +404,6 @@ commands = mysqldb_contrib: pytest {posargs} tests/contrib/mysqldb psycopg_contrib: pytest {posargs} tests/contrib/psycopg pylibmc_contrib: pytest {posargs} tests/contrib/pylibmc - pylons_contrib: pytest {posargs} tests/contrib/pylons pymemcache_contrib: pytest {posargs} --ignore="tests/contrib/pymemcache/autopatch" tests/contrib/pymemcache/ pymemcache_contrib_autopatch: python tests/ddtrace_run.py pytest {posargs} tests/contrib/pymemcache/autopatch/ pymongo_contrib: pytest {posargs} tests/contrib/pymongo @@ -487,22 +464,12 @@ basepython=python3.7 # same job will cause problem for tests that use ddtrace-run [celery_contrib] usedevelop = False -[testenv:celery_contrib-py27-celery31-redis210] -usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py34-celery31-redis210] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py35-celery31-redis210] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py36-celery31-redis210] usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery40-redis210-kombu43] -usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery40-redis320-kombu44] -usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery41-redis210-kombu43] -usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery41-redis320-kombu44] -usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py34-celery40-redis210-kombu43] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py34-celery40-redis320-kombu44] @@ -527,16 +494,12 @@ usedevelop = {[celery_contrib]usedevelop} usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py36-celery41-redis320-kombu44] usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery42-redis210-kombu43] -usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py34-celery42-redis210-kombu43] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py35-celery42-redis210-kombu43] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py36-celery42-redis210-kombu43] usedevelop = {[celery_contrib]usedevelop} -[testenv:celery_contrib-py27-celery43-redis320-kombu44] -usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py34-celery43-redis320-kombu44] usedevelop = {[celery_contrib]usedevelop} [testenv:celery_contrib-py35-celery43-redis320-kombu44] @@ -549,21 +512,6 @@ usedevelop = {[celery_contrib]usedevelop} [falcon_autopatch] setenv = DATADOG_SERVICE_NAME=my-falcon -[testenv:falcon_contrib_autopatch-py27-falcon10] -setenv = - {[falcon_autopatch]setenv} -[testenv:falcon_contrib_autopatch-py27-falcon11] -setenv = - {[falcon_autopatch]setenv} -[testenv:falcon_contrib_autopatch-py27-falcon12] -setenv = - {[falcon_autopatch]setenv} -[testenv:falcon_contrib_autopatch-py27-falcon13] -setenv = - {[falcon_autopatch]setenv} -[testenv:falcon_contrib_autopatch-py27-falcon14] -setenv = - {[falcon_autopatch]setenv} [testenv:falcon_contrib_autopatch-py34-falcon10] setenv = {[falcon_autopatch]setenv} @@ -630,16 +578,6 @@ setenv = setenv = DATADOG_SERVICE_NAME = foobar DATADOG_PYRAMID_DISTRIBUTED_TRACING = True -[testenv:pyramid_contrib_autopatch-py27-pyramid17-webtest] -setenv = - {[pyramid_autopatch]setenv} - -[testenv:pyramid_contrib_autopatch-py27-pyramid18-webtest] -setenv = - {[pyramid_autopatch]setenv} -[testenv:pyramid_contrib_autopatch-py27-pyramid19-webtest] -setenv = - {[pyramid_autopatch]setenv} [testenv:pyramid_contrib_autopatch-py34-pyramid17-webtest] setenv = {[pyramid_autopatch]setenv} @@ -682,18 +620,6 @@ setenv = setenv = DATADOG_SERVICE_NAME = test.flask.service DATADOG_PATCH_MODULES = jinja2:false -[testenv:flask_contrib_autopatch-py27-flask010-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask011-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask012-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask10-blinker] -setenv = - {[flask_autopatch]setenv} [testenv:flask_contrib_autopatch-py34-flask010-blinker] setenv = {[flask_autopatch]setenv} @@ -740,15 +666,6 @@ setenv = setenv = {[flask_autopatch]setenv} [testenv:flask_contrib_autopatch-py37-flask10-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask010-flaskcache013-memcached-redis210-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask011-flaskcache013-memcached-redis210-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask012-flaskcache013-memcached-redis210-blinker] setenv = {[flask_autopatch]setenv} [testenv:flask_contrib_autopatch-py34-flask010-flaskcache013-memcached-redis210-blinker] @@ -787,20 +704,10 @@ setenv = [testenv:flask_contrib_autopatch-py37-flask012-flaskcache013-memcached-redis210-blinker] setenv = {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask010-flaskcache012-memcached-redis210-blinker] -setenv = - {[flask_autopatch]setenv} -[testenv:flask_contrib_autopatch-py27-flask011-flaskcache012-memcached-redis210-blinker] -setenv = - {[flask_autopatch]setenv} - [bottle_autopatch] setenv = DATADOG_SERVICE_NAME = bottle-app -[testenv:bottle_contrib_autopatch-py27-bottle11-webtest] -setenv = - {[bottle_autopatch]setenv} [testenv:bottle_contrib_autopatch-py34-bottle11-webtest] setenv = {[bottle_autopatch]setenv} @@ -811,9 +718,6 @@ setenv = setenv = {[bottle_autopatch]setenv} [testenv:bottle_contrib_autopatch-py37-bottle11-webtest] -setenv = - {[bottle_autopatch]setenv} -[testenv:bottle_contrib_autopatch-py27-bottle12-webtest] setenv = {[bottle_autopatch]setenv} [testenv:bottle_contrib_autopatch-py34-bottle12-webtest]