예제 #1
0
def safety(session: Session) -> None:
    """Check all dependencies for known vulnerabilities."""
    if "skip_install" not in session.posargs:
        extras = "poetry safety"
        session.poetry_install(
            extras,
            no_root=True,
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")

    venv_path = get_venv_path()
    req_file_path = (get_venv_tmp_dir(venv_path, create_if_missing=True) /
                     "requirements.txt")

    #: Use `poetry show` to fill `requirements.txt`
    command = [str(get_venv_bin_dir(venv_path) / "poetry"), "show"]
    # TODO:#i# simplify when py36 is not longer supported.
    if sys.version_info[0:2] > (3, 6):
        cmd = subprocess.run(command, check=True,
                             capture_output=True)  # noqa: S603
    else:
        cmd = subprocess.run(command, check=True,
                             stdout=subprocess.PIPE)  # noqa: S603
    with open(req_file_path, "w") as req_file:
        req_file.write(
            re.sub(r"([\w-]+)[ (!)]+([\d.a-z-]+).*", r"\1==\2",
                   cmd.stdout.decode()))

    session.run("safety", "check", "-r", str(req_file_path), "--full-report")
예제 #2
0
def docs(session: Session) -> None:
    """Build docs with sphinx."""
    extras = "docs"
    cmd = "sphinx-build"
    args = ["-b", "html", "-aE", "docs/source", "docs/build/html"]

    if "autobuild" in session.posargs or "ab" in session.posargs:
        extras += " sphinx-autobuild"
        cmd = "sphinx-autobuild"
        args += ["--open-browser"]

    if "skip_install" not in session.posargs:
        session.poetry_install(
            extras,
            no_root=(TOX_CALLS or SKIP_INSTALL),
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")

    #: Remove processed posargs
    for arg in ("skip_install", "autobuild", "ab"):
        with contextlib.suppress(ValueError):
            session.posargs.remove(arg)

    color = ["--color"] if FORCE_COLOR else []

    session.run(cmd, *color, *args, *session.posargs)

    index_file = NOXFILE_DIR / "docs/build/html/index.html"
    print(f"DOCUMENTATION AVAILABLE UNDER: {index_file.as_uri()}")
예제 #3
0
def setup_pre_commit(session: Session) -> None:
    """Set up pre-commit.

    (Re)Create pre-commit tox env, install pre-commit hook, run tox env.
    """
    _tox_caller(session, "pre_commit", ["TOX_ARGS=-r,--notest"])
    session.run("pre-commit", "install", "-t", "pre-commit", "-t",
                "commit-msg")
    _tox_caller(session, "pre_commit", [])
예제 #4
0
def package(session: Session) -> None:
    """Check sdist and wheel."""
    if "skip_install" not in session.posargs:
        extras = "poetry twine"
        session.poetry_install(
            extras,
            no_root=True,
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")

    color = ["--ansi"] if FORCE_COLOR else []
    session.run("poetry", "build", *color, "-vvv")
    session.run("twine", "check", "--strict", "dist/*")
예제 #5
0
def _coverage(session: Session, job: str) -> None:
    if "skip_install" not in session.posargs:
        extras = "coverage"
        if job in ("report", "all"):
            extras += " diff-cover"
        session.poetry_install(
            extras,
            no_root=True,
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")
        #: Remove processed posargs
        with contextlib.suppress(ValueError):
            session.posargs.remove("skip_install")

    session.env["COVERAGE_FILE"] = str(COV_CACHE_DIR / ".coverage")
    cov_xml_file = f"{COV_CACHE_DIR / 'coverage.xml'}"
    cov_html_dir = f"{COV_CACHE_DIR / 'htmlcov'}"

    if job in ("merge", "all"):
        session.run("coverage", "combine")
        session.run("coverage", "xml", "-o", cov_xml_file)
        session.run("coverage", "html", "-d", cov_html_dir)

    if job in ("report", "all"):
        raise_error = False
        min_cov = session.env.get("MIN_COVERAGE") or 100

        try:
            session.run("coverage", "report", "-m", f"--fail-under={min_cov}")
        except CommandFailed:
            raise_error = True

        session.run(
            "diff-cover",
            f"--compare-branch={session.env.get('DIFF_AGAINST') or 'origin/main'}",
            "--ignore-staged",
            "--ignore-unstaged",
            f"--fail-under={session.env.get('MIN_DIFF_COVERAGE') or 100}",
            f"--diff-range-notation={session.env.get('DIFF_RANGE_NOTATION') or '..'}",
            cov_xml_file,
        )

        if raise_error:
            raise CommandFailed
예제 #6
0
def _tox_caller(session: Session,
                tox_env: str,
                posargs: Optional[List[str]] = None) -> None:
    """Call given tox env with given posargs.

    :param session: nox session object
    :param tox_env: tox env(s) to call; parameter to ``tox -e``
    :param posargs: posargs; defaults to None
    """
    if posargs is None:
        posargs = session.posargs

    #: Extract tox args
    tox_args: List[str] = []
    for arg in posargs:
        if arg.startswith("TOX_ARGS="):
            tox_args = arg[9:].split(",")
            posargs.remove(arg)
            break

    #: Extract nox args for nox called by tox
    nox_args: List[str] = []
    for arg in posargs:
        if arg.startswith("NOX_ARGS="):
            nox_args = arg[9:].split(",")
            posargs.remove(arg)
            break

    if not find_spec("tox"):
        session.poetry_install(
            "tox",
            no_root=True,
            no_dev=IN_CI,
            pip_require_venv=poetry_require_venv(session),
        )

    session.env["_TOX_SKIP_SDIST"] = str(SKIP_INSTALL)
    if FORCE_COLOR:
        #: Force color for nox when called by tox
        session.env["_TOX_FORCE_NOX_COLOR"] = "--forcecolor"
        #: Activate colorful output for tox
        session.env["PY_COLORS"] = "1"

    session.run("tox", "-e", tox_env, *tox_args, "--", *nox_args, "--",
                *posargs)
예제 #7
0
def install_extras(session: Session) -> None:
    """Install all specified extras."""
    extras = PYPROJECT["tool"]["poetry"].get("extras")

    if not extras:
        session.skip("No extras found to be installed.")

    extras_to_install = ""
    for extra in extras:
        extras_to_install += f" {extra}"

    session.poetry_install(
        extras_to_install.strip(),
        no_root=True,
        no_dev=False,
        pip_require_venv=poetry_require_venv(session),
    )
    session.run("python", "-m", "pip", "list", "--format=columns")
    print(f"PYTHON INTERPRETER LOCATION: {sys.executable}")
예제 #8
0
def test_code(session: Session) -> None:
    """Run tests with given python version."""
    if "skip_install" not in session.posargs:
        extras = "testing"
        session.poetry_install(
            extras,
            no_root=(TOX_CALLS or SKIP_INSTALL),
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")
        #: Remove processed posargs
        with contextlib.suppress(ValueError):
            session.posargs.remove("skip_install")

    interpreter = sys.implementation.__getattribute__("name")
    version = ".".join([str(v) for v in sys.version_info[0:2]])
    name = f"{OS}.{interpreter}{version}"
    session.env["COVERAGE_FILE"] = str(COV_CACHE_DIR / f".coverage.{name}")

    cov_source_dir = Path("no-spec-found")
    with contextlib.suppress(AttributeError, TypeError):
        cov_source_dir = Path(
            find_spec(
                PACKAGE_NAME).origin  # type: ignore[union-attr, arg-type]
        ).parent

    color = ["--color=yes"] if FORCE_COLOR else []
    posargs = session.posargs if session.posargs else ["tests"]

    session.run(
        "pytest",
        *color,
        f"--basetemp={get_venv_tmp_dir(get_venv_path(), create_if_missing=True)}",
        f"--junitxml={NOXFILE_DIR / '.junit_cache' / f'junit.{name}.xml'}",
        f"--cov={cov_source_dir}",
        f"--cov-fail-under={session.env.get('MIN_COVERAGE') or 100}",
        f"--numprocesses={session.env.get('PYTEST_XDIST_N') or 'auto'}",
        *posargs,
    )
예제 #9
0
def test_docs(session: Session, builder: str) -> None:
    """Build and check docs with (see env name) sphinx builder."""
    if "skip_install" not in session.posargs:
        extras = "docs"
        session.poetry_install(
            extras,
            no_root=(TOX_CALLS or SKIP_INSTALL),
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")
        #: Remove processed posargs
        with contextlib.suppress(ValueError):
            session.posargs.remove("skip_install")

    source_dir = "docs/source"
    target_dir = f"docs/build/test/{builder}"
    std_args = ["-aE", "-v", "-nW", "--keep-going", source_dir, target_dir]

    color = ["--color"] if FORCE_COLOR else []

    session.run("sphinx-build", "-b", builder, *color, *std_args,
                *session.posargs)
예제 #10
0
def dev(session: Session) -> None:
    """Call basic dev setup nox sessions."""
    session.run("nox", "--session", "install_extras", "setup_pre_commit",
                "create_spellignore")
예제 #11
0
def pre_commit(session: Session) -> None:  # noqa: R0912
    """Format and check the code."""
    if "skip_install" not in session.posargs:
        extras = "pre-commit testing docs poetry dev_nox"
        session.poetry_install(
            extras,
            no_root=(TOX_CALLS or SKIP_INSTALL),
            no_dev=(TOX_CALLS or IN_CI),
            pip_require_venv=poetry_require_venv(session),
        )
    else:
        session.log("Skipping install step.")

    #: Set 'show-diff' and 'skip identity hook'
    show_diff = []
    env = {"SKIP": "identity"}
    if (session.interactive and "diff" in session.posargs) or (
            not session.interactive and "nodiff" not in session.posargs):
        show_diff = ["--show-diff-on-failure"]
        env = {}

    #: Add SKIP from posargs to env
    skip = ""
    for arg in session.posargs:
        if arg.startswith("SKIP="):
            skip = arg
            break

    if skip:
        env = {"SKIP": f"{skip[5:]},{env.get('SKIP', '')}"}

    #: Get hooks from posargs
    hooks_arg = ""
    for arg in session.posargs:
        if arg.startswith("HOOKS="):
            hooks_arg = arg
            break

    #: Remove processed posargs
    for arg in ("skip_install", "diff", "nodiff", skip, hooks_arg):
        with contextlib.suppress(ValueError):
            session.posargs.remove(arg)

    hooks = hooks_arg[6:].split(",") if hooks_arg else [""]

    color = ["--color=always"] if FORCE_COLOR else []

    error_hooks = []
    for hook in hooks:
        add_args = show_diff + session.posargs + ([hook] if hook else [])
        try:
            session.run("pre-commit",
                        "run",
                        *color,
                        "--all-files",
                        *add_args,
                        env=env)
        except CommandFailed:
            error_hooks.append(hook)

    print(
        "HINT: to add checks as pre-commit hook run: ",
        f'"{get_venv_bin_dir(get_venv_path()) / "pre-commit"} '
        "install -t pre-commit -t commit-msg.",
    )

    if error_hooks:
        if hooks != [""]:
            nox_logger.error(
                f"The following pre-commit hooks failed: {error_hooks}."  # noqa: G004
            )
        raise CommandFailed