diff --git a/.github/workflows/UnitTesting.yaml b/.github/workflows/UnitTesting.yaml new file mode 100644 index 00000000..7d71c61f --- /dev/null +++ b/.github/workflows/UnitTesting.yaml @@ -0,0 +1,52 @@ +name: Unit Testing +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + # "setup-python" action doesn't provide python 3.4 binaries for ubuntu-latest. + # Sticking to Ubuntu 18.04 as recommended here: + # https://github.com/actions/setup-python/issues/185#issuecomment-768232756 + runs-on: ubuntu-18.04 + env: + py27: 2.7 + py34: 3.4 + py35: 3.5 + py36: 3.6 + py37: 3.7 + py38: 3.8 + py39: 3.9 + strategy: + fail-fast: false + matrix: + python-version: [py27, py34, py35, py36, py37, py38, py39] + testenv: [core, ext] + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ env[matrix.python-version] }} + + - name: Install tox + run: pip install -U tox-factor + + - name: Cache tox environment + # Preserves .tox directory between runs for faster installs + uses: actions/cache@v2 + with: + path: | + .tox + ~/.cache/pip + key: tox-cache-${{ matrix.python-version }}-${{ matrix.testenv }}-${{ hashFiles('tox.ini') }} + + - name: Run tox + run: | + tox -f ${{ matrix.python-version }}-${{ matrix.testenv }} diff --git a/aws_xray_sdk/core/sampling/local/sampler.py b/aws_xray_sdk/core/sampling/local/sampler.py index 5b2b3a81..ebc09413 100644 --- a/aws_xray_sdk/core/sampling/local/sampler.py +++ b/aws_xray_sdk/core/sampling/local/sampler.py @@ -5,7 +5,8 @@ from .sampling_rule import SamplingRule from ...exceptions.exceptions import InvalidSamplingManifestError -local_sampling_rule = json.loads(pkgutil.get_data(__name__, 'sampling_rule.json')) +# `.decode('utf-8')` needed for Python 3.4, 3.5. +local_sampling_rule = json.loads(pkgutil.get_data(__name__, 'sampling_rule.json').decode('utf-8')) SUPPORTED_RULE_VERSION = (1, 2) diff --git a/aws_xray_sdk/ext/boto_utils.py b/aws_xray_sdk/ext/boto_utils.py index 06532fe7..cb65c854 100644 --- a/aws_xray_sdk/ext/boto_utils.py +++ b/aws_xray_sdk/ext/boto_utils.py @@ -11,8 +11,8 @@ from aws_xray_sdk.ext.util import inject_trace_header, to_snake_case - -whitelist = json.loads(pkgutil.get_data(__name__, 'resources/aws_para_whitelist.json')) +# `.decode('utf-8')` needed for Python 3.4, 3.5 +whitelist = json.loads(pkgutil.get_data(__name__, 'resources/aws_para_whitelist.json').decode('utf-8')) def inject_header(wrapped, instance, args, kwargs): diff --git a/tests/ext/aiobotocore/test_aiobotocore.py b/tests/ext/aiobotocore/test_aiobotocore.py index 4389c8f2..ba43c5d2 100644 --- a/tests/ext/aiobotocore/test_aiobotocore.py +++ b/tests/ext/aiobotocore/test_aiobotocore.py @@ -1,6 +1,6 @@ import pytest -import aiobotocore +from aiobotocore.session import get_session from botocore.stub import Stubber, ANY from botocore.exceptions import ClientError @@ -28,7 +28,7 @@ async def test_describe_table(loop, recorder): req_id = '1234' response = {'ResponseMetadata': {'RequestId': req_id, 'HTTPStatusCode': 403}} - session = aiobotocore.get_session() + session = get_session() async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('describe_table', response, {'TableName': 'mytable'}) @@ -53,7 +53,7 @@ async def test_s3_parameter_capture(loop, recorder): version_id = 'myversionid' response = {'ResponseMetadata': {'RequestId': '1234', 'HTTPStatusCode': 200}} - session = aiobotocore.get_session() + session = get_session() async with session.create_client('s3', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('get_object', response, @@ -87,7 +87,7 @@ async def test_list_parameter_counting(loop, recorder): } } - session = aiobotocore.get_session() + session = get_session() async with session.create_client('sqs', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('list_queues', response, {'QueueNamePrefix': queue_name_prefix}) @@ -117,7 +117,7 @@ async def test_map_parameter_grouping(loop, recorder): } } - session = aiobotocore.get_session() + session = get_session() async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('batch_write_item', response, {'RequestItems': ANY}) @@ -137,7 +137,7 @@ async def test_context_missing_not_swallow_return(loop, recorder): response = {'ResponseMetadata': {'RequestId': '1234', 'HTTPStatusCode': 403}} - session = aiobotocore.get_session() + session = get_session() async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('describe_table', response, {'TableName': 'mytable'}) @@ -150,7 +150,7 @@ async def test_context_missing_not_suppress_exception(loop, recorder): xray_recorder.configure(service='test', sampling=False, context=AsyncContext(loop=loop), context_missing='LOG_ERROR') - session = aiobotocore.get_session() + session = get_session() async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_client_error('describe_table', expected_params={'TableName': ANY}) diff --git a/tests/test_local_sampling_benchmark.py b/tests/test_local_sampling_benchmark.py index 74e69cb2..c98b89c7 100644 --- a/tests/test_local_sampling_benchmark.py +++ b/tests/test_local_sampling_benchmark.py @@ -5,7 +5,8 @@ # Faster def test_pkgutil_static_read(benchmark): def get_sampling_rule(): - return json.loads(pkgutil.get_data(__name__, 'mock_sampling_rule.json')) + # `.decode('utf-8')` needed for Python 3.4, 3.5 + return json.loads(pkgutil.get_data(__name__, 'mock_sampling_rule.json').decode('utf-8')) benchmark(get_sampling_rule) # Slower diff --git a/tox.ini b/tox.ini index 8607c4e1..56bdfa9b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,122 +1,157 @@ [tox] +skip_missing_interpreters = True envlist = - py{27,34,35,36,37,38,39}-default - py{35,36,37,38,39}-aiohttp2 - py{35,36,37,38,39}-django22 - py{36,37,38,39}-django30 - py{36,37,38,39}-django31 - py{36,37,38,39}-django32 - py{27,36,37,38,39}-sqlalchemy - py{34,35}-sqlalchemy - coverage-report + py{27,34,35,36,37,38,39}-core -skip_missing_interpreters = True + ; Unavailable for python 2.7 & 3.4 + py{35,36,37,38,39}-ext-aiobotocore + + ; Unavailable for python 2.7 & 3.4 + py{35,36,37,38,39}-ext-aiohttp + + py{27,34,35,36,37,38,39}-ext-botocore + + py{27,34,35,36,37,38,39}-ext-bottle + + ; Django2 (2.2+) is only for python 3.5 + + py{35,36,37,38,39}-ext-django-2 + + ; Django3 is only for python 3.6+ + py{36,37,38,39}-ext-django-3 + + py{27,34,35,36,37,38,39}-ext-flask + + py{27,34,35,36,37,38,39}-ext-flask_sqlalchemy + + py{27,34,35,36,37,38,39}-ext-httplib + + py{27,34,35,36,37,38,39}-ext-pg8000 + + py{27,34,35,36,37,38,39}-ext-psycopg2 + + py{27,34,35,36,37,38,39}-ext-pymysql + + py{27,34,35,36,37,38,39}-ext-pynamodb + + py{27,34,35,36,37,38,39}-ext-requests + + py{27,34,35,36,37,38,39}-ext-sqlalchemy + + py{27,34,35,36,37,38,39}-ext-sqlalchemy_core + + py{27,34,35,36,37,38,39}-ext-sqlite3 [testenv] -passenv = TOXENV CI TRAVIS TRAVIS_* CODECOV_* +passenv = TOXENV CI CODECOV_* + deps = + ; Testing packages pytest > 3.0.0 pytest-benchmark - coverage==4.5.4 + coverage == 4.5.4 codecov - requests - bottle >= 0.10 - flask >= 0.10 - sqlalchemy - Flask-SQLAlchemy + + ; Packages common to all test environments future - django22: Django==2.2.* - django30: Django==3.0.* - django31: Django==3.1.* - django32: Django==3.2.* - django{22,30,31,32}: django-fake-model - pynamodb >= 3.3.1 - psycopg2 - pg8000 - testing.postgresql - testing.mysqld - webtest - # Python2 only deps - py{27}: enum34 mock - - # pymysql deps - py{27,34,35}: pymysql < 1.0.0 - py{36,37,38,39}: pymysql >= 1.0.0 - - # Python3.4 only deps + wrapt + + ; Python 2.7 only deps + py{27}: enum34 + py{27}: mock + + ; Python 3.4 only deps py34: typing >= 3.7.4.3 - # Python3.5+ only deps - py{35,36,37,38,39}: aiohttp >= 3.0.0 + ; Python 3.5+ only deps + ; for some reason pytest-aiohttp is required for "core" tests + ; TODO: find and replace by more direct dependency py{35,36,37,38,39}: pytest-aiohttp - py{35,36,37,38,39}: aiobotocore >= 0.10.0 -commands = - coverage erase - py{27,34}-default: coverage run --append --source aws_xray_sdk -m py.test tests --ignore tests/ext/aiohttp --ignore tests/ext/aiobotocore --ignore tests/ext/django --ignore tests/test_async_local_storage.py --ignore tests/test_async_recorder.py --ignore tests/ext/sqlalchemy_core - py{35,36,37,38,39}-default: coverage run --append --source aws_xray_sdk -m py.test tests --ignore tests/ext/django --ignore tests/ext/sqlalchemy_core - py{27,36,37,38,39}-default: coverage run --append --source aws_xray_sdk -m py.test tests/ext/sqlalchemy_core - py{34,35}-default: coverage run --append --source aws_xray_sdk -m py.test tests/ext/sqlalchemy_core/ --ignore tests/ext/sqlalchemy_core/test_sqlalchemy_core_2.py - django{22,30,31,32}: coverage run --append --source aws_xray_sdk -m py.test tests/ext/django - codecov + ext-aiobotocore: aiobotocore >= 0.10.0 + ext-aiobotocore: pytest-aiohttp + + ext-aiohttp: aiohttp >= 3.0.0 + ; Breaking change where the `test_client` fixture was renamed. + ; Also, the stable version is only supported for Python 3.7+ + ext-aiohttp: pytest-aiohttp < 1.0.0 + + ext-requests: requests + + ext-bottle: bottle >= 0.10 + ext-bottle: webtest + + ext-flask: flask >= 0.10 + + ext-flask_sqlalchemy: flask >= 0.10 + ext-flask_sqlalchemy: Flask-SQLAlchemy + + ext-sqlalchemy: sqlalchemy + + ext-sqlalchemy_core: sqlalchemy + ext-sqlalchemy_core: testing.postgresql + ext-sqlalchemy_core: psycopg2 + + ext-django-2: Django >=2.0,<3.0 + ext-django-3: Django >=3.0,<4.0 + ext-django: django-fake-model + + ext-pynamodb: pynamodb >= 3.3.1 + + ext-psycopg2: psycopg2 + ext-psycopg2: testing.postgresql + + ext-pg8000: pg8000 <= 1.20.0 + ext-pg8000: testing.postgresql + + ext-pymysql: testing.mysqld + py{27,34,35}-ext-pymysql: pymysql < 1.0.0 + py{36,37,38,39}-ext-pymysql: pymysql >= 1.0.0 setenv = DJANGO_SETTINGS_MODULE = tests.ext.django.app.settings AWS_SECRET_ACCESS_KEY = fake_key AWS_ACCESS_KEY_ID=fake_id -[testenv:py35-aiohttp2] -deps = - pytest > 3.0.0 - aiohttp >= 2.3.0,<3.0.0 - pytest-aiohttp - botocore - coverage==4.5.4 - commands = - py{35}: coverage run --source aws_xray_sdk -m py.test tests/ext/aiohttp --ignore tests/ext/aiohttp/test_client.py + coverage erase -[testenv:py36-aiohttp2] -deps = - pytest > 3.0.0 - aiohttp >= 2.3.0,<3.0.0 - pytest-aiohttp - botocore - coverage==4.5.4 + ; Async methods are only available for python 3.5+ + py{27,34}-core: coverage run --append --source aws_xray_sdk -m pytest --ignore tests/ext --ignore tests/test_async_local_storage.py --ignore tests/test_async_recorder.py + py{35,36,37,38,39}-core: coverage run --append --source aws_xray_sdk -m pytest --ignore tests/ext -commands = - py{36}: coverage run --source aws_xray_sdk -m py.test tests/ext/aiohttp --ignore tests/ext/aiohttp/test_client.py + ext-aiobotocore: coverage run --append --source aws_xray_sdk -m pytest tests/ext/aiobotocore -[testenv:py37-aiohttp2] -deps = - pytest > 3.0.0 - aiohttp >= 2.3.0,<3.0.0 - pytest-aiohttp - botocore - coverage==4.5.4 + ext-aiohttp: coverage run --append --source aws_xray_sdk -m pytest tests/ext/aiohttp -commands = - py{37}: coverage run --source aws_xray_sdk -m py.test tests/ext/aiohttp --ignore tests/ext/aiohttp/test_client.py + ext-botocore: coverage run --append --source aws_xray_sdk -m pytest tests/ext/botocore -[testenv:py38-aiohttp2] -deps = - pytest > 5.2.0 - aiohttp >= 3.6 - pytest-aiohttp - botocore - coverage==4.5.4 + ext-bottle: coverage run --append --source aws_xray_sdk -m pytest tests/ext/bottle -commands = - py{38}: coverage run --source aws_xray_sdk -m py.test tests/ext/aiohttp --ignore tests/ext/aiohttp/test_client.py + ext-django: coverage run --append --source aws_xray_sdk -m pytest tests/ext/django -[testenv:coverage-report] -deps = coverage -skip_install = true -commands = - # might need to add coverage combine at some point - py{38}: coverage report - py{38}: coverage html + ext-flask: coverage run --append --source aws_xray_sdk -m pytest tests/ext/flask + + ext-flask_sqlalchemy: coverage run --append --source aws_xray_sdk -m pytest tests/ext/flask_sqlalchemy + + ext-httplib: coverage run --append --source aws_xray_sdk -m pytest tests/ext/httplib + + ext-pg8000: coverage run --append --source aws_xray_sdk -m pytest tests/ext/pg8000 + + ext-psycopg2: coverage run --append --source aws_xray_sdk -m pytest tests/ext/psycopg2 + + ext-pymysql: coverage run --append --source aws_xray_sdk -m pytest tests/ext/pymysql + + ext-pynamodb: coverage run --append --source aws_xray_sdk -m pytest tests/ext/pynamodb + + ext-requests: coverage run --append --source aws_xray_sdk -m pytest tests/ext/requests + + ext-sqlalchemy: coverage run --append --source aws_xray_sdk -m pytest tests/ext/sqlalchemy + + ; sqlalchemy_core - 2.0 style execution is not supported for python 3.4 & 3.5 + py{27,36,37,38,39}-ext-sqlalchemy_core: coverage run --append --source aws_xray_sdk -m pytest tests/ext/sqlalchemy_core + py{34,35}-ext-sqlalchemy_core: coverage run --append --source aws_xray_sdk -m pytest tests/ext/sqlalchemy_core --ignore tests/ext/sqlalchemy_core/test_sqlalchemy_core_2.py + + ext-sqlite3: coverage run --append --source aws_xray_sdk -m pytest tests/ext/sqlite3 -[flake8] -max-line-length=120 -exclude=tests + ; TODO: add additional logic to combine coverage from "core" and "ext" test runs + ; codecov