diff --git a/docs/changelog/2718.bugfix.rst b/docs/changelog/2718.bugfix.rst new file mode 100644 index 000000000..02e3cab8b --- /dev/null +++ b/docs/changelog/2718.bugfix.rst @@ -0,0 +1 @@ +Fix space not accepted in factor filter expression - by :user:`gaborbernat`. diff --git a/src/tox/config/loader/ini/factor.py b/src/tox/config/loader/ini/factor.py index 1c6f59fd7..731fe610c 100644 --- a/src/tox/config/loader/ini/factor.py +++ b/src/tox/config/loader/ini/factor.py @@ -46,18 +46,18 @@ def explode_factor(group: list[tuple[str, bool]]) -> str: return "-".join([name for name, _ in group]) -def expand_factors(value: str) -> Iterator[tuple[Iterator[list[tuple[str, bool]]] | None, str]]: +def expand_factors(value: str) -> Iterator[tuple[list[list[tuple[str, bool]]] | None, str]]: for line in value.split("\n"): - match = re.match(r"^((?P[\w{}.!,-]+):\s+)?(?P.*?)$", line) - if match is None: # pragma: no cover - raise RuntimeError("for a valid factor regex this cannot happen") - groups = match.groupdict() - factor_expr, content = groups["factor_expr"], groups["content"] - if factor_expr is not None: - factors = find_factor_groups(factor_expr) - yield factors, content - else: - yield None, content + factors: list[list[tuple[str, bool]]] | None = None + marker_at, content = line.find(":"), line + if marker_at != -1: + try: + factors = list(find_factor_groups(line[:marker_at].strip())) + except ValueError: + pass # when cannot extract factors keep the entire line + else: + content = line[marker_at + 1 :].strip() + yield factors, content def find_factor_groups(value: str) -> Iterator[list[tuple[str, bool]]]: @@ -76,6 +76,8 @@ def expand_env_with_negation(value: str) -> Iterator[str]: parts = [re.sub(r"\s+", "", elem).split(",") for elem in elements] for variant in product(*parts): variant_str = "".join(variant) + if not re.fullmatch(r"!?[\w._][\w._-]*", variant_str): + raise ValueError(variant_str) yield variant_str diff --git a/tests/config/loader/ini/replace/test_replace_posargs.py b/tests/config/loader/ini/replace/test_replace_posargs.py index 840869ac6..0ca258218 100644 --- a/tests/config/loader/ini/replace/test_replace_posargs.py +++ b/tests/config/loader/ini/replace/test_replace_posargs.py @@ -37,8 +37,8 @@ def test_replace_pos_args(syntax: str, replace_one: ReplaceOne) -> None: [ ("magic", "magic"), ("magic:colon", "magic:colon"), - ("magic\n b:c", "magic\nb:c"), # an unescaped newline keeps the newline - ("magi\\\n c:d", "magic:d"), # an escaped newline merges the lines + ("magic\n b c", "magic\nb c"), # an unescaped newline keeps the newline + ("magi\\\n c d", "magic d"), # an escaped newline merges the lines ("\\{a\\}", "{a}"), # escaped curly braces ], ) diff --git a/tests/config/loader/ini/test_factor.py b/tests/config/loader/ini/test_factor.py index 29c70a482..0a4e025a2 100644 --- a/tests/config/loader/ini/test_factor.py +++ b/tests/config/loader/ini/test_factor.py @@ -27,6 +27,7 @@ def complex_example() -> str: py: py only !py: not py {py,!pi}-{a,b}{,-dev},c: complex + py, d: space extra: extra more-default """, @@ -46,6 +47,7 @@ def test_factor_env_discover(complex_example: str) -> None: "pi-b", "pi-b-dev", "c", + "d", "extra", ] diff --git a/tests/session/cmd/test_list_envs.py b/tests/session/cmd/test_list_envs.py index 3d9b693a2..0ac0d5991 100644 --- a/tests/session/cmd/test_list_envs.py +++ b/tests/session/cmd/test_list_envs.py @@ -36,6 +36,7 @@ def test_list_env(project: ToxProject) -> None: additional environments: fix -> fix it + pypy -> with pypy """ outcome.assert_out_err(expected, "") @@ -62,6 +63,7 @@ def test_list_env_quiet(project: ToxProject) -> None: py31 py fix + pypy """ outcome.assert_out_err(expected, "")