/
niceSOFT
/
python3-hatchling
Обзор
Документация
Войти
/
niceSOFT
/
python3-hatchling
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/hatch/cli/test/__init__.py
234 строки
9 KB
Floze
Erase stale coverage before hatch test (#2349)
23 июл 2026, 05:14
Не верифицирован
23 июл 2026, 05:14
a82e427
Код
Авторство
О чём код?
from __future__ import annotations from typing import TYPE_CHECKING import click if TYPE_CHECKING: from hatch.cli.application import Application from hatch.env.plugin.interface import EnvironmentInterface @click.command(short_help="Run tests", context_settings={"ignore_unknown_options": True}) @click.argument("args", nargs=-1) @click.option("--randomize", "-r", is_flag=True, help="Randomize the order of test execution") @click.option("--parallel", "-p", is_flag=True, help="Parallelize test execution") @click.option("--retries", type=int, help="Number of times to retry failed tests") @click.option("--retry-delay", type=float, help="Seconds to wait between retries") @click.option("--cover", "-c", is_flag=True, help="Measure code coverage") @click.option("--cover-quiet", is_flag=True, help="Disable coverage reporting after tests, implicitly enabling --cover") @click.option( "--cover-xml", is_flag=True, help="Generate XML coverage report after tests, implicitly enabling --cover", ) @click.option( "--cover-xml-output", type=click.Path(), help="Path for the XML coverage report file", ) @click.option("--all", "-a", "test_all", is_flag=True, help="Test all environments in the matrix") @click.option("--python", "-py", help="The Python versions to test, equivalent to: -i py=...") @click.option("--include", "-i", "included_variable_specs", multiple=True, help="The matrix variables to include") @click.option("--exclude", "-x", "excluded_variable_specs", multiple=True, help="The matrix variables to exclude") @click.option("--show", "-s", is_flag=True, help="Show information about environments in the matrix") @click.pass_context def test( ctx: click.Context, *, args: tuple[str, ...], randomize: bool, parallel: bool, retries: int | None, retry_delay: float | None, cover: bool, cover_quiet: bool, cover_xml: bool, cover_xml_output: str | None, test_all: bool, python: str | None, included_variable_specs: tuple[str, ...], excluded_variable_specs: tuple[str, ...], show: bool, ): """Run tests using the `hatch-test` environment matrix. If no filtering options are selected, then tests will be run in the first compatible environment found in the matrix with priority given to those matching the current interpreter. The `-i`/`--include` and `-x`/`--exclude` options may be used to include or exclude certain variables, optionally followed by specific comma-separated values, and may be selected multiple times. For example, if you have the following configuration: \b ```toml config-example [[tool.hatch.envs.hatch-test.matrix]] python = ["3.9", "3.10"] version = ["42", "3.14", "9000"] ``` then running: \b ``` hatch test -i py=3.10 -x version=9000 ``` would run tests in the environments `hatch-test.py3.10-42` and `hatch-test.py3.10-3.14`. The `-py`/`--python` option is a shortcut for specifying the inclusion `-i py=...`. \b !!! note The inclusion option is treated as an intersection while the exclusion option is treated as a union i.e. an environment must match all of the included variables to be selected while matching any of the excluded variables will prevent selection. The test environment comes with default dependencies (pytest, coverage, etc.). Use the `-s`/`--show` option to see the full resolved configuration for each environment. To customize dependencies or other settings, see the testing configuration docs: https://hatch.pypa.io/latest/config/internal/testing/ """ app: Application = ctx.obj if show: import os from hatch.cli.env.show import show as show_env ctx.invoke( show_env, internal=True, hide_titles=True, force_ascii=os.environ.get("HATCH_SELF_TESTING") == "true", envs=ctx.obj.project.config.internal_matrices["hatch-test"]["envs"] if app.verbose else ["hatch-test"], ) return if cover_quiet: cover = True if cover_xml: cover = True import sys from hatch.cli.test.core import PatchedCoverageConfig from hatch.utils.runner import parse_matrix_variables, select_environments if python is not None: included_variable_specs = (f"py={python}", *included_variable_specs) try: included_variables = parse_matrix_variables(included_variable_specs) except ValueError as e: app.abort(f"Duplicate included variable: {e}") try: excluded_variables = parse_matrix_variables(excluded_variable_specs) except ValueError as e: app.abort(f"Duplicate excluded variable: {e}") if test_all and (included_variables or excluded_variables): app.abort("The --all option cannot be used with the --include or --exclude options.") if retries is None and retry_delay is not None: app.abort("The --retry-delay option requires the --retries option to be set as well.") app.ensure_environment_plugin_dependencies() test_envs = app.project.config.internal_matrices["hatch-test"]["envs"] selected_envs: list[str] = [] multiple_possible = True if test_all: selected_envs.extend(test_envs) elif included_variables or excluded_variables: selected_envs.extend(select_environments(test_envs, included_variables, excluded_variables)) else: multiple_possible = False # Prioritize candidates that seems to match the running interpreter current_version = ".".join(map(str, sys.version_info[:2])) candidate_names: list[str] = list(test_envs) candidate_names.sort(key=lambda name: test_envs[name].get("python") != current_version) candidate_envs: list[EnvironmentInterface] = [] for candidate_name in candidate_names: environment = app.project.get_environment(candidate_name) if environment.exists(): selected_envs.append(candidate_name) break candidate_envs.append(environment) if not selected_envs: # If none of the candidates exist, then do a check for compatibility for candidate_env in candidate_envs: try: candidate_env.check_compatibility() except Exception: # noqa: BLE001, S110 pass else: selected_envs.append(candidate_env.name) break else: app.abort(f"No compatible environments found: {candidate_envs}") test_script = "run-cov" if cover else "run" patched_coverage = PatchedCoverageConfig(app.project.location, app.data_dir / ".config") coverage_config_file = str(patched_coverage.internal_config_path) if cover: patched_coverage.write_config_file() for context in app.runner_context([selected_envs[0]]): context.add_shell_command("coverage erase") context.env_vars["COVERAGE_RCFILE"] = coverage_config_file for context in app.runner_context(selected_envs, ignore_compat=multiple_possible, display_header=multiple_possible): internal_arguments: list[str] = list(context.env.config.get("extra-args", [])) if not context.env.config.get("randomize", randomize): internal_arguments.extend(["-p", "no:randomly"]) if context.env.config.get("parallel", parallel): internal_arguments.extend(["-n", "logical"]) if (num_retries := context.env.config.get("retries", retries)) is not None: if "-r" not in args: internal_arguments.extend(["-r", "aR"]) internal_arguments.extend(["--reruns", str(num_retries)]) if (seconds_delay := context.env.config.get("retry-delay", retry_delay)) is not None: internal_arguments.extend(["--reruns-delay", str(seconds_delay)]) internal_args = context.env.join_command_args(internal_arguments) if internal_args: # Add an extra space if required internal_args = f" {internal_args}" arguments: list[str] = [] if args: arguments.extend(args) else: arguments.extend(context.env.config.get("default-args", ["tests"])) context.add_shell_command([test_script, *arguments]) context.env_vars["HATCH_TEST_ARGS"] = internal_args if cover: context.env_vars["COVERAGE_RCFILE"] = coverage_config_file context.env_vars["COVERAGE_PROCESS_START"] = coverage_config_file if cover: for context in app.runner_context([selected_envs[0]]): context.add_shell_command("cov-combine") if not cover_quiet: for context in app.runner_context([selected_envs[0]]): context.add_shell_command("cov-report") if cover_xml: xml_command = f"coverage xml -o {cover_xml_output}" if cover_xml_output else "coverage xml" for context in app.runner_context([selected_envs[0]]): context.add_shell_command(xml_command)