Skip to content

Braced range expansion in tox.envlist vs testenv #3571

@moreati

Description

@moreati

Issue

  1. In tox.envlist braced lists and ranges are expanded the same. So foo{3-5} <=> foo{3,4,5} <=> foo3 foo4 foo5.
  2. In testenv only braced lists are expanded. So foo{3-5} is not equivalent to foo{3,4,5} or foo3,foo4,foo5

I'm unsure if the different behaviour in testenv is intended or a bug. The docs at https://tox.wiki/en/latest/config.html#generative-environment-list don't explicitly state either way.

Environment

Provide at least:

  • OS:
Output of pip list of the host Python, where tox is installed
tox_range uv pip list -p ~/.local/share/uv/tools/tox
Package                   Version
------------------------- --------
ansible-compat            25.6.0
ansible-core              2.19.0
attrs                     25.3.0
cachetools                6.1.0
cffi                      1.17.1
chardet                   5.2.0
colorama                  0.4.6
cryptography              45.0.5
distlib                   0.4.0
execnet                   2.1.1
filelock                  3.18.0
iniconfig                 2.1.0
jinja2                    3.1.6
jsonschema                4.25.0
jsonschema-specifications 2025.4.1
markupsafe                3.0.2
packaging                 25.0
pip                       25.1.1
platformdirs              4.3.8
pluggy                    1.6.0
pycparser                 2.22
pygments                  2.19.2
pyproject-api             1.9.1
pytest                    8.4.1
pytest-ansible            25.6.3
pytest-plus               0.8.1
pytest-sugar              1.0.0
pytest-xdist              3.8.0
pyyaml                    6.0.2
referencing               0.36.2
resolvelib                1.2.0
rpds-py                   0.26.0
subprocess-tee            0.4.2
termcolor                 3.1.0
tox                       4.28.3
tox-ansible               25.5.5
tox-uv                    1.26.2
typing-extensions         4.14.1
uv                        0.8.3
virtualenv                20.32.0

Output of running tox

