Skip to content

glob pattern support for depends option doesn't work with ini files #3822

@hynek

Description

@hynek

Issue

I got really excited when I saw glob support being added, but I always get a raise ValueError(variant_str) from expand_env_with_negation. Looking at #3697 I noticed that all tests use toml examples, so could it be that ini just was never tested?

Environment

Provide at least:

  • OS: macOS
Output of pip list of the host Python, where tox is installed
uv pip list -p /Users/hynek/.local/share/uv/tools/tox/bin/python3
Package       Version
------------- -------
cachetools    7.0.1
colorama      0.4.6
distlib       0.4.0
filelock      3.24.3
packaging     26.0
platformdirs  4.9.2
pluggy        1.6.0
pyproject-api 1.10.0
tox           4.46.0
virtualenv    20.39.0

Output of running tox

for example, if I replace the depends in structlog's tox.ini by depends = 3.*:

Output of tox -rvv
$ uvx --with tox-uv tox
ROOT: Internal Error
Traceback (most recent call last):
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 368, in _queue_and_wait
    env_list = next(envs_to_run_generator, [])
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 423, in ready_to_run_envs
    order, todo = run_order(state, to_run)
                  ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 441, in run_order
    depends = set(cast("EnvList", run_env.conf["depends"]).envs)
                                  ~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/sets.py", line 150, in __getitem__
    return self.load(item)
           ~~~~~~~~~^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/sets.py", line 162, in load
    return config_definition.__call__(self._conf, self.loaders, ConfigLoadArgs(chain, self.name, self.env_name))  # noqa: PLC2801
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/of_type.py", line 113, in __call__
    value = loader.load(primary_key, self.of_type, self.factory, conf, args, all_keys=alias_keys)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/api.py", line 153, in load
    converted = self.build(alias, of_type, factory, conf, raw, args)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/__init__.py", line 91, in build
    converted = self.to(prepared, of_type, factory)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/convert.py", line 49, in to
    return cast("V", self.to_env_list(raw))
                     ~~~~~~~~~~~~~~~~^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/str_convert.py", line 118, in to_env_list
    elements = list(chain.from_iterable(extend_factors(expr) for expr in value.split("\n")))
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 61, in extend_factors
    for group in find_factor_groups(value):
                 ~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 87, in find_factor_groups
    for env in expand_env_with_negation(value):
               ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 105, in expand_env_with_negation
    raise ValueError(variant_str)
ValueError: 3.*
Exception in thread tox-interrupt:
Traceback (most recent call last):
  File "/Users/hynek/.local/share/uv/python/cpython-3.14.3-macos-aarch64-none/lib/python3.14/threading.py", line 1082, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/python/cpython-3.14.3-macos-aarch64-none/lib/python3.14/threading.py", line 1024, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 368, in _queue_and_wait
    env_list = next(envs_to_run_generator, [])
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 423, in ready_to_run_envs
    order, todo = run_order(state, to_run)
                  ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 441, in run_order
    depends = set(cast("EnvList", run_env.conf["depends"]).envs)
                                  ~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/sets.py", line 150, in __getitem__
    return self.load(item)
           ~~~~~~~~~^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/sets.py", line 162, in load
    return config_definition.__call__(self._conf, self.loaders, ConfigLoadArgs(chain, self.name, self.env_name))  # noqa: PLC2801
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/of_type.py", line 113, in __call__
    value = loader.load(primary_key, self.of_type, self.factory, conf, args, all_keys=alias_keys)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/api.py", line 153, in load
    converted = self.build(alias, of_type, factory, conf, raw, args)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/__init__.py", line 91, in build
    converted = self.to(prepared, of_type, factory)
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/convert.py", line 49, in to
    return cast("V", self.to_env_list(raw))
                     ~~~~~~~~~~~~~~~~^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/str_convert.py", line 118, in to_env_list
    elements = list(chain.from_iterable(extend_factors(expr) for expr in value.split("\n")))
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 61, in extend_factors
    for group in find_factor_groups(value):
                 ~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 87, in find_factor_groups
    for env in expand_env_with_negation(value):
               ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.cache/uv/archive-v0/-JMzBqXwaUnJf294a81Sx/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 105, in expand_env_with_negation
    raise ValueError(variant_str)
