summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/changelog/2766.feature.rst1
-rw-r--r--src/tox/session/env_select.py33
-rw-r--r--tests/session/test_env_select.py29
3 files changed, 54 insertions, 9 deletions
diff --git a/docs/changelog/2766.feature.rst b/docs/changelog/2766.feature.rst
new file mode 100644
index 00000000..3e4d6710
--- /dev/null
+++ b/docs/changelog/2766.feature.rst
@@ -0,0 +1 @@
+``-f`` can be used multiple times and on hyphenated factors (e.g. ``-f py311-django -f py39``) - by :user:`sirosen`.
diff --git a/src/tox/session/env_select.py b/src/tox/session/env_select.py
index c746c25b..f926beda 100644
--- a/src/tox/session/env_select.py
+++ b/src/tox/session/env_select.py
@@ -91,8 +91,19 @@ def register_env_select_flags(
if multiple:
help_msg = "labels to evaluate"
add_to.add_argument("-m", dest="labels", metavar="label", help=help_msg, default=[], type=str, nargs="+")
- help_msg = "factors to evaluate"
- add_to.add_argument("-f", dest="factors", metavar="factor", help=help_msg, default=[], type=str, nargs="+")
+ help_msg = (
+ "factors to evaluate (passing multiple factors means 'AND', passing this option multiple times means 'OR')"
+ )
+ add_to.add_argument(
+ "-f",
+ dest="factors",
+ metavar="factor",
+ help=help_msg,
+ default=[],
+ type=str,
+ nargs="+",
+ action="append",
+ )
help_msg = "exclude all environments selected that match this regular expression"
add_to.add_argument("--skip-env", dest="skip_env", metavar="re", help=help_msg, default="", type=str)
return add_to
@@ -288,9 +299,17 @@ class EnvSelector:
self._manager.tox_add_env_config(pkg_conf, self._state)
return pkg_env
+ def _parse_factors(self) -> tuple[set[str], ...]:
+ # factors is a list of lists, from the combination of nargs="+" and action="append"
+ # also parse hyphenated factors into lists of factors
+ # so that `-f foo-bar` and `-f foo bar` are treated equivalently
+ raw_factors = getattr(self._state.conf.options, "factors", [])
+ return tuple({f for factor in factor_list for f in factor.split("-")} for factor_list in raw_factors)
+
def _mark_active(self) -> None:
labels = set(getattr(self._state.conf.options, "labels", []))
- factors = set(getattr(self._state.conf.options, "factors", []))
+ factors = self._parse_factors()
+
assert self._defined_envs_ is not None
if labels or factors:
for env_info in self._defined_envs_.values():
@@ -302,10 +321,12 @@ class EnvSelector:
for env_info in self._defined_envs_.values():
if labels.intersection(env_info.env.conf["labels"]):
env_info.is_active = True
- if self._state.conf.options.factors: # if matches mark it active
+ if factors: # if matches mark it active
for name, env_info in self._defined_envs_.items():
- if factors.issubset(set(name.split("-"))):
- env_info.is_active = True
+ for factor_set in factors:
+ if factor_set.issubset(set(name.split("-"))):
+ env_info.is_active = True
+ break
def __getitem__(self, item: str) -> RunToxEnv | PackageToxEnv:
"""
diff --git a/tests/session/test_env_select.py b/tests/session/test_env_select.py
index b6683541..496107a3 100644
--- a/tests/session/test_env_select.py
+++ b/tests/session/test_env_select.py
@@ -1,5 +1,7 @@
from __future__ import annotations
+import pytest
+
from tox.pytest import MonkeyPatch, ToxProjectCreator
@@ -61,15 +63,36 @@ def test_label_core_and_trait(tox_project: ToxProjectCreator) -> None:
outcome.assert_out_err("py310\npy39\nflake8\ntype\n", "")
-def test_factor_select(tox_project: ToxProjectCreator) -> None:
+@pytest.mark.parametrize(
+ ("selection_arguments", "expect_envs"),
+ [
+ (
+ ("-f", "cov", "django20"),
+ ("py310-django20-cov", "py39-django20-cov"),
+ ),
+ (
+ ("-f", "cov-django20"),
+ ("py310-django20-cov", "py39-django20-cov"),
+ ),
+ (
+ ("-f", "py39", "django20", "-f", "py310", "django21"),
+ ("py310-django21-cov", "py310-django21", "py39-django20-cov", "py39-django20"),
+ ),
+ ],
+)
+def test_factor_select(
+ tox_project: ToxProjectCreator,
+ selection_arguments: tuple[str, ...],
+ expect_envs: tuple[str, ...],
+) -> None:
ini = """
[tox]
env_list = py3{10,9}-{django20,django21}{-cov,}
"""
project = tox_project({"tox.ini": ini})
- outcome = project.run("l", "--no-desc", "-f", "cov", "django20")
+ outcome = project.run("l", "--no-desc", *selection_arguments)
outcome.assert_success()
- outcome.assert_out_err("py310-django20-cov\npy39-django20-cov\n", "")
+ outcome.assert_out_err("{}\n".format("\n".join(expect_envs)), "")
def test_tox_skip_env(tox_project: ToxProjectCreator, monkeypatch: MonkeyPatch) -> None: