NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #950 most downloaded on PyPI
tox is a generic virtualenv management and test command line tool
Last release today
03 Oct 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
2 versions withdrawn
withdrawn after publishing
16 years old
318 releases · first in 2010
🐛 fix(pip): combine --no-binary and --only-binary across requirement lines by @SulimanAbdulrazzaq in #4110
Full Changelog: 4.64.7...4.64.8
One column per quarter.
tox combines --no-binary and --only-binary across the lines of deps and of requirements files the way pip does, where the last line used to replace the earlier ones. With --only-binary :all: and a --no-binary exception, tox passes --only-binary to pip first, so pip keeps the exception. tox config shows the merged values tox passes to pip - by @SulimanAbdulrazzaq . ( #4110 )
tox config shows deps with every global option merged the way tox passes it to pip, a later -i replacing an earlier one and repeated flags shown once, and reports the error tox run would raise for invalid deps . constraints shows the configured files instead of the path of tox.ini , and a comment line in constraints no longer fails the run. deps that give pip nothing to install no longer conflict with pylock . set_env includes PIP_USER , TOX_ENV_DIR , TOX_ENV_NAME , TOX_WORK_DIR and VIRTUAL_ENV , the values tox sets for commands - by @gaborbernat . ( #4111 )
A TOML ref to another environment’s deps , constraints or commands works in tox config and tox run . It used to fail with 'PythonDeps' object is not iterable or Command(args=[...]) is not list - by @gaborbernat . ( #4112 )
In INI commands , commands_pre and commands_post , a reference such as pip install {[testenv:x]deps} that shares its line with other text joins the referenced lines into that one command as arguments, as in tox 3. tox used to run each referenced line after the first as a separate command. A reference alone on its line still expands to one command per referenced line. This changes echo {[testenv:x]commands} , which now runs one echo with every referenced command as its arguments - by @gaborbernat . ( #4113 )
The JSON schema described requires with the text of provision_tox_env , so editors showed the wrong hover for it - by @gaborbernat . ( #4115 )
fix(man): give each shell a working MANPATH line by @Rodrigo-Palma in #4109
Full Changelog: 4.64.6...4.64.7
tox man suggests ~/.profile instead of ~/.zshrc when the shell is not fish, bash, zsh, csh or tcsh, for example sh , ksh or an unset SHELL - by @Rodrigo-Palma .
tox picks the shell from the executable name, so /home/zshuser/bin/bash gets ~/.bashrc .
Outside fish, csh and tcsh, the reload hint reads . <file> instead of source <file> , since dash has no source .
The fish line keeps the default man path when MANPATH was unset, instead of hiding every other man page.
csh and tcsh get a setenv line for ~/.tcshrc or ~/.cshrc instead of an export line for ~/.profile . ( #4109 )
fix(config): tie spliced set_env markers to their value by @feiiiiii5 in #4107
Full Changelog: 4.64.5...4.64.6
tox -e and TOXENV accept every interpreter name that picks a base_python for an environment the configuration does not declare - by @breken-ai .
cpython313 , cpython-3.13 , graalpy311 , jython2.7 , rustpython3 and ironpython3 select that interpreter instead of failing with “provided environments not found”.
The d debug-build suffix works too, for example py313d .
cython313 now fails as an unknown environment; it used to run on the Python that tox runs under. ( #4106 )
A set_env block pulled in with {[section]set_env} now applies a PEP-508 marker only to the value it came with, as the inline and TOML forms already do - by @feiiiiii5 . A false marker on an earlier line no longer hides a value that the inheriting environment, a later unconditional line or an environment file supplies. ( #4107 )
set_env values resolve when tox reads them, so {env:KEY} sees the keys a block pulls in with {[section]set_env} , and a { replace = "env" } table sees the keys next to it - by @gaborbernat , found by @breken-ai . Configurations that see a difference:
A value pulled in from another section that refers to a key of the same block now gets the block’s value instead of the host variable.
A pulled-in line that refers to its own key, such as PYTHONHASHSEED = {env:PYTHONHASHSEED:0} , reads the host variable or its default, as the same line written in place does. It used to read the value tox sets.
A key written in an INI set_env wins over the same key pulled in from another section, and a later pulled-in section wins over an earlier one, at every nesting level. A nested section could override the section that included it before.
tox config shows the keys of every pulled-in section that tox run sets.
A section that pulls itself in, a TOML ref to itself, and values that refer to each other fail the environment with the cycle. They used to fail with a traceback or “could not find python interpreter”, and values that referred to each other inside a pulled-in section read the host variables without a warning. ( #4108 )
fix(github): restore issue template metadata by @gaborbernat in #4100
Full Changelog: 4.64.4...4.64.5
An invalid platform regular expression now fails the environment with a one-line error instead of a traceback, also for the provisioning environment - by @00200200 . ( #4103 )
fix(pip): pass --pre once in the default command by @Rodrigo-Palma in #4097
Full Changelog: 4.64.3...4.64.4
Pass --pre once, before the packages, in the default install_command with pip_pre set to true . ( #4097 )
Fix list conversion for command line defaults from environment variables and user configuration files. For example, TOX_LABELS=old;new selects environments labeled old or new , and TOX_LIST_KEYS_ONLY=env_name;deps selects both configuration keys. Support list-valued options without an explicit default or element type, including append actions. ( #4098 )
Create change_dir before running the external package build command by @Rodrigo-Palma in #4095
Full Changelog: 4.64.2...4.64.3
A set_env written as a list of tables in TOML now keeps every file entry and honors the order of the entries, so merging one environment’s set_env into another no longer drops the environment file it referenced ( #4093 ) - by @Rodrigo-Palma .
Each { file = "..." } entry is read; previously only the last one survived, because the entries were merged into a single table first and a table cannot hold the key twice.
A variable set after a file entry wins over the value the file provides, matching the file| form in INI; previously a repeated variable was pulled back to its first position, letting the file override it.
A later entry that sets a variable without a marker clears the marker an earlier entry gave it, again matching the INI form. ( #4093 )
Create the package build environment change_dir directory before running the build commands, as the documentation already promises. ( #4095 )
🐛 fix(run): keep .venv redirect off for a .venv env by @gaborbernat in #4091
Full Changelog: 4.64.1...4.64.2
A tox environment with env_dir = "{tox_root}/.venv" no longer crashes with FileExistsError after another environment ran ( #4090 ) - by @gaborbernat .
With venv_redirect unset, the new default, tox writes the redirect file unless a tox environment lives at .venv ; with true , tox fails the run with an error naming that environment.
tox deletes a redirect file it wrote where an environment should live, and fails the environment with an error naming any other file there. ( #4091 )
The PEP 832 .venv redirect file now names a deliberate development environment - by @gaborbernat .
tox picks venv_redirect_env , else an environment named dev , else the first that installs the project in development mode, and writes nothing without one, so a project that uses tox for tests alone gets no file.
tox devenv points the redirect at the environment it creates, and later runs leave it in place.
tox tells virtualenv to write no redirect file of its own beside the environments it builds. ( #4092 )
🐛 fix(config): apply overrides to sections the file lacks by @gaborbernat in #4089
Full Changelog: 4.64.0...4.64.1
Apply -x / --override and TOX_OVERRIDE values that target a section the configuration file does not define, such as tox.skip_missing_interpreters on a tox.ini without a [tox] section, instead of dropping them - by @gaborbernat . ( #4089 )
docs: fix the TOML string substitution example by @agammann in #4081
Full Changelog: 4.63.0...4.64.0
Point a PEP 832 .venv redirect file at a tox environment after each run, so editors such as VS Code or PyCharm pick it as the project interpreter instead of asking you for a path under .tox - by @gaborbernat .
tox picks an environment named dev , then one installing the project in development mode, then the first of env_list ; set venv_redirect_env to choose another.
tox leaves a .venv directory alone, and a redirect file pointing outside its environments.
tox removes the redirect while it recreates the target and writes it back once the run ends.
Set venv_redirect to false to opt out.
The feature is provisional while PEP 832 is a draft: a minor or patch release may change it in backward incompatible ways to follow the PEP. ( #4013 )
🐛 fix(run): keep package env interrupt logs in parallel by @gaborbernat in #4079
Full Changelog: 4.62.1...4.63.0
Plugins and test helpers can use tox.execute.api.FinishedExecuteStatus to report a fixed exit code for a command that already finished or never started - by @gaborbernat . ( #4080 )
test: skip POSIX-venv wheel-build tests on Windows by @iam-kira in #4078
Full Changelog: 4.62.0...4.62.1
Clear obsolete markers when unconditional set_env values override conditional entries, including environment files. ( #4076 )
🐛 fix(type): pass the latest ty and close Any gaps by @gaborbernat in #4075
Full Changelog: 4.61.5...4.62.0
Plugins can read a configuration value that may be unset through ConfigSet.get_optional , which verifies the value against its declared type and returns None when it is not set - by @gaborbernat . ( #4075 )
chore(type): narrow execute_async cwd for pyrefly 1.3 by @yuefdev in #4069
Full Changelog: 4.61.4...4.61.5
An empty set_env list in TOML no longer fails to load; like an empty table, it clears inherited variables - by @yuefdev . ( #4065 )
A TOML factor range now rejects true or false as start or stop instead of reading them as 1 and 0 - by @yuefdev . ( #4066 )
Preserve an explicitly empty {factor:label:} fallback instead of using the declared factor group default. ( #4072 )
🐛 fix(execute): keep spaces in quoted shebang interpreter paths by @r3wretrhy in #4064
Full Changelog: 4.61.3...4.61.4
Shebang parsing now uses shlex , so a quoted interpreter path that contains spaces is kept as one argument when TOX_LIMITED_SHEBANG rewrites the invocation - by @r3wretrhy . ( #4064 )
📝 docs: name the actor across the factor and override prose by @gaborbernat in #4058
Full Changelog: 4.61.2...4.61.3
A set_env environment file ( file|.env ) is now read as UTF-8 instead of the platform’s locale encoding, so non-ASCII values no longer arrive mangled on Windows - by @MohammedAlkindi . ( #4059 )
Preserve UNC and extended-length paths in Windows commands, including quoted paths - by @MohammedAlkindi . ( #4060 )
Normalize project and extra names in dependency-group self-references - by @MohammedAlkindi . ( #4061 )
Register factor labels and defaults for bare labeled dicts in env_list - by @dylanpulver . ( #4062 )
A set_env environment file (file|.env) is now read as UTF-8 instead of the platform's locale encoding, so non-ASCII values no longer arrive mangled on Windows - by MohammedAlkindi. (4059)
Preserve UNC and extended-length paths in Windows commands, including quoted paths - by MohammedAlkindi. (4060)
Normalize project and extra names in dependency-group self-references - by MohammedAlkindi. (4061)
Register factor labels and defaults for bare labeled dicts in env_list - by dylanpulver. (4062)
chore: add license-files, remove licence classifier (PEP 639) by @mwtoews in #4052
Full Changelog: 4.61.1...4.61.2
tox now expands a generative digit range only where a factor starts, so an environment such as py313-django4-2 keeps its name rather than becoming py313-django4 , 3 and 2 - by @MohammedAlkindi . ( #4053 )
🐛 fix(schema): declare prefix in the labeled factor group's not clause by @gaborbernat in #4051
Full Changelog: 4.61.0...4.61.1
Publish a JSON Schema that compiles under SchemaStore’s validator again, so editors pick up the configuration keys added since 4.56 - by @gaborbernat . ( #4051 )
Replace deprecated load_module in docs conf by @gaborbernat in #4043
Full Changelog: 4.60.1...4.61.0
A labeled factor group can now declare a default for {factor:label} to fall back on when no factor of that group is active in the environment name. Setting TOX_FACTOR_<label> resolves that label to a given value for a single run - by @gaborbernat . ( #4045 )
A factor range now takes a label when you nest it under one, as in factors = [{ py_version = { prefix = "3.", start = 12, stop = 14 } }] , which makes {factor:py_version} available for ranges - by @gaborbernat . ( #4046 )
Stop Windows pytest-timeout kills in CI by @gaborbernat in #4026
Full Changelog: 4.60.0...4.60.1
Report a malformed tox.ini or setup.cfg as a handled error during config discovery instead of raising an unhandled configparser.Error traceback - by @VXNCXNX ( #4027 )
Report an invalid value in the ini [tox] core section (such as min_version , requires or env_list ) as a handled error instead of an unhandled traceback, matching the existing TOML loader behavior - by @VXNCXNX ( #4028 )
Report an invalid --skip-env / TOX_SKIP_ENV regular expression as a handled error instead of raising an unhandled re.error traceback - by @VXNCXNX ( #4029 )
Keep ; inside values read from a set_env environment file ( file|.env ). Environment file lines are plain KEY=VALUE pairs and were incorrectly parsed with the PEP-508 marker splitter, which truncated values such as DATABASE_URL=postgresql://host/db?opt=1;sslmode=require at the first semicolon - by @VXNCXNX ( #4030 )
Report a handled configuration error instead of leaking a traceback when the selected configuration file exists but cannot be read - by @SirHegel . ( #4031 )
Report an empty install_command or list_dependencies_command in a TOML configuration as a handled error instead of an unhandled traceback - by @dylanpulver ( #4041 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.60.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.60.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.59.0...4.60.0
Full Changelog: 4.59.0...4.60.0
Add {home} and {tox_root_name} substitutions; set work_dir to e.g. "{home}/.local/state/tox/{tox_root_name}" to keep environments outside of the project tree - by @WhyNotHugo . ( #4020 )
📝 docs: mark the INI configuration format as deprecated by @gaborbernat in https://github.com/tox-dev/tox/pull/4017
<!-- Release notes generated using configuration in .github/release.yaml at 4.59.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.58.0...4.59.0
Full Changelog: 4.58.0...4.59.0
The plugin and internal APIs now declare their types, and the code base passes mypy --strict and pyrefly next to ty - by @gaborjbernat .
ConfigSet.get returns a configuration value after checking it against its declared type, so plugins can drop cast around conf["key"] reads.
The tox_on_install hook and Installer type their arguments as InstallArguments ; installer subclasses may narrow it through the new second type parameter.
Parsed declares the CLI option surface, so options.<name> reads type-check.
ConfigSet.add_config accepts default=None for optional values and infers the value type from callable defaults.
The journal, the environment cache, and execute metadata hold JsonValue content; structured set_env entries match the SetEnvEntry shape. ( #4014 )
👷 ci: run the test matrix against Python 3.15 by @gaborbernat in #4001
Full Changelog: 4.57.2...4.58.0
<!-- Release notes generated using configuration in .github/release.yaml at 4.58.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.57.2...4.58.0
Test against Python 3.15 (beta) in CI and declare its classifier - by @gaborbernat . ( #4001 )
Test against Python 3.15 (beta) in CI and declare its classifier - by gaborbernat. (4001)
🐛 fix(session): fix package env failure cascade and state corruption by @gaborbernat in #3991
Full Changelog: 4.57.1...4.57.2
<!-- Release notes generated using configuration in .github/release.yaml at 4.57.2 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.57.1...4.57.2
Fix package environment creation failures being misreported:
discard the partially registered configuration on failure, so the real error surfaces instead of a spurious duplicate configuration definition ;
report the first creation failure in definition order, not a hash-random one;
build the PEP 517 frontend lazily, so tox c and tox l work when pyproject.toml cannot be read. ( #3987 )
Fix state corruption in the environment creation machinery:
a failed run environment build no longer removes shared package environments other run environments still use;
a run environment registers with its package environment once, not twice with triple-counted usage;
circular depends reports the cycle as an error instead of a raw traceback;
Skip raised by a plugin for a package environment marks the run environment package-skipped instead of crashing with UnboundLocalError . ( #3991 )
Fix two packaging regressions:
package = editable on a build backend without PEP-660 support now falls back to editable-legacy with a warning instead of crashing, also when the project metadata is static;
editable and wheel environments running after an sdist-wheel environment build from the project sources again, instead of a stale temporary copy that made source edits invisible. ( #3992 )
An environment serving as both a run environment and a package environment now behaves predictably:
one defined through a [testenv:...] -style section and referenced by package_env inherits from pkgenv / env_pkg_base as documented, instead of silently keeping run environment defaults;
one listed in env_list while referenced as a package environment is reported as a configuration conflict up front, instead of failing late with cannot run packaging environment or silently disappearing from tox l . ( #3993 )
pylock.toml installs now match the lock file:
a package locked once per Python range installs only the version matching the environment’s interpreter;
packages locked to a local directory, VCS repository, or archive install from that source instead of resolving the name against the package index;
locked hashes are verified when every entry carries one;
changing a pip environment variable such as PIP_INDEX_URL reinstalls, as it already did for deps . ( #3994 )
Re-running tox no longer rebuilds an environment at random when deps uses --no-binary or --only-binary with more than one package name. ( #3995 )
A corrupted environment status file ( .tox-info.json ) now always triggers an environment recreation instead of crashing tox with an internal error for some corruption shapes. ( #3996 )
PEP 723 scripts saved with a UTF-8 byte order mark now have their inline metadata honored (previously requires-python and dependencies were silently ignored), and a script with invalid metadata reports a clear configuration error instead of an internal one. ( #3997 )
Fix three command line defects:
tox c -o FILE writes the default (ini) format to the file like the json and toml formats, without color codes;
tox devenv -e ALL reports that exactly one environment is required instead of crashing;
--no-provision FILE records the pinned version for a tox==X requirement instead of an empty string. ( #3998 )
Restore the documented fail-fast contract and make cleanup resilient:
--fail-fast in parallel mode lets already running environments finish and report their real outcomes, instead of interrupting them after a second and mislabeling them as skipped;
environments canceled by fail fast before starting report as skipped;
the overall exit code under fail fast is the first failed environment’s exit code, as documented;
one environment failing to clean up no longer prevents the remaining environments from cleaning up (which could leave tox hanging on exit). ( #3999 )
Polish four behaviors around the edges of a run:
an empty [tool.tox] stub in pyproject.toml no longer hides a tox.toml next to it;
setting CI=false (or empty) is respected as opting out of CI behavior;
spinner animation frames stay out of redirected output, which now holds plain text only;
the JSON report marks skipped environments with "skipped": true so they are distinguishable from passes. ( #4000 )
Run sequentially for run-parallel -p 0 instead of at CPU count by @chuenchen309 in #3989
Full Changelog: 4.57.0...4.57.1
<!-- Release notes generated using configuration in .github/release.yaml at 4.57.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.57.0...4.57.1
Stop a lone quote in a set_env value (e.g. an apostrophe in can't ) from swallowing the ; platform marker, which silently set the variable unconditionally with the marker text left in the value - by @chuenchen309 . ( #3988 )
Make tox run-parallel -p 0 turn parallelism off and run sequentially, as the -p help (“zero is turn off”) and the legacy command already promise, instead of running at the CPU count - by @chuenchen309 . ( #3989 )
💰 Surface the org itself in the FUNDING config by @webknjaz in #3979
Full Changelog: 4.56.4...4.57.0
<!-- Release notes generated using configuration in .github/release.yaml at 4.57.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.56.4...4.57.0
Support debug ( Py_DEBUG ) interpreters the way free-threaded builds are already handled. The py313d and py313td factors select a debug build, a base_python pointing at one matches those factors and conflicts with factors that lack the suffix, and a debug build gets its own d -tagged wheel build environment - by @chuenchen309 . ( #3986 )
🐛 fix(discovery): find debug interpreters like python3.13-dbg by @gaborbernat in #3978
Full Changelog: 4.56.3...4.56.4
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.4 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.56.3...4.56.4
Discover debug ( Py_DEBUG ) interpreters set as base_python , such as Debian’s python3.13-dbg - the spec was misread as a machine named dbg and no interpreter matched. Requires python-discovery>=1.4.4 - by @gaborbernat . ( #3977 )
Don't double-close the child pty fd (fixes parallel Bad file descriptor) ( #3975 ) by @apoorvdarshan in #3976
Full Changelog: 4.56.2...4.56.3
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.3 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.56.2...4.56.3
Stop double-closing the child pty file descriptor when running under a tty, which could race a parallel run that had reused the freed descriptor number and cause intermittent Bad file descriptor / Input/output error failures - by @apoorvdarshan . ( #3975 )
🔧 chore(type): drop stale ty invalid-assignment ignores by @gaborbernat in #3970
Full Changelog: 4.56.1...4.56.2
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.2 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.56.1...4.56.2
Fix a batch of latent defects found in a codebase audit:
stop duplicating --extra-index-url when a merged requirements line repeats an already-seen index;
render --no-binary / --only-binary as comma-joined strings instead of leaking the internal set into the pip command line;
report a clear error instead of IndexError for a bare one-argument flag ( -c , -r , -f , -e ) in deps or constraints;
canonicalize optional-dependencies keys and self-referential (recursive) extra names so non-canonical spellings no longer silently drop dependencies;
raise a clear error instead of KeyError when dependency_groups is requested without a [dependency-groups] table;
stop a native TOML { replace = "env" } reference from leaking its resolution chain into sibling list entries and tripping a spurious circular chain error;
expand the range that actually matched in a factor expression rather than the first identical digit-range substring;
strip the backslash from an escaped ; in set_env values;
skip option values during command auto-detection so an environment named like a subcommand (e.g. tox -e list ) is no longer misread;
treat an empty list-typed environment variable (e.g. TOX_DISCOVER= ) as an empty list rather than [""] ;
report an environment whose name is an empty string as present in Config membership tests;
fail evaluation gracefully instead of crashing the driver thread when tox p -p all is run with an empty selection;
avoid rebuilding a throwaway PEP 517 frontend against the previous root when a package environment’s root is reassigned before a frontend exists;
avoid crashing create_session_view when the package and its session copy share no common path (e.g. different Windows drives);
close the pseudo-terminal file descriptors that leaked on every command run under a tty and on the terminal-attribute inheritance error path;
honor the stop signal in the Windows overlapped-I/O reader while a read is still pending, so shutdown no longer risks hanging on a child that holds its pipe open without producing output. ( #3974 )
treat scalar string then/else as a single item in if-replace extend by @HrachShah in #3969
Full Changelog: 4.56.0...4.56.1
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.56.0...4.56.1
Fix { replace = "if" ... extend = true } corrupting the resulting list when then or else is a scalar string (e.g. then = "-v" ): a non-empty scalar string is now appended as a single element instead of being iterated character-by-character, while a false if with no else (yielding "" ) contributes nothing rather than an empty element, and list/set results are still spread into the parent. ( #3969 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.56.0 -->
development.rst by @mushitoriami in https://github.com/tox-dev/tox/pull/3961reference/config.rst by @mushitoriami in https://github.com/tox-dev/tox/pull/3962Full Changelog: https://github.com/tox-dev/tox/compare/4.55.1...4.56.0
Derive virtualenv_spec automatically: when an environment targets a Python version the installed virtualenv can no longer create (e.g. py38 with virtualenv 21.5+ ), tox now pins the newest virtualenv that still supports it and bootstraps it for that environment only. The downgrade happens only when every base_python candidate is unsupported, so environments targeting current interpreters are unaffected. This fixes py38 (and other end-of-life interpreters) being silently skipped after a virtualenv upgrade - by @gaborbernat . ( #3965 ) ( #3965 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.55.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.55.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.55.0...4.55.1
TOX_OVERRIDE (and -x / --override ) now propagates through configuration references. Previously, overriding a base value that was later referenced via {[section]key} (ini) or {replace = "ref", of = [...]} (toml) was ignored because reference resolution read the raw file value, bypassing the override system - by @tales-aparecida . ( #3950 ) ( #3950 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.55.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.55.0 -->
TERMINFO when in an interactive shell by @edgarrmondragon in https://github.com/tox-dev/tox/pull/3946Full Changelog: https://github.com/tox-dev/tox/compare/4.54.0...4.55.0
Automatically pass the TERMINFO environment variable to tox subprocesses if the output is a TTY. This variable is used by Ghostty to communicate terminal capabilities to programs. ( #3946 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.54.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.54.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.53.1...4.54.0
Declare the runtime dependencies of the tox.pytest plugin ( pytest , devpi-process and pytest-mock ) under a new testing extra, so plugin authors can pull them in via tox[testing] - by @gaborbernat . ( #3938 , #3940 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.53.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.53.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.53.0...4.53.1
Hardening pass on user-facing logging and config parsing:
Mask secret-looking --key=value flag values in command logs (terminal warnings, .tox/<env>/log/*.log , and Outcome repr ) using the same keyword regex previously applied to environment variable values.
Resolve PEP 723 script paths and reject any that escape tox_root ; cap the script read at 5 MiB so a symlink to /dev/zero cannot exhaust memory.
Replace eval() of a constructed Literal[...] string in the CLI parser with a direct Literal[tuple(action.choices)] subscript.
Pass timeout=30 to urlopen when fetching a remote requirements file so a slow or unresponsive mirror cannot hang tox indefinitely. ( #3924 )
Allow the generated TOML schema to validate array values for deps . ( #3929 )
Correct type annotations for ArgumentParser.parse_args and parse_known_args overrides following typeshed PR #15613 , which widened the args parameter from Sequence[str] to Iterable[str] . The narrower type in tox’s overrides violated the Liskov substitution principle and caused invalid-method-override errors with ty 0.0.33. Also correct the option_spec annotation in docs/tox_conf.py to ClassVar[dict[str, Callable[[str], Any]]] matching the docutils stubs type. ( #3932 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.53.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.53.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.52.1...4.53.0
TOML env_list now accepts bare range dicts ( { prefix = "3.", start = 12, stop = 14 } ) and bare labeled dicts ( { ecosystem = ["oci", "python"] } ) as top-level items, removing the { product = [...] } wrapper when there is only a single factor group - by @gaborbernat . ( #3923 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.52.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.52.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.52.0...4.52.1
Changing a resolution-affecting environment variable via set_env (e.g. PIP_INDEX_URL ) now invalidates the install cache and triggers a reinstall, rather than incorrectly reusing the cached environment - by @gaborbernat . ( #3917 )
Use normalize_isa from python-discovery for architecture factor matching, supporting aliases like i686 → x86 and aarch64 → arm64 - by @rahuldevikar . ( #3919 )
Changing a resolution-affecting environment variable via set_env (e.g. PIP_INDEX_URL) now invalidates the install cache and triggers a reinstall, rather than incorrectly reusing the cached environment - by gaborbernat. (3917)
Use normalize_isa from python-discovery for architecture factor matching, supporting aliases like i686 → x86 and aarch64 → arm64 - by rahuldevikar. (3919)
<!-- Release notes generated using configuration in .github/release.yaml at 4.52.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.52.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.51.0...4.52.0
Add virtualenv-pep-723 runner that reads dependencies and Python version from PEP 723 inline script metadata — no need to duplicate them in tox config - by @gaborbernat . ( #3897 )
Support escaped dots ( . ) in -x / --override keys, allowing overrides to target environments with dots in their names such as py3.14 - by @gaborbernat . ( #3910 )
Add virtualenv-pep-723 runner that reads dependencies and Python version from PEP 723 inline script metadata — no need to duplicate them in tox config - by gaborbernat. (3897)
Support escaped dots (\.) in -x/--override keys, allowing overrides to target environments with dots in their names such as py3.14 - by gaborbernat. (3910)
<!-- Release notes generated using configuration in .github/release.yaml at 4.51.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.51.0 -->
base_python_file config option by @rahuldevikar in https://github.com/tox-dev/tox/pull/3899Full Changelog: https://github.com/tox-dev/tox/compare/4.50.3...4.51.0
Add base_python_file configuration option to read the base Python version from a file (e.g. .python-version ), similar to GitHub Actions’ python-version-file - by @rahuldevikar ( #3894 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.3 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.3 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.50.2...4.50.3
Use separate .pkg environments for free-threaded Python targets by including the t suffix in the wheel build environment name (e.g., .pkg-cpython314t ), preventing wheel tag mismatches when building for py314t - by @gaborbernat . ( #3314 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.2 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.2 -->
docs/man/tox.1.rst by @branchv in https://github.com/tox-dev/tox/pull/3890Full Changelog: https://github.com/tox-dev/tox/compare/4.50.1...4.50.2
Fix the sdist to include the missing docs/man/tox.1.rst file for building the manpage ( #3889 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.50.0...4.50.1
Detect and suggest normalized environment names when users specify dotted Python versions (e.g., py3.10-lint ) that match existing environments with compact notation (e.g., py310-lint ), preventing silent fallback to base [testenv] configuration - by @gaborbernat . ( #3877 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.50.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.49.1...4.50.0
Add interrupt_post_commands option to run cleanup commands even after SIGINT - by @gaborbernat . ( #3858 )
Add factor['NAME'] and env['VAR'] subscript syntax for conditional expressions, enabling checks of factors with non-identifier names like factor['3.14'] . Add env_name variable to check the full environment name in conditions. ( #3880 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.49.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.49.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.49.0...4.49.1
Break deadlock in execution interrupt chain that caused ~18 flaky timeout failures across 9 tests on Windows/macOS CI
Break deadlock in execution interrupt chain that caused ~18 flaky timeout failures across 9 tests on Windows/macOS CI - by gaborbernat. (3869)
<!-- Release notes generated using configuration in .github/release.yaml at 4.49.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.49.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.48.1...4.49.0
Add {factor:label} substitution for TOML configs – factor groups in product matrices and env_base factors can now be labeled with single-key dicts (e.g., {ecosystem = ["oci", "python"]} ), enabling {factor:ecosystem} in any string value (descriptions, commands, etc.) to resolve to the active factor. Plain lists automatically get positional labels ( {factor:0} , {factor:1} , …) - by @gaborbernat . ( #3860 ) ( #3860 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.48.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.48.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.48.0...4.48.1
Command-type configuration values like list_dependencies_command can now be referenced in TOML using the structured {replace = "ref"} syntax. The reference automatically extracts the command’s argument list for compatibility with TOML’s list[list[str]] structure - by @gaborbernat . ( #3830 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.48.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.48.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.47.3...4.48.0
Add --format flag ( ini , json , toml ) and -o / --output-file to the config command for machine-readable output with native types. JSON and TOML use the same key structure as tox.toml ( env.<name> for environments, tox for core) and get syntax-highlighted on stdout - by @gaborbernat . ( #3854 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.3 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.3 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.47.2...4.47.3
ignore_base_python_conflict now also suppresses errors when an environment name contains multiple conflicting version-like factors (e.g., unit-py3.10-2.16 where both py3.10 and 2.16 are detected as Python versions). Previously this flag only handled conflicts between explicit base_python settings and the environment name - by @gaborbernat . ( #3850 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.2 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.2 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.47.1...4.47.2
Handle OSError when checking if positional arguments are existing paths in pos_args() — long arguments (e.g., pytest -k expressions exceeding the 255-character filesystem name limit) no longer crash with OSError: [Errno 36] File name too long - by @yarikoptic . ( #3847 )
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.1 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.47.0...4.47.1
New-style version factors (e.g., 3.10-tests ) now correctly set base_python - by @gaborbernat . ( #3845 )
New-style version factors (e.g., 3.10-tests) now correctly set base_python - by gaborbernat. (3845)
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.0 -->
<!-- Release notes generated using configuration in .github/release.yaml at 4.47.0 -->
base_python in TOML by @gaborbernat in https://github.com/tox-dev/tox/pull/3843Full Changelog: https://github.com/tox-dev/tox/compare/4.46.3...4.47.0
Show clean error messages instead of stack traces when TOML configuration contains type mismatches (e.g., deps = [1] ), wrapping failures in HandledError with environment and key context - by @gaborbernat . ( #3831 )
Add comprehensive tests asserting full error messages for every configurable field type when TOML config contains type mismatches - by @gaborbernat . ( #3840 )
Show clean error messages instead of stack traces when TOML configuration contains type mismatches (e.g., deps = [1]), wrapping failures in HandledError with environment and key context - by gaborbernat. (3831)
Add comprehensive tests asserting full error messages for every configurable field type when TOML config contains type mismatches - by gaborbernat. (3840)
<!-- Release notes generated using configuration in .github/release.yml at 4.46.3 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.46.3 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.46.2...4.46.3
Fix SchemaStore update workflow creating PRs with merge conflicts by branching from upstream master - by @gaborbernat . ( #3828 )
Fixed the minimum version of virtualenv, to avoid incompatibility. ( #3829 )
<!-- Release notes generated using configuration in .github/release.yml at 4.46.2 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.46.2 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.46.1...4.46.2
Fix SchemaStore update workflow authentication and PR creation for fork repositories - by @gaborbernat . ( #3826 )
Fix SchemaStore update workflow authentication and PR creation for fork repositories - by gaborbernat. (3826)
<!-- Release notes generated using configuration in .github/release.yml at 4.46.1 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.46.1 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.46.0...4.46.1
Allow glob patterns ( * , ? ) in depends configuration for ini files - by @gaborbernat . ( #3822 )
Fix false positive schema validation errors with tombi by adding additionalProperties: true to partial-tox.json on SchemaStore - by @gaborbernat . ( #3823 )
Allow glob patterns (*, ?) in depends configuration for ini files - by gaborbernat. (3822)
Fix false positive schema validation errors with tombi by adding additionalProperties: true to partial-tox.json on SchemaStore - by gaborbernat. (3823)
<!-- Release notes generated using configuration in .github/release.yml at 4.46.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.46.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.45.0...4.46.0
Add architecture (ISA) awareness to Python discovery. tox now propagates the machine field (e.g. arm64 , x86_64 ) from virtualenv ’s interpreter info — derived from sysconfig.get_platform() — through the Python specification, validation, and journal. This allows selecting a Python interpreter by CPU architecture via base_python (e.g. cpython3.12-64-arm64 ) and ensures tox validates the running interpreter’s architecture matches the requested one. Requires virtualenv >= 20.39 - by @rahuldevikar . ( #3069 )
Add env_base TOML sections for named environment templates that generate multiple environments from factor combinations via Cartesian product – by @gaborbernat . ( #3817 )
<!-- Release notes generated using configuration in .github/release.yml at 4.45.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.45.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.44.0...4.45.0
Enhance tox schema command: add x-taplo metadata for IDE integration, product dict support for env_list , handle int and PythonConstraints types, fix $schema draft-07 URI, and add schema freshness test. Add tox.toml to SchemaStore catalog for automatic IDE validation - by @gaborbernat . ( #1388 )
<!-- Release notes generated using configuration in .github/release.yml at 4.44.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.44.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.43.0...4.44.0
Support PEP 751 pylock.toml lock files as dependency input via the pylock configuration option (mutually exclusive with deps ). Packages are filtered by extras, dependency groups, and platform markers evaluated against the target Python interpreter, then installed via pip with --no-deps - by @gaborbernat . ( #3665 )
<!-- Release notes generated using configuration in .github/release.yml at 4.43.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.43.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.42.0...4.43.0
Add TOML-native generative env_list via product dict syntax – Cartesian product of factor groups with optional range dicts and exclusions - by @gaborbernat . ( #3797 )
<!-- Release notes generated using configuration in .github/release.yml at 4.42.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.42.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.41.0...4.42.0
Platform-dependent commands can now be specified using factor syntax without requiring the platform name in the environment name. The current platform ( sys.platform value like linux , darwin , win32 ) is automatically available as an implicit factor, allowing configurations like linux: pytest in INI or factor.linux in TOML conditional expressions to work in any environment. Additionally, TOML configurations can now use factor.NAME in conditional expressions to filter settings based on environment name factors, achieving feature parity with INI’s factor system - by @gaborbernat . ( #2092 )
Add {env_site_packages_dir_plat} / {envsitepackagesdir_plat} substitution that returns the platform-specific (platlib) site-packages directory. On some Linux distributions (Fedora, RHEL) this resolves to lib64 instead of lib , which is no longer symlinked since virtualenv 20.x - by @gaborbernat . ( #2302 )
External packaging environments ( package = external ) now only run their build commands once per session, reusing the built package for all test environments that depend on them - by @gaborbernat . ( #2729 )
Add default_base_python configuration key to specify a fallback Python interpreter when no Python factor or explicit base_python is defined. This allows projects to pin a default Python version for reproducibility across different machines without conflicting with pyXY factor-named environments - by @gaborbernat . ( #2846 )
CLI options added by plugins listed in requires are now accepted during provisioning instead of failing with “unrecognized arguments” - by @gaborbernat . ( #2935 )
Warn about unused configuration keys during tox run -v - by @gaborbernat . ( #3188 )
Improve towncrier changelog structure with more granular categories: breaking , deprecation , feature , bugfix , doc , packaging , contrib , and misc - by @gaborbernat . ( #3200 )
Add --skip-env-install CLI flag to skip dependency and package installation, allowing reuse of existing environments when offline or when installation is unnecessary - by @gaborbernat . ( #3310 )
Add recreate_commands configuration option to run cleanup commands (e.g. clearing external caches like pre-commit) before the environment directory is removed during recreation ( -r ) - by @gaborbernat . ( #3423 )
Environments with unavailable runners (missing plugins) are now gracefully skipped instead of causing a fatal error, shown with status “NOT AVAILABLE”. If such an environment is explicitly requested with -e , a clear error message is shown indicating which runner is missing and that the plugin may not be installed. Unavailable environments in the configuration don’t cause the overall run to fail - by @gaborbernat . ( #3504 )
Add open-ended range expansion in generative env lists — py3{9-} expands up to the latest supported CPython minor version and py3{-13} expands down to the oldest supported one, both hardcoded at release time per the Python release cycle - by @gaborbernat . ( #3583 )
Add --no-capture ( -i ) CLI flag to disable output capture, allowing interactive programs like Python REPL to access the terminal directly. When enabled, subprocess inherits parent console handles instead of pipes, fixing issues where terminal APIs (e.g., querying console dimensions) fail on pipe handles. Mutually exclusive with --result-json and parallel mode - by @gaborjbernat . Fixes #3635 . ( #3635 )
Add virtualenv_spec configuration key that allows pinning a specific virtualenv version per environment (e.g. virtualenv_spec = "virtualenv<20.22.0" ). When set, tox bootstraps the specified version in an isolated environment and drives it via subprocess, enabling environments targeting Python versions incompatible with the installed virtualenv - by @gaborbernat . ( #3656 )
<!-- Release notes generated using configuration in .github/release.yml at 4.41.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.41.0 -->
package = "deps-only" mode by @gaborbernat in https://github.com/tox-dev/tox/pull/3774Full Changelog: https://github.com/tox-dev/tox/compare/4.40.0...4.41.0
Add package = "deps-only" mode that installs the package’s dependencies (including extras) without building or installing the package itself. For projects with static PEP 621 metadata, dependencies are read directly from pyproject.toml without creating a packaging environment - by @gaborbernat . ( #2301 )
Prefer TOML-native configuration ( [tool.tox] ) over legacy_tox_ini when both are present in pyproject.toml , allowing users to include a legacy_tox_ini section with min_version for older tox versions while using native TOML config for newer ones - by @rahuldevikar . ( #3402 )
<!-- Release notes generated using configuration in .github/release.yml at 4.40.0 -->
<!-- Release notes generated using configuration in .github/release.yml at 4.40.0 -->
Full Changelog: https://github.com/tox-dev/tox/compare/4.39.0...4.40.0
Add {glob:PATTERN} substitution to expand file system glob/wildcard patterns in configuration values. Supports default values, recursive ** matching, and both INI string syntax and TOML dict syntax ( { replace = "glob", pattern = "..." } ) - by @gaborbernat . ( #1571 )
Add commands_retry configuration option to automatically retry failed commands - by @gaborbernat . ( #1578 )
Automatically create a .gitignore file containing * in the tox work directory (typically .tox/ ) and in environment info directories, so that tox-managed files are not tracked by git - by @rahuldevikar . ( #2530 )
Add conditional value selection via replace = "if" in TOML configuration. Supports a condition expression with env.VAR lookups, == / != comparisons, and and / or / not boolean logic to select between then and else values - by @gaborbernat . ( #3650 )
Add sdist-wheel package type that builds a wheel from a source distribution, ensuring the sdist is complete and the package can be correctly built from it — by @rahuldevikar . ( #3687 )
Your coding agent can read these notes before it upgrades. Set up the MCP server →