[tox] envlist = py{37,38,39,310,311}-{local,integ,ddb,examples}-fast, nocmk, sourcebuildcheck, docs, bandit, doc8, readme, flake8{,-tests,-examples}, pylint{,-tests,-examples}, vulture, test-upstream-requirements-py3{11,7} # Additional environments: # # vulture :: Runs vulture. Prone to false-positives. # linters :: Runs all linters over all source code. # linters-tests :: Runs all linters over all tests. # Autoformatter helper environments: # # autoformat : Apply all autoformatters # # black-check : Check for "black" issues # blacken : Fix all "black" issues # # isort-seed : Generate a known_third_party list for isort. # NOTE: make the "known_third_party = " line in setup.cfg before running this # NOTE: currently it incorrectly identifies this library too; make sure you remove it # isort-check : Check for isort issues # isort : Fix isort issues # Operational helper environments: # # docs :: Builds Sphinx documentation. # serve-docs :: Starts local webserver to serve built documentation. # park :: Builds name-parking packages using pypi-parker. # build :: Builds source and wheel dist files. # test-release :: Builds dist files and uploads to testpypi pypirc profile. # release :: Builds dist files and uploads to pypi pypirc profile. # Reporting environments: # # coverage :: Runs code coverage, failing the build if coverage is below the configured threshold [testenv:base-command] commands = pytest -n auto --basetemp={envtmpdir} -l {posargs} [testenv] passenv = # Identifies AWS KMS key id to use in integration tests AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ # Identifies AWS KMS MRK key ids to use in integration tests AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID \ AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ # DynamoDB Table to use in integration tests DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME \ # Pass through AWS credentials AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ # AWS Role access in CodeBuild is via the contaner URI AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ # Pass through AWS profile name (useful for local testing) AWS_PROFILE \ # Pass through the default AWS region (used for integration tests) AWS_DEFAULT_REGION sitepackages = False deps = -rdev_requirements/test-requirements.txt # 'download' forces tox to always upgrade pip to the latest download = true commands = # Only run small test scenario sets local-fast: {[testenv:base-command]commands} test/ -m "local and not slow and not veryslow and not nope" integ-fast: {[testenv:base-command]commands} test/ -m "integ and not ddb_integ and not slow and not veryslow and not nope" ddb-fast: {[testenv:base-command]commands} test/ -m "ddb_integ and not slow and not veryslow and not nope" all-fast: {[testenv:base-command]commands} test/ -m "not slow and not veryslow and not nope" # Also run moderately large test scenario sets local-slow: {[testenv:base-command]commands} test/ -m "local and not veryslow and not nope" integ-slow: {[testenv:base-command]commands} test/ -m "integ and not ddb_integ and not veryslow and not nope" ddb-slow: {[testenv:base-command]commands} test/ -m "ddb_integ and not veryslow and not nope" all-slow: {[testenv:base-command]commands} test/ -m "not veryslow and not nope" # Only run those tests that need to be isolated in Travis CI travis-isolation: {[testenv:base-command]commands} test/ -m travis_isolation travis-local-slow: {[testenv:base-command]commands} test/ -m "local and not veryslow and not nope and not travis_isolation" travis-integ-slow: {[testenv:base-command]commands} test/ -m "integ and not ddb_integ and not veryslow and not nope and not travis_isolation" # Also run very large test scenario sets local-full: {[testenv:base-command]commands} test/ -m "local and not nope" integ-full: {[testenv:base-command]commands} test/ -m "integ and not ddb_integ and not nope" ddb-full: {[testenv:base-command]commands} test/ -m "ddb_integ and not nope" all-full: {[testenv:base-command]commands} test/ -m "not nope" # Only run extremely large test scenario sets local-nope: {[testenv:base-command]commands} test/ -m "local and nope" integ-nope: {[testenv:base-command]commands} test/ -m "integ and not ddb_integ and nope" ddb-nope: {[testenv:base-command]commands} test/ -m "ddb_integ and nope" all-nope: {[testenv:base-command]commands} test/ -m "nope" # Do not select any specific markers manual: {[testenv:base-command]commands} # Only run examples tests examples: {[testenv:base-command]commands} examples/test -m "examples" # Run code coverage on the unit tests [testenv:coverage] commands = {[testenv:base-command]commands} --cov dynamodb_encryption_sdk test/ -m "local and not slow and not veryslow and not nope" # Verify that local tests work without environment variables present [testenv:nocmk] basepython = python3 sitepackages = False ######################################################### # Do not pass through or set any environment variables! # passenv = setenv = ######################################################### deps = -rdev_requirements/test-requirements.txt commands = {[testenv:base-command]commands} -m "local and not slow and not veryslow and not nope" --ignore=examples # Collect requirements for use in upstream tests [testenv:freeze-upstream-requirements-base] sitepackages = False skip_install = True recreate = True deps = commands = {toxinidir}/test/freeze-upstream-requirements.sh # Freeze for Python 3.7 [testenv:freeze-upstream-requirements-py37] basepython = python3.7 sitepackages = {[testenv:freeze-upstream-requirements-base]sitepackages} skip_install = {[testenv:freeze-upstream-requirements-base]skip_install} recreate = {[testenv:freeze-upstream-requirements-base]recreate} deps = {[testenv:freeze-upstream-requirements-base]deps} commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-requirements-py37.txt # Freeze for Python 3.11 [testenv:freeze-upstream-requirements-py311] basepython = python3.11 sitepackages = {[testenv:freeze-upstream-requirements-base]sitepackages} skip_install = {[testenv:freeze-upstream-requirements-base]skip_install} recreate = {[testenv:freeze-upstream-requirements-base]recreate} deps = {[testenv:freeze-upstream-requirements-base]deps} commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-requirements-py311.txt # Test frozen upstream requirements [testenv:test-upstream-requirements-base] sitepackages = False recreate = True passenv = commands = {[testenv:base-command]commands} -m "local and not slow and not veryslow and not nope" --ignore=examples # Test frozen upstream requirements for Python 3.7 [testenv:test-upstream-requirements-py37] basepython = python3.7 passenv = deps = -rtest/upstream-requirements-py37.txt sitepackages = {[testenv:test-upstream-requirements-base]sitepackages} recreate = {[testenv:test-upstream-requirements-base]recreate} commands = {[testenv:test-upstream-requirements-base]commands} # Test frozen upstream requirements for Python 3.11 [testenv:test-upstream-requirements-py311] basepython = python3.11 passenv = deps = -rtest/upstream-requirements-py311.txt sitepackages = {[testenv:test-upstream-requirements-base]sitepackages} recreate = {[testenv:test-upstream-requirements-base]recreate} commands = {[testenv:test-upstream-requirements-base]commands} # Verify that tests can be successfully run from the source build. [testenv:sourcebuildcheck] basepython = python3 sitepackages = False recreate = True deps = {[testenv:build]deps} -rdev_requirements/test-requirements.txt commands = {[testenv:build]commands} {toxinidir}/test/source-build-check.sh {envtmpdir} {toxinidir}/dist # mypy [testenv:mypy-coverage] commands = # Make mypy linecoverage report readable by coverage python -c \ "t = open('.coverage', 'w');\ c = open('build/coverage.json').read();\ t.write('!coverage.py: This is a private format, don\'t read it directly!\n');\ t.write(c);\ t.close()" coverage report -m [testenv:mypy-common] basepython = python3 deps = coverage mypy mypy_extensions typing>=3.6.2 [testenv:mypy-py3] basepython = {[testenv:mypy-common]basepython} deps = {[testenv:mypy-common]deps} commands = python -m mypy \ --linecoverage-report build \ src/dynamodb_encryption_sdk/ \ {posargs} {[testenv:mypy-coverage]commands} # Linters [testenv:flake8] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = flake8 \ src/dynamodb_encryption_sdk/ \ setup.py \ doc/conf.py [testenv:flake8-tests] basepython = {[testenv:flake8]basepython} deps = -rdev_requirements/linter-requirements.txt commands = flake8 \ # Ignore F811 redefinition errors in tests (breaks with pytest-mock use) # Ignore D101-107 docstring requirements for tests # E203 is not PEP8 compliant https://github.com/ambv/black#slices # W503 is not PEP8 compliant https://github.com/ambv/black#line-breaks--binary-operators --ignore F811,D101,D102,D103,D107,E203,W503 \ test/ [testenv:flake8-examples] basepython = {[testenv:flake8]basepython} deps = {[testenv:flake8]deps} commands = flake8 \ # Ignore C901 complexity requirements (examples optimize for straightforward readability) --ignore C901 \ examples/src/dynamodb_encryption_sdk_examples/ flake8 \ # Ignore F811 redefinition errors in tests (breaks with fixture use) # Ignore D103 docstring requirements for tests --ignore F811,D103 \ # Our path munging confuses isort, so disable flake8-isort checks on that file --per-file-ignores="examples/test/examples_test_utils.py:I003,I004,I005,examples/test/test_aws_kms_encrypted_examples.py:I005" \ examples/test/ [testenv:pylint] basepython = python3 deps = {[testenv]deps} -rdev_requirements/linter-requirements.txt commands = pylint \ --rcfile=src/pylintrc \ src/dynamodb_encryption_sdk/ \ setup.py \ doc/conf.py [testenv:pylint-tests] basepython = {[testenv:pylint]basepython} deps = {[testenv:pylint]deps} commands = pylint \ --rcfile=test/pylintrc \ test/unit/ \ test/acceptance/ \ test/functional/ \ test/integration/ [testenv:pylint-examples] basepython = {[testenv:pylint]basepython} deps = {[testenv:pylint]deps} commands = pylint --rcfile=examples/src/pylintrc examples/src/dynamodb_encryption_sdk_examples pylint --rcfile=examples/test/pylintrc examples/test/ [testenv:blacken-src] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = black --line-length 120 \ src/dynamodb_encryption_sdk/ \ setup.py \ doc/conf.py \ test/ \ examples/src \ examples/test \ {posargs} [testenv:blacken] basepython = python3 deps = {[testenv:blacken-src]deps} commands = {[testenv:blacken-src]commands} [testenv:black-check] basepython = python3 deps = {[testenv:blacken]deps} commands = {[testenv:blacken-src]commands} --diff [testenv:isort-seed] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = seed-isort-config [testenv:isort] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = isort \ src \ test \ examples/src \ examples/test \ doc \ setup.py \ --skip examples/test/examples_test_utils.py \ {posargs} [testenv:isort-check] basepython = python3 deps = {[testenv:isort]deps} commands = {[testenv:isort]commands} -c [testenv:autoformat] basepython = python3 deps = {[testenv:blacken]deps} {[testenv:isort]deps} commands = {[testenv:isort]commands} {[testenv:blacken]commands} # Clear out any generated files from doc/ [testenv:resetdocs] whitelist_externals = mkdir rm commands = # Make sure that the directory exists to avoid # potential side effects of using rm -f mkdir -p {toxinidir}/doc/lib/generated rm -r {toxinidir}/doc/lib/generated [testenv:doc8] basepython = python3 whitelist_externals = {[testenv:resetdocs]whitelist_externals} deps = -rdev_requirements/doc-requirements.txt -rdev_requirements/linter-requirements.txt commands = {[testenv:resetdocs]commands} doc8 doc/index.rst doc/lib/ README.rst CHANGELOG.rst [testenv:readme] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = python setup.py check -r -s [testenv:bandit] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = bandit -r src/dynamodb_encryption_sdk/ # Prone to false positives: only run independently [testenv:vulture] basepython = python3 deps = -rdev_requirements/linter-requirements.txt commands = vulture src/dynamodb_encryption_sdk/ [testenv:linters] basepython = python3 deps = {[testenv:flake8]deps} {[testenv:pylint]deps} {[testenv:doc8]deps} {[testenv:readme]deps} {[testenv:bandit]deps} commands = {[testenv:flake8]commands} {[testenv:pylint]commands} {[testenv:doc8]commands} {[testenv:readme]commands} {[testenv:bandit]commands} [testenv:linters-tests] basepython = python3 deps = {[testenv:flake8-tests]deps} {[testenv:pylint-tests]deps} commands = {[testenv:flake8-tests]commands} {[testenv:pylint-tests]commands} # Documentation [testenv:docs] basepython = python3 deps = -rdev_requirements/doc-requirements.txt commands = sphinx-build -E -c doc/ -b html doc/ doc/build/html sphinx-build -E -c doc/ -b linkcheck doc/ doc/build/html [testenv:serve-docs] basepython = python3 skip_install = true changedir = doc/build/html deps = commands = python -m http.server {posargs} # Release tooling [testenv:park] basepython = python3 skip_install = true deps = -rdev_requirements/release-requirements.txt commands = python setup.py park # Release tooling [testenv:build] basepython = python3 skip_install = true deps = -rdev_requirements/release-requirements.txt commands = python setup.py sdist bdist_wheel [testenv:release-base] basepython = python3 skip_install = true deps = -rdev_requirements/release-requirements.txt passenv = # Intentionally omit TWINE_REPOSITORY_URL from the passenv list, # as this overrides other ways of setting the repository and could # unexpectedly result in releasing to the wrong repo {[testenv]passenv} \ TWINE_USERNAME \ TWINE_PASSWORD commands = {[testenv:build]commands} [testenv:release-private] basepython = python3 skip_install = true deps = {[testenv:release-base]deps} passenv = {[testenv:release-base]passenv} \ TWINE_REPOSITORY_URL setenv = # Explicitly set the URL as the env variable value, which will cause us to # throw an error if the variable is not set. Otherwise, omission of the # env variable could cause us to unintentionally upload to the wrong repo TWINE_REPOSITORY_URL = {env:TWINE_REPOSITORY_URL} commands = {[testenv:release-base]commands} # Omitting an explicit repository will cause twine to use the repository # specified in the environment variable twine upload --skip-existing {toxinidir}/dist/* [testenv:test-release] basepython = python3 skip_install = true deps = {[testenv:release-base]deps} passenv = {[testenv:release-base]passenv} commands = {[testenv:release-base]commands} twine upload --skip-existing --repository testpypi {toxinidir}/dist/* [testenv:release] basepython = python3 skip_install = true deps = {[testenv:release-base]deps} passenv = {[testenv:release-base]passenv} commands = {[testenv:release-base]commands} twine upload --skip-existing --repository pypi {toxinidir}/dist/*