diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..6deafc2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/.gitignore b/.gitignore index b38ab80..02450f9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.pytest_cache .coverage .python-version +.nox .tox build/ dist/ diff --git a/README.md b/README.md index 218b77f..a85bb66 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,16 @@ It's also possible to install the supported instrumentors as package extras: ```bash # Supported extras are dbapi, django, flask, pymongo, pymysql, redis, requests, tornado - $ pip install --process-dependency-links 'signalfx-tracing[django,redis,requests]' + $ pip install 'signalfx-tracing[django,redis,requests]' ``` -**Note: It's necessary to include `--process-dependency-links` to obtain the desired instrumentor versions.** +**Note: For pip versions earlier than 18.0, it's necessary to include `--process-dependency-links` to +obtain the desired instrumentor versions.** + +```bash + # pip versions <18.0 + $ pip install --process-dependency-links 'signalfx-tracing[jaeger,tornado]' +``` ### Tracer Not all stable versions of OpenTracing-compatible tracers support the 2.0 API, so we provide @@ -103,7 +109,9 @@ ready for reporting to SignalFx. You can obtain an instance of the suggested Jae ```sh $ sfx-py-trace-bootstrap - # or as package extra (please note required --process-dependency-links) + # or as package extra + $ pip install 'signalfx-tracing[jaeger]' + # please use required --process-dependency-links for pip versions <18.0 $ pip install --process-dependency-links 'signalfx-tracing[jaeger]' # or from project source tree, along with applicable instrumentors diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..34e07ba --- /dev/null +++ b/noxfile.py @@ -0,0 +1,238 @@ +# Copyright (C) 2019 SignalFx, Inc. All rights reserved. +import nox + +# pytest-django incompatible w/ pytest 4.2 +pytest = 'pytest<4.2' # https://github.com/pytest-dev/pytest-django/issues/698 + + +def install_unit_tests(session, *other_packaages): + return session.install(pytest, '.[unit_tests]', *other_packaages) + + +def pip_check(session): + return session.run('pip', 'check') + + +def pip_freeze(session): + return session.run('pip', 'freeze') + + +@nox.session(reuse_venv=True) +def flake8(session): + session.install('flake8') + pip_freeze(session) + session.run('flake8', 'setup.py' 'bootstrap.py', 'signalfx_tracing', 'tests', 'noxfile.py') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def unit(session): + install_unit_tests(session) + pip_freeze(session) + session.run('pytest', 'tests/unit', '--ignore', 'tests/unit/libraries', '-p', 'no:django') + + +def test_django(session): + session.run('pytest', 'tests/unit/libraries/django_') + session.run('pytest', 'tests/integration/django_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def django18_via_bootrap(session): + # provides coverage for desired version installation via bootstrap + install_unit_tests(session, 'django>=1.8,<1.9', 'pytest-django', 'django-opentracing') + session.run('sfx-py-trace-bootstrap') + pip_check(session) + pip_freeze(session) + test_django(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('pip', ('<11', '>=18,<19', '>=19,<20')) +def django18_via_extras(session, pip): + install_unit_tests(session, f'pip{pip}', 'django>=1.8,<1.9', 'pytest-django') + + django_extra_args = ['.[django]'] + if pip == '<11': + django_extra_args.insert(0, '--process-dependency-links') + session.install(*django_extra_args) + + pip_check(session) + pip_freeze(session) + test_django(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('django', ('>=1.9,<1.10', '>=1.10,<1.11', '>=1.11,<1.12')) +def django19_110_111_via_extras(session, django): + install_unit_tests(session, f'django{django}', 'pytest-django', '.[django]') + pip_check(session) + pip_freeze(session) + test_django(session) + + +@nox.session(python=('3.4', '3.5', '3.6'), reuse_venv=True) +def django20_via_extras(session): + install_unit_tests(session, 'django>=2.0,<2.1', 'pytest-django', '.[django]') + pip_check(session) + pip_freeze(session) + test_django(session) + + +@nox.session(python=('3.5', '3.6'), reuse_venv=True) +def django21_via_extras(session): + install_unit_tests(session, 'django>=2.1,<2.2', 'pytest-django', '.[django]') + pip_check(session) + pip_freeze(session) + test_django(session) + + +def test_elasticsearch(session, image_version): + session.run('pytest', 'tests/unit/libraries/elasticsearch_') + session.run('pytest', '--elasticsearch-image-version', image_version, 'tests/integration/elasticsearch_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('elasticsearch', ('>=2.0,<2.1', '>=2.1,<2.2', '>=2.2,<2.3', '>=2.3,<2.4', '>=2.4,<2.5')) +def elasticsearch2_via_extras(session, elasticsearch): + install_unit_tests(session, f'elasticsearch{elasticsearch}', 'docker', '.[elasticsearch]') + pip_check(session) + pip_freeze(session) + test_elasticsearch(session, '2.4.6') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('elasticsearch', ('>=5.0,<5.1', '>=5.1,<5.2', '>=5.2,<5.3', '>=5.3,<5.4', '>=5.4,<5.5', '>=5.5,<5.6')) +def elasticsearch5_via_extras(session, elasticsearch): + install_unit_tests(session, f'elasticsearch{elasticsearch}', 'docker', '.[elasticsearch]') + pip_check(session) + pip_freeze(session) + test_elasticsearch(session, '5.6.14') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('elasticsearch', ('>=6.0,<6.1', '>=6.1,<6.2', '>=6.2,<6.3', '>=6.3,<6.4')) +def elasticsearch6i_via_extras(session, elasticsearch): + install_unit_tests(session, f'elasticsearch{elasticsearch}', 'docker', '.[elasticsearch]') + pip_check(session) + pip_freeze(session) + test_elasticsearch(session, '6.5.4') + + +def test_flask(session): + session.run('pytest', 'tests/unit/libraries/flask_') + session.run('pytest', 'tests/integration/flask_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def flask010_via_bootrap(session): + # provides coverage for desired version installation via bootstrap + install_unit_tests(session, 'flask>=0.10,<0.11', 'requests', 'flask-opentracing') + session.run('sfx-py-trace-bootstrap') + pip_check(session) + pip_freeze(session) + test_flask(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('pip', ('<11', '>=18,<19', '>=19,<20')) +def flask010_via_extras(session, pip): + install_unit_tests(session, f'pip{pip}', 'flask>=0.10,<0.11', 'requests') + flask_extra_args = ['.[flask]'] + if pip == '<11': + flask_extra_args.insert(0, '--process-dependency-links') + session.install(*flask_extra_args) + + pip_check(session) + pip_freeze(session) + test_flask(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('flask', ('>=0.11,<0.12', '>=0.12,<0.13', '>=1.0,<1.1')) +def flask_via_extras(session, flask): + install_unit_tests(session, f'flask{flask}', 'requests', '.[flask]') + pip_check(session) + pip_freeze(session) + test_flask(session) + + +def test_jaeger(session): + session.run('pytest', 'tests/integration/test_jaeger_client.py') + session.run('pytest', 'tests/integration/test_runner.py') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def jaeger_via_bootrap(session): + # provides coverage for desired version installation via bootstrap + install_unit_tests(session, 'jaeger-client') + session.run('sfx-py-trace-bootstrap') + pip_check(session) + pip_freeze(session) + test_jaeger(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('pip', ('<11', '>=18,<19', '>=19,<20')) +def jaeger_via_extras(session, pip): + install_unit_tests(session, f'pip{pip}') + + jaeger_extra_args = ['.[jaeger,tornado]'] + if pip == '<11': + jaeger_extra_args.insert(0, '--process-dependency-links') + session.install(*jaeger_extra_args) + + pip_check(session) + pip_freeze(session) + test_jaeger(session) + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def psycopg2_via_extras(session): + install_unit_tests(session, 'psycopg2>=2.7,<2.8', 'docker', '.[psycopg2]') + session.run('pytest', 'tests/unit/libraries/psycopg2_') + session.run('pytest', 'tests/integration/psycopg2_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('pymongo', ('>=3.1,<3.2', '>=3.2,<3.3', '>=3.3,<3.4', '>=3.4,<3.5', + '>=3.5,<3.6', '>=3.6,<3.7', '>=3.7,<3.8')) +def pymongo_via_extras(session, pymongo): + install_unit_tests(session, 'pymongo{pymongo}', 'docker', 'mockupdb', '.[pymongo]') + session.run('pytest', 'tests/unit/libraries/pymongo_') + session.run('pytest', 'tests/integration/pymongo_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('pymysql', ('>=0.8,<0.9', '>=0.9,<0.10')) +def pymysql_via_extras(session, pymysql): + install_unit_tests(session, f'pymysql{pymysql}', 'docker', '.[pymysql]') + session.run('pytest', 'tests/unit/libraries/pymysql_') + session.run('pytest', 'tests/integration/pymysql_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +def redis_via_extras(session): + install_unit_tests(session, f'redis>=2.10,<2.11', 'docker', '.[redis]') + session.run('pytest', 'tests/unit/libraries/redis_') + session.run('pytest', 'tests/integration/redis_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('requests', ('>=2.0,<2.1', '>=2.10,<2.11', '>=2.11,<2.12', '>=2.12,<2.13', + '>=2.13,<2.14', '>=2.14,<2.15', '>=2.15,<2.16', '>=2.16,<2.17', + '>=2.17,<2.18', '>=2.18,<2.19', '>=2.19,<2.20', '>=2.20,<2.21', + '>=2.21,<2.22', '>=2.1,<2.2', '>=2.2,<2.3', '>=2.3,<2.4', + '>=2.4,<2.5', '>=2.5,<2.6', '>=2.6,<2.7', '>=2.7,<2.8', + '>=2.8,<2.9', '>=2.9,<2.10')) +def requests_via_extras(session, requests): + install_unit_tests(session, f'requests{requests}', 'docker', '.[requests]') + session.run('pytest', 'tests/unit/libraries/requests_') + session.run('pytest', 'tests/integration/requests_') + + +@nox.session(python=('2.7', '3.4', '3.5', '3.6'), reuse_venv=True) +@nox.parametrize('tornado', ('>=4.3,<4.4', '>=4.4,<4.5', '>=4.5,<5.0', '>=5.0,<5.1', '>=5.1,<5.2')) +def tornado_via_extras(session, tornado): + install_unit_tests(session, f'tornado{tornado}', 'requests', '.[tornado]') + session.run('pytest', 'tests/unit/libraries/tornado_') + session.run('pytest', 'tests/integration/tornado_') diff --git a/setup.py b/setup.py index 52eccf8..511b770 100755 --- a/setup.py +++ b/setup.py @@ -8,6 +8,19 @@ version = '0.0.4' +protocols = ('http://', 'https://', 'ssh://', 'svn://') + +pep508 = False +try: + import pip + # PEP 508 url support was added in pip 18 and dependency link support was + # dropped in 19: https://github.com/pypa/pip/issues/4187 + if pip.__version__ >= '18.0.0': + pep508 = True +except ImportError: + pass + + class PyTest(TestCommand): user_options = [] @@ -19,21 +32,64 @@ def run_tests(self): sys.exit(pytest.main(['tests/unit', '--ignore', 'tests/unit/libraries', '-p', 'no:django'])) -def stripped_dependencies(deps): - """Reduces any pip-friendly vcs/url to package name for setuptools compatibility""" - return [dep if 'egg=' not in dep else dep.split('egg=')[1] for dep in deps] - - -def isolated_dependencies(deps): - """Ensures `pip install --process-dependency-links signalfx-tracing[django,jaeger,redis]` installs desired extras""" - # Takes advantage of limitation of pip and setuptools dependency links. - # Should guarantee supported instrumentor version is installed. - # https://github.com/pypa/pip/issues/3610#issuecomment-356687173 - isolated = [] - for dep in deps: - if dep[:9] == 'git+https': - isolated.append('{}-999999999'.format(dep)) - return isolated +class DependencyMap(object): + + def __init__(self, deps): + """ + Takes a list of dependencies from a requirements file and constructs a dictionary of the form + {dependency_name: (versioned_dependency_name, dependency_url)} where dependency_url has an + exceedingly high egg version to ensure preferential installation. + + `self.dep_map`: + {'requests': ('requests>=1,<2', None), + 'django': (None, 'git+https://github.com/django/django.git@master#egg=django-999999999')} + + Assumes that all deps are not using PEP 508 URL based lookup (e.g. `package @ package_url`), + though this can be easily constructed from the resulting map w/ `self.map(use_pep508=True)` + + Also assumes all url-based requirements end with `#egg=package_name` or else it is impossible to + determine the package name without downloading! + """ + self.dep_map = {} + version_operators = ('>=', '==', '<=', '<', '>') + + for dep in deps: + dep_name = dep + versioned_dep_name = None + dep_url = None + + if any([proto in dep for proto in protocols]): + dep_name = dep.split('egg=')[1] + dep_url = '{}-999999999'.format(dep) + elif any([op in dep for op in version_operators]): + # There are multiple hits for complex version constraints (`package>=x,=1.7,<1.8', + 'torndao': 'tornado @ git+https://github.com/tornadoweb/tornado.git@v1.0.0#egg=tornado-999999999'} + """ + mapped = {} + for dep_name, dep_tuple in self.dep_map.items(): + versioned_dep_name, dep_url = dep_tuple + if versioned_dep_name is not None: + map_url = versioned_dep_name + elif dep_url is not None: + map_url = '{} @ {}'.format(dep_name, dep_url) if use_pep508 else dep_url + else: + map_url = dep_name + mapped[dep_name] = map_url + return mapped cwd = os.path.abspath(os.path.dirname(__file__)) @@ -46,61 +102,81 @@ def isolated_dependencies(deps): with open(os.path.join(cwd, 'requirements-inst.txt')) as inst_requirements_file: instrumentors = inst_requirements_file.read().splitlines() - instrumentation_requirements = stripped_dependencies(instrumentors) - dependency_links = isolated_dependencies(instrumentors) + instrumentor_dependency_map = DependencyMap(instrumentors) + instrumentor_map = instrumentor_dependency_map.map(pep508) with open(os.path.join(cwd, 'README.md')) as readme_file: long_description = readme_file.read() unit_test_requirements = ['mock', 'pytest', 'six'] -setup(name='signalfx-tracing', - version=version, - author='SignalFx, Inc.', - author_email='info@signalfx.com', - url='http://github.com/signalfx/signalfx-python-tracing', - download_url='http://github.com/signalfx/signalfx-python-tracing/tarball/master', - description='Provides auto-instrumentation for OpenTracing-traced libraries and frameworks', - long_description=long_description, - long_description_content_type="text/markdown", - license='Apache Software License v2', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Natural Language :: English', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - ], - packages=find_packages(), - install_requires=requirements, - tests_require=unit_test_requirements, - dependency_links=dependency_links, - extras_require=dict( - unit_tests=unit_test_requirements, - instrumentation_tests=integration_test_requirements + instrumentation_requirements, - # track with extras list in README - dbapi='dbapi-opentracing', - django='django-opentracing', - elasticsearch='elasticsearch-opentracing', - flask='flask_opentracing', - jaeger='jaeger-client', - psycopg2='dbapi-opentracing', - pymongo='pymongo-opentracing', - pymysql='dbapi-opentracing', - redis='redis-opentracing', - requests='requests-opentracing', - tornado='tornado-opentracing>=1.0.1', - ), - entry_points=dict( - console_scripts=[ - 'sfx-py-trace = scripts.sfx_py_trace:main', - 'sfx-py-trace-bootstrap = scripts.bootstrap:console_script' - ] - ), - cmdclass=dict(test=PyTest)) +setup_args = dict( + name='signalfx-tracing', + version=version, + author='SignalFx, Inc.', + author_email='info@signalfx.com', + url='http://github.com/signalfx/signalfx-python-tracing', + download_url='http://github.com/signalfx/signalfx-python-tracing/tarball/master', + description='Provides auto-instrumentation for OpenTracing-traced libraries and frameworks', + long_description=long_description, + long_description_content_type="text/markdown", + license='Apache Software License v2', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ], + packages=find_packages(), + install_requires=requirements, + tests_require=unit_test_requirements, + entry_points=dict( + console_scripts=[ + 'sfx-py-trace = scripts.sfx_py_trace:main', + 'sfx-py-trace-bootstrap = scripts.bootstrap:console_script' + ] + ), + cmdclass=dict(test=PyTest) +) + +if not pep508: + dependency_links = [] + for potential_url in instrumentor_map.values(): + if any([proto in potential_url for proto in protocols]): + dependency_links.append(potential_url) + setup_args['dependency_links'] = dependency_links + +if pep508: + instrumentation_test_requirements = integration_test_requirements + list(instrumentor_map.values()) +else: + instrumentation_test_requirements = integration_test_requirements + list(instrumentor_map.keys()) + + +def extras_require(lib): + return instrumentor_map[lib] if pep508 else lib + + +setup_args['extras_require'] = dict( + unit_tests=unit_test_requirements, + instrumentation_tests = instrumentation_test_requirements, + dbapi=extras_require('dbapi-opentracing'), + django=extras_require('django-opentracing'), + elasticsearch=extras_require('elasticsearch-opentracing'), + flask=extras_require('flask_opentracing'), + jaeger=extras_require('jaeger-client'), + psycopg2=extras_require('dbapi-opentracing'), + pymongo=extras_require('pymongo-opentracing'), + pymysql=extras_require('dbapi-opentracing'), + redis=extras_require('redis-opentracing'), + requests=extras_require('requests-opentracing'), + tornado=extras_require('tornado_opentracing') +) + +setup(**setup_args) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 569b8c6..0000000 --- a/tox.ini +++ /dev/null @@ -1,163 +0,0 @@ -[tox] -envlist= - flake8 - py{27,34,35,36}-unit - py{27,34,35,36}-django18-via-bootstrap - py{27,34,35,36}-django{18,19,110,111}-via-extras - py{34,35,36}-django20-via-extras - py{35,36}-django21-via-extras - py{27,34,35,36}-elasticsearch{20,21,22,23,24,50,51,52,53,54,55,60,61,62,63} - py{27,34,35,36}-flask010-via-bootstrap - py{27,34,35,36}-flask{010,011,012,10}-via-extras - py{27,34,35,36}-jaeger - py{27,34,35,36}-psycopg2-27 - py{27,34,35,36}-pymongo{31,32,33,34,35,36,37} - py{27,34,35,36}-pymysql{08,09} - py{27,34,35,36}-redis210 - py{27,34,35,36}-requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221} - py{27,34,35,36}-tornado{43,44,45,50,51} - -[testenv] -basepython = - flake8: python2.7 - py27: python2.7 - py34: python3.4 - py35: python3.5 - py36: python3.6 -install_command = pip install --process-dependency-links {opts} {packages} -extras = - jaeger: jaeger - py{27,34,35,36}: unit_tests - django{18,19,110,111,20,21}-via-extras: django - elasticsearch{20,21,22,23,24,50,51,52,53,54,55,60,61,62,63}: elasticsearch - flask{010,011,012,10}-via-extras: flask - psycopg2-27: psycopg2 - pymongo{31,32,33,34,35,36,37}: pymongo - pymysql{08,09}: pymysql - redis210: redis - requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221}: requests - tornado{43,44,45,50,51}: tornado -deps = - flake8: flake8 - django110: django>=1.10,<1.11 - django111: django>=1.11,<1.12 - django18: django>=1.8,<1.9 - django19: django>=1.9,<1.10 - django20: django>=2.0,<2.1 - django21: django>=2.1,<2.2 - django{18,19,110,111,20,21}: pytest-django - elasticsearch20: elasticsearch>=2.0,<2.1 - elasticsearch21: elasticsearch>=2.1,<2.2 - elasticsearch22: elasticsearch>=2.2,<2.3 - elasticsearch23: elasticsearch>=2.3,<2.4 - elasticsearch24: elasticsearch>=2.4,<2.5 - elasticsearch50: elasticsearch>=5.0,<5.1 - elasticsearch51: elasticsearch>=5.1,<5.2 - elasticsearch52: elasticsearch>=5.2,<5.3 - elasticsearch53: elasticsearch>=5.3,<5.4 - elasticsearch54: elasticsearch>=5.4,<5.5 - elasticsearch55: elasticsearch>=5.5,<5.6 - elasticsearch60: elasticsearch>=6.0,<6.1 - elasticsearch61: elasticsearch>=6.1,<6.2 - elasticsearch62: elasticsearch>=6.2,<6.3 - elasticsearch63: elasticsearch>=6.3,<6.4 - elasticsearch{20,21,22,23,24,50,51,52,53,54,55,60,61,62,63}: docker - flask010: flask>=0.10,<0.11 - flask011: flask>=0.11,<0.12 - flask012: flask>=0.12,<0.13 - flask10: flask>=1.0,<1.1 - flask{010,011,012,10}: requests - psycopg2-27: psycopg2>=2.7,<2.8 - psycopg2-27: docker - pymongo31: pymongo>=3.1,<3.2 - pymongo32: pymongo>=3.2,<3.3 - pymongo33: pymongo>=3.3,<3.4 - pymongo34: pymongo>=3.4,<3.5 - pymongo35: pymongo>=3.5,<3.6 - pymongo36: pymongo>=3.6,<3.7 - pymongo37: pymongo>=3.7,<3.8 - pymongo{31,32,33,34,35,36,37}: docker - pymongo{31,32,33,34,35,36,37}: mockupdb - pymysql08: pymysql>=0.8,<0.9 - pymysql09: pymysql>=0.9,<1.0 - pymysql{08,09}: docker - redis210: redis>=2.10,<2.11 - redis210: docker - requests20: requests>=2.0,<2.1 - requests210: requests>=2.10,<2.11 - requests211: requests>=2.11,<2.12 - requests212: requests>=2.12,<2.13 - requests213: requests>=2.13,<2.14 - requests214: requests>=2.14,<2.15 - requests215: requests>=2.15,<2.16 - requests216: requests>=2.16,<2.17 - requests217: requests>=2.17,<2.18 - requests218: requests>=2.18,<2.19 - requests219: requests>=2.19,<2.20 - requests220: requests>=2.20,<2.21 - requests221: requests>=2.21,<2.22 - requests21: requests>=2.1,<2.2 - requests22: requests>=2.2,<2.3 - requests23: requests>=2.3,<2.4 - requests24: requests>=2.4,<2.5 - requests25: requests>=2.5,<2.6 - requests26: requests>=2.6,<2.7 - requests27: requests>=2.7,<2.8 - requests28: requests>=2.8,<2.9 - requests29: requests>=2.9,<2.10 - requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221}: docker - tornado43: tornado>=4.3,<4.4 - tornado44: tornado>=4.4,<4.5 - tornado45: tornado>=4.5,<5.0 - tornado50: tornado>=5.0,<5.1 - tornado51: tornado>=5.1,<5.2 - tornado{43,44,45,50,51}: requests -commands = - flake8: flake8 setup.py bootstrap.py signalfx_tracing tests - py{27,34,35,36}-unit: pytest tests/unit --ignore tests/unit/libraries -p no:django - django18-via-bootstrap: pip install -U django-opentracing # provides coverage for desired version installation via bootstrap - django18-via-bootstrap: sfx-py-trace-bootstrap - django18-via-bootstrap: pip check - django{18,19,110,111,20,21}: pytest tests/unit/libraries/django_ - django{18,19,110,111,20,21}: pytest tests/integration/django_ - elasticsearch{20,21,22,23,24}: pytest --elasticsearch-image-version 2.4.6 tests/integration/elasticsearch_ - elasticsearch{50,51,52,53,54,55}: pytest --elasticsearch-image-version 5.6.14 tests/integration/elasticsearch_ - elasticsearch{60,61,62,63}: pytest --elasticsearch-image-version 6.5.4 tests/integration/elasticsearch_ - elasticsearch{20,21,22,23,24,50,51,52,53,54,55,60,61,62,63}: pytest tests/unit/libraries/elasticsearch_ - flask010-via-bootstrap: pip install -U flask-opentracing # provides coverage for desired version installation via bootstrap - flask010-via-bootstrap: sfx-py-trace-bootstrap - flask010-via-bootstrap: pip check - flask{010,011,012,10}: pytest tests/integration/flask_ - flask{010,011,012,10}: pytest tests/unit/libraries/flask_ - jaeger: pip install -U jaeger-client # provides coverage for desired version installation via bootstrap - jaeger: sfx-py-trace-bootstrap - jaeger: pip check - jaeger: pytest tests/integration/test_jaeger_client.py - jaeger: pytest tests/integration/test_runner.py - psycopg2-27: pytest tests/unit/libraries/psycopg2_ - psycopg2-27: pytest tests/integration/psycopg2_ - pymongo{31,32,33,34,35,36,37}: pytest tests/integration/pymongo_ - pymongo{31,32,33,34,35,36,37}: pytest tests/unit/libraries/pymongo_ - pymysql{08,09}: pytest tests/integration/pymysql_ - pymysql{08,09}: pytest tests/unit/libraries/pymysql_ - redis210: pytest tests/integration/redis_ - redis210: pytest tests/unit/libraries/redis_ - requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221}: pytest tests/integration/requests_ - requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221}: pytest tests/unit/libraries/requests_ - tornado{43,44,45,50,51}: pytest tests/integration/tornado_ - tornado{43,44,45,50,51}: pytest tests/unit/libraries/tornado_ - -[testenv:py27-jaeger] -alwayscopy = true - -[testenv:py34-jaeger] -alwayscopy = true - -[testenv:py35-jaeger] -alwayscopy = true - -[testenv:py36-jaeger] -alwayscopy = true - -[flake8] -max-line-length = 120