+++ /dev/null
-"""
-Allows configuring optional test markers in config, see pyproject.toml.
-
-Run optional tests with `pytest --run-optional=...`.
-
-Mark tests to run only if an optional test ISN'T selected by prepending the mark with
-"no_".
-
-You can specify a "no_" prefix straight in config, in which case you can mark tests
-to run when this tests ISN'T selected by omitting the "no_" prefix.
-
-Specifying the name of the default behavior in `--run-optional=` is harmless.
-
-Adapted from https://pypi.org/project/pytest-optional-tests/, (c) 2019 Reece Hart
-"""
-
-from functools import lru_cache
-import itertools
-import logging
-import re
-from typing import FrozenSet, List, Set, TYPE_CHECKING
-
-import pytest
-from _pytest.store import StoreKey
-
-log = logging.getLogger(__name__)
-
-
-if TYPE_CHECKING:
- from _pytest.config.argparsing import Parser
- from _pytest.config import Config
- from _pytest.mark.structures import MarkDecorator
- from _pytest.nodes import Node
-
-
-ALL_POSSIBLE_OPTIONAL_MARKERS = StoreKey[FrozenSet[str]]()
-ENABLED_OPTIONAL_MARKERS = StoreKey[FrozenSet[str]]()
-
-
-def pytest_addoption(parser: "Parser") -> None:
- group = parser.getgroup("collect")
- group.addoption(
- "--run-optional",
- action="append",
- dest="run_optional",
- default=None,
- help="Optional test markers to run; comma-separated",
- )
- parser.addini("optional-tests", "List of optional tests markers", "linelist")
-
-
-def pytest_configure(config: "Config") -> None:
- """Optional tests are markers.
-
- Use the syntax in https://docs.pytest.org/en/stable/mark.html#registering-marks.
- """
- ot_ini = config.inicfg.get("optional-tests") or []
- ot_markers = set()
- ot_run: Set[str] = set()
- if isinstance(ot_ini, str):
- ot_ini = ot_ini.strip().split("\n")
- marker_re = re.compile(r"^\s*(?P<no>no_)?(?P<marker>\w+)(:\s*(?P<description>.*))?")
- for ot in ot_ini:
- m = marker_re.match(ot)
- if not m:
- raise ValueError(f"{ot!r} doesn't match pytest marker syntax")
-
- marker = (m.group("no") or "") + m.group("marker")
- description = m.group("description")
- config.addinivalue_line("markers", f"{marker}: {description}")
- config.addinivalue_line(
- "markers", f"{no(marker)}: run when `{marker}` not passed"
- )
- ot_markers.add(marker)
-
- # collect requested optional tests
- passed_args = config.getoption("run_optional")
- if passed_args:
- ot_run.update(itertools.chain.from_iterable(a.split(",") for a in passed_args))
- ot_run |= {no(excluded) for excluded in ot_markers - ot_run}
- ot_markers |= {no(m) for m in ot_markers}
-
- log.info("optional tests to run:", ot_run)
- unknown_tests = ot_run - ot_markers
- if unknown_tests:
- raise ValueError(f"Unknown optional tests wanted: {unknown_tests!r}")
-
- store = config._store
- store[ALL_POSSIBLE_OPTIONAL_MARKERS] = frozenset(ot_markers)
- store[ENABLED_OPTIONAL_MARKERS] = frozenset(ot_run)
-
-
-def pytest_collection_modifyitems(config: "Config", items: "List[Node]") -> None:
- store = config._store
- all_possible_optional_markers = store[ALL_POSSIBLE_OPTIONAL_MARKERS]
- enabled_optional_markers = store[ENABLED_OPTIONAL_MARKERS]
-
- for item in items:
- all_markers_on_test = set(m.name for m in item.iter_markers())
- optional_markers_on_test = all_markers_on_test & all_possible_optional_markers
- if not optional_markers_on_test or (
- optional_markers_on_test & enabled_optional_markers
- ):
- continue
- log.info("skipping non-requested optional", item)
- item.add_marker(skip_mark(frozenset(optional_markers_on_test)))
-
-
-@lru_cache()
-def skip_mark(tests: FrozenSet[str]) -> "MarkDecorator":
- names = ", ".join(sorted(tests))
- return pytest.mark.skip(reason=f"Marked with disabled optional tests ({names})")
-
-
-@lru_cache()
-def no(name: str) -> str:
- if name.startswith("no_"):
- return name[len("no_") :]
- return "no_" + name