Output of tox -rvv
tox_range tox -rvv
py313-foo1: 151 W remove tox env folder /Users/alex/tmp/tox_range/.tox/py313-foo1 [tox/tox_env/api.py:357]
py313-foo1: 154 W venv> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo1 [tox/tox_env/api.py:459]
DEBUG uv 0.8.3 (7e78f54e7 2025-07-24)
DEBUG Using Python request `/Users/alex/.local/share/uv/tools/tox/bin/python3` from explicit request
DEBUG Checking for Python interpreter at path `/Users/alex/.local/share/uv/tools/tox/bin/python3`
Using CPython 3.13.5 interpreter at: /Users/alex/.local/share/uv/tools/tox/bin/python3
Creating virtual environment at: .tox/py313-foo1
DEBUG Assessing Python executable as base candidate: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Using base executable for virtual environment: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
py313-foo1: 198 I exit 0 (0.04 seconds) /Users/alex/tmp/tox_range> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo1 pid=89493 [tox/execute/api.py:294]
py313-foo1: 199 W commands[0]> python -c 'import os;print(os.environ["FOO"])' [tox/tox_env/api.py:459]
bar
py313-foo1: 222 I exit 0 (0.02 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89494 [tox/execute/api.py:294]
py313-foo1: OK ✔ in 0.07 seconds
py313-foo2: 222 W remove tox env folder /Users/alex/tmp/tox_range/.tox/py313-foo2 [tox/tox_env/api.py:357]
py313-foo2: 226 W venv> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo2 [tox/tox_env/api.py:459]
DEBUG uv 0.8.3 (7e78f54e7 2025-07-24)
DEBUG Using Python request `/Users/alex/.local/share/uv/tools/tox/bin/python3` from explicit request
DEBUG Checking for Python interpreter at path `/Users/alex/.local/share/uv/tools/tox/bin/python3`
Using CPython 3.13.5 interpreter at: /Users/alex/.local/share/uv/tools/tox/bin/python3
Creating virtual environment at: .tox/py313-foo2
DEBUG Assessing Python executable as base candidate: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Using base executable for virtual environment: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
py313-foo2: 239 I exit 0 (0.01 seconds) /Users/alex/tmp/tox_range> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo2 pid=89495 [tox/execute/api.py:294]
py313-foo2: 240 W commands[0]> python -c 'import os;print(os.environ["FOO"])' [tox/tox_env/api.py:459]
bar
py313-foo2: 261 I exit 0 (0.02 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89496 [tox/execute/api.py:294]
py313-foo2: OK ✔ in 0.04 seconds
py313-foo3: 261 W remove tox env folder /Users/alex/tmp/tox_range/.tox/py313-foo3 [tox/tox_env/api.py:357]
py313-foo3: 264 W venv> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo3 [tox/tox_env/api.py:459]
DEBUG uv 0.8.3 (7e78f54e7 2025-07-24)
DEBUG Using Python request `/Users/alex/.local/share/uv/tools/tox/bin/python3` from explicit request
DEBUG Checking for Python interpreter at path `/Users/alex/.local/share/uv/tools/tox/bin/python3`
Using CPython 3.13.5 interpreter at: /Users/alex/.local/share/uv/tools/tox/bin/python3
Creating virtual environment at: .tox/py313-foo3
DEBUG Assessing Python executable as base candidate: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Using base executable for virtual environment: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
py313-foo3: 277 I exit 0 (0.01 seconds) /Users/alex/tmp/tox_range> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo3 pid=89497 [tox/execute/api.py:294]
py313-foo3: 279 W commands[0]> python -c 'import os;print(os.environ["FOO"])' [tox/tox_env/api.py:459]
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import os;print(os.environ["FOO"])
                    ~~~~~~~~~~^^^^^^^
  File "<frozen os>", line 717, in __getitem__
KeyError: 'FOO'
py313-foo3: 306 C exit 1 (0.03 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89498 [tox/execute/api.py:294]
py313-foo3: FAIL ✖ in 0.05 seconds
py313-foo4: 306 W remove tox env folder /Users/alex/tmp/tox_range/.tox/py313-foo4 [tox/tox_env/api.py:357]
py313-foo4: 309 W venv> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo4 [tox/tox_env/api.py:459]
DEBUG uv 0.8.3 (7e78f54e7 2025-07-24)
DEBUG Using Python request `/Users/alex/.local/share/uv/tools/tox/bin/python3` from explicit request
DEBUG Checking for Python interpreter at path `/Users/alex/.local/share/uv/tools/tox/bin/python3`
Using CPython 3.13.5 interpreter at: /Users/alex/.local/share/uv/tools/tox/bin/python3
Creating virtual environment at: .tox/py313-foo4
DEBUG Assessing Python executable as base candidate: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Using base executable for virtual environment: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
py313-foo4: 324 I exit 0 (0.01 seconds) /Users/alex/tmp/tox_range> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo4 pid=89499 [tox/execute/api.py:294]
py313-foo4: 325 W commands[0]> python -c 'import os;print(os.environ["FOO"])' [tox/tox_env/api.py:459]
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import os;print(os.environ["FOO"])
                    ~~~~~~~~~~^^^^^^^
  File "<frozen os>", line 717, in __getitem__
KeyError: 'FOO'
py313-foo4: 354 C exit 1 (0.03 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89500 [tox/execute/api.py:294]
py313-foo4: FAIL ✖ in 0.05 seconds
py313-foo5: 355 W venv> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo5 [tox/tox_env/api.py:459]
DEBUG uv 0.8.3 (7e78f54e7 2025-07-24)
DEBUG Using Python request `/Users/alex/.local/share/uv/tools/tox/bin/python3` from explicit request
DEBUG Checking for Python interpreter at path `/Users/alex/.local/share/uv/tools/tox/bin/python3`
Using CPython 3.13.5 interpreter at: /Users/alex/.local/share/uv/tools/tox/bin/python3
Creating virtual environment at: .tox/py313-foo5
DEBUG Assessing Python executable as base candidate: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Using base executable for virtual environment: /Users/alex/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
py313-foo5: 368 I exit 0 (0.01 seconds) /Users/alex/tmp/tox_range> /Users/alex/.local/share/uv/tools/tox/bin/uv venv -p /Users/alex/.local/share/uv/tools/tox/bin/python3 --allow-existing -v --python-preference system /Users/alex/tmp/tox_range/.tox/py313-foo5 pid=89501 [tox/execute/api.py:294]
py313-foo5: 370 W commands[0]> python -c 'import os;print(os.environ["FOO"])' [tox/tox_env/api.py:459]
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import os;print(os.environ["FOO"])
                    ~~~~~~~~~~^^^^^^^
  File "<frozen os>", line 717, in __getitem__
KeyError: 'FOO'
py313-foo5: 399 C exit 1 (0.03 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89502 [tox/execute/api.py:294]
  py313-foo1: OK (0.07=setup[0.05]+cmd[0.02] seconds)
  py313-foo2: OK (0.04=setup[0.02]+cmd[0.02] seconds)
  py313-foo3: FAIL code 1 (0.05=setup[0.02]+cmd[0.03] seconds)
  py313-foo4: FAIL code 1 (0.05=setup[0.02]+cmd[0.03] seconds)
  py313-foo5: FAIL code 1 (0.04=setup[0.02]+cmd[0.03] seconds)
  evaluation failed :( (0.31 seconds)

Minimal example

Factors foo1 and foo2 get the setenv applied, but foo3 to foo5 don't.

[tox]
requires =
    tox >= 4.28.3
envlist =
    py313-foo{1,2,3-5}

[testenv]
setenv =
    foo{1,2}: FOO=bar
    foo{3-5}: FOO=baz
commands =
    python -c 'import os;print(os.environ["FOO"])'
tox_range tox -e py313-foo5
py313-foo5: commands[0]> python -c 'import os;print(os.environ["FOO"])'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import os;print(os.environ["FOO"])
                    ~~~~~~~~~~^^^^^^^
  File "<frozen os>", line 717, in __getitem__
KeyError: 'FOO'
py313-foo5: exit 1 (0.03 seconds) /Users/alex/tmp/tox_range> python -c 'import os;print(os.environ["FOO"])' pid=89907
  py313-foo5: FAIL code 1 (0.03=setup[0.00]+cmd[0.03] seconds)
  evaluation failed :( (0.07 seconds)

Similar to, possibly a duplicate of

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug:minordoes not affect many people or has no big impacthelp:wantedIssues that have been acknowledged, a solution determined and a PR might likely be accepted.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions