[tox] envlist = autoformat, py{ {%- if cookiecutter.support_legacy_python == "yes" %}27,{% endif -%} {{ cookiecutter.supported_modern_python_versions.replace(".", "").split()|join(",") }}}-{local,integ,examples}, noenvvars, sourcebuildcheck, {flake8,pylint}{,-tests,-examples}, mypy-py {%- if cookiecutter.support_legacy_python == "yes" -%} {2,3} {%- else -%} 3 {%- endif -%}, bandit, doc8, readme, docs, # prone to false positives vulture ############################################################################################## # Additional environments: # # # # autoformat : Apply all autoformatters. # # lint :: Run all linters. # # vulture :: Run vulture. Prone to false-positives. # # # # Operational helper environments: # # # # docs :: Build Sphinx documentation. # # autodocs :: Build Sphinx documentation and start server, autobuilding on any file changes. # # park :: Build name-parking packages using pypi-parker. # # build :: Build source and wheel dist files. # # test-release :: Build dist files and upload to testpypi pypirc profile. # # release :: Build dist files and upload to pypi pypirc profile. # ############################################################################################## ######### # Tests # ######### [testenv:base-command] commands = pytest --basetemp={envtmpdir} -l --cov {{cookiecutter.module_name}} {posargs} [testenv] passenv = # Pass through AWS credentials AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ # 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 = -rtest/requirements.txt commands = # Local tests: no network access required local: {[testenv:base-command]commands} test/ -m local # Integration tests: requires network access and might require service credentials integ: {[testenv:base-command]commands} test/ -m integ # Acceptance tests: testing against static test vectors : same requirements as integ accept: {[testenv:base-command]commands} test/ -m accept # Test the examples : same requirements as integ examples: {[testenv:base-command]commands} examples/test/ -m examples # Run all known tests : same requirements as integ all: {[testenv:base-command]commands} test/ examples/test/ # You decide what tests to run manual: {[testenv:base-command]commands} # Verify that local tests work without environment variables present [testenv:noenvvars] basepython = python3 sitepackages = False deps = {[testenv]deps} commands = {[testenv:base-command]commands} test/ -m local # Verify that tests can be successfully run from the source build. [testenv:sourcebuildcheck] basepython = python3 sitepackages = False recreate = True deps = {[testenv:build]deps} commands = {[testenv:build]commands} {toxinidir}/test/source-build-check.sh {envtmpdir} {toxinidir}/dist ############### # Type checks # ############### [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>=0.650 mypy_extensions typing>=3.6.2 {% if cookiecutter.support_legacy_python == "yes" -%} [testenv:mypy-py3] {% else -%} [testenv:mypy] {% endif -%} basepython = {[testenv:mypy-common]basepython} deps = {[testenv:mypy-common]deps} commands = python -m mypy \ --linecoverage-report build \ src/{{cookiecutter.module_name}}/ \ {posargs} {[testenv:mypy-coverage]commands} {% if cookiecutter.support_legacy_python == "yes" %} [testenv:mypy-py2] basepython = {[testenv:mypy-common]basepython} deps = {[testenv:mypy-common]deps} commands = python -m mypy \ --py2 \ --linecoverage-report build \ src/{{cookiecutter.module_name}}/ \ {posargs} {[testenv:mypy-coverage]commands} [testenv:mypy] basepython = {[testenv:mypy-common]basepython} deps = {[testenv:mypy-py2]deps} {[testenv:mypy-py3]deps} commands = {[testenv:mypy-py2]commands} {[testenv:mypy-py3]commands} {% endif %} ############################### # Formatting and style checks # ############################### [testenv:flake8] basepython = python3 deps = flake8 flake8-isort flake8-print>=3.1.0 flake8-bugbear flake8-breakpoint flake8-docstrings>=1.5.0 commands = flake8 \ src/{{cookiecutter.module_name}}/ \ setup.py \ doc/conf.py [testenv:flake8-tests] basepython = {[testenv:flake8]basepython} deps = {[testenv:flake8]deps} commands = flake8 \ # Ignore F811 redefinition errors in tests (breaks with pytest-mock use) # Ignore F841 local variable assigned but never used (useful for collecting locals for test reports) # Ignore D101,D102,D103 docstring requirements for tests --ignore F811,F841,D101,D102,D103 \ test/ [testenv:flake8-examples] basepython = {[testenv:flake8]basepython} deps = {[testenv:flake8]deps} commands = flake8 \ examples/src/ flake8 \ # Ignore F811 redefinition errors in tests (breaks with pytest-mock use) # Ignore F841 local variable assigned but never used (useful for collecting locals for test reports) # Ignore D101,D102,D103 docstring requirements for tests --ignore F811,F841,D101,D102,D103 \ examples/test/ [testenv:pylint] basepython = python3 deps = {[testenv]deps} pyflakes pylint>=2.0.0 commands = pylint \ --rcfile=src/pylintrc \ src/{{cookiecutter.module_name}}/ \ setup.py \ doc/conf.py [testenv:pylint-tests] basepython = {[testenv:pylint]basepython} deps = {[testenv:pylint]deps} commands = pylint \ --rcfile=test/pylintrc \ test/unit/ \ test/functional/ \ test/integration/ [testenv:pylint-examples] basepython = {[testenv:pylint]basepython} deps = {[testenv:pylint]deps} commands = pylint --rcfile=examples/src/pylintrc examples/src/ pylint --rcfile=examples/test/pylintrc examples/test/ [testenv:bandit] basepython = python3 deps = bandit commands = bandit \ {%- if cookiecutter.support_legacy_python == "no" %} # B322: Ignore Python 2 input check: we only support Python 3 -s B322 \ {%- endif %} -r src/{{cookiecutter.module_name}}/ # Prone to false positives: only run manually [testenv:vulture] basepython = python3 deps = vulture commands = vulture src/{{cookiecutter.module_name}}/ [testenv:blacken-src] basepython = python3 deps = black commands = black --line-length 120 \ src/{{cookiecutter.module_name}}/ \ setup.py \ doc/conf.py \ test/ \ examples/ \ {posargs} [testenv:blacken] basepython = python3 deps = {[testenv:blacken-src]deps} commands = {[testenv:blacken-src]commands} [testenv:isort-seed] basepython = python3 deps = seed-isort-config commands = seed-isort-config [testenv:isort] basepython = python3 deps = isort commands = isort \ -rc \ src \ test \ examples \ doc \ setup.py \ {posargs} [testenv:autoformat] basepython = python3 deps = {[testenv:isort-seed]deps} {[testenv:isort]deps} {[testenv:blacken]deps} commands = {[testenv:isort-seed]commands} {[testenv:isort]commands} {[testenv:blacken]commands} [testenv:doc8] basepython = python3 whitelist_externals = {[testenv:resetdocs]whitelist_externals} deps = sphinx doc8 commands = {[testenv:resetdocs]commands} doc8 doc/index.rst doc/lib/ README.rst CHANGELOG.rst [testenv:readme] basepython = python3 deps = {[testenv:build]deps} twine commands = {[testenv:build]commands} twine check dist/* [testenv:checkmanifest] basepython = python3 deps = check-manifest commands = check-manifest --verbose {posargs} [testenv:lint] basepython = python3 # This does not actually ignore errors, # it just runs all commands regardless of whether any fail. # If any fail, the final result is still a fail. ignore_errors = true whitelist_externals = {[testenv:resetdocs]whitelist_externals} deps = {[testenv:autoformat]deps} {[testenv:flake8]deps} {[testenv:flake8-tests]deps} {[testenv:flake8-examples]deps} {[testenv:pylint]deps} {[testenv:pylint-tests]deps} {[testenv:pylint-examples]deps} {[testenv:doc8]deps} {[testenv:readme]deps} {[testenv:checkmanifest]deps} {[testenv:bandit]deps} commands = {[testenv:autoformat]commands} {[testenv:flake8]commands} {[testenv:flake8-tests]commands} {[testenv:flake8-examples]commands} {[testenv:pylint]commands} {[testenv:pylint-tests]commands} {[testenv:pylint-examples]commands} {[testenv:doc8]commands} {[testenv:readme]commands} {[testenv:checkmanifest]commands} {[testenv:bandit]commands} ################# # Documentation # ################# # Clear out any generated files from doc/ [testenv:resetdocs] skip_install = true deps = 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:assert-file-is-empty] basepython = python3 commands = python -c \ "import sys;\ f = open(sys.argv[-1], 'r');\ contents = f.read();\ sys.exit(contents if contents.strip() else 0);\ f.close()" \ {posargs} [testenv:docs-build] basepython = python3 deps = {[testenv:docs]deps} commands = sphinx-build -E -c {toxinidir}/doc/ -b html {toxinidir}/doc/ {toxinidir}/doc/build/html {[testenv:assert-file-is-empty]commands} "{toxinidir}/doc/build/html/output.txt" [testenv:docs-spelling] basepython = python3 deps = {[testenv:docs]deps} commands = sphinx-build -E -c {toxinidir}/doc/ -b spelling {toxinidir}/doc/ {toxinidir}/doc/build/spelling {[testenv:assert-file-is-empty]commands} "{toxinidir}/doc/build/spelling/output.txt" [testenv:docs-linkcheck] basepython = python3 deps = {[testenv:docs]deps} commands = sphinx-build -E -c {toxinidir}/doc/ -b linkcheck {toxinidir}/doc/ {toxinidir}/doc/build/linkcheck {[testenv:assert-file-is-empty]commands} "{toxinidir}/doc/build/linkcheck/output.txt" [testenv:docs] basepython = python3 deps = {[testenv]deps} -r{toxinidir}/doc/requirements.txt commands = {[testenv:docs-build]commands} {[testenv:docs-spelling]commands} {[testenv:docs-linkcheck]commands} [testenv:autodocs] basepython = python3 deps = {[testenv:docs]deps} sphinx-autobuild commands = sphinx-autobuild -E -c {toxinidir}/doc/ -b html {toxinidir}/doc/ {toxinidir}/doc/build/html ################### # Release tooling # ################### [testenv:park] basepython = python3 skip_install = true deps = pypi-parker setuptools commands = python setup.py park [testenv:build] basepython = python3 skip_install = true deps = wheel setuptools commands = python setup.py sdist bdist_wheel [testenv:test-release] basepython = python3 skip_install = true deps = {[testenv:park]deps} {[testenv:build]deps} twine commands = {[testenv:park]commands} {[testenv:build]commands} twine upload --skip-existing --repository testpypi dist/* [testenv:release] basepython = python3 skip_install = true deps = {[testenv:park]deps} {[testenv:build]deps} twine commands = {[testenv:park]commands} {[testenv:build]commands} twine upload --skip-existing --repository pypi dist/*