def flake8(session: PowerSession): """Launch flake8 qualimetry.""" session.install("-r", str(Folders.ci_tools / "flake8-requirements.txt")) session.install("genbadge[flake8]") session.run2("pip install -e .[flake8]") rm_folder(Folders.flake8_reports) Folders.flake8_reports.mkdir(parents=True, exist_ok=True) rm_file(Folders.flake8_intermediate_file) # Options are set in `setup.cfg` file session.run("flake8", pkg_name, "--exit-zero", "--format=html", "--htmldir", str(Folders.flake8_reports), "--statistics", "--tee", "--output-file", str(Folders.flake8_intermediate_file)) # generate our badge session.run2("genbadge flake8 -i %s -o %s" % (Folders.flake8_intermediate_file, Folders.flake8_badge)) rm_file(Folders.flake8_intermediate_file)
def docs(session: PowerSession): """Generates the doc and serves it on a local http server. Pass '-- build' to build statically instead.""" # we need to install self for the doc gallery examples to work session.run2("pip install .") session.install_reqs(phase="docs", phase_reqs=[ "mkdocs-material", "mkdocs", "pymdown-extensions", "pygments", "mkdocs-gallery", "pillow" ]) if session.posargs: # use posargs instead of "serve" session.run2("mkdocs %s" % " ".join(session.posargs)) else: session.run2("mkdocs serve")
def docs(session: PowerSession): """Generates the doc and serves it on a local http server. Pass '-- build' to build statically instead.""" session.install_reqs(phase="docs", phase_reqs=["mkdocs-material", "mkdocs", "pymdown-extensions", "pygments"]) if session.posargs: # use posargs instead of "serve" session.run2("mkdocs -f ./docs/mkdocs.yml %s" % " ".join(session.posargs)) else: session.run2("mkdocs serve -f ./docs/mkdocs.yml")
def publish(session: PowerSession): """Deploy the docs+reports on github pages. Note: this rebuilds the docs""" session.install_reqs(phase="mkdocs", phase_reqs=["mkdocs-material", "mkdocs", "pymdown-extensions", "pygments"]) # possibly rebuild the docs in a static way (mkdocs serve does not build locally) session.run2("mkdocs build -f ./docs/mkdocs.yml") # check that the doc has been generated with coverage if not Folders.site_reports.exists(): raise ValueError("Test reports have not been built yet. Please run 'nox -s tests-3.7' first") # publish the docs session.run2("mkdocs gh-deploy -f ./docs/mkdocs.yml")
def publish(session: PowerSession): """Deploy the docs+reports on github pages. Note: this rebuilds the docs""" # we need to install self for the doc gallery examples to work session.run2("pip install .") session.install_reqs(phase="publish", phase_reqs=[ "mkdocs-material", "mkdocs", "pymdown-extensions", "pygments", "mkdocs-gallery", "pillow", "matplotlib" ]) # possibly rebuild the docs in a static way (mkdocs serve does not build locally) session.run2("mkdocs build") # check that the doc has been generated with coverage if not Folders.site_reports.exists(): raise ValueError( "Test reports have not been built yet. Please run 'nox -s tests-3.7' first" ) # publish the docs session.run2("mkdocs gh-deploy")
def tests(session: PowerSession, coverage, pkg_specs): """Run the test suite, including test reports generation and coverage reports. """ # As soon as this runs, we delete the target site and coverage files to avoid reporting wrong coverage/etc. rm_folder(Folders.site) rm_folder(Folders.reports_root) # delete the .coverage files if any (they are not supposed to be any, but just in case) rm_file(Folders.coverage_intermediate_file) rm_file(Folders.root / "coverage.xml") # CI-only dependencies # Did we receive a flag through positional arguments ? (nox -s tests -- <flag>) # install_ci_deps = False # if len(session.posargs) == 1: # assert session.posargs[0] == "keyrings.alt" # install_ci_deps = True # elif len(session.posargs) > 1: # raise ValueError("Only a single positional argument is accepted, received: %r" % session.posargs) # uncomment and edit if you wish to uninstall something without deleting the whole env # session.run2("pip uninstall pytest-asyncio --yes") # install all requirements session.install_reqs(setup=True, install=True, tests=True, versions_dct=pkg_specs) # install CI-only dependencies # if install_ci_deps: # session.install2("keyrings.alt") # list all (conda list alone does not work correctly on github actions) # session.run2("conda list") conda_prefix = Path(session.bin) if conda_prefix.name == "bin": conda_prefix = conda_prefix.parent session.run2("conda list", env={ "CONDA_PREFIX": str(conda_prefix), "CONDA_DEFAULT_ENV": session.get_session_id() }) # Fail if the assumed python version is not the actual one session.run2("python ci_tools/check_python_version.py %s" % session.python) # install self so that it is recognized by pytest session.run2("pip install -e . --no-deps") # session.install("-e", ".", "--no-deps") # check that it can be imported even from a different folder # Important: do not surround the command into double quotes as in the shell ! session.run('python', '-c', 'import os; os.chdir(\'./docs/\'); import %s' % pkg_name) # finally run all tests if not coverage: # simple: pytest only session.run2("python -m pytest --cache-clear -v %s/tests/" % pkg_name) else: # coverage + junit html reports + badge generation session.install_reqs( phase="coverage", phase_reqs=["coverage", "pytest-html", "genbadge[tests,coverage]"], versions_dct=pkg_specs) # --coverage + junit html reports session.run2( "coverage run --source {pkg_name} " "-m pytest --cache-clear --junitxml={test_xml} --html={test_html} -v {pkg_name}/tests/" "".format(pkg_name=pkg_name, test_xml=Folders.test_xml, test_html=Folders.test_html)) session.run2("coverage report") session.run2( "coverage xml -o {covxml}".format(covxml=Folders.coverage_xml)) session.run2( "coverage html -d {dst}".format(dst=Folders.coverage_reports)) # delete this intermediate file, it is not needed anymore rm_file(Folders.coverage_intermediate_file) # --generates the badge for the test results and fail build if less than x% tests pass nox_logger.info("Generating badge for tests coverage") # Use our own package to generate the badge session.run2("genbadge tests -i %s -o %s -t 100" % (Folders.test_xml, Folders.test_badge)) session.run2("genbadge coverage -i %s -o %s" % (Folders.coverage_xml, Folders.coverage_badge))
def release(session: PowerSession): """Create a release on github corresponding to the latest tag""" # Get current tag using setuptools_scm and make sure this is not a dirty/dev one from setuptools_scm import get_version # (note that this import is not from the session env but the main nox env) from setuptools_scm.version import guess_next_dev_version version = [] def my_scheme(version_): version.append(version_) return guess_next_dev_version(version_) current_tag = get_version(".", version_scheme=my_scheme) # create the package session.install_reqs(phase="setup.py#dist", phase_reqs=["setuptools_scm"]) rm_folder(Folders.dist) session.run2("python setup.py sdist bdist_wheel") if version[0].dirty or not version[0].exact: raise ValueError( "You need to execute this action on a clean tag version with no local changes." ) # Did we receive a token through positional arguments ? (nox -s release -- <token>) if len(session.posargs) == 1: # Run from within github actions - no need to publish on pypi gh_token = session.posargs[0] publish_on_pypi = False elif len(session.posargs) == 0: # Run from local commandline - assume we want to manually publish on PyPi publish_on_pypi = True # keyring set https://docs.github.com/en/rest token import keyring # (note that this import is not from the session env but the main nox env) gh_token = keyring.get_password("https://docs.github.com/en/rest", "token") assert len(gh_token) > 0 else: raise ValueError("Only a single positional arg is allowed for now") # publish the package on PyPi if publish_on_pypi: # keyring set https://upload.pypi.org/legacy/ your-username # keyring set https://test.pypi.org/legacy/ your-username session.install_reqs(phase="PyPi", phase_reqs=["twine"]) session.run2("twine upload dist/* -u smarie") # -r testpypi # create the github release session.install_reqs(phase="release", phase_reqs=["click", "PyGithub"]) session.run2("python ci_tools/github_release.py -s {gh_token} " "--repo-slug {gh_org}/{gh_repo} -cf ./docs/changelog.md " "-d https://{gh_org}.github.io/{gh_repo}/changelog {tag}" "".format(gh_token=gh_token, gh_org=gh_org, gh_repo=gh_repo, tag=current_tag))
def tests(session: PowerSession, coverage, pkg_specs): """Run the test suite, including test reports generation and coverage reports. """ # As soon as this runs, we delete the target site and coverage files to avoid reporting wrong coverage/etc. rm_folder(Folders.site) rm_folder(Folders.reports_root) # delete the .coverage files if any (they are not supposed to be any, but just in case) rm_file(Folders.root / ".coverage") rm_file(Folders.root / "coverage.xml") # CI-only dependencies # Did we receive a flag through positional arguments ? (nox -s tests -- <flag>) # install_ci_deps = False # if len(session.posargs) == 1: # assert session.posargs[0] == "keyrings.alt" # install_ci_deps = True # elif len(session.posargs) > 1: # raise ValueError("Only a single positional argument is accepted, received: %r" % session.posargs) # uncomment and edit if you wish to uninstall something without deleting the whole env # session.run2("pip uninstall pytest-asyncio --yes") # install all requirements session.install_reqs(setup=True, install=True, tests=True, versions_dct=pkg_specs) # install CI-only dependencies # if install_ci_deps: # session.install2("keyrings.alt") # list all (conda list alone does not work correctly on github actions) # session.run2("conda list") conda_prefix = Path(session.bin) if conda_prefix.name == "bin": conda_prefix = conda_prefix.parent session.run2("conda list", env={ "CONDA_PREFIX": str(conda_prefix), "CONDA_DEFAULT_ENV": session.get_session_id() }) # Fail if the assumed python version is not the actual one session.run2("python ci_tools/check_python_version.py %s" % session.python) # install self so that it is recognized by pytest session.run2("pip install -e . --no-deps") # check that it can be imported even from a different folder session.run2([ 'python', '-c', '"import os; os.chdir(\'./docs/\'); import %s"' % pkg_name ]) # finally run all tests if not coverage: # simple: pytest only session.run2("python -m pytest -v %s/tests/" % pkg_name) else: # coverage + junit html reports + badge generation session.install_reqs( phase="coverage", phase_reqs=["coverage", "pytest-html", "requests", "xunitparser"], versions_dct=pkg_specs) # --coverage + junit html reports # First the raw for coverage print("\n\n****** Running tests : 1/2 RAW (for coverage) ******\n\n") session.run2( "coverage run --source {pkg_name} " "-m pytest -v {pkg_name}/tests_raw/" "".format(pkg_name=pkg_name, dst=Folders.test_reports), success_codes=( 0, 1 ) # since some tests are purposedly failing we accept code 1 ) print( "\n\n****** Running tests : 2/2 META (appending to coverage) ******\n\n" ) session.run2( "coverage run --append --source {pkg_name} " "-m pytest --junitxml={dst}/junit.xml --html={dst}/report.html -v {pkg_name}/tests/" "".format(pkg_name=pkg_name, dst=Folders.test_reports)) # session.run2("coverage report") # this shows in terminal + fails under XX%, same as --cov-report term --cov-fail-under=70 # noqa session.run2( "coverage xml -o {covxml}".format(covxml=Folders.coverage_xml)) session.run2( "coverage html -d {dst}".format(dst=Folders.coverage_reports)) # delete this intermediate file, it is not needed anymore rm_file(Folders.root / ".coverage") # --generates the badge for the test results and fail build if less than x% tests pass nox_logger.info("Generating badge for tests coverage") session.run2("python ci_tools/generate-junit-badge.py 100 %s" % Folders.test_reports)