ValueError: 3.*
  pre-commit: SKIP (0.01 seconds)
  py39-tests: SKIP (0.01 seconds)
  py39-mypy: SKIP (0.01 seconds)
  py310-tests: SKIP (0.01 seconds)
  py310-mypy: SKIP (0.01 seconds)
  py311-tests: SKIP (0.01 seconds)
  py311-mypy: SKIP (0.01 seconds)
  py312-tests: SKIP (0.01 seconds)
  py312-mypy: SKIP (0.01 seconds)
  py313-tests: SKIP (0.01 seconds)
  py313-mypy: SKIP (0.01 seconds)
  py314-tests: SKIP (0.01 seconds)
  py314-mypy: SKIP (0.01 seconds)
  py39-tests-colorama: SKIP (0.01 seconds)
  py39-tests-be: SKIP (0.01 seconds)
  py39-tests-rich: SKIP (0.01 seconds)
  py313-tests-colorama: SKIP (0.01 seconds)
  py313-tests-be: SKIP (0.01 seconds)
  py313-tests-rich: SKIP (0.01 seconds)
  typing-mypy: SKIP (0.01 seconds)
  typing-pyright: SKIP (0.01 seconds)
  typing-ty: SKIP (0.01 seconds)
  typing-pyrefly: SKIP (0.01 seconds)
  docs-sponsors: SKIP (0.01 seconds)
  docs-build: SKIP (0.01 seconds)
  docs-doctests: SKIP (0.01 seconds)
  coverage-combine: SKIP (0.01 seconds)
  coverage-report: SKIP (0.01 seconds)
  evaluation failed :( (0.07 seconds)
error: Recipe `tox` failed on line 22 with exit code 1

Minimal example

[tox]
env_list = foo, bar

[testenv:foo]
skip_install = true
commands = echo foo

[testenv:bar]
skip_install = true
depends = f*
commands = echo bar

fails with:

ROOT: Internal Error
Traceback (most recent call last):
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 368, in _queue_and_wait
    env_list = next(envs_to_run_generator, [])
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 423, in ready_to_run_envs
    order, todo = run_order(state, to_run)
                  ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 441, in run_order
    depends = set(cast("EnvList", run_env.conf["depends"]).envs)
                                  ~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/sets.py", line 150, in __getitem__
    return self.load(item)
           ~~~~~~~~~^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/sets.py", line 162, in load
    return config_definition.__call__(self._conf, self.loaders, ConfigLoadArgs(chain, self.name, self.env_name))  # noqa: PLC2801
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/of_type.py", line 113, in __call__
    value = loader.load(primary_key, self.of_type, self.factory, conf, args, all_keys=alias_keys)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/api.py", line 153, in load
    converted = self.build(alias, of_type, factory, conf, raw, args)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/__init__.py", line 91, in build
    converted = self.to(prepared, of_type, factory)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/convert.py", line 49, in to
    return cast("V", self.to_env_list(raw))
                     ~~~~~~~~~~~~~~~~^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/str_convert.py", line 118, in to_env_list
    elements = list(chain.from_iterable(extend_factors(expr) for expr in value.split("\n")))
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 61, in extend_factors
    for group in find_factor_groups(value):
                 ~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 87, in find_factor_groups
    for env in expand_env_with_negation(value):
               ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 105, in expand_env_with_negation
    raise ValueError(variant_str)
ValueError: f*
Exception in thread tox-interrupt:
Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.14/3.14.3_1/Frameworks/Python.framework/Versions/3.14/lib/python3.14/threading.py", line 1082, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.14/3.14.3_1/Frameworks/Python.framework/Versions/3.14/lib/python3.14/threading.py", line 1024, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 368, in _queue_and_wait
    env_list = next(envs_to_run_generator, [])
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 423, in ready_to_run_envs
    order, todo = run_order(state, to_run)
                  ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/session/cmd/run/common.py", line 441, in run_order
    depends = set(cast("EnvList", run_env.conf["depends"]).envs)
                                  ~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/sets.py", line 150, in __getitem__
    return self.load(item)
           ~~~~~~~~~^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/sets.py", line 162, in load
    return config_definition.__call__(self._conf, self.loaders, ConfigLoadArgs(chain, self.name, self.env_name))  # noqa: PLC2801
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/of_type.py", line 113, in __call__
    value = loader.load(primary_key, self.of_type, self.factory, conf, args, all_keys=alias_keys)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/api.py", line 153, in load
    converted = self.build(alias, of_type, factory, conf, raw, args)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/__init__.py", line 91, in build
    converted = self.to(prepared, of_type, factory)
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/convert.py", line 49, in to
    return cast("V", self.to_env_list(raw))
                     ~~~~~~~~~~~~~~~~^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/str_convert.py", line 118, in to_env_list
    elements = list(chain.from_iterable(extend_factors(expr) for expr in value.split("\n")))
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 61, in extend_factors
    for group in find_factor_groups(value):
                 ~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 87, in find_factor_groups
    for env in expand_env_with_negation(value):
               ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/Users/hynek/.local/share/uv/tools/tox/lib/python3.14/site-packages/tox/config/loader/ini/factor.py", line 105, in expand_env_with_negation
    raise ValueError(variant_str)
ValueError: f*
  foo: SKIP (0.01 seconds)
  bar: SKIP (0.01 seconds)
  evaluation failed :( (0.02 seconds)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug:normalaffects many people or has quite an impact

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions