diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml
index a599dd798f..6187165878 100644
--- a/.github/workflows/check.yaml
+++ b/.github/workflows/check.yaml
@@ -28,7 +28,6 @@ jobs:
- "3.12"
- "3.11"
- "3.10"
- - "3.9"
os:
- ubuntu-24.04
- windows-2025
@@ -66,6 +65,7 @@ jobs:
matrix:
tox_env:
- type
+ - type-3.10
- dev
- docs
- pkg_meta
@@ -89,7 +89,10 @@ jobs:
shell: bash
run: echo "$USERPROFILE/.local/bin" >> $GITHUB_PATH
- name: Install tox@self
- run: uv tool install --python-preference only-managed --python 3.13 tox@.
+ run: uv tool install --python-preference only-managed --python 3.14 tox@.
+ - name: Install Python 3.10 for type-3.10
+ if: matrix.tox_env == 'type-3.10'
+ run: uv python install 3.10
- name: Setup check suite
run: tox r -vv --notest --skip-missing-interpreters false -e ${{ matrix.tox_env }}
- name: Run check for ${{ matrix.tox_env }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index c22cb6281d..0db2cb126a 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -21,7 +21,7 @@ jobs:
cache-dependency-glob: "pyproject.toml"
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build package
- run: uv build --python 3.13 --python-preference only-managed --sdist --wheel . --out-dir dist
+ run: uv build --python 3.14 --python-preference only-managed --sdist --wheel . --out-dir dist
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 94915b19ae..402705492b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,9 +13,9 @@ repos:
rev: v2.4.1
hooks:
- id: codespell
- additional_dependencies: ["tomli>=2.2.1"]
+ additional_dependencies: ["tomli>=2.3"]
- repo: https://github.com/tox-dev/pyproject-fmt
- rev: "v2.6.0"
+ rev: "v2.8.0"
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
@@ -23,7 +23,7 @@ repos:
hooks:
- id: validate-pyproject
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: "v0.13.2"
+ rev: "v0.14.0"
hooks:
- id: ruff-check
args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
@@ -32,7 +32,7 @@ repos:
rev: 1.20.0
hooks:
- id: blacken-docs
- additional_dependencies: [black==25.1]
+ additional_dependencies: [black==25.9]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 75a59bfa74..18c949f983 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -5,6 +5,7 @@ build:
python: "3"
commands:
- pip install uv
- - uv venv
+ - uv venv -p 3.14
- uv pip install tox-uv tox@.
- - .venv/bin/tox run -e docs --
+ - .venv/bin/tox run -e docs --notest
+ - .venv/bin/tox run -e docs --skip-pkg-install --
diff --git a/docs/installation.rst b/docs/installation.rst
index 51b1bcad74..2e124b1e6e 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -4,7 +4,7 @@ Installation
As tool
-------
-:pypi:`tox` is a CLI tool that needs a Python interpreter (version 3.9 or higher) to run. We recommend either
+:pypi:`tox` is a CLI tool that needs a Python interpreter (version 3.10 or higher) to run. We recommend either
:pypi:`pipx` or :pypi:`uv` to install tox into an isolated environment. This has the added benefit that later you'll
be able to upgrade tox without affecting other parts of the system. We provide method for ``pip`` too here but we
discourage that path if you can:
@@ -80,7 +80,7 @@ Python and OS Compatibility
tox works with the following Python interpreter implementations:
-- `CPython `_ versions 3.9, 3.10, 3.11, 3.12, 3.13
+- `CPython `_ versions 3.10, 3.11, 3.12, 3.13, 3.14
This means tox works on the latest patch version of each of these minor versions. Previous patch versions are supported
on a best effort approach.
diff --git a/pyproject.toml b/pyproject.toml
index a7323bf836..c519a2f2b5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,7 +26,7 @@ maintainers = [
authors = [
{ name = "Bernát Gábor", email = "gaborjbernat@gmail.com" },
]
-requires-python = ">=3.9"
+requires-python = ">=3.10"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Framework :: tox",
@@ -36,7 +36,6 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -50,17 +49,17 @@ dynamic = [
"version",
]
dependencies = [
- "cachetools>=6.1",
+ "cachetools>=6.2",
"chardet>=5.2",
"colorama>=0.4.6",
- "filelock>=3.18",
+ "filelock>=3.19.1",
"packaging>=25",
- "platformdirs>=4.3.8",
+ "platformdirs>=4.5",
"pluggy>=1.6",
"pyproject-api>=1.9.1",
- "tomli>=2.2.1; python_version<'3.11'",
- "typing-extensions>=4.14.1; python_version<'3.11'",
- "virtualenv>=20.31.2",
+ "tomli>=2.3; python_version<'3.11'",
+ "typing-extensions>=4.15; python_version<'3.11'",
+ "virtualenv>=20.34",
]
urls.Documentation = "https://tox.wiki"
urls.Homepage = "http://tox.readthedocs.org"
@@ -76,55 +75,55 @@ dev = [
{ include-group = "type" },
]
test = [
- "build[virtualenv]>=1.2.2.post1",
+ "build[virtualenv]>=1.3",
"covdefaults>=2.3",
- "coverage>=7.9.2",
+ "coverage>=7.10.7",
"detect-test-pollution>=1.2",
"devpi-process>=1.0.2",
- "diff-cover>=9.6",
+ "diff-cover>=9.7.1",
"distlib>=0.4",
"flaky>=3.8.1",
"hatch-vcs>=0.5",
"hatchling>=1.27",
- "pdm-backend",
- "psutil>=7",
- "pytest>=8.4.1",
- "pytest-cov>=6.2.1",
- "pytest-mock>=3.14.1",
+ "pdm-backend>=2.4.5",
+ "psutil>=7.1",
+ "pytest>=8.4.2",
+ "pytest-cov>=7",
+ "pytest-mock>=3.15.1",
"pytest-xdist>=3.8",
"re-assert>=1.1",
"setuptools>=80.9",
- "time-machine>=2.16; implementation_name!='pypy'",
+ "time-machine>=2.19; implementation_name!='pypy'",
"wheel>=0.45.1",
]
type = [
- "mypy==1.17",
- "types-cachetools>=6.1.0.20250717",
+ "mypy==1.18.2",
+ "types-cachetools>=6.2.0.20250827",
"types-chardet>=5.0.4.6",
{ include-group = "test" },
]
docs = [
- "furo>=2025.7.19",
+ "furo>=2025.9.25",
"sphinx>=8.2.3",
- "sphinx-argparse-cli>=1.19",
- "sphinx-autodoc-typehints>=3.2",
+ "sphinx-argparse-cli>=1.20.1",
+ "sphinx-autodoc-typehints>=3.4",
"sphinx-copybutton>=0.5.2",
"sphinx-inline-tabs>=2023.4.21",
"sphinxcontrib-towncrier>=0.2.1a0",
- "towncrier>=24.8",
+ "towncrier>=25.8",
]
fix = [
- "pre-commit-uv>=4.1.4",
+ "pre-commit-uv>=4.1.5",
]
pkg-meta = [
- "check-wheel-contents>=0.6.2",
- "twine>=6.1",
- "uv>=0.8",
+ "check-wheel-contents>=0.6.3",
+ "twine>=6.2",
+ "uv>=0.9",
]
release = [
- "gitpython>=3.1.44",
+ "gitpython>=3.1.45",
"packaging>=25",
- "towncrier>=24.8",
+ "towncrier>=25.8",
]
[tool.hatch]
@@ -140,7 +139,6 @@ build.targets.sdist.include = [
version.source = "vcs"
[tool.ruff]
-target-version = "py38"
line-length = 120
format.preview = true
format.docstring-code-line-length = 100
@@ -238,7 +236,6 @@ template = "docs/changelog/template.jinja2"
# possible types, all default: feature, bugfix, doc, removal, misc
[tool.mypy]
-python_version = "3.11"
show_error_codes = true
strict = true
overrides = [
diff --git a/src/tox/config/cli/env_var.py b/src/tox/config/cli/env_var.py
index 92162945f7..41cf07255d 100644
--- a/src/tox/config/cli/env_var.py
+++ b/src/tox/config/cli/env_var.py
@@ -4,7 +4,7 @@
import logging
import os
-from typing import Any, List
+from typing import Any
from tox.config.loader.str_convert import StrConvert
@@ -25,7 +25,7 @@ def get_env_var(key: str, of_type: type[Any]) -> tuple[Any, str] | None:
value = os.environ[environ_key]
origin = getattr(of_type, "__origin__", of_type.__class__)
try:
- if origin in {list, List}:
+ if origin in {list, list}:
entry_type = of_type.__args__[0]
result = [CONVERT.to(raw=v, of_type=entry_type, factory=None) for v in value.split(";")]
else:
diff --git a/src/tox/config/cli/parse.py b/src/tox/config/cli/parse.py
index a61de06baa..e206857c58 100644
--- a/src/tox/config/cli/parse.py
+++ b/src/tox/config/cli/parse.py
@@ -6,7 +6,7 @@
import os
from contextlib import redirect_stderr
from pathlib import Path
-from typing import TYPE_CHECKING, Callable, NamedTuple, Sequence, cast
+from typing import TYPE_CHECKING, NamedTuple, cast
from tox.config.source import Source, discover_source
from tox.report import ToxHandler, setup_report
@@ -14,6 +14,8 @@
from .parser import Parsed, ToxParser
if TYPE_CHECKING:
+ from collections.abc import Callable, Sequence
+
from tox.session.state import State
@@ -47,9 +49,10 @@ def _get_base(args: Sequence[str]) -> tuple[int, ToxHandler, Source]:
tox_parser = ToxParser.base()
parsed = Parsed()
try:
- with Path(os.devnull).open(
- "w", encoding=locale.getpreferredencoding(do_setlocale=False)
- ) as file_handler, redirect_stderr(file_handler):
+ with (
+ Path(os.devnull).open("w", encoding=locale.getpreferredencoding(do_setlocale=False)) as file_handler,
+ redirect_stderr(file_handler),
+ ):
tox_parser.parse_known_args(args, namespace=parsed)
except SystemExit:
... # ignore parse errors, such as -va raises ignored explicit argument 'a'
diff --git a/src/tox/config/cli/parser.py b/src/tox/config/cli/parser.py
index 0b1133b4ba..e739c27355 100644
--- a/src/tox/config/cli/parser.py
+++ b/src/tox/config/cli/parser.py
@@ -9,7 +9,8 @@
import sys
from argparse import SUPPRESS, Action, ArgumentDefaultsHelpFormatter, ArgumentError, ArgumentParser, Namespace
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple, Type, TypeVar, cast
+from types import UnionType
+from typing import TYPE_CHECKING, Any, Literal, TypeVar, cast
from colorama import Fore
@@ -26,6 +27,8 @@
from typing_extensions import Self
if TYPE_CHECKING:
+ from collections.abc import Callable, Sequence
+
from tox.session.state import State
@@ -64,7 +67,7 @@ def get_type(action: Action) -> type[Any]:
of_type: type[Any] | None = getattr(action, "of_type", None)
if of_type is None:
if isinstance(action, argparse._AppendAction): # noqa: SLF001
- of_type = List[action.type] # type: ignore[name-defined]
+ of_type = list[action.type] # type: ignore[name-defined]
elif isinstance(action, argparse._StoreAction) and action.choices: # noqa: SLF001
loc = locals()
loc["Literal"] = Literal
@@ -135,7 +138,7 @@ def is_colored(self) -> bool:
exit_and_dump_after: int
-ArgumentArgs = Tuple[Tuple[str, ...], Optional[Type[Any]], Dict[str, Any]]
+ArgumentArgs = tuple[tuple[str, ...], type[Any] | UnionType | None, dict[str, Any]]
class ToxParser(ArgumentParserWithEnvAndConfig):
@@ -230,7 +233,7 @@ def __call__(
help="set PYTHONHASHSEED to SEED before running commands. Defaults to a random integer in the range "
"[1, 4294967295] ([1, 1024] on Windows). Passing 'notset' suppresses this behavior.",
action=SeedAction,
- of_type=Optional[int], # type: ignore[arg-type]
+ of_type=int | None,
default=hashseed_default,
dest="hash_seed",
)
@@ -239,7 +242,7 @@ def __call__(
dest="discover",
nargs="+",
metavar="path",
- of_type=List[str],
+ of_type=list[str],
help="for Python discovery first try these Python executables",
default=[],
)
@@ -280,7 +283,7 @@ def add_argument(*a_args: str, of_type: type[Any] | None = None, **a_kwargs: Any
self._groups.append((args, kwargs, excl))
return result
- def add_argument(self, *args: str, of_type: type[Any] | None = None, **kwargs: Any) -> Action:
+ def add_argument(self, *args: str, of_type: type[Any] | UnionType | None = None, **kwargs: Any) -> Action:
result = super().add_argument(*args, **kwargs)
if self.of_cmd is None and result.dest != "help":
self._arguments.append((args, of_type, kwargs))
@@ -403,7 +406,7 @@ def add_core_arguments(parser: ArgumentParser) -> None:
metavar="file",
default=None,
type=Path,
- of_type=Optional[Path],
+ of_type=Path | None,
help="configuration file/folder for tox (if not specified will discover one)",
)
parser.add_argument(
@@ -412,7 +415,7 @@ def add_core_arguments(parser: ArgumentParser) -> None:
metavar="dir",
default=None,
type=Path,
- of_type=Optional[Path],
+ of_type=Path | None,
help="tox working directory (if not specified will be the folder of the config file)",
)
parser.add_argument(
@@ -421,7 +424,7 @@ def add_core_arguments(parser: ArgumentParser) -> None:
metavar="dir",
default=None,
type=Path,
- of_type=Optional[Path],
+ of_type=Path | None,
help="project root directory (if not specified will be the folder of the config file)",
)
diff --git a/src/tox/config/loader/api.py b/src/tox/config/loader/api.py
index e4f1eb8b7b..83873c8910 100644
--- a/src/tox/config/loader/api.py
+++ b/src/tox/config/loader/api.py
@@ -2,7 +2,8 @@
from abc import abstractmethod
from argparse import ArgumentTypeError
-from typing import TYPE_CHECKING, Any, List, Mapping, TypeVar
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any, TypeVar
from tox.plugin import impl
from tox.tox_env.python.pip.req_file import PythonDeps
@@ -11,6 +12,8 @@
from .str_convert import StrConvert
if TYPE_CHECKING:
+ from types import UnionType
+
from tox.config.cli.parser import ToxParser
from tox.config.main import Config
@@ -70,7 +73,7 @@ def copy(self) -> ConfigLoadArgs:
return ConfigLoadArgs(self.chain.copy(), self.name, self.env_name)
-OverrideMap = Mapping[str, List[Override]]
+OverrideMap = Mapping[str, list[Override]]
T = TypeVar("T")
V = TypeVar("V")
@@ -119,7 +122,7 @@ def __contains__(self, item: str) -> bool:
def load(
self,
key: str,
- of_type: type[V],
+ of_type: type[V] | UnionType,
factory: Factory[V],
conf: Config | None,
args: ConfigLoadArgs,
@@ -169,7 +172,7 @@ def load(
def build( # noqa: PLR0913
self,
key: str, # noqa: ARG002
- of_type: type[V],
+ of_type: type[V] | UnionType,
factory: Factory[V],
conf: Config | None, # noqa: ARG002
raw: T,
diff --git a/src/tox/config/loader/convert.py b/src/tox/config/loader/convert.py
index e844f40173..00f78f592a 100644
--- a/src/tox/config/loader/convert.py
+++ b/src/tox/config/loader/convert.py
@@ -3,23 +3,27 @@
import typing
from abc import ABC, abstractmethod
from collections import OrderedDict
+from collections.abc import Callable, Iterator
from inspect import isclass
from pathlib import Path
-from typing import Any, Callable, Dict, Generic, Iterator, List, Literal, Optional, Set, TypeVar, Union, cast
+from typing import Any, Generic, Literal, TypeVar, Union, cast
from tox.config.types import Command, EnvList
+if typing.TYPE_CHECKING:
+ from types import UnionType
+
_NO_MAPPING = object()
T = TypeVar("T")
V = TypeVar("V")
-Factory = Optional[Callable[[object], T]] # note the argument is anything, due e.g. memory loader can inject anything
+Factory = Callable[[object], T] | None # note the argument is anything, due e.g. memory loader can inject anything
class Convert(ABC, Generic[T]):
"""A class that converts a raw type to a given tox (python) type."""
- def to(self, raw: T, of_type: type[V], factory: Factory[V]) -> V: # noqa: PLR0911, C901
+ def to(self, raw: T, of_type: type[V] | UnionType, factory: Factory[V]) -> V: # noqa: PLR0911
"""
Convert given raw type to python type.
@@ -29,7 +33,9 @@ def to(self, raw: T, of_type: type[V], factory: Factory[V]) -> V: # noqa: PLR09
:return: the converted type
"""
from_module = getattr(of_type, "__module__", None)
- if from_module in {"typing", "typing_extensions"}:
+ if from_module in {"typing", "typing_extensions"} or (
+ hasattr(typing, "GenericAlias") and isinstance(of_type, typing.GenericAlias)
+ ):
return self._to_typing(raw, of_type, factory)
if isclass(of_type):
if issubclass(of_type, Path):
@@ -42,35 +48,32 @@ def to(self, raw: T, of_type: type[V], factory: Factory[V]) -> V: # noqa: PLR09
return self.to_env_list(raw) # type: ignore[return-value]
if issubclass(of_type, str):
return self.to_str(raw) # type: ignore[return-value]
- # python does not allow use of parametrized generics with isinstance, so we need to check for them.
- if hasattr(typing, "GenericAlias") and isinstance(of_type, typing.GenericAlias):
- return list(self.to_list(raw, of_type=of_type)) # type: ignore[return-value]
- if isinstance(raw, of_type): # already target type no need to transform it
+ if isinstance(raw, cast("type[V]", of_type)): # already target type no need to transform it
# do it this late to allow normalization - e.g. string strip
return raw
if factory:
return factory(raw)
- return of_type(raw) # type: ignore[no-any-return]
+ return cast("type[V]", of_type)(raw) # type: ignore[call-arg]
- def _to_typing(self, raw: T, of_type: type[V], factory: Factory[V]) -> V: # noqa: C901
+ def _to_typing(self, raw: T, of_type: type[V] | UnionType, factory: Factory[V]) -> V: # noqa: C901
origin = getattr(of_type, "__origin__", of_type.__class__)
result: Any = _NO_MAPPING
- if origin in {list, List}:
- entry_type = of_type.__args__[0] # type: ignore[attr-defined]
+ if origin in {list, list}:
+ entry_type = cast("type[V]", of_type).__args__[0] # type: ignore[attr-defined]
result = [self.to(i, entry_type, factory) for i in self.to_list(raw, entry_type)]
if isclass(entry_type) and issubclass(entry_type, Command):
result = [i for i in result if i is not None]
- elif origin in {set, Set}:
- entry_type = of_type.__args__[0] # type: ignore[attr-defined]
+ elif origin in {set, set}:
+ entry_type = cast("type[V]", of_type).__args__[0] # type: ignore[attr-defined]
result = {self.to(i, entry_type, factory) for i in self.to_set(raw, entry_type)}
- elif origin in {dict, Dict}:
- key_type, value_type = of_type.__args__[0], of_type.__args__[1] # type: ignore[attr-defined]
+ elif origin in {dict, dict}:
+ key_type, value_type = cast("type[V]", of_type).__args__[0], cast("type[V]", of_type).__args__[1] # type: ignore[attr-defined]
result = OrderedDict(
(self.to(k, key_type, factory), self.to(v, value_type, factory))
for k, v in self.to_dict(raw, (key_type, value_type))
)
elif origin == Union: # handle Optional values
- args: list[type[Any]] = of_type.__args__ # type: ignore[attr-defined]
+ args: list[type[Any]] = of_type.__args__ # type: ignore[union-attr,assignment]
none = type(None)
if len(args) == 2 and none in args: # noqa: PLR2004
if isinstance(raw, str):
@@ -81,7 +84,7 @@ def _to_typing(self, raw: T, of_type: type[V], factory: Factory[V]) -> V: # noq
new_type = next(i for i in args if i != none) # pragma: no cover
result = self.to(raw, new_type, factory)
elif origin in {Literal, type(Literal)}:
- choice = of_type.__args__ # type: ignore[attr-defined]
+ choice = cast("type[V]", of_type).__args__ # type: ignore[attr-defined]
if raw not in choice:
msg = f"{raw} must be one of {choice}"
raise ValueError(msg)
diff --git a/src/tox/config/loader/ini/__init__.py b/src/tox/config/loader/ini/__init__.py
index 9d97a3312f..ffb5fc588f 100644
--- a/src/tox/config/loader/ini/__init__.py
+++ b/src/tox/config/loader/ini/__init__.py
@@ -14,6 +14,7 @@
if TYPE_CHECKING:
from configparser import ConfigParser, SectionProxy
+ from types import UnionType
from tox.config.loader.convert import Factory
from tox.config.loader.section import Section
@@ -60,7 +61,7 @@ def process_raw(conf: Config | None, env_name: str | None, value: str) -> str:
def build( # noqa: PLR0913
self,
key: str,
- of_type: type[V],
+ of_type: type[V] | UnionType,
factory: Factory[V],
conf: Config | None,
raw: str,
diff --git a/src/tox/config/loader/ini/factor.py b/src/tox/config/loader/ini/factor.py
index 11b4fa66d5..9a5bc91d9b 100644
--- a/src/tox/config/loader/ini/factor.py
+++ b/src/tox/config/loader/ini/factor.py
@@ -4,7 +4,10 @@
import re
from itertools import chain, groupby, product
-from typing import Iterator
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from collections.abc import Iterator
def filter_for_env(value: str, name: str | None) -> str:
diff --git a/src/tox/config/loader/ini/replace.py b/src/tox/config/loader/ini/replace.py
index f225cedbc6..9802281cf4 100644
--- a/src/tox/config/loader/ini/replace.py
+++ b/src/tox/config/loader/ini/replace.py
@@ -4,13 +4,16 @@
import re
from configparser import SectionProxy
-from functools import lru_cache
-from typing import TYPE_CHECKING, Iterator, Pattern
+from functools import cache
+from re import Pattern
+from typing import TYPE_CHECKING
from tox.config.loader.replacer import ReplaceReference
from tox.config.loader.stringify import stringify
if TYPE_CHECKING:
+ from collections.abc import Iterator
+
from tox.config.loader.api import ConfigLoadArgs
from tox.config.loader.ini import IniLoader
from tox.config.main import Config
@@ -82,7 +85,7 @@ def _config_value_sources(
yield value
-@lru_cache(maxsize=None)
+@cache
def _replace_ref(env: str | None) -> Pattern[str]:
return re.compile(
rf"""
diff --git a/src/tox/config/loader/memory.py b/src/tox/config/loader/memory.py
index 0484dee765..2b8b0daaeb 100644
--- a/src/tox/config/loader/memory.py
+++ b/src/tox/config/loader/memory.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Iterator
+from typing import TYPE_CHECKING, Any
from tox.config.types import Command, EnvList
@@ -10,6 +10,8 @@
from .str_convert import StrConvert
if TYPE_CHECKING:
+ from collections.abc import Iterator
+
from tox.config.main import Config
diff --git a/src/tox/config/loader/replacer.py b/src/tox/config/loader/replacer.py
index 5d9fd70ebd..c91ed5b397 100644
--- a/src/tox/config/loader/replacer.py
+++ b/src/tox/config/loader/replacer.py
@@ -6,7 +6,8 @@
import os
import sys
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Final, Sequence, Union
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Any, Final, Union
from tox.config.types import CircularChainError
from tox.execute.request import shell_cmd
diff --git a/src/tox/config/loader/str_convert.py b/src/tox/config/loader/str_convert.py
index 248afe2685..7fda1c9bae 100644
--- a/src/tox/config/loader/str_convert.py
+++ b/src/tox/config/loader/str_convert.py
@@ -7,13 +7,14 @@
from inspect import isclass
from itertools import chain
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Iterator
+from typing import TYPE_CHECKING, Any
from tox.config.loader.convert import Convert
from tox.config.loader.ini.factor import expand_ranges
from tox.config.types import Command, EnvList
if TYPE_CHECKING:
+ from collections.abc import Iterator
from typing import Final
diff --git a/src/tox/config/loader/stringify.py b/src/tox/config/loader/stringify.py
index 94d7d96ab9..098382b88a 100644
--- a/src/tox/config/loader/stringify.py
+++ b/src/tox/config/loader/stringify.py
@@ -1,7 +1,8 @@
from __future__ import annotations
+from collections.abc import Mapping, Sequence
from pathlib import Path
-from typing import Any, Mapping, Sequence, Set
+from typing import Any
from tox.config.set_env import SetEnv
from tox.config.types import Command, EnvList
@@ -23,7 +24,7 @@ def stringify(value: Any) -> tuple[str, bool]: # noqa: PLR0911
return "\n".join(f"{stringify(k)[0]}={stringify(v)[0]}" for k, v in value.items()), True
if isinstance(value, Sequence):
return "\n".join(stringify(i)[0] for i in value), True
- if isinstance(value, Set): # sort it to make it stable
+ if isinstance(value, set): # sort it to make it stable
return "\n".join(sorted(stringify(i)[0] for i in value)), True
if isinstance(value, EnvList):
return "\n".join(e for e in value.envs), True
diff --git a/src/tox/config/loader/toml/__init__.py b/src/tox/config/loader/toml/__init__.py
index 4c4c6cb033..a17f2a7549 100644
--- a/src/tox/config/loader/toml/__init__.py
+++ b/src/tox/config/loader/toml/__init__.py
@@ -3,7 +3,7 @@
import inspect
import logging
from pathlib import Path
-from typing import TYPE_CHECKING, Dict, Iterator, List, Mapping, TypeVar, cast
+from typing import TYPE_CHECKING, TypeVar, cast
from tox.config.loader.api import ConfigLoadArgs, Loader, Override
from tox.config.set_env import SetEnv
@@ -15,6 +15,9 @@
from ._validate import validate
if TYPE_CHECKING:
+ from collections.abc import Iterator, Mapping
+ from types import UnionType
+
from tox.config.loader.convert import Factory
from tox.config.loader.section import Section
from tox.config.main import Config
@@ -59,7 +62,7 @@ def load_raw_from_root(self, path: str) -> TomlTypes:
def build( # noqa: PLR0913
self,
key: str, # noqa: ARG002
- of_type: type[_T],
+ of_type: type[_T] | UnionType,
factory: Factory[_T],
conf: Config | None,
raw: TomlTypes,
@@ -84,17 +87,17 @@ def to_bool(value: TomlTypes) -> bool:
@staticmethod
def to_list(value: TomlTypes, of_type: type[_T]) -> Iterator[_T]:
- of = List[of_type] # type: ignore[valid-type] # no mypy support
+ of = list[of_type] # type: ignore[valid-type] # no mypy support
return iter(validate(value, of)) # type: ignore[call-overload,no-any-return]
@staticmethod
def to_set(value: TomlTypes, of_type: type[_T]) -> Iterator[_T]:
- of = List[of_type] # type: ignore[valid-type] # no mypy support
+ of = list[of_type] # type: ignore[valid-type] # no mypy support
return iter(validate(value, of)) # type: ignore[call-overload,no-any-return]
@staticmethod
def to_dict(value: TomlTypes, of_type: tuple[type[_T], type[_V]]) -> Iterator[tuple[_T, _V]]:
- of = Dict[of_type[0], of_type[1]] # type: ignore[valid-type] # no mypy support
+ of = dict[of_type[0], of_type[1]] # type: ignore[valid-type] # no mypy support
return validate(value, of).items() # type: ignore[attr-defined,no-any-return]
@staticmethod
@@ -104,7 +107,7 @@ def to_path(value: TomlTypes) -> Path:
@staticmethod
def to_command(value: TomlTypes) -> Command | None:
if value:
- return Command(args=cast("List[str]", value)) # validated during load in _ensure_type_correct
+ return Command(args=cast("list[str]", value)) # validated during load in _ensure_type_correct
return None
@staticmethod
diff --git a/src/tox/config/loader/toml/_api.py b/src/tox/config/loader/toml/_api.py
index 9a9596df9d..f23f7c0373 100644
--- a/src/tox/config/loader/toml/_api.py
+++ b/src/tox/config/loader/toml/_api.py
@@ -1,16 +1,11 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Dict, List, Union
+from typing import TYPE_CHECKING
if TYPE_CHECKING:
- import sys
+ from typing import TypeAlias
- if sys.version_info >= (3, 10): # pragma: no cover (py310+)
- from typing import TypeAlias
- else: # pragma: no cover (py310+)
- from typing_extensions import TypeAlias
-
-TomlTypes: TypeAlias = Union[Dict[str, "TomlTypes"], List["TomlTypes"], str, int, float, bool, None]
+TomlTypes: TypeAlias = dict[str, "TomlTypes"] | list["TomlTypes"] | str | int | float | bool | None
__all__ = [
"TomlTypes",
diff --git a/src/tox/config/loader/toml/_replace.py b/src/tox/config/loader/toml/_replace.py
index a7664aee1e..4324693246 100644
--- a/src/tox/config/loader/toml/_replace.py
+++ b/src/tox/config/loader/toml/_replace.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import re
-from typing import TYPE_CHECKING, Any, Iterator, List, cast
+from typing import TYPE_CHECKING, Any, cast
from tox.config.loader.replacer import MatchRecursionError, ReplaceReference, load_posargs, replace, replace_env
from tox.config.loader.stringify import stringify
@@ -9,6 +9,8 @@
from ._validate import validate
if TYPE_CHECKING:
+ from collections.abc import Iterator
+
from tox.config.loader.api import ConfigLoadArgs
from tox.config.loader.toml import TomlLoader
from tox.config.main import Config
@@ -40,7 +42,7 @@ def __call__(self, value: TomlTypes, depth: int = 0) -> TomlTypes: # noqa: C901
for val in value: # apply replacement for every entry
got = self(val, depth)
if isinstance(val, dict) and val.get("replace") and val.get("extend"):
- res_list.extend(cast("List[Any]", got))
+ res_list.extend(cast("list[Any]", got))
else:
res_list.append(got)
value = res_list
@@ -50,7 +52,7 @@ def __call__(self, value: TomlTypes, depth: int = 0) -> TomlTypes: # noqa: C901
if replace_type == "posargs" and self.conf is not None:
got_posargs = load_posargs(self.conf, self.args)
return (
- [self(v, depth) for v in cast("List[str]", value.get("default", []))]
+ [self(v, depth) for v in cast("list[str]", value.get("default", []))]
if got_posargs is None
else list(got_posargs)
)
@@ -76,7 +78,7 @@ def _replace_ref(self, value: dict[str, TomlTypes], depth: int) -> TomlTypes:
if self.conf is not None and (env := value.get("env")) and (key := value.get("key")):
return cast("TomlTypes", self.conf.get_env(cast("str", env))[cast("str", key)])
if of := value.get("of"):
- validated_of = cast("List[str]", validate(of, List[str]))
+ validated_of = cast("list[str]", validate(of, list[str]))
loaded = self.loader.load_raw_from_root(self.loader.section.SEP.join(validated_of))
return self(loaded, depth)
return value
diff --git a/src/tox/config/loader/toml/_validate.py b/src/tox/config/loader/toml/_validate.py
index 89258990e7..b56031ae62 100644
--- a/src/tox/config/loader/toml/_validate.py
+++ b/src/tox/config/loader/toml/_validate.py
@@ -4,8 +4,6 @@
from typing import (
TYPE_CHECKING,
Any,
- Dict,
- List,
Literal,
TypeVar,
Union,
@@ -22,7 +20,7 @@
if sys.version_info >= (3, 11): # pragma: no cover (py311+)
from typing import TypeGuard
else: # pragma: no cover (py311+)
- from typing_extensions import TypeGuard
+ from typing import TypeGuard
T = TypeVar("T")
@@ -30,7 +28,7 @@
def validate(val: TomlTypes, of_type: type[T]) -> TypeGuard[T]: # noqa: C901, PLR0912
casting_to = getattr(of_type, "__origin__", of_type.__class__)
msg = ""
- if casting_to in {list, List}:
+ if casting_to in {list, list}:
entry_type = of_type.__args__[0] # type: ignore[attr-defined]
if isinstance(val, list):
for va in val:
@@ -39,8 +37,8 @@ def validate(val: TomlTypes, of_type: type[T]) -> TypeGuard[T]: # noqa: C901, P
msg = f"{val!r} is not list"
elif isclass(of_type) and issubclass(of_type, Command):
# first we cast it to list then create commands, so for now validate it as a nested list
- validate(val, List[str])
- elif casting_to in {dict, Dict}:
+ validate(val, list[str])
+ elif casting_to in {dict, dict}:
key_type, value_type = of_type.__args__[0], of_type.__args__[1] # type: ignore[attr-defined]
if isinstance(val, dict):
for va in val:
diff --git a/src/tox/config/main.py b/src/tox/config/main.py
index 9fbd9f4bcb..48c75cbe37 100644
--- a/src/tox/config/main.py
+++ b/src/tox/config/main.py
@@ -4,11 +4,13 @@
from collections import OrderedDict, defaultdict
from itertools import chain, tee
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Iterable, Iterator, Sequence, TypeVar
+from typing import TYPE_CHECKING, Any, TypeVar
from .sets import ConfigSet, CoreConfigSet, EnvConfigSet
if TYPE_CHECKING:
+ from collections.abc import Iterable, Iterator, Sequence
+
from tox.config.loader.api import Loader, OverrideMap
from .cli.parser import Parsed
diff --git a/src/tox/config/of_type.py b/src/tox/config/of_type.py
index 4d49d16ef3..899a19cdd6 100644
--- a/src/tox/config/of_type.py
+++ b/src/tox/config/of_type.py
@@ -4,12 +4,15 @@
from abc import ABC, abstractmethod
from itertools import product
-from typing import TYPE_CHECKING, Callable, Generic, Iterable, TypeVar, cast
+from typing import TYPE_CHECKING, Generic, TypeVar, cast
from tox.config.loader.api import ConfigLoadArgs, Loader
from tox.config.types import CircularChainError
if TYPE_CHECKING:
+ from collections.abc import Callable, Iterable
+ from types import UnionType
+
from tox.config.loader.convert import Factory
from tox.config.main import Config # pragma: no cover
@@ -74,7 +77,7 @@ def __init__( # noqa: PLR0913
self,
keys: Iterable[str],
desc: str,
- of_type: type[T],
+ of_type: type[T] | UnionType,
default: Callable[[Config, str | None], T] | T,
post_process: Callable[[T], T] | None = None,
factory: Factory[T] | None = None,
diff --git a/src/tox/config/set_env.py b/src/tox/config/set_env.py
index f74c0c1920..010efc6d87 100644
--- a/src/tox/config/set_env.py
+++ b/src/tox/config/set_env.py
@@ -1,8 +1,8 @@
from __future__ import annotations
+from collections.abc import Callable, Iterator, Mapping
from functools import reduce
from pathlib import Path
-from typing import Callable, Iterator, Mapping
from tox.config.loader.api import ConfigLoadArgs
from tox.tox_env.errors import Fail
diff --git a/src/tox/config/sets.py b/src/tox/config/sets.py
index 97e60b0e57..5d261c9e0b 100644
--- a/src/tox/config/sets.py
+++ b/src/tox/config/sets.py
@@ -3,13 +3,16 @@
import sys
from abc import ABC, abstractmethod
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator, Mapping, Sequence, TypeVar, cast
+from typing import TYPE_CHECKING, Any, TypeVar, cast, overload
from .of_type import ConfigConstantDefinition, ConfigDefinition, ConfigDynamicDefinition, ConfigLoadArgs
from .set_env import SetEnv
from .types import EnvList
if TYPE_CHECKING:
+ from collections.abc import Callable, Generator, Iterator, Mapping, Sequence
+ from types import UnionType
+
from tox.config.loader.api import Loader
from tox.config.main import Config
@@ -46,7 +49,8 @@ def register_config(self) -> None:
def mark_finalized(self) -> None:
self._final = True
- def add_config( # noqa: PLR0913
+ @overload
+ def add_config(
self,
keys: str | Sequence[str],
of_type: type[V],
@@ -54,6 +58,27 @@ def add_config( # noqa: PLR0913
desc: str,
post_process: Callable[[V], V] | None = None,
factory: Factory[Any] | None = None,
+ ) -> ConfigDynamicDefinition[V]: ...
+
+ @overload
+ def add_config(
+ self,
+ keys: str | Sequence[str],
+ of_type: UnionType,
+ default: Callable[[Config, str | None], V | None] | V | None,
+ desc: str,
+ post_process: Callable[[V | None], V | None] | None = None,
+ factory: Factory[Any] | None = None,
+ ) -> ConfigDynamicDefinition[V | None]: ...
+
+ def add_config( # noqa: PLR0913
+ self,
+ keys: str | Sequence[str],
+ of_type: type[V] | UnionType,
+ default: Callable[[Config, str | None], V] | V,
+ desc: str,
+ post_process: Callable[[V], V] | None = None,
+ factory: Factory[Any] | None = None,
) -> ConfigDynamicDefinition[V]:
"""
Add configuration value.
diff --git a/src/tox/config/source/api.py b/src/tox/config/source/api.py
index b8b703e625..484071082b 100644
--- a/src/tox/config/source/api.py
+++ b/src/tox/config/source/api.py
@@ -3,11 +3,12 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Iterator, List
+from typing import TYPE_CHECKING, Any
from tox.config.loader.section import Section
if TYPE_CHECKING:
+ from collections.abc import Iterator
from pathlib import Path
from tox.config.loader.api import Loader, OverrideMap
@@ -57,7 +58,7 @@ def get_loaders(
if base is not None:
conf.add_config(
keys="base",
- of_type=List[str],
+ of_type=list[str],
desc="inherit missing keys from these sections",
default=base,
)
diff --git a/src/tox/config/source/ini.py b/src/tox/config/source/ini.py
index bee5bc72cb..44dcf6262c 100644
--- a/src/tox/config/source/ini.py
+++ b/src/tox/config/source/ini.py
@@ -5,7 +5,7 @@
from collections import defaultdict
from configparser import ConfigParser
from itertools import chain
-from typing import TYPE_CHECKING, Iterable, Iterator
+from typing import TYPE_CHECKING
from tox.config.loader.ini import IniLoader
from tox.config.loader.ini.factor import find_envs
@@ -15,6 +15,7 @@
from .ini_section import CORE, PKG_ENV_PREFIX, TEST_ENV_PREFIX, IniSection
if TYPE_CHECKING:
+ from collections.abc import Iterable, Iterator
from pathlib import Path
from tox.config.loader.api import OverrideMap
diff --git a/src/tox/config/source/toml_pyproject.py b/src/tox/config/source/toml_pyproject.py
index b70b1404b3..2b90266986 100644
--- a/src/tox/config/source/toml_pyproject.py
+++ b/src/tox/config/source/toml_pyproject.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import sys
-from typing import TYPE_CHECKING, Any, Final, Iterator, Mapping, cast
+from collections.abc import Iterator, Mapping
+from typing import TYPE_CHECKING, Any, Final, cast
from tox.config.loader.section import Section
from tox.config.loader.toml import TomlLoader
diff --git a/src/tox/config/types.py b/src/tox/config/types.py
index 0e9070ad40..a32adb41f2 100644
--- a/src/tox/config/types.py
+++ b/src/tox/config/types.py
@@ -1,10 +1,13 @@
from __future__ import annotations
from collections import OrderedDict
-from typing import Iterator, Sequence
+from typing import TYPE_CHECKING
from tox.execute.request import shell_cmd
+if TYPE_CHECKING:
+ from collections.abc import Iterator, Sequence
+
class CircularChainError(ValueError):
"""circular chain in config"""
diff --git a/src/tox/execute/api.py b/src/tox/execute/api.py
index b236110b2b..37b079fd10 100644
--- a/src/tox/execute/api.py
+++ b/src/tox/execute/api.py
@@ -6,8 +6,9 @@
import sys
import time
from abc import ABC, abstractmethod
+from collections.abc import Callable, Iterator, Sequence
from contextlib import contextmanager
-from typing import TYPE_CHECKING, Any, Callable, Iterator, NoReturn, Sequence, cast
+from typing import TYPE_CHECKING, Any, NoReturn, cast
from colorama import Fore
diff --git a/src/tox/execute/local_sub_process/__init__.py b/src/tox/execute/local_sub_process/__init__.py
index e20e0210f2..e7ec71f23a 100644
--- a/src/tox/execute/local_sub_process/__init__.py
+++ b/src/tox/execute/local_sub_process/__init__.py
@@ -8,7 +8,7 @@
import shutil
import sys
from subprocess import DEVNULL, PIPE, TimeoutExpired
-from typing import TYPE_CHECKING, Any, Generator, Sequence
+from typing import TYPE_CHECKING, Any
from tox.execute.api import Execute, ExecuteInstance, ExecuteOptions, ExecuteStatus
from tox.execute.request import ExecuteRequest, StdinSource
@@ -17,6 +17,7 @@
if TYPE_CHECKING:
import io
+ from collections.abc import Generator, Sequence
from types import TracebackType
from tox.execute.stream import SyncWrite
diff --git a/src/tox/execute/local_sub_process/read_via_thread.py b/src/tox/execute/local_sub_process/read_via_thread.py
index d96d696eb5..7ddfd969e3 100644
--- a/src/tox/execute/local_sub_process/read_via_thread.py
+++ b/src/tox/execute/local_sub_process/read_via_thread.py
@@ -4,10 +4,11 @@
from abc import ABC, abstractmethod
from threading import Event, Thread
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
if TYPE_CHECKING:
import sys
+ from collections.abc import Callable
from types import TracebackType
if sys.version_info >= (3, 11): # pragma: no cover (py311+)
diff --git a/src/tox/execute/local_sub_process/read_via_thread_unix.py b/src/tox/execute/local_sub_process/read_via_thread_unix.py
index f4a22560c6..48e816f833 100644
--- a/src/tox/execute/local_sub_process/read_via_thread_unix.py
+++ b/src/tox/execute/local_sub_process/read_via_thread_unix.py
@@ -5,10 +5,13 @@
import errno # pragma: win32 no cover
import os # pragma: win32 no cover
import select # pragma: win32 no cover
-from typing import Callable
+from typing import TYPE_CHECKING
from .read_via_thread import ReadViaThread # pragma: win32 no cover
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
STOP_EVENT_CHECK_PERIODICITY_IN_MS = 0.01 # pragma: win32 no cover
diff --git a/src/tox/execute/local_sub_process/read_via_thread_windows.py b/src/tox/execute/local_sub_process/read_via_thread_windows.py
index 3e73ae41c1..cebc077a8c 100644
--- a/src/tox/execute/local_sub_process/read_via_thread_windows.py
+++ b/src/tox/execute/local_sub_process/read_via_thread_windows.py
@@ -6,10 +6,13 @@
import logging # pragma: win32 cover
from asyncio.windows_utils import BUFSIZE # type: ignore[attr-defined] # pragma: win32 cover
from time import sleep # pragma: win32 cover
-from typing import Callable # pragma: win32 cover
+from typing import TYPE_CHECKING
from .read_via_thread import ReadViaThread # pragma: win32 cover
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
# mypy: warn-unused-ignores=false
diff --git a/src/tox/execute/pep517_backend.py b/src/tox/execute/pep517_backend.py
index 566d66b353..0d82e63808 100644
--- a/src/tox/execute/pep517_backend.py
+++ b/src/tox/execute/pep517_backend.py
@@ -5,7 +5,7 @@
import time
from subprocess import TimeoutExpired
from threading import Lock
-from typing import TYPE_CHECKING, Sequence
+from typing import TYPE_CHECKING
from pyproject_api import BackendFailed
@@ -16,6 +16,7 @@
from tox.execute.stream import SyncWrite
if TYPE_CHECKING:
+ from collections.abc import Sequence
from pathlib import Path
from types import TracebackType
diff --git a/src/tox/execute/request.py b/src/tox/execute/request.py
index 8174440807..11b96cabf6 100644
--- a/src/tox/execute/request.py
+++ b/src/tox/execute/request.py
@@ -5,7 +5,10 @@
import sys
from enum import Enum
from pathlib import Path
-from typing import Sequence
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from collections.abc import Sequence
class StdinSource(Enum):
diff --git a/src/tox/execute/stream.py b/src/tox/execute/stream.py
index ce0125e7c8..928a48f4e6 100644
--- a/src/tox/execute/stream.py
+++ b/src/tox/execute/stream.py
@@ -2,12 +2,13 @@
from contextlib import contextmanager
from threading import Event, Lock, Timer
-from typing import IO, TYPE_CHECKING, Iterator
+from typing import IO, TYPE_CHECKING
from colorama import Fore
if TYPE_CHECKING:
import sys
+ from collections.abc import Iterator
from types import TracebackType
if sys.version_info >= (3, 11): # pragma: no cover (py311+)
diff --git a/src/tox/plugin/__init__.py b/src/tox/plugin/__init__.py
index c8bff4b03f..83b0c2eec8 100644
--- a/src/tox/plugin/__init__.py
+++ b/src/tox/plugin/__init__.py
@@ -31,7 +31,8 @@ def tox_add_option(parser: ToxParser) -> None:
from __future__ import annotations
-from typing import Any, Callable, TypeVar
+from collections.abc import Callable
+from typing import Any, TypeVar
import pluggy
diff --git a/src/tox/plugin/manager.py b/src/tox/plugin/manager.py
index df6dcfdd1a..323d5ae413 100644
--- a/src/tox/plugin/manager.py
+++ b/src/tox/plugin/manager.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import os
-from typing import TYPE_CHECKING, Any, Iterable
+from typing import TYPE_CHECKING, Any
import pluggy
@@ -19,6 +19,7 @@
from .inline import load_inline
if TYPE_CHECKING:
+ from collections.abc import Iterable
from pathlib import Path
from types import ModuleType
diff --git a/src/tox/plugin/spec.py b/src/tox/plugin/spec.py
index e69f206b5e..3570033924 100644
--- a/src/tox/plugin/spec.py
+++ b/src/tox/plugin/spec.py
@@ -1,12 +1,14 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Any, Iterable
+from typing import TYPE_CHECKING, Any
import pluggy
from . import NAME
if TYPE_CHECKING:
+ from collections.abc import Iterable
+
from tox.config.cli.parser import ToxParser
from tox.config.sets import ConfigSet, EnvConfigSet
from tox.execute import Outcome
@@ -46,7 +48,7 @@ def tox_extend_envs() -> Iterable[str]:
:class:`tox.config.loader.memory.MemoryLoader` early enough before tox
starts caching configuration values sourced elsewhere.
"""
- return () # <- Please MyPy
+ return ()
@_spec
diff --git a/src/tox/provision.py b/src/tox/provision.py
index 86fedf9122..7beb63eeb9 100644
--- a/src/tox/provision.py
+++ b/src/tox/provision.py
@@ -8,7 +8,7 @@
import sys
from importlib.metadata import PackageNotFoundError, distribution
from pathlib import Path
-from typing import TYPE_CHECKING, List, cast
+from typing import TYPE_CHECKING, cast
from packaging.requirements import Requirement
from packaging.utils import canonicalize_name
@@ -76,7 +76,7 @@ def add_tox_requires_min_version(reqs: list[Requirement]) -> list[Requirement]:
state.conf.core.add_config(
keys="requires",
- of_type=List[Requirement],
+ of_type=list[Requirement],
default=[],
desc="Name of the virtual environment used to provision a tox.",
post_process=add_tox_requires_min_version,
diff --git a/src/tox/pytest.py b/src/tox/pytest.py
index bfdc7e6d4c..31b3e6f027 100644
--- a/src/tox/pytest.py
+++ b/src/tox/pytest.py
@@ -13,7 +13,7 @@
from contextlib import closing, contextmanager
from pathlib import Path
from types import ModuleType, TracebackType
-from typing import TYPE_CHECKING, Any, Callable, Iterator, Protocol, Sequence, cast
+from typing import TYPE_CHECKING, Any, Protocol, cast
import pytest
from _pytest.fixtures import SubRequest # noqa: PLC2701
@@ -32,6 +32,7 @@
from tox.tox_env.api import ToxEnv
if TYPE_CHECKING:
+ from collections.abc import Callable, Iterator, Sequence
from unittest.mock import MagicMock
from pytest_mock import MockerFixture
@@ -285,8 +286,6 @@ def our_setup_state(value: Sequence[str]) -> State:
m.setenv("VIRTUALENV_SYMLINK_APP_DATA", "1")
m.setenv("VIRTUALENV_SYMLINKS", "1")
m.setenv("VIRTUALENV_PIP", "embed")
- if sys.version_info[:2] < (3, 9):
- m.setenv("VIRTUALENV_WHEEL", "embed")
m.setenv("VIRTUALENV_SETUPTOOLS", "embed")
try:
tox_run(args)
diff --git a/src/tox/report.py b/src/tox/report.py
index ef46bc702f..929f86bd9e 100644
--- a/src/tox/report.py
+++ b/src/tox/report.py
@@ -11,10 +11,13 @@
from pathlib import Path
from threading import Thread, current_thread, local
from threading import enumerate as enumerate_threads
-from typing import IO, ClassVar, Iterator, Tuple
+from typing import IO, TYPE_CHECKING, ClassVar
from colorama import Fore, Style, init
+if TYPE_CHECKING:
+ from collections.abc import Iterator
+
LEVELS = {
0: logging.CRITICAL,
1: logging.ERROR,
@@ -26,7 +29,7 @@
MAX_LEVEL = max(LEVELS.keys())
LOGGER = logging.getLogger()
-OutErr = Tuple[TextIOWrapper, TextIOWrapper]
+OutErr = tuple[TextIOWrapper, TextIOWrapper]
class _LogThreadLocal(local):
diff --git a/src/tox/run.py b/src/tox/run.py
index c4c857ed1c..5cfc9f8664 100644
--- a/src/tox/run.py
+++ b/src/tox/run.py
@@ -7,12 +7,15 @@
import os
import sys
import time
-from typing import Sequence
+from typing import TYPE_CHECKING
from tox.config.cli.parse import get_options
from tox.report import HandledError, ToxHandler
from tox.session.state import State
+if TYPE_CHECKING:
+ from collections.abc import Sequence
+
def run(args: Sequence[str] | None = None) -> None:
try:
diff --git a/src/tox/session/cmd/run/common.py b/src/tox/session/cmd/run/common.py
index 4655076339..29c9777346 100644
--- a/src/tox/session/cmd/run/common.py
+++ b/src/tox/session/cmd/run/common.py
@@ -10,7 +10,7 @@
from pathlib import Path
from signal import SIGINT, Handlers, signal
from threading import Event, Thread
-from typing import TYPE_CHECKING, Any, Iterator, Optional, Sequence, cast
+from typing import TYPE_CHECKING, Any, cast
from colorama import Fore
@@ -21,6 +21,8 @@
from tox.util.spinner import MISS_DURATION, Spinner
if TYPE_CHECKING:
+ from collections.abc import Iterator, Sequence
+
from tox.config.types import EnvList
from tox.session.state import State
from tox.tox_env.api import ToxEnv
@@ -91,7 +93,7 @@ def env_run_create_flags(parser: ArgumentParser, mode: str) -> None:
"--installpkg",
help="use specified package for installation into venv, instead of packaging the project",
default=None,
- of_type=Optional[Path],
+ of_type=Path | None,
action=InstallPackageAction,
dest="install_pkg",
)
diff --git a/src/tox/session/cmd/show_config.py b/src/tox/session/cmd/show_config.py
index 0a4f4e45a1..d4287fda91 100644
--- a/src/tox/session/cmd/show_config.py
+++ b/src/tox/session/cmd/show_config.py
@@ -4,7 +4,7 @@
import os
from textwrap import indent
-from typing import TYPE_CHECKING, Iterable
+from typing import TYPE_CHECKING
from colorama import Fore
@@ -14,6 +14,8 @@
from tox.session.env_select import CliEnv, register_env_select_flags
if TYPE_CHECKING:
+ from collections.abc import Iterable
+
from tox.config.cli.parser import ToxParser
from tox.config.sets import ConfigSet
from tox.session.state import State
diff --git a/src/tox/session/cmd/version_flag.py b/src/tox/session/cmd/version_flag.py
index 91c44e9303..5f968e1022 100644
--- a/src/tox/session/cmd/version_flag.py
+++ b/src/tox/session/cmd/version_flag.py
@@ -5,7 +5,7 @@
import sys
from argparse import SUPPRESS, Action, ArgumentParser, Namespace
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Sequence, cast
+from typing import TYPE_CHECKING, Any, cast
import tox
from tox.plugin import impl
@@ -13,6 +13,8 @@
from tox.version import version
if TYPE_CHECKING:
+ from collections.abc import Sequence
+
from tox.config.cli.parser import HelpFormatter, ToxParser
diff --git a/src/tox/session/env_select.py b/src/tox/session/env_select.py
index 6bbe58dc21..91c971524b 100644
--- a/src/tox/session/env_select.py
+++ b/src/tox/session/env_select.py
@@ -6,7 +6,7 @@
from dataclasses import dataclass
from difflib import get_close_matches
from itertools import chain
-from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, cast
+from typing import TYPE_CHECKING, cast
from tox.config.loader.str_convert import StrConvert
from tox.config.types import EnvList
@@ -19,6 +19,7 @@
if TYPE_CHECKING:
from argparse import ArgumentParser
+ from collections.abc import Iterable, Iterator
from tox.session.state import State
@@ -48,7 +49,7 @@ class CliEnv: # noqa: PLW1641
def __init__(self, value: list[str] | str | None = None) -> None:
if isinstance(value, str):
- value = StrConvert().to(value, of_type=List[str], factory=None)
+ value = StrConvert().to(value, of_type=list[str], factory=None)
self._names: list[str] | None = value
def __iter__(self) -> Iterator[str]:
@@ -157,7 +158,7 @@ def __init__(self, state: State) -> None:
self._journal = self._state._journal # noqa: SLF001
self._provision: tuple[bool, str] | None = None
- self._state.conf.core.add_config("labels", Dict[str, EnvList], {}, "core labels")
+ self._state.conf.core.add_config("labels", dict[str, EnvList], {}, "core labels")
tox_env_filter_regex = getattr(state.conf.options, "skip_env", "").strip()
self._filter_re = re.compile(tox_env_filter_regex) if tox_env_filter_regex else None
diff --git a/src/tox/session/state.py b/src/tox/session/state.py
index 3f59ecd73f..e60d8ef6a7 100644
--- a/src/tox/session/state.py
+++ b/src/tox/session/state.py
@@ -2,7 +2,7 @@
import sys
from itertools import chain, tee
-from typing import TYPE_CHECKING, Sequence
+from typing import TYPE_CHECKING
from tox.config.main import Config
from tox.journal import Journal
@@ -12,6 +12,8 @@
from .env_select import EnvSelector
if TYPE_CHECKING:
+ from collections.abc import Sequence
+
from tox.config.cli.parse import Options
from tox.config.cli.parser import ToxParser
diff --git a/src/tox/tox_env/api.py b/src/tox/tox_env/api.py
index d07177de35..c6bbb161cc 100644
--- a/src/tox/tox_env/api.py
+++ b/src/tox/tox_env/api.py
@@ -11,7 +11,7 @@
from abc import ABC, abstractmethod
from contextlib import contextmanager
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Iterator, List, NamedTuple, Sequence, Set, cast
+from typing import TYPE_CHECKING, Any, NamedTuple, cast
from tox.execute.request import ExecuteRequest
from tox.tox_env.errors import Fail, Recreate, Skip
@@ -19,6 +19,7 @@
from tox.util.path import ensure_empty_dir
if TYPE_CHECKING:
+ from collections.abc import Iterator, Sequence
from io import BytesIO
from tox.config.cli.parser import Parsed
@@ -129,7 +130,7 @@ def register_config(self) -> None:
)
self.conf.add_config(
keys=["labels"],
- of_type=Set[str],
+ of_type=set[str],
default=set(),
desc="labels attached to the tox environment",
)
@@ -176,7 +177,7 @@ def pass_env_post_process(values: list[str]) -> list[str]:
self.conf.add_config(
keys=["pass_env", "passenv"],
- of_type=List[str],
+ of_type=list[str],
default=[],
desc="environment variables to pass on to the tox environment",
post_process=pass_env_post_process,
@@ -195,7 +196,7 @@ def pass_env_post_process(values: list[str]) -> list[str]:
)
self.conf.add_config(
"allowlist_externals",
- of_type=List[str],
+ of_type=list[str],
default=[],
desc="external command glob to allow calling",
)
diff --git a/src/tox/tox_env/info.py b/src/tox/tox_env/info.py
index 83ee376acb..bd351ad749 100644
--- a/src/tox/tox_env/info.py
+++ b/src/tox/tox_env/info.py
@@ -7,9 +7,10 @@
import json
from contextlib import contextmanager
-from typing import TYPE_CHECKING, Any, Iterator
+from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
+ from collections.abc import Iterator
from pathlib import Path
diff --git a/src/tox/tox_env/package.py b/src/tox/tox_env/package.py
index a097b5a67d..272d8d1a6c 100644
--- a/src/tox/tox_env/package.py
+++ b/src/tox/tox_env/package.py
@@ -6,13 +6,15 @@
from pathlib import Path
from threading import RLock
from types import MethodType
-from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator, cast
+from typing import TYPE_CHECKING, Any, cast
from filelock import FileLock
from .api import ToxEnv, ToxEnvCreateArgs
if TYPE_CHECKING:
+ from collections.abc import Callable, Generator, Iterator
+
from tox.config.main import Config
from tox.config.sets import EnvConfigSet
diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py
index ae0375904c..8cf18364c4 100644
--- a/src/tox/tox_env/python/api.py
+++ b/src/tox/tox_env/python/api.py
@@ -9,7 +9,7 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path
-from typing import TYPE_CHECKING, Any, List, NamedTuple
+from typing import TYPE_CHECKING, Any, NamedTuple
from virtualenv.discovery.py_spec import PythonSpec
@@ -79,7 +79,7 @@ def validate_base_python(value: list[str]) -> list[str]:
self.conf.add_config(
keys=["base_python", "basepython"],
- of_type=List[str],
+ of_type=list[str],
default=self.default_base_python,
desc="environment identifier for python, first one found wins",
post_process=validate_base_python,
diff --git a/src/tox/tox_env/python/package.py b/src/tox/tox_env/python/package.py
index 262b14e8a7..a34004b2fd 100644
--- a/src/tox/tox_env/python/package.py
+++ b/src/tox/tox_env/python/package.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Generator, Iterator, List, Sequence, cast
+from typing import TYPE_CHECKING, cast
from packaging.requirements import Requirement
@@ -13,6 +13,7 @@
from .api import NoInterpreter, Python
if TYPE_CHECKING:
+ from collections.abc import Generator, Iterator, Sequence
from pathlib import Path
from tox.config.main import Config
@@ -69,7 +70,7 @@ def register_run_env(self, run_env: RunToxEnv) -> Generator[tuple[str, str], Pac
if run_env.conf["package"] != "skip" and "deps" not in self.conf:
self.conf.add_config(
keys="deps",
- of_type=List[Requirement],
+ of_type=list[Requirement],
default=[],
desc="Name of the python dependencies as specified by PEP-440",
)
diff --git a/src/tox/tox_env/python/pip/pip_install.py b/src/tox/tox_env/python/pip/pip_install.py
index f36d0b9c9e..2595ac4e2f 100644
--- a/src/tox/tox_env/python/pip/pip_install.py
+++ b/src/tox/tox_env/python/pip/pip_install.py
@@ -4,9 +4,10 @@
import operator
from abc import ABC, abstractmethod
from collections import defaultdict
+from collections.abc import Callable, Sequence
from functools import partial
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Sequence, cast
+from typing import TYPE_CHECKING, Any, cast
from packaging.requirements import Requirement
diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py
index e2c5b4aaeb..6f78ef0cd0 100644
--- a/src/tox/tox_env/python/pip/req/args.py
+++ b/src/tox/tox_env/python/pip/req/args.py
@@ -3,10 +3,13 @@
import bisect
import re
from argparse import Action, ArgumentParser, ArgumentTypeError, Namespace
-from typing import Any, NoReturn, Protocol, Sequence, TypeVar
+from typing import TYPE_CHECKING, Any, NoReturn, Protocol, TypeVar
from tox.tox_env.python.pip.req.util import handle_binary_option
+if TYPE_CHECKING:
+ from collections.abc import Sequence
+
_T_contra = TypeVar("_T_contra", contravariant=True)
@@ -22,8 +25,7 @@ def print_usage(self, file: _SupportsWrite[str] | None = None) -> None:
def exit(self, status: int = 0, message: str | None = None) -> NoReturn: # noqa: ARG002, PLR6301
message = "" if message is None else message
msg = message.lstrip(": ").rstrip()
- if msg.startswith("error: "):
- msg = msg[len("error: ") :]
+ msg = msg.removeprefix("error: ")
raise ValueError(msg)
diff --git a/src/tox/tox_env/python/pip/req/file.py b/src/tox/tox_env/python/pip/req/file.py
index 03357dede3..70ff528953 100644
--- a/src/tox/tox_env/python/pip/req/file.py
+++ b/src/tox/tox_env/python/pip/req/file.py
@@ -8,8 +8,9 @@
import sys
import urllib.parse
from argparse import ArgumentParser, Namespace
+from collections.abc import Iterator
from pathlib import Path
-from typing import IO, Any, Iterator, List, Tuple, cast
+from typing import IO, Any, cast
from urllib.request import urlopen
import chardet
@@ -26,7 +27,7 @@
# https://www.python.org/dev/peps/pep-0508/#extras
_EXTRA_PATH = re.compile(r"(.*)\[([-._,\sa-zA-Z0-9]*)]")
_EXTRA_ELEMENT = re.compile(r"[a-zA-Z0-9]*[-._a-zA-Z0-9]")
-ReqFileLines = Iterator[Tuple[int, str]]
+ReqFileLines = Iterator[tuple[int, str]]
DEFAULT_INDEX_URL = "https://pypi.org/simple"
@@ -168,7 +169,7 @@ def options(self) -> Namespace:
@property
def requirements(self) -> list[ParsedRequirement]:
self._ensure_requirements_parsed()
- return cast("List[ParsedRequirement]", self._requirements)
+ return cast("list[ParsedRequirement]", self._requirements)
@property
def _parser(self) -> ArgumentParser:
diff --git a/src/tox/tox_env/python/runner.py b/src/tox/tox_env/python/runner.py
index 5c012857d4..b1884cd8d1 100644
--- a/src/tox/tox_env/python/runner.py
+++ b/src/tox/tox_env/python/runner.py
@@ -4,7 +4,7 @@
from abc import ABC
from functools import partial
-from typing import TYPE_CHECKING, Set
+from typing import TYPE_CHECKING
from packaging.utils import canonicalize_name
@@ -42,7 +42,7 @@ def register_config(self) -> None:
)
self.conf.add_config(
keys=["dependency_groups"],
- of_type=Set[str],
+ of_type=set[str],
default=set(),
desc="dependency groups to install of the target package",
post_process=_normalize_extras,
@@ -144,7 +144,7 @@ def skip_missing_interpreters_post_process(value: bool) -> bool: # noqa: FBT001
def add_extras_to_env(conf: EnvConfigSet) -> None:
conf.add_config(
keys=["extras"],
- of_type=Set[str],
+ of_type=set[str],
default=set(),
desc="extras to install of the target package",
post_process=_normalize_extras,
diff --git a/src/tox/tox_env/python/virtual_env/package/cmd_builder.py b/src/tox/tox_env/python/virtual_env/package/cmd_builder.py
index cac1d39333..bd823188e7 100644
--- a/src/tox/tox_env/python/virtual_env/package/cmd_builder.py
+++ b/src/tox/tox_env/python/virtual_env/package/cmd_builder.py
@@ -7,7 +7,7 @@
from functools import partial
from io import TextIOWrapper
from pathlib import Path
-from typing import TYPE_CHECKING, Generator, Iterator, List, cast
+from typing import TYPE_CHECKING, cast
from zipfile import ZipFile
from packaging.requirements import Requirement
@@ -26,6 +26,7 @@
from .util import dependencies_with_extras
if TYPE_CHECKING:
+ from collections.abc import Generator, Iterator
from os import PathLike
from tox.config.sets import EnvConfigSet
@@ -54,7 +55,7 @@ def register_config(self) -> None:
)
self.conf.add_config(
keys=["commands"],
- of_type=List[Command],
+ of_type=list[Command],
default=[],
desc="the commands to be called for testing",
)
diff --git a/src/tox/tox_env/python/virtual_env/package/pyproject.py b/src/tox/tox_env/python/virtual_env/package/pyproject.py
index 37fb4625a2..805879c815 100644
--- a/src/tox/tox_env/python/virtual_env/package/pyproject.py
+++ b/src/tox/tox_env/python/virtual_env/package/pyproject.py
@@ -9,7 +9,7 @@
from itertools import chain
from pathlib import Path
from threading import RLock
-from typing import TYPE_CHECKING, Any, Dict, Generator, Iterator, Literal, NoReturn, Optional, Sequence, cast
+from typing import TYPE_CHECKING, Any, Literal, NoReturn, cast
from cachetools import cached
from packaging.requirements import Requirement
@@ -38,6 +38,8 @@
from .util import dependencies_with_extras, dependencies_with_extras_from_markers
if TYPE_CHECKING:
+ from collections.abc import Generator, Iterator, Sequence
+
from tox.config.sets import EnvConfigSet
from tox.execute.api import ExecuteStatus
from tox.tox_env.api import ToxEnvCreateArgs
@@ -52,7 +54,7 @@
else: # pragma: no cover (py311+)
import tomli as tomllib
-ConfigSettings = Optional[Dict[str, Any]]
+ConfigSettings = dict[str, Any] | None
class ToxBackendFailed(Fail, BackendFailed):
@@ -170,7 +172,7 @@ def _add_config_settings(self, build_type: str) -> None:
for key in keys.get(build_type, []):
self.conf.add_config(
keys=[f"config_settings_{key}"],
- of_type=Dict[str, str],
+ of_type=dict[str, str],
default=None, # type: ignore[arg-type]
desc=f"config settings passed to the {key} backend API endpoint",
)
diff --git a/src/tox/tox_env/python/virtual_env/package/util.py b/src/tox/tox_env/python/virtual_env/package/util.py
index 4d262a7f54..21f0ebf337 100644
--- a/src/tox/tox_env/python/virtual_env/package/util.py
+++ b/src/tox/tox_env/python/virtual_env/package/util.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from copy import deepcopy
-from typing import TYPE_CHECKING, Optional, Set, cast
+from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from packaging._parser import Op, Variable
@@ -70,7 +70,7 @@ def _extract_extra_markers(req: Requirement) -> tuple[Requirement, set[str | Non
cast("Marker", req.marker)._markers = new_markers # noqa: SLF001
else:
req.marker = None
- return req, cast("Set[Optional[str]]", extra_markers) or {None}
+ return req, cast("set[str | None]", extra_markers) or {None}
def _get_extra(_marker: str | tuple[Variable, Op, Variable]) -> str | None:
diff --git a/src/tox/tox_env/register.py b/src/tox/tox_env/register.py
index b421c0e7c0..6b26c90226 100644
--- a/src/tox/tox_env/register.py
+++ b/src/tox/tox_env/register.py
@@ -2,9 +2,11 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Iterable
+from typing import TYPE_CHECKING
if TYPE_CHECKING:
+ from collections.abc import Iterable
+
from tox.plugin.manager import Plugin
from .package import PackageToxEnv
diff --git a/src/tox/tox_env/runner.py b/src/tox/tox_env/runner.py
index 9d9b61bb34..a2ce0d026f 100644
--- a/src/tox/tox_env/runner.py
+++ b/src/tox/tox_env/runner.py
@@ -5,7 +5,7 @@
import re
from abc import ABC, abstractmethod
from hashlib import sha256
-from typing import TYPE_CHECKING, Any, Iterable, List
+from typing import TYPE_CHECKING, Any
from tox.config.types import Command, EnvList
@@ -14,6 +14,8 @@
from .util import add_change_dir_conf
if TYPE_CHECKING:
+ from collections.abc import Iterable
+
from tox.journal import EnvJournal
@@ -44,19 +46,19 @@ def ensure_one_line(value: str) -> str:
super().register_config()
self.conf.add_config(
keys=["commands_pre"],
- of_type=List[Command],
+ of_type=list[Command],
default=[],
desc="the commands to be called before testing",
)
self.conf.add_config(
keys=["commands"],
- of_type=List[Command],
+ of_type=list[Command],
default=[],
desc="the commands to be called for testing",
)
self.conf.add_config(
keys=["commands_post"],
- of_type=List[Command],
+ of_type=list[Command],
default=[],
desc="the commands to be called after testing",
)
diff --git a/src/tox/util/spinner.py b/src/tox/util/spinner.py
index a6d2b905b9..faf5a92e16 100644
--- a/src/tox/util/spinner.py
+++ b/src/tox/util/spinner.py
@@ -8,7 +8,7 @@
import threading
import time
from collections import OrderedDict
-from typing import IO, TYPE_CHECKING, NamedTuple, Sequence, TypeVar
+from typing import IO, TYPE_CHECKING, NamedTuple, TypeVar
from colorama import Fore
@@ -18,6 +18,7 @@
from typing_extensions import Self
if TYPE_CHECKING:
+ from collections.abc import Sequence
from types import TracebackType
from typing import Any, ClassVar
diff --git a/tests/config/cli/conftest.py b/tests/config/cli/conftest.py
index 69da088438..fa5eb9984b 100644
--- a/tests/config/cli/conftest.py
+++ b/tests/config/cli/conftest.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
@@ -16,6 +16,8 @@
from tox.session.cmd.show_config import show_config
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from tox.session.state import State
diff --git a/tests/config/cli/test_cli_env_var.py b/tests/config/cli/test_cli_env_var.py
index 442fa65e5f..bf64c41032 100644
--- a/tests/config/cli/test_cli_env_var.py
+++ b/tests/config/cli/test_cli_env_var.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
from unittest.mock import ANY
import pytest
@@ -11,6 +11,8 @@
from tox.util.ci import is_ci
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from tox.pytest import CaptureFixture, LogCaptureFixture, MonkeyPatch
from tox.session.state import State
diff --git a/tests/config/cli/test_cli_ini.py b/tests/config/cli/test_cli_ini.py
index c78957dbea..48d2275ec5 100644
--- a/tests/config/cli/test_cli_ini.py
+++ b/tests/config/cli/test_cli_ini.py
@@ -4,7 +4,7 @@
import sys
import textwrap
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable
+from typing import TYPE_CHECKING, Any
from unittest.mock import ANY
import pytest
@@ -19,6 +19,8 @@
from tox.util.ci import is_ci
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from pytest_mock import MockerFixture
from tox.pytest import CaptureFixture, LogCaptureFixture, MonkeyPatch
diff --git a/tests/config/loader/ini/conftest.py b/tests/config/loader/ini/conftest.py
index 7e056ac464..14993633d3 100644
--- a/tests/config/loader/ini/conftest.py
+++ b/tests/config/loader/ini/conftest.py
@@ -1,11 +1,12 @@
from __future__ import annotations
from configparser import ConfigParser
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
diff --git a/tests/config/loader/ini/replace/test_replace_env_var.py b/tests/config/loader/ini/replace/test_replace_env_var.py
index a0911ad648..d6bcdc9a2b 100644
--- a/tests/config/loader/ini/replace/test_replace_env_var.py
+++ b/tests/config/loader/ini/replace/test_replace_env_var.py
@@ -1,11 +1,13 @@
from __future__ import annotations
import threading
-from typing import TYPE_CHECKING, Generator
+from typing import TYPE_CHECKING
import pytest
if TYPE_CHECKING:
+ from collections.abc import Generator
+
from tests.config.loader.conftest import ReplaceOne
from tox.pytest import LogCaptureFixture, MonkeyPatch
diff --git a/tests/config/loader/ini/replace/test_replace_tox_env.py b/tests/config/loader/ini/replace/test_replace_tox_env.py
index 663a61251a..53bfe4a131 100644
--- a/tests/config/loader/ini/replace/test_replace_tox_env.py
+++ b/tests/config/loader/ini/replace/test_replace_tox_env.py
@@ -1,7 +1,8 @@
from __future__ import annotations
+from collections.abc import Callable
from pathlib import Path
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
diff --git a/tests/config/loader/ini/test_factor.py b/tests/config/loader/ini/test_factor.py
index 5dbc50b576..049658a822 100644
--- a/tests/config/loader/ini/test_factor.py
+++ b/tests/config/loader/ini/test_factor.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from textwrap import dedent
-from typing import TYPE_CHECKING, Callable, List
+from typing import TYPE_CHECKING
import pytest
@@ -10,6 +10,7 @@
from tox.config.source.ini_section import IniSection
if TYPE_CHECKING:
+ from collections.abc import Callable
from configparser import ConfigParser
from tests.conftest import ToxIniCreator
@@ -140,7 +141,7 @@ def test_factor_config(tox_ini_conf: ToxIniCreator) -> None:
assert list(config) == ["py36-django15", "py36-django16", "py37-django15", "py37-django16"]
for env in config.core["env_list"]:
env_config = config.get_env(env)
- env_config.add_config(keys="deps-x", of_type=List[str], default=[], desc="deps")
+ env_config.add_config(keys="deps-x", of_type=list[str], default=[], desc="deps")
deps = env_config["deps-x"]
assert "pytest" in deps
if "py36" in env:
@@ -296,12 +297,12 @@ def test_generative_section_name(tox_ini_conf: ToxIniCreator) -> None:
assert list(config) == ["py311-black", "py311-lint", "py310-black", "py310-lint"]
env_config = config.get_env("py311-black")
- env_config.add_config(keys="deps-x", of_type=List[str], default=[], desc="deps")
+ env_config.add_config(keys="deps-x", of_type=list[str], default=[], desc="deps")
deps = env_config["deps-x"]
assert deps == ["black"]
env_config = config.get_env("py311-lint")
- env_config.add_config(keys="deps-x", of_type=List[str], default=[], desc="deps")
+ env_config.add_config(keys="deps-x", of_type=list[str], default=[], desc="deps")
deps = env_config["deps-x"]
assert deps == ["flake8"]
diff --git a/tests/config/loader/ini/test_ini_loader.py b/tests/config/loader/ini/test_ini_loader.py
index f05f850417..78e9de9913 100644
--- a/tests/config/loader/ini/test_ini_loader.py
+++ b/tests/config/loader/ini/test_ini_loader.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
@@ -9,6 +9,7 @@
from tox.config.source.ini_section import IniSection
if TYPE_CHECKING:
+ from collections.abc import Callable
from configparser import ConfigParser
diff --git a/tests/config/loader/test_memory_loader.py b/tests/config/loader/test_memory_loader.py
index 508566df17..fdb8d9aa16 100644
--- a/tests/config/loader/test_memory_loader.py
+++ b/tests/config/loader/test_memory_loader.py
@@ -2,7 +2,7 @@
import os
from pathlib import Path
-from typing import Any, Dict, List, Optional, Set
+from typing import Any, Dict, List, Optional, Set # noqa: UP035
import pytest
@@ -27,28 +27,34 @@ def test_memory_loader_override() -> None:
@pytest.mark.parametrize(
("value", "of_type", "outcome"),
[
- (True, bool, True),
- (1, int, 1),
- ("magic", str, "magic"),
- ({"1"}, Set[str], {"1"}),
- ([1], List[int], [1]),
- ({1: 2}, Dict[int, int], {1: 2}),
- (Path.cwd(), Path, Path.cwd()),
- (Command(["a"]), Command, Command(["a"])),
- (EnvList("a,b"), EnvList, EnvList("a,b")),
- (1, Optional[int], 1),
- ("1", Optional[str], "1"),
- (0, bool, False),
- (1, bool, True),
- ("1", int, 1),
- (1, str, "1"),
- ({1}, Set[str], {"1"}),
- ({"1"}, List[int], [1]),
- ({"1": "2"}, Dict[int, int], {1: 2}),
- (os.getcwd(), Path, Path.cwd()), # noqa: PTH109
- ("pip list", Command, Command(["pip", "list"])),
- ("a\nb", EnvList, EnvList(["a", "b"])),
- ("1", Optional[int], 1),
+ pytest.param(True, bool, True, id="bool_true"),
+ pytest.param(1, int, 1, id="int_1"),
+ pytest.param("magic", str, "magic", id="str_magic"),
+ pytest.param({"1"}, set[str], {"1"}, id="set_str_1"),
+ pytest.param({"1"}, Set[str], {"1"}, id="set_typing_str_1"), # noqa: UP006
+ pytest.param([1], list[int], [1], id="list_int_1"),
+ pytest.param([1], List[int], [1], id="list_typing_int_1"), # noqa: UP006
+ pytest.param({1: 2}, dict[int, int], {1: 2}, id="dict_int_int_1_2"),
+ pytest.param({1: 2}, Dict[int, int], {1: 2}, id="dict_int_int_1_2"), # noqa: UP006
+ pytest.param(Path.cwd(), Path, Path.cwd(), id="path_cwd"),
+ pytest.param(Command(["a"]), Command, Command(["a"]), id="command_a"),
+ pytest.param(EnvList("a,b"), EnvList, EnvList("a,b"), id="envlist_a_b"),
+ pytest.param(1, Optional[int], 1, id="optional_int_1"), # noqa: UP045
+ pytest.param("1", Optional[str], "1", id="optional_str_1"), # noqa: UP045
+ pytest.param(1, int | None, 1, id="int_or_none_1"),
+ pytest.param("1", str | None, "1", id="str_or_none_1"),
+ pytest.param(0, bool, False, id="bool_false_from_0"),
+ pytest.param(1, bool, True, id="bool_true_from_1"),
+ pytest.param("1", int, 1, id="int_from_str_1"),
+ pytest.param(1, str, "1", id="str_from_int_1"),
+ pytest.param({1}, set[str], {"1"}, id="set_str_from_int_1"),
+ pytest.param({"1"}, list[int], [1], id="list_int_from_str_1"),
+ pytest.param({"1": "2"}, dict[int, int], {1: 2}, id="dict_int_int_from_str_1_2"),
+ pytest.param(os.getcwd(), Path, Path.cwd(), id="path_from_getcwd"), # noqa: PTH109
+ pytest.param("pip list", Command, Command(["pip", "list"]), id="command_pip_list"),
+ pytest.param("a\nb", EnvList, EnvList(["a", "b"]), id="envlist_a_b_newline"),
+ pytest.param("1", Optional[int], 1, id="optional_int_from_str_1"), # noqa: UP045
+ pytest.param("1", int | None, 1, id="int_or_none_from_str_1"),
],
)
def test_memory_loader(value: Any, of_type: type[Any], outcome: Any) -> None:
@@ -62,10 +68,10 @@ def test_memory_loader(value: Any, of_type: type[Any], outcome: Any) -> None:
("value", "of_type", "exception", "msg"),
[
("m", int, ValueError, "invalid literal for int"),
- ({"m"}, Set[int], ValueError, "invalid literal for int"),
- (["m"], List[int], ValueError, "invalid literal for int"),
- ({"m": 1}, Dict[int, int], ValueError, "invalid literal for int"),
- ({1: "m"}, Dict[int, int], ValueError, "invalid literal for int"),
+ ({"m"}, set[int], ValueError, "invalid literal for int"),
+ (["m"], list[int], ValueError, "invalid literal for int"),
+ ({"m": 1}, dict[int, int], ValueError, "invalid literal for int"),
+ ({1: "m"}, dict[int, int], ValueError, "invalid literal for int"),
(object, Path, TypeError, r"str(, bytes)? or (an )?os\.PathLike object"),
(1, Command, TypeError, "1"),
(1, EnvList, TypeError, "1"),
diff --git a/tests/config/loader/test_str_convert.py b/tests/config/loader/test_str_convert.py
index 7c24c1fad2..fc8f23992d 100644
--- a/tests/config/loader/test_str_convert.py
+++ b/tests/config/loader/test_str_convert.py
@@ -5,7 +5,7 @@
from pathlib import Path
from textwrap import dedent
from types import ModuleType
-from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, TypeVar, Union
+from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
import pytest
@@ -21,37 +21,43 @@
@pytest.mark.parametrize(
("raw", "value", "of_type"),
[
- ("true", True, bool),
- ("false", False, bool),
- ("True", True, bool),
- ("False", False, bool),
- ("TruE", True, bool),
- ("FalsE", False, bool),
- ("1", True, bool),
- ("0", False, bool),
- ("1", 1, int),
- ("0", 0, int),
- ("+1", 1, int),
- ("-1", -1, int),
- ("1.1", 1.1, float),
- ("0.1", 0.1, float),
- ("+1.1", 1.1, float),
- ("-1.1", -1.1, float),
- ("magic", "magic", str),
- ("1", {"1"}, Set[str]),
- ("1", [1], List[int]),
- ("1=2", {1: 2}, Dict[int, int]),
- ("a=1\n\nc=2", {"a": 1, "c": 2}, Dict[str, int]),
- ("a", Path("a"), Path),
- ("a", Command(["a"]), Command),
- ("a,b", EnvList(["a", "b"]), EnvList),
- ("", None, Optional[int]),
- ("1", 1, Optional[int]),
- ("", None, Optional[str]),
- ("1", "1", Optional[str]),
- ("", None, Optional[List[str]]),
- ("1,2", ["1", "2"], Optional[List[str]]),
- ("1", "1", Literal["1", "2"]),
+ pytest.param("true", True, bool, id="str_true_to_bool"),
+ pytest.param("false", False, bool, id="str_false_to_bool"),
+ pytest.param("True", True, bool, id="str_True_to_bool"),
+ pytest.param("False", False, bool, id="str_False_to_bool"),
+ pytest.param("TruE", True, bool, id="str_TruE_to_bool"),
+ pytest.param("FalsE", False, bool, id="str_FalsE_to_bool"),
+ pytest.param("1", True, bool, id="str_1_to_bool"),
+ pytest.param("0", False, bool, id="str_0_to_bool"),
+ pytest.param("1", 1, int, id="str_1_to_int"),
+ pytest.param("0", 0, int, id="str_0_to_int"),
+ pytest.param("+1", 1, int, id="str_plus1_to_int"),
+ pytest.param("-1", -1, int, id="str_minus1_to_int"),
+ pytest.param("1.1", 1.1, float, id="str_1.1_to_float"),
+ pytest.param("0.1", 0.1, float, id="str_0.1_to_float"),
+ pytest.param("+1.1", 1.1, float, id="str_plus1.1_to_float"),
+ pytest.param("-1.1", -1.1, float, id="str_minus1.1_to_float"),
+ pytest.param("magic", "magic", str, id="str_magic_to_str"),
+ pytest.param("1", {"1"}, set[str], id="str_1_to_set_str"),
+ pytest.param("1", [1], list[int], id="str_1_to_list_int"),
+ pytest.param("1=2", {1: 2}, dict[int, int], id="str_1eq2_to_dict_int_int"),
+ pytest.param("a=1\n\nc=2", {"a": 1, "c": 2}, dict[str, int], id="str_multiline_dict_str_int"),
+ pytest.param("a", Path("a"), Path, id="str_a_to_path"),
+ pytest.param("a", Command(["a"]), Command, id="str_a_to_command"),
+ pytest.param("a,b", EnvList(["a", "b"]), EnvList, id="str_a_b_to_envlist"),
+ pytest.param("", None, Optional[int], id="empty_to_optional_int"), # noqa: UP045
+ pytest.param("1", 1, Optional[int], id="str_1_to_optional_int"), # noqa: UP045
+ pytest.param("", None, Optional[str], id="empty_to_optional_str"), # noqa: UP045
+ pytest.param("1", "1", Optional[str], id="str_1_to_optional_str"), # noqa: UP045
+ pytest.param("", None, Optional[list[str]], id="empty_to_optional_list_str"), # noqa: UP045
+ pytest.param("1,2", ["1", "2"], Optional[list[str]], id="str_1_2_to_optional_list_str"), # noqa: UP045
+ pytest.param("", None, int | None, id="empty_to_int_or_none"),
+ pytest.param("1", 1, int | None, id="str_1_to_int_or_none"),
+ pytest.param("", None, str | None, id="empty_to_str_or_none"),
+ pytest.param("1", "1", str | None, id="str_1_to_str_or_none"),
+ pytest.param("", None, list[str] | None, id="empty_to_list_str_or_none"),
+ pytest.param("1,2", ["1", "2"], list[str] | None, id="str_1_2_to_list_str_or_none"),
+ pytest.param("1", "1", Literal["1", "2"], id="str_1_to_literal_1_2"),
],
)
def test_str_convert_ok(raw: str, value: Any, of_type: type[Any]) -> None:
@@ -59,28 +65,26 @@ def test_str_convert_ok(raw: str, value: Any, of_type: type[Any]) -> None:
assert result == value
-# Tests that can work only with py39 or newer due to type not being subscriptible before
-if sys.version_info >= (3, 9):
-
- @pytest.mark.parametrize(
- ("raw", "value", "of_type"),
- [
- ("", None, Optional[list[str]]),
- ("1,2", ["1", "2"], Optional[list[str]]),
- ],
- )
- def test_str_convert_ok_py39(raw: str, value: Any, of_type: type[Any]) -> None:
- result = StrConvert().to(raw, of_type, None)
- assert result == value
-
-
@pytest.mark.parametrize(
("raw", "of_type", "exc_type", "msg"),
[
- ("a", TypeVar, TypeError, r"a cannot cast to .*typing.TypeVar.*"),
- ("3", Literal["1", "2"], ValueError, r"3 must be one of \('1', '2'\)"),
- ("3", Union[str, int], TypeError, r"3 cannot cast to (typing.Union\[str, int\]|str \| int)"),
- ("", Command, ValueError, r"attempting to parse '' into a command failed"),
+ pytest.param("a", TypeVar, TypeError, r"a cannot cast to .*typing.TypeVar.*", id="fail_typevar"),
+ pytest.param("3", Literal["1", "2"], ValueError, r"3 must be one of \('1', '2'\)", id="fail_literal_1_2"),
+ pytest.param(
+ "3",
+ Union[str, int], # noqa: UP007
+ TypeError,
+ r"3 cannot cast to (typing.Union\[str, int\]|str \| int)",
+ id="fail_union_str_int",
+ ),
+ pytest.param(
+ "3",
+ str | int,
+ TypeError,
+ r"3 cannot cast to (typing.Union\[str, int\]|str \| int)",
+ id="fail_union_bar_str_int",
+ ),
+ pytest.param("", Command, ValueError, r"attempting to parse '' into a command failed", id="fail_empty_command"),
],
)
def test_str_convert_nok(raw: str, of_type: type[Any], msg: str, exc_type: type[Exception]) -> None:
diff --git a/tests/config/loader/test_toml_loader.py b/tests/config/loader/test_toml_loader.py
index 3a3b835459..655060f549 100644
--- a/tests/config/loader/test_toml_loader.py
+++ b/tests/config/loader/test_toml_loader.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from pathlib import Path
-from typing import Any, Dict, List, Literal, Optional, TypeVar
+from typing import Any, Literal, TypeVar
import pytest
@@ -59,36 +59,36 @@ def test_toml_loader_bool_nok() -> None:
def test_toml_loader_list_ok() -> None:
- assert perform_load(["a"], List[str]) == ["a"]
+ assert perform_load(["a"], list[str]) == ["a"]
def test_toml_loader_list_nok() -> None:
with pytest.raises(TypeError, match=r"{} is not list"):
- perform_load({}, List[str])
+ perform_load({}, list[str])
def test_toml_loader_list_nok_element() -> None:
with pytest.raises(TypeError, match="2 is not of type 'str'"):
- perform_load(["a", 2], List[str])
+ perform_load(["a", 2], list[str])
def test_toml_loader_dict_ok() -> None:
- assert perform_load({"a": "1"}, Dict[str, str]) == {"a": "1"}
+ assert perform_load({"a": "1"}, dict[str, str]) == {"a": "1"}
def test_toml_loader_dict_nok() -> None:
with pytest.raises(TypeError, match=r"{'a'} is not dictionary"):
- perform_load({"a"}, Dict[str, str])
+ perform_load({"a"}, dict[str, str])
def test_toml_loader_dict_nok_key() -> None:
with pytest.raises(TypeError, match="1 is not of type 'str'"):
- perform_load({"a": 1, 1: "2"}, Dict[str, int])
+ perform_load({"a": 1, 1: "2"}, dict[str, int])
def test_toml_loader_dict_nok_value() -> None:
with pytest.raises(TypeError, match="'2' is not of type 'int'"):
- perform_load({"a": 1, "b": "2"}, Dict[str, int])
+ perform_load({"a": 1, "b": "2"}, dict[str, int])
def test_toml_loader_path_ok() -> None:
@@ -101,7 +101,7 @@ def test_toml_loader_path_nok() -> None:
def test_toml_loader_command_ok() -> None:
- commands = perform_load([["a", "b"], ["c"]], List[Command])
+ commands = perform_load([["a", "b"], ["c"]], list[Command])
assert isinstance(commands, list)
assert len(commands) == 2
assert all(isinstance(i, Command) for i in commands)
@@ -112,7 +112,7 @@ def test_toml_loader_command_ok() -> None:
def test_toml_loader_command_nok() -> None:
with pytest.raises(TypeError, match="1 is not of type 'str'"):
- perform_load([["a", 1]], List[Command])
+ perform_load([["a", 1]], list[Command])
def test_toml_loader_env_list_ok() -> None:
@@ -127,18 +127,18 @@ def test_toml_loader_env_list_nok() -> None:
def test_toml_loader_list_optional_ok() -> None:
- assert perform_load(["a", None], List[Optional[str]]) == ["a", None]
+ assert perform_load(["a", None], list[str | None]) == ["a", None]
def test_toml_loader_list_optional_nok() -> None:
with pytest.raises(TypeError, match="1 is not union of str, NoneType"):
- perform_load(["a", None, 1], List[Optional[str]])
+ perform_load(["a", None, 1], list[str | None])
def test_toml_loader_list_literal_ok() -> None:
- assert perform_load(["a", "b"], List[Literal["a", "b"]]) == ["a", "b"]
+ assert perform_load(["a", "b"], list[Literal["a", "b"]]) == ["a", "b"]
def test_toml_loader_list_literal_nok() -> None:
with pytest.raises(TypeError, match="'c' is not one of literal 'a','b'"):
- perform_load(["a", "c"], List[Literal["a", "b"]])
+ perform_load(["a", "c"], list[Literal["a", "b"]])
diff --git a/tests/config/test_main.py b/tests/config/test_main.py
index 853ef9bb44..42d4e7d9fc 100644
--- a/tests/config/test_main.py
+++ b/tests/config/test_main.py
@@ -3,7 +3,7 @@
import os
from functools import partial
from pathlib import Path
-from typing import TYPE_CHECKING, List
+from typing import TYPE_CHECKING
import pytest
@@ -74,7 +74,7 @@ def test_config_override_appends_to_list(tox_ini_conf: ToxIniCreator) -> None:
passenv = foo
"""
conf = tox_ini_conf(example, override=[Override("testenv.passenv+=bar")]).get_env("testenv")
- conf.add_config("passenv", of_type=List[str], default=[], desc="desc")
+ conf.add_config("passenv", of_type=list[str], default=[], desc="desc")
assert conf["passenv"] == ["foo", "bar"]
@@ -85,13 +85,13 @@ def test_config_override_sequence(tox_ini_conf: ToxIniCreator) -> None:
"""
overrides = [Override("testenv.passenv=bar"), Override("testenv.passenv+=baz")]
conf = tox_ini_conf(example, override=overrides).get_env("testenv")
- conf.add_config("passenv", of_type=List[str], default=[], desc="desc")
+ conf.add_config("passenv", of_type=list[str], default=[], desc="desc")
assert conf["passenv"] == ["bar", "baz"]
def test_config_override_appends_to_empty_list(tox_ini_conf: ToxIniCreator) -> None:
conf = tox_ini_conf("[testenv]", override=[Override("testenv.passenv+=bar")]).get_env("testenv")
- conf.add_config("passenv", of_type=List[str], default=[], desc="desc")
+ conf.add_config("passenv", of_type=list[str], default=[], desc="desc")
assert conf["passenv"] == ["bar"]
diff --git a/tests/config/test_sets.py b/tests/config/test_sets.py
index 91aec92c15..b7b4cac8e2 100644
--- a/tests/config/test_sets.py
+++ b/tests/config/test_sets.py
@@ -2,8 +2,9 @@
import re
from collections import OrderedDict
+from collections.abc import Callable
from pathlib import Path
-from typing import TYPE_CHECKING, Callable, Dict, Optional, Set, TypeVar
+from typing import TYPE_CHECKING, TypeVar
import pytest
@@ -46,7 +47,7 @@ def test_config_path(conf_builder: ConfBuilder) -> None:
def test_config_set(conf_builder: ConfBuilder) -> None:
config_set = conf_builder("set = 1\n 2\n 3")
- config_set.add_config(keys="set", of_type=Set[int], default=set(), desc="set")
+ config_set.add_config(keys="set", of_type=set[int], default=set(), desc="set")
set_materialize = config_set["set"]
assert set_materialize == {1, 2, 3}
@@ -55,7 +56,7 @@ def test_config_optional_none(conf_builder: ConfBuilder) -> None:
config_set = conf_builder("")
config_set.add_config(
keys="optional_none",
- of_type=Optional[int], # type: ignore[arg-type]
+ of_type=int | None,
default=None,
desc="optional_none",
)
@@ -65,7 +66,7 @@ def test_config_optional_none(conf_builder: ConfBuilder) -> None:
def test_config_dict(conf_builder: ConfBuilder) -> None:
config_set = conf_builder("dict = a=1\n b=2\n c=3")
- config_set.add_config(keys="dict", of_type=Dict[str, int], default={}, desc="dict")
+ config_set.add_config(keys="dict", of_type=dict[str, int], default={}, desc="dict")
dict_val = config_set["dict"]
assert dict_val == OrderedDict([("a", 1), ("b", 2), ("c", 3)])
@@ -82,7 +83,7 @@ def test_config_bad_type(conf_builder: ConfBuilder) -> None:
def test_config_bad_dict(conf_builder: ConfBuilder) -> None:
config_set = conf_builder("bad_dict = something")
- config_set.add_config(keys="bad_dict", of_type=Dict[str, str], default={}, desc="bad_dict")
+ config_set.add_config(keys="bad_dict", of_type=dict[str, str], default={}, desc="bad_dict")
with pytest.raises(TypeError) as context:
assert config_set["bad_dict"]
assert str(context.value) == "dictionary lines must be of form key=value, found 'something'"
diff --git a/tests/conftest.py b/tests/conftest.py
index 4732f5ac32..2eabaabd78 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,7 +4,7 @@
import sys
import sysconfig
from pathlib import Path
-from typing import TYPE_CHECKING, Callable, Iterator, Protocol, Sequence
+from typing import TYPE_CHECKING, Protocol
from unittest.mock import patch
from uuid import uuid4
@@ -20,6 +20,8 @@
from tox.tox_env.python.virtual_env.api import VirtualEnv
if TYPE_CHECKING:
+ from collections.abc import Callable, Iterator, Sequence
+
from build import DistributionType
from pytest_mock import MockerFixture
diff --git a/tests/execute/local_subprocess/test_local_subprocess.py b/tests/execute/local_subprocess/test_local_subprocess.py
index 9f782ece83..e8097b00c3 100644
--- a/tests/execute/local_subprocess/test_local_subprocess.py
+++ b/tests/execute/local_subprocess/test_local_subprocess.py
@@ -165,9 +165,13 @@ def test_local_execute_terminal_size(os_env: dict[str, str], monkeypatch: Monkey
main, child = pty.openpty() # type: ignore[attr-defined, unused-ignore]
# Use ReadViaThreadUnix to help with debugging the test itself.
pipe_out = ReadViaThreadUnix(main, sys.stdout.buffer.write, name="testout", drain=True) # type: ignore[arg-type]
- with pipe_out, monkeypatch.context() as monkey, open( # noqa: PTH123
- child, "w", encoding=locale.getpreferredencoding(False)
- ) as stdout_mock:
+ with (
+ pipe_out,
+ monkeypatch.context() as monkey,
+ open( # noqa: PTH123
+ child, "w", encoding=locale.getpreferredencoding(False)
+ ) as stdout_mock,
+ ):
# Switch stdout with test pty
monkey.setattr(sys, "stdout", stdout_mock)
monkey.setenv("COLUMNS", "84")
diff --git a/tests/plugin/conftest.py b/tests/plugin/conftest.py
index e9aeac36c6..78de7a518e 100644
--- a/tests/plugin/conftest.py
+++ b/tests/plugin/conftest.py
@@ -1,7 +1,10 @@
from __future__ import annotations
from pathlib import Path
-from typing import Any, Sequence
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+ from collections.abc import Sequence
HERE = Path(__file__).parent
diff --git a/tests/plugin/test_plugin.py b/tests/plugin/test_plugin.py
index c3c1f051cd..2ccc25e33d 100644
--- a/tests/plugin/test_plugin.py
+++ b/tests/plugin/test_plugin.py
@@ -304,9 +304,11 @@ def fake_load_entrypoints(self: pluggy.PluginManager, name: str) -> None: # noq
self.register(DummyPluginB(), name="dummy_plugin_b")
project = tox_project({"tox.ini": ""})
- with patch("pluggy.PluginManager.load_setuptools_entrypoints", fake_load_entrypoints), patch(
- "tox.plugin.manager.MANAGER", Plugin()
- ), patch.dict(os.environ, {"TOX_DISABLED_EXTERNAL_PLUGINS": env_val}, clear=False):
+ with (
+ patch("pluggy.PluginManager.load_setuptools_entrypoints", fake_load_entrypoints),
+ patch("tox.plugin.manager.MANAGER", Plugin()),
+ patch.dict(os.environ, {"TOX_DISABLED_EXTERNAL_PLUGINS": env_val}, clear=False),
+ ):
result = project.run("--version")
result.assert_success()
diff --git a/tests/plugin/test_plugin_custom_config_set.py b/tests/plugin/test_plugin_custom_config_set.py
index 7ef35ff92d..cfa0e7d8d6 100644
--- a/tests/plugin/test_plugin_custom_config_set.py
+++ b/tests/plugin/test_plugin_custom_config_set.py
@@ -2,7 +2,7 @@
import logging
from functools import partial
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING
import pytest
@@ -33,7 +33,7 @@ def factory(for_env: str, raw: object) -> DockerConfigSet:
env_conf.add_config(
"docker",
- of_type=Optional[DockerConfigSet], # type: ignore[arg-type] # mypy fails to understand the type info
+ of_type=DockerConfigSet | None,
default=None,
desc="docker env",
factory=partial(factory, env_conf.name),
diff --git a/tests/pytest_/test_init.py b/tests/pytest_/test_init.py
index 50764a503b..db6022146c 100644
--- a/tests/pytest_/test_init.py
+++ b/tests/pytest_/test_init.py
@@ -4,7 +4,7 @@
import sys
from itertools import chain, combinations
from textwrap import dedent
-from typing import TYPE_CHECKING, Sequence
+from typing import TYPE_CHECKING
import pytest
@@ -12,6 +12,7 @@
from tox.report import HandledError
if TYPE_CHECKING:
+ from collections.abc import Sequence
from pathlib import Path
from pytest_mock import MockerFixture
diff --git a/tests/session/cmd/test_depends.py b/tests/session/cmd/test_depends.py
index a10ace70e1..dd5fe11864 100644
--- a/tests/session/cmd/test_depends.py
+++ b/tests/session/cmd/test_depends.py
@@ -2,9 +2,11 @@
import sys
from textwrap import dedent
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from tox.pytest import ToxProjectCreator
diff --git a/tests/session/cmd/test_show_config.py b/tests/session/cmd/test_show_config.py
index d869c5a867..d91939347b 100644
--- a/tests/session/cmd/test_show_config.py
+++ b/tests/session/cmd/test_show_config.py
@@ -4,13 +4,14 @@
import sys
from configparser import ConfigParser
from textwrap import dedent
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
from tox.config.types import Command
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
from pytest_mock import MockerFixture
diff --git a/tests/session/test_env_select.py b/tests/session/test_env_select.py
index 52d8030b7e..703d6466f7 100644
--- a/tests/session/test_env_select.py
+++ b/tests/session/test_env_select.py
@@ -37,7 +37,6 @@
def test_clienv(user_input: str, env_names: tuple[str], is_all: bool, is_default: bool) -> None:
ce = CliEnv(user_input)
assert (ce.is_all, ce.is_default_list, tuple(ce)) == (is_all, is_default, tuple(env_names))
- assert ce is not ce.is_default_list
assert CliEnv(user_input) == ce
diff --git a/tests/test_provision.py b/tests/test_provision.py
index 2cde4f68bd..d1b6b2db77 100644
--- a/tests/test_provision.py
+++ b/tests/test_provision.py
@@ -7,7 +7,7 @@
from contextlib import contextmanager
from pathlib import Path
from subprocess import check_call
-from typing import TYPE_CHECKING, Callable, Iterator, Sequence
+from typing import TYPE_CHECKING
from unittest import mock
from zipfile import ZipFile
@@ -16,6 +16,8 @@
from packaging.requirements import Requirement
if TYPE_CHECKING:
+ from collections.abc import Callable, Iterator, Sequence
+
from build import DistributionType
from devpi_process import Index, IndexServer
diff --git a/tests/tox_env/python/pip/req/test_file.py b/tests/tox_env/python/pip/req/test_file.py
index b888287568..bd363e1f56 100644
--- a/tests/tox_env/python/pip/req/test_file.py
+++ b/tests/tox_env/python/pip/req/test_file.py
@@ -4,13 +4,14 @@
import sys
from contextlib import contextmanager
from io import BytesIO
-from typing import IO, TYPE_CHECKING, Any, Iterator
+from typing import IO, TYPE_CHECKING, Any
import pytest
from tox.tox_env.python.pip.req.file import ParsedRequirement, RequirementsFile
if TYPE_CHECKING:
+ from collections.abc import Iterator
from pathlib import Path
from pytest_mock import MockerFixture
diff --git a/tests/tox_env/python/test_python_api.py b/tests/tox_env/python/test_python_api.py
index 413725ecd8..f0e3cfeaff 100644
--- a/tests/tox_env/python/test_python_api.py
+++ b/tests/tox_env/python/test_python_api.py
@@ -3,7 +3,7 @@
import sys
import sysconfig
from types import SimpleNamespace
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
from unittest.mock import patch
import pytest
@@ -12,6 +12,7 @@
from tox.tox_env.python.api import Python
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
from pytest_mock import MockerFixture
diff --git a/tests/tox_env/python/virtual_env/package/conftest.py b/tests/tox_env/python/virtual_env/package/conftest.py
index e96910954e..cd17480160 100644
--- a/tests/tox_env/python/virtual_env/package/conftest.py
+++ b/tests/tox_env/python/virtual_env/package/conftest.py
@@ -2,11 +2,12 @@
import sys
from textwrap import dedent
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
import pytest
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
diff --git a/tests/tox_env/python/virtual_env/package/test_package_cmd_builder.py b/tests/tox_env/python/virtual_env/package/test_package_cmd_builder.py
index ee4aed0310..15ac1aaff5 100644
--- a/tests/tox_env/python/virtual_env/package/test_package_cmd_builder.py
+++ b/tests/tox_env/python/virtual_env/package/test_package_cmd_builder.py
@@ -1,11 +1,12 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Callable
+from typing import TYPE_CHECKING
from zipfile import ZipFile
import pytest
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
from tox.pytest import ToxProjectCreator
diff --git a/tests/type_check/add_config_container_factory.py b/tests/type_check/add_config_container_factory.py
index 3193c07b14..50ad5a270c 100644
--- a/tests/type_check/add_config_container_factory.py
+++ b/tests/type_check/add_config_container_factory.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import List
-
from tox.config.sets import ConfigSet
@@ -14,7 +12,7 @@ def factory(container_name: object) -> str:
self.add_config(
keys=["k"],
- of_type=List[str],
+ of_type=list[str],
default=[],
desc="desc",
factory=factory,
diff --git a/tox.toml b/tox.toml
index d0986bc708..dcb19a4eb5 100644
--- a/tox.toml
+++ b/tox.toml
@@ -1,5 +1,5 @@
requires = [ "tox>=4.27" ]
-env_list = [ "fix", "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10", "3.9", "cov", "type", "docs", "pkg_meta" ]
+env_list = [ "fix", "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10", "cov", "type", "docs", "pkg_meta" ]
skip_missing_interpreters = true
[env_run_base]
@@ -62,13 +62,19 @@ pass_env = [
commands = [ [ "pre-commit", "run", "--all-files", "--show-diff-on-failure", { replace = "posargs", extend = true } ] ]
[env.type]
+base_python = ["3.14"]
description = "run type check on code base"
dependency_groups = [ "type" ]
commands = [ [ "mypy", "src{/}tox" ], [ "mypy", "tests" ] ]
+[env.type-3.10]
+base_python = ["3.10"]
+base = ["env", "type"]
+
[env.docs]
description = "build documentation"
dependency_groups = [ "docs" ]
+base_pythons = ["3.14"]
commands = [
[
"sphinx-build",