From 4da557acb59b3da9264013cc27fb4829c6cdb4f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 08:03:17 -0800 Subject: [PATCH 01/20] Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 (#3469) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.12.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.12.4) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index aa16302b5..247c532c0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -43,6 +43,6 @@ jobs: name: ${{ env.dists-artifact-name }} path: dist/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: attestations: true From 851e9872dbfb469b7918a16a75314c2bb2ae9ed7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 10:04:03 -0800 Subject: [PATCH 02/20] [pre-commit.ci] pre-commit autoupdate (#3470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/codespell-project/codespell: v2.3.0 → v2.4.0](https://github.com/codespell-project/codespell/compare/v2.3.0...v2.4.0) - [github.com/astral-sh/ruff-pre-commit: v0.9.2 → v0.9.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.2...v0.9.3) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- docs/user_guide.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5751860a5..e0656aceb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: check-github-workflows args: ["--verbose"] - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.0 hooks: - id: codespell additional_dependencies: ["tomli>=2.1"] @@ -23,7 +23,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.2" + rev: "v0.9.3" hooks: - id: ruff-format - id: ruff diff --git a/docs/user_guide.rst b/docs/user_guide.rst index e3c568786..b4f43fa8a 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -305,7 +305,7 @@ The primary tox states are: :ref:`runner`. For `virtualenv` tox will use the `virtualenv discovery logic `_ where the python specification is defined by the tox environments :ref:`base_python` (if not set will default to the environments name). This is - created at first run only to be re-used at subsequent runs. If certain aspects of the project change (python + created at first run only to be reused at subsequent runs. If certain aspects of the project change (python version, dependencies removed, etc.), a re-creation of the environment is automatically triggered. To force the recreation tox can be invoked with the :ref:`recreate` flag (``-r``). From 8d302ebb664a20d612dc20aa3a707880d9ad9303 Mon Sep 17 00:00:00 2001 From: Panaintescu Adrian Date: Tue, 28 Jan 2025 20:04:30 +0200 Subject: [PATCH 03/20] TOX-3117 bugfix -c pyproject with non legacy (#3471) Running tox -c pyproject.toml with content not ini legacy end up with a failure: could not recognize config file pyproject.toml [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/changelog/3117.bugfix.rst | 1 + src/tox/config/source/discover.py | 4 ++-- tests/config/source/test_toml_pyproject.py | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/3117.bugfix.rst diff --git a/docs/changelog/3117.bugfix.rst b/docs/changelog/3117.bugfix.rst new file mode 100644 index 000000000..0d18bf7e5 --- /dev/null +++ b/docs/changelog/3117.bugfix.rst @@ -0,0 +1 @@ +multiple source_type supports for the same filename. Like pyproject.toml can be load by both TomlPyProject & LegacyToml diff --git a/src/tox/config/source/discover.py b/src/tox/config/source/discover.py index 60cd2a832..f04b9a7a0 100644 --- a/src/tox/config/source/discover.py +++ b/src/tox/config/source/discover.py @@ -68,8 +68,8 @@ def _locate_source() -> Source | None: def _load_exact_source(config_file: Path) -> Source: # if the filename matches to the letter some config file name do not fallback to other source types - exact_match = next((s for s in SOURCE_TYPES if config_file.name == s.FILENAME), None) # pragma: no cover - for src_type in (exact_match,) if exact_match is not None else SOURCE_TYPES: # pragma: no branch + exact_match = [s for s in SOURCE_TYPES if config_file.name == s.FILENAME] # pragma: no cover + for src_type in exact_match or SOURCE_TYPES: # pragma: no branch try: return src_type(config_file) except ValueError: # noqa: PERF203 diff --git a/tests/config/source/test_toml_pyproject.py b/tests/config/source/test_toml_pyproject.py index 682a60fc7..66cd45b9b 100644 --- a/tests/config/source/test_toml_pyproject.py +++ b/tests/config/source/test_toml_pyproject.py @@ -64,6 +64,22 @@ def test_config_in_toml_extra(tox_project: ToxProjectCreator) -> None: assert "# !!! unused: " not in outcome.out, outcome.out +def test_config_in_toml_explicit_mentioned(tox_project: ToxProjectCreator) -> None: + project = tox_project({ + "pyproject.toml": """ + [tool.tox.env_run_base] + description = "Do magical things" + commands = [ + ["python", "--version"] + ] + """ + }) + + outcome = project.run("l", "-c", "pyproject.toml") + outcome.assert_success() + assert "could not recognize config file pyproject.toml" not in outcome.out, outcome.out + + def test_config_in_toml_replace_default(tox_project: ToxProjectCreator) -> None: project = tox_project({"pyproject.toml": '[tool.tox.env_run_base]\ndescription = "{missing:miss}"'}) outcome = project.run("c", "-k", "description") From b588b696e0940c1813014b31b68d7660d8a1914f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:11:42 -0800 Subject: [PATCH 04/20] [pre-commit.ci] pre-commit autoupdate (#3476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.31.0 → 0.31.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.0...0.31.1) - [github.com/codespell-project/codespell: v2.4.0 → v2.4.1](https://github.com/codespell-project/codespell/compare/v2.4.0...v2.4.1) - [github.com/astral-sh/ruff-pre-commit: v0.9.3 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.3...v0.9.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0656aceb..3fd20ee01 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,12 +5,12 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.31.0 + rev: 0.31.1 hooks: - id: check-github-workflows args: ["--verbose"] - repo: https://github.com/codespell-project/codespell - rev: v2.4.0 + rev: v2.4.1 hooks: - id: codespell additional_dependencies: ["tomli>=2.1"] @@ -23,7 +23,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.3" + rev: "v0.9.4" hooks: - id: ruff-format - id: ruff From 6e53aea9e22ef4908ad81d3edd401202430ce094 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:26:02 -0800 Subject: [PATCH 05/20] [pre-commit.ci] pre-commit autoupdate (#3479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.4 → v0.9.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.4...v0.9.6) - [github.com/rbubley/mirrors-prettier: v3.4.2 → v3.5.0](https://github.com/rbubley/mirrors-prettier/compare/v3.4.2...v3.5.0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix CI Signed-off-by: Bernát Gábor --------- Signed-off-by: Bernát Gábor Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bernát Gábor --- .pre-commit-config.yaml | 8 +++--- pyproject.toml | 36 +++++++++++++------------- src/tox/config/cli/parser.py | 11 +++++--- src/tox/config/types.py | 2 +- src/tox/report.py | 2 +- src/tox/tox_env/python/pip/req/args.py | 11 ++++++-- src/tox/util/spinner.py | 7 ++++- tox.toml | 2 +- 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3fd20ee01..aa38c2a0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: rev: v2.4.1 hooks: - id: codespell - additional_dependencies: ["tomli>=2.1"] + additional_dependencies: ["tomli>=2.2.1"] - repo: https://github.com/tox-dev/pyproject-fmt rev: "v2.5.0" hooks: @@ -23,7 +23,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.4" + rev: "v0.9.6" hooks: - id: ruff-format - id: ruff @@ -32,13 +32,13 @@ repos: rev: 1.19.1 hooks: - id: blacken-docs - additional_dependencies: [black==24.10] + additional_dependencies: [black==25.1] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: rst-backticks - repo: https://github.com/rbubley/mirrors-prettier - rev: "v3.4.2" + rev: "v3.5.0" hooks: - id: prettier - repo: local diff --git a/pyproject.toml b/pyproject.toml index 16f2e44e9..9c1116c23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "hatchling.build" requires = [ "hatch-vcs>=0.4", - "hatchling>=1.26.3", + "hatchling>=1.27", ] [project] @@ -50,7 +50,7 @@ dynamic = [ "version", ] dependencies = [ - "cachetools>=5.5", + "cachetools>=5.5.1", "chardet>=5.2", "colorama>=0.4.6", "filelock>=3.16.1", @@ -58,13 +58,13 @@ dependencies = [ "platformdirs>=4.3.6", "pluggy>=1.5", "pyproject-api>=1.8", - "tomli>=2.1; python_version<'3.11'", + "tomli>=2.2.1; python_version<'3.11'", "typing-extensions>=4.12.2; python_version<'3.11'", - "virtualenv>=20.27.1", + "virtualenv>=20.29.1", ] optional-dependencies.test = [ "devpi-process>=1.0.2", - "pytest>=8.3.3", + "pytest>=8.3.4", "pytest-mock>=3.14", ] urls.Documentation = "https://tox.wiki" @@ -89,20 +89,20 @@ test = [ "distlib>=0.3.9", "flaky>=3.8.1", "hatch-vcs>=0.4", - "hatchling>=1.26.3", - "psutil>=6.1", - "pytest>=8.3.3", + "hatchling>=1.27", + "psutil>=6.1.1", + "pytest>=8.3.4", "pytest-cov>=5", "pytest-mock>=3.14", "pytest-xdist>=3.6.1", "re-assert>=1.1", - "setuptools>=75.1; python_version<='3.8'", - "setuptools>=75.6; python_version>'3.8'", + "setuptools>=75.3; python_version<='3.8'", + "setuptools>=75.8; python_version>'3.8'", "time-machine>=2.15; implementation_name!='pypy'", - "wheel>=0.45", + "wheel>=0.45.1", ] type = [ - "mypy==1.13", + "mypy==1.15", "types-cachetools>=5.5.0.20240820", "types-chardet>=5.0.4.6", { include-group = "test" }, @@ -110,8 +110,8 @@ type = [ docs = [ "furo>=2024.8.6", "sphinx>=8.1.3", - "sphinx-argparse-cli>=1.18.2", - "sphinx-autodoc-typehints>=2.5", + "sphinx-argparse-cli>=1.19", + "sphinx-autodoc-typehints>=3.0.1", "sphinx-copybutton>=0.5.2", "sphinx-inline-tabs>=2023.4.21", "sphinxcontrib-towncrier>=0.2.1a0", @@ -121,12 +121,12 @@ fix = [ "pre-commit-uv>=4.1.4", ] pkg-meta = [ - "check-wheel-contents>=0.6", - "twine>=5.1.1", - "uv>=0.5.3", + "check-wheel-contents>=0.6.1", + "twine>=6.1", + "uv>=0.5.29", ] release = [ - "gitpython>=3.1.43", + "gitpython>=3.1.44", "packaging>=24.2", "towncrier>=24.8", ] diff --git a/src/tox/config/cli/parser.py b/src/tox/config/cli/parser.py index 87d441c3c..87629ae49 100644 --- a/src/tox/config/cli/parser.py +++ b/src/tox/config/cli/parser.py @@ -1,4 +1,4 @@ -"""Customize argparse logic for tox (also contains the base options).""" # noqa: A005 +"""Customize argparse logic for tox (also contains the base options).""" from __future__ import annotations @@ -20,6 +20,11 @@ from .env_var import get_env_var from .ini import IniConfig +if sys.version_info >= (3, 11): # pragma: >=3.11 cover + from typing import Self +else: # pragma: <3.11 cover + from typing_extensions import Self + if TYPE_CHECKING: from tox.session.state import State @@ -287,11 +292,11 @@ def add_argument(self, *args: str, of_type: type[Any] | None = None, **kwargs: A return result @classmethod - def base(cls: type[ToxParserT]) -> ToxParserT: + def base(cls) -> Self: return cls(add_help=False, root=True) @classmethod - def core(cls: type[ToxParserT]) -> ToxParserT: + def core(cls) -> Self: return cls( prog=NAME, formatter_class=HelpFormatter, diff --git a/src/tox/config/types.py b/src/tox/config/types.py index 571372596..8d1300aa4 100644 --- a/src/tox/config/types.py +++ b/src/tox/config/types.py @@ -1,4 +1,4 @@ -from __future__ import annotations # noqa: A005 +from __future__ import annotations from collections import OrderedDict from typing import Iterator, Sequence diff --git a/src/tox/report.py b/src/tox/report.py index 07c8e8b64..ef46bc702 100644 --- a/src/tox/report.py +++ b/src/tox/report.py @@ -140,7 +140,7 @@ def stderr(self) -> TextIOWrapper: """:return: the current standard error""" return self._local.out_err[1] - @property # type: ignore[override] + @property def stream(self) -> IO[str]: """:return: the current stream to write to (alias for the current standard output)""" return self.stdout diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py index c5af7299d..e2c5b4aae 100644 --- a/src/tox/tox_env/python/pip/req/args.py +++ b/src/tox/tox_env/python/pip/req/args.py @@ -3,13 +3,20 @@ import bisect import re from argparse import Action, ArgumentParser, ArgumentTypeError, Namespace -from typing import IO, Any, NoReturn, Sequence +from typing import Any, NoReturn, Protocol, Sequence, TypeVar from tox.tox_env.python.pip.req.util import handle_binary_option +_T_contra = TypeVar("_T_contra", contravariant=True) + + +# stable +class _SupportsWrite(Protocol[_T_contra]): + def write(self, s: _T_contra, /) -> object: ... + class _OurArgumentParser(ArgumentParser): - def print_usage(self, file: IO[str] | None = None) -> None: + def print_usage(self, file: _SupportsWrite[str] | None = None) -> None: """ """ def exit(self, status: int = 0, message: str | None = None) -> NoReturn: # noqa: ARG002, PLR6301 diff --git a/src/tox/util/spinner.py b/src/tox/util/spinner.py index e82acaa13..a6d2b905b 100644 --- a/src/tox/util/spinner.py +++ b/src/tox/util/spinner.py @@ -12,6 +12,11 @@ from colorama import Fore +if sys.version_info >= (3, 11): # pragma: >=3.11 cover + from typing import Self +else: # pragma: <3.11 cover + from typing_extensions import Self + if TYPE_CHECKING: from types import TracebackType from typing import Any, ClassVar @@ -104,7 +109,7 @@ def frame(self) -> str: text_frame = textwrap.shorten(text_frame, width=self.max_width - 1, placeholder="...") return f"{frame} {text_frame}" - def __enter__(self: T) -> T: + def __enter__(self) -> Self: if self.enabled: self.disable_cursor() self.render_frame() diff --git a/tox.toml b/tox.toml index 227381280..8889cb5a7 100644 --- a/tox.toml +++ b/tox.toml @@ -1,4 +1,4 @@ -requires = ["tox>=4.23.2"] +requires = ["tox>=4.24.1"] env_list = ["fix", "3.13", "3.12", "3.11", "3.10", "3.9", "3.8", "cov", "type", "docs", "pkg_meta"] skip_missing_interpreters = true From 6b40c0c1598bd6736fedf6e9775d594b1b27dea6 Mon Sep 17 00:00:00 2001 From: Gil Forcada Codinachs Date: Fri, 14 Feb 2025 16:44:53 +0100 Subject: [PATCH 06/20] fix(docs): update expected code (#3480) * fix(docs): update expected code The example on Generative section names was not the one would expect. * Add news fragment --- docs/changelog/3480.bugfix.rst | 1 + docs/config.rst | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 docs/changelog/3480.bugfix.rst diff --git a/docs/changelog/3480.bugfix.rst b/docs/changelog/3480.bugfix.rst new file mode 100644 index 000000000..ed7b700f9 --- /dev/null +++ b/docs/changelog/3480.bugfix.rst @@ -0,0 +1 @@ +fix example on the docs diff --git a/docs/config.rst b/docs/config.rst index 06e140d5f..07d9ba777 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1586,10 +1586,8 @@ create your virtual env for the developers. py -> [no description] additional environments: - py310-black -> [no description] - py310-lint -> [no description] - py311-black -> [no description] - py311-lint -> [no description] + py311-x86-venv -> [no description] + py311-x64-venv -> [no description] .. _substitution: From 28212ab9f259656319ca179421a78ae3a2d510f4 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 15 Feb 2025 17:17:35 +0100 Subject: [PATCH 07/20] Add missing bracket in config example (#3481) --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 07d9ba777..ee9d1b9fe 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1366,7 +1366,7 @@ You can reference other configurations via the ``ref`` replacement. This can eit [env.src] extras = ["A", "{env_name}"] [env.dest] - extras = [{ replace = "ref", env = "src", key = "extras", extend = true }, "B" + extras = [{ replace = "ref", env = "src", key = "extras", extend = true }, "B"] In this case ``dest`` environments ``extras`` will be ``A``, ``src``, ``B``. From 65d404e0e4c5150a72601f8d78e648d964df9888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Sat, 15 Feb 2025 19:20:34 +0100 Subject: [PATCH 08/20] Gh issue 3456 update environment variable documentation (#3482) * Fix typos * Fix docs for retrieving missing environment variable. Fixes #3456 --- docs/changelog/3456.doc.rst | 1 + docs/config.rst | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 docs/changelog/3456.doc.rst diff --git a/docs/changelog/3456.doc.rst b/docs/changelog/3456.doc.rst new file mode 100644 index 000000000..1b5c9ca46 --- /dev/null +++ b/docs/changelog/3456.doc.rst @@ -0,0 +1 @@ +Updates the documentation for ``os.environ['KEY']`` when the variable does not exist - by :user:`jugmac00`. diff --git a/docs/config.rst b/docs/config.rst index ee9d1b9fe..db1c27d73 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1635,7 +1635,8 @@ If you specify a substitution string like this:: {env:KEY} -then the value will be retrieved as ``os.environ['KEY']`` and raise an Error if the environment variable does not exist. +then the value will be retrieved as ``os.environ['KEY']`` and replaced with an empty string if the environment variable +does not exist. Environment variable substitutions with default values @@ -1645,14 +1646,14 @@ If you specify a substitution string like this:: {env:KEY:DEFAULTVALUE} -then the value will be retrieved as ``os.environ['KEY']`` and replace with DEFAULTVALUE if the environment variable does +then the value will be retrieved as ``os.environ['KEY']`` and replaced with DEFAULTVALUE if the environment variable does not exist. If you specify a substitution string like this:: {env:KEY:} -then the value will be retrieved as ``os.environ['KEY']`` and replace with an empty string if the environment variable +then the value will be retrieved as ``os.environ['KEY']`` and replaced with an empty string if the environment variable does not exist. Substitutions can also be nested. In that case they are expanded starting from the innermost expression:: From 0a4ba54e5ec68491fe3a82325f2c3fdc8c26d0b4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:27:05 -0800 Subject: [PATCH 09/20] [pre-commit.ci] pre-commit autoupdate (#3484) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/rbubley/mirrors-prettier: v3.5.0 → v3.5.1](https://github.com/rbubley/mirrors-prettier/compare/v3.5.0...v3.5.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aa38c2a0b..fae4e2758 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: hooks: - id: rst-backticks - repo: https://github.com/rbubley/mirrors-prettier - rev: "v3.5.0" + rev: "v3.5.1" hooks: - id: prettier - repo: local From 32879c8d06752cc860471e98dfd6769be9ccd13e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:43:41 -0800 Subject: [PATCH 10/20] [pre-commit.ci] pre-commit autoupdate (#3486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.31.1 → 0.31.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.1...0.31.2) - [github.com/astral-sh/ruff-pre-commit: v0.9.6 → v0.9.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.7) - [github.com/rbubley/mirrors-prettier: v3.5.1 → v3.5.2](https://github.com/rbubley/mirrors-prettier/compare/v3.5.1...v3.5.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fae4e2758..1b5347a33 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.31.1 + rev: 0.31.2 hooks: - id: check-github-workflows args: ["--verbose"] @@ -23,7 +23,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.6" + rev: "v0.9.7" hooks: - id: ruff-format - id: ruff @@ -38,7 +38,7 @@ repos: hooks: - id: rst-backticks - repo: https://github.com/rbubley/mirrors-prettier - rev: "v3.5.1" + rev: "v3.5.2" hooks: - id: prettier - repo: local From fd4490414d9abf7b63380fcdfe5bd38b021cc2e0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 18:52:07 -0800 Subject: [PATCH 11/20] [pre-commit.ci] pre-commit autoupdate (#3488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/tox-dev/pyproject-fmt: v2.5.0 → v2.5.1](https://github.com/tox-dev/pyproject-fmt/compare/v2.5.0...v2.5.1) - [github.com/astral-sh/ruff-pre-commit: v0.9.7 → v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.7...v0.9.9) - [github.com/rbubley/mirrors-prettier: v3.5.2 → v3.5.3](https://github.com/rbubley/mirrors-prettier/compare/v3.5.2...v3.5.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b5347a33..60631f23f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - id: codespell additional_dependencies: ["tomli>=2.2.1"] - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.5.0" + rev: "v2.5.1" 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.9.7" + rev: "v0.9.9" hooks: - id: ruff-format - id: ruff @@ -38,7 +38,7 @@ repos: hooks: - id: rst-backticks - repo: https://github.com/rbubley/mirrors-prettier - rev: "v3.5.2" + rev: "v3.5.3" hooks: - id: prettier - repo: local From 1dac11f5f2ba46272d5a9f0a0731ea243e744aa0 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Fri, 7 Mar 2025 02:58:50 +0530 Subject: [PATCH 12/20] fix: Respect `--parallel N` with `--parallel-no-spinner` (#3495) * fix: Respect `--parallel N` with `--parallel-no-spinner` * add changelog entry * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix backticks --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/changelog/3495.bugfix.rst | 1 + src/tox/session/cmd/run/parallel.py | 2 +- tests/session/cmd/test_parallel.py | 20 +++++++++++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 docs/changelog/3495.bugfix.rst diff --git a/docs/changelog/3495.bugfix.rst b/docs/changelog/3495.bugfix.rst new file mode 100644 index 000000000..6e7aab0b1 --- /dev/null +++ b/docs/changelog/3495.bugfix.rst @@ -0,0 +1 @@ +- ``--parallel-no-spinner`` now respects max CPU set by ``--parallel N`` diff --git a/src/tox/session/cmd/run/parallel.py b/src/tox/session/cmd/run/parallel.py index 71f898e90..5b3d05f75 100644 --- a/src/tox/session/cmd/run/parallel.py +++ b/src/tox/session/cmd/run/parallel.py @@ -91,7 +91,7 @@ def run_parallel(state: State) -> int: option = state.conf.options return execute( state, - max_workers=None if option.parallel_no_spinner is True else option.parallel, + max_workers=auto_detect_cpus() if option.parallel == 0 else option.parallel, has_spinner=option.parallel_no_spinner is False and option.parallel_live is False, live=option.parallel_live, ) diff --git a/tests/session/cmd/test_parallel.py b/tests/session/cmd/test_parallel.py index 89c007fb6..5f072a2ac 100644 --- a/tests/session/cmd/test_parallel.py +++ b/tests/session/cmd/test_parallel.py @@ -14,6 +14,7 @@ from tox.session.cmd.run.parallel import parse_num_processes from tox.tox_env.api import ToxEnv from tox.tox_env.errors import Fail +from tox.util.cpu import auto_detect_cpus if TYPE_CHECKING: from pathlib import Path @@ -180,7 +181,20 @@ def test_parallel_no_spinner(tox_project: ToxProjectCreator) -> None: mocked.assert_called_once_with( mock.ANY, - max_workers=None, + max_workers=auto_detect_cpus(), + has_spinner=False, + live=False, + ) + + +def test_parallel_no_spinner_with_parallel(tox_project: ToxProjectCreator) -> None: + """Ensure `--parallel N` is still respected with `--parallel-no-spinner`.""" + with mock.patch.object(parallel, "execute") as mocked: + tox_project({"tox.ini": ""}).run("p", "--parallel-no-spinner", "--parallel", "2") + + mocked.assert_called_once_with( + mock.ANY, + max_workers=2, has_spinner=False, live=False, ) @@ -197,7 +211,7 @@ def test_parallel_no_spinner_ci( mocked.assert_called_once_with( mock.ANY, - max_workers=None, + max_workers=auto_detect_cpus(), has_spinner=False, live=False, ) @@ -209,7 +223,7 @@ def test_parallel_no_spinner_legacy(tox_project: ToxProjectCreator) -> None: mocked.assert_called_once_with( mock.ANY, - max_workers=None, + max_workers=auto_detect_cpus(), has_spinner=False, live=False, ) From 48522626d78e3d01ca18d815b3c02c1701ea7cb0 Mon Sep 17 00:00:00 2001 From: Judit Novak Date: Thu, 6 Mar 2025 23:18:15 +0100 Subject: [PATCH 13/20] TOML set_env file support (#3478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BUGFIX] TOML config interpreting set_env=file|... * Tests * Changelog entry * Added set_env 'file' option in TOML as dict * Testing set_env 'file' option in TOML as dict * Updating docs on set_env 'file' option (INI vs TOML) * Removing INI-specific check from TOML internal replacer function * Additional extension on docs, highlighting internal references * PR feedback Signed-off-by: Bernát Gábor --------- Signed-off-by: Bernát Gábor Co-authored-by: Bernát Gábor Co-authored-by: Bernát Gábor --- docs/changelog/3474.bugfix.rst | 1 + docs/config.rst | 39 +++++++++++++- src/tox/config/loader/toml/__init__.py | 9 +++- src/tox/config/set_env.py | 7 ++- tests/config/test_set_env.py | 75 +++++++++++++++++++++++--- 5 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 docs/changelog/3474.bugfix.rst diff --git a/docs/changelog/3474.bugfix.rst b/docs/changelog/3474.bugfix.rst new file mode 100644 index 000000000..5cf3c29e3 --- /dev/null +++ b/docs/changelog/3474.bugfix.rst @@ -0,0 +1 @@ +Support ``set_env = { file = "conf{/}local.env"}`` for TOML format - by :user:`juditnovak`. diff --git a/docs/config.rst b/docs/config.rst index db1c27d73..fb099c3d9 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -551,8 +551,43 @@ Base options .. conf:: :keys: set_env, setenv - A dictionary of environment variables to set when running commands in the tox environment. Lines starting with a - ``file|`` prefix define the location of environment file. + A dictionary of environment variables to set when running commands in the tox environment. + + In addition, there is an option to include an existing environment file. See the different syntax for TOML and INI below. + + .. tab:: TOML + + .. code-block:: toml + + [tool.tox.env_run_base] + set_env = { file = "conf{/}local.env", TEST_TIMEOUT = 30 } + + .. tab:: INI + + .. code-block:: ini + + [testenv] + set_env = file|conf{/}local.env + TEST_TIMEOUT = 30 + + + The env file path may include previously defined tox variables: + + + .. tab:: TOML + + .. code-block:: toml + + [tool.tox.env_run_base] + set_env = { file = "{env:variable}" } + + .. tab:: INI + + .. code-block:: ini + + [testenv] + set_env = file|{env:variable} + .. note:: diff --git a/src/tox/config/loader/toml/__init__.py b/src/tox/config/loader/toml/__init__.py index fb402bcfe..4c4c6cb03 100644 --- a/src/tox/config/loader/toml/__init__.py +++ b/src/tox/config/loader/toml/__init__.py @@ -1,11 +1,14 @@ from __future__ import annotations +import inspect import logging from pathlib import Path from typing import TYPE_CHECKING, Dict, Iterator, List, Mapping, TypeVar, cast from tox.config.loader.api import ConfigLoadArgs, Loader, Override +from tox.config.set_env import SetEnv from tox.config.types import Command, EnvList +from tox.report import HandledError from ._api import TomlTypes from ._replace import Unroll @@ -63,7 +66,10 @@ def build( # noqa: PLR0913 args: ConfigLoadArgs, ) -> _T: exploded = Unroll(conf=conf, loader=self, args=args)(raw) - return self.to(exploded, of_type, factory) + result = self.to(exploded, of_type, factory) + if inspect.isclass(of_type) and issubclass(of_type, SetEnv): + result.use_replacer(lambda c, s: c, args=args) # type: ignore[attr-defined] # noqa: ARG005 + return result def found_keys(self) -> set[str]: return set(self.content.keys()) - self._unused_exclude @@ -107,5 +113,6 @@ def to_env_list(value: TomlTypes) -> EnvList: __all__ = [ + "HandledError", "TomlLoader", ] diff --git a/src/tox/config/set_env.py b/src/tox/config/set_env.py index cf29cee0c..0686200c3 100644 --- a/src/tox/config/set_env.py +++ b/src/tox/config/set_env.py @@ -11,7 +11,7 @@ class SetEnv: - def __init__( # noqa: C901 + def __init__( # noqa: C901, PLR0912 self, raw: str | dict[str, str] | list[dict[str, str]], name: str, env_name: str | None, root: Path ) -> None: self.changed = False @@ -25,13 +25,16 @@ def __init__( # noqa: C901 if isinstance(raw, dict): self._raw = raw + if "file" in raw: # environment files to be handled later + self._env_files.append(raw["file"]) + self._raw.pop("file") return if isinstance(raw, list): self._raw = reduce(lambda a, b: {**a, **b}, raw) return for line in raw.splitlines(): # noqa: PLR1702 if line.strip(): - if line.startswith("file|"): + if line.startswith("file|"): # environment files to be handled later self._env_files.append(line[len("file|") :]) else: try: diff --git a/tests/config/test_set_env.py b/tests/config/test_set_env.py index d4440ab22..f72a23299 100644 --- a/tests/config/test_set_env.py +++ b/tests/config/test_set_env.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from unittest.mock import ANY import pytest @@ -51,10 +51,15 @@ def test_set_env_bad_line() -> None: SetEnv("A", "py", "py", Path()) +ConfigFileFormat = Literal["ini", "toml"] + + class EvalSetEnv(Protocol): def __call__( self, - tox_ini: str, + config: str, + *, + of_type: ConfigFileFormat = "ini", extra_files: dict[str, Any] | None = ..., from_cwd: Path | None = ..., ) -> SetEnv: ... @@ -62,8 +67,14 @@ def __call__( @pytest.fixture def eval_set_env(tox_project: ToxProjectCreator) -> EvalSetEnv: - def func(tox_ini: str, extra_files: dict[str, Any] | None = None, from_cwd: Path | None = None) -> SetEnv: - prj = tox_project({"tox.ini": tox_ini, **(extra_files or {})}) + def func( + config: str, + *, + of_type: ConfigFileFormat = "ini", + extra_files: dict[str, Any] | None = None, + from_cwd: Path | None = None, + ) -> SetEnv: + prj = tox_project({f"tox.{of_type}": config, **(extra_files or {})}) result = prj.run("c", "-k", "set_env", "-e", "py", from_cwd=None if from_cwd is None else prj.path / from_cwd) result.assert_success() set_env: SetEnv = result.env_conf("py")["set_env"] @@ -149,7 +160,20 @@ def test_set_env_honor_override(eval_set_env: EvalSetEnv) -> None: assert set_env.load("PIP_DISABLE_PIP_VERSION_CHECK") == "0" -def test_set_env_environment_file(eval_set_env: EvalSetEnv) -> None: +@pytest.mark.parametrize( + ("of_type", "config"), + [ + pytest.param("ini", "[testenv]\npackage=skip\nset_env=file|A{/}a.txt\nchange_dir=C", id="ini"), + pytest.param("toml", '[env_run_base]\npackage="skip"\nset_env={file="A{/}a.txt"}\nchange_dir="C"', id="toml"), + pytest.param("ini", "[testenv]\npackage=skip\nset_env=file|{env:env_file}\nchange_dir=C", id="ini-env"), + pytest.param( + "toml", '[env_run_base]\npackage="skip"\nset_env={file="{env:env_file}"}\nchange_dir="C"', id="toml-env" + ), + ], +) +def test_set_env_environment_file( + of_type: ConfigFileFormat, config: str, eval_set_env: EvalSetEnv, monkeypatch: MonkeyPatch +) -> None: env_file = """ A=1 B= 2 @@ -158,9 +182,10 @@ def test_set_env_environment_file(eval_set_env: EvalSetEnv) -> None: E = "1" F = """ + monkeypatch.setenv("env_file", "A{/}a.txt") + extra = {"A": {"a.txt": env_file}, "B": None, "C": None} - ini = "[testenv]\npackage=skip\nset_env=file|A{/}a.txt\nchange_dir=C" - set_env = eval_set_env(ini, extra_files=extra, from_cwd=Path("B")) + set_env = eval_set_env(config, of_type=of_type, extra_files=extra, from_cwd=Path("B")) content = {k: set_env.load(k) for k in set_env} assert content == { "PIP_DISABLE_PIP_VERSION_CHECK": "1", @@ -174,6 +199,42 @@ def test_set_env_environment_file(eval_set_env: EvalSetEnv) -> None: } +@pytest.mark.parametrize( + ("of_type", "config"), + [ + pytest.param("ini", "[testenv]\npackage=skip\nset_env=file|A{/}a.txt\n X=y\nchange_dir=C", id="ini"), + pytest.param( + "toml", '[env_run_base]\npackage="skip"\nset_env={file="A{/}a.txt", X="y"}\nchange_dir="C"', id="toml" + ), + pytest.param("ini", "[testenv]\npackage=skip\nset_env=file|{env:env_file}\n X=y\nchange_dir=C", id="ini-env"), + pytest.param( + "toml", + '[env_run_base]\npackage="skip"\nset_env={file="{env:env_file}", X="y"}\nchange_dir="C"', + id="toml-env", + ), + ], +) +def test_set_env_environment_file_combined_with_normal_setting( + of_type: ConfigFileFormat, config: str, eval_set_env: EvalSetEnv, monkeypatch: MonkeyPatch +) -> None: + env_file = """ + A=1 + """ + # Monkeypatch only used for some of the parameters + monkeypatch.setenv("env_file", "A{/}a.txt") + + extra = {"A": {"a.txt": env_file}, "B": None, "C": None} + set_env = eval_set_env(config, of_type=of_type, extra_files=extra, from_cwd=Path("B")) + content = {k: set_env.load(k) for k in set_env} + assert content == { + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "PYTHONHASHSEED": ANY, + "A": "1", + "X": "y", + "PYTHONIOENCODING": "utf-8", + } + + def test_set_env_environment_file_missing(tox_project: ToxProjectCreator) -> None: project = tox_project({"tox.ini": "[testenv]\npackage=skip\nset_env=file|magic.txt"}) result = project.run("r") From 05835bfe5db31dfaa71d9fb146602ffcc6b7bfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Fri, 7 Mar 2025 10:33:26 -0800 Subject: [PATCH 14/20] release 4.24.2 --- docs/changelog.rst | 14 ++++++++++++++ docs/changelog/3117.bugfix.rst | 1 - docs/changelog/3456.doc.rst | 1 - docs/changelog/3474.bugfix.rst | 1 - docs/changelog/3480.bugfix.rst | 1 - docs/changelog/3495.bugfix.rst | 1 - 6 files changed, 14 insertions(+), 5 deletions(-) delete mode 100644 docs/changelog/3117.bugfix.rst delete mode 100644 docs/changelog/3456.doc.rst delete mode 100644 docs/changelog/3474.bugfix.rst delete mode 100644 docs/changelog/3480.bugfix.rst delete mode 100644 docs/changelog/3495.bugfix.rst diff --git a/docs/changelog.rst b/docs/changelog.rst index fb757767d..623d7a456 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,20 @@ Release History .. towncrier release notes start +v4.24.2 (2025-03-07) +-------------------- + +Bugfixes - 4.24.2 +~~~~~~~~~~~~~~~~~ +- multiple source_type supports for the same filename. Like pyproject.toml can be load by both TomlPyProject & LegacyToml (:issue:`3117`) +- Support ``set_env = { file = "conf{/}local.env"}`` for TOML format - by :user:`juditnovak`. (:issue:`3474`) +- fix example on the docs (:issue:`3480`) +- - ``--parallel-no-spinner`` now respects max CPU set by ``--parallel N`` (:issue:`3495`) + +Improved Documentation - 4.24.2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Updates the documentation for ``os.environ['KEY']`` when the variable does not exist - by :user:`jugmac00`. (:issue:`3456`) + v4.24.1 (2025-01-21) -------------------- diff --git a/docs/changelog/3117.bugfix.rst b/docs/changelog/3117.bugfix.rst deleted file mode 100644 index 0d18bf7e5..000000000 --- a/docs/changelog/3117.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -multiple source_type supports for the same filename. Like pyproject.toml can be load by both TomlPyProject & LegacyToml diff --git a/docs/changelog/3456.doc.rst b/docs/changelog/3456.doc.rst deleted file mode 100644 index 1b5c9ca46..000000000 --- a/docs/changelog/3456.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Updates the documentation for ``os.environ['KEY']`` when the variable does not exist - by :user:`jugmac00`. diff --git a/docs/changelog/3474.bugfix.rst b/docs/changelog/3474.bugfix.rst deleted file mode 100644 index 5cf3c29e3..000000000 --- a/docs/changelog/3474.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Support ``set_env = { file = "conf{/}local.env"}`` for TOML format - by :user:`juditnovak`. diff --git a/docs/changelog/3480.bugfix.rst b/docs/changelog/3480.bugfix.rst deleted file mode 100644 index ed7b700f9..000000000 --- a/docs/changelog/3480.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -fix example on the docs diff --git a/docs/changelog/3495.bugfix.rst b/docs/changelog/3495.bugfix.rst deleted file mode 100644 index 6e7aab0b1..000000000 --- a/docs/changelog/3495.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -- ``--parallel-no-spinner`` now respects max CPU set by ``--parallel N`` From 794e6be20f8314f989c78699723f4039ab3b22f2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:16:24 -0700 Subject: [PATCH 15/20] [pre-commit.ci] pre-commit autoupdate (#3496) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.31.2 → 0.31.3](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.2...0.31.3) - [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60631f23f..597f4cb81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.31.2 + rev: 0.31.3 hooks: - id: check-github-workflows args: ["--verbose"] @@ -23,7 +23,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.9" + rev: "v0.9.10" hooks: - id: ruff-format - id: ruff From beba4be197d49abdb8797ae1218dad1e6d1ee005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 18 Mar 2025 15:34:06 +0100 Subject: [PATCH 16/20] Tests: Adjust expected exception message for Python 3.14.0a6 (#3500) E AssertionError: Regex pattern did not match. E Regex: '3 cannot cast to typing.Union\\[str, int\\]' E Input: '3 cannot cast to str | int' Change caused likely by https://github.com/python/cpython/commit/dc6d66f44c0a25b69dfec7e4ffc4a6fa5e4feada --- docs/changelog/3500.bugfix.rst | 2 ++ tests/config/loader/test_str_convert.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/3500.bugfix.rst diff --git a/docs/changelog/3500.bugfix.rst b/docs/changelog/3500.bugfix.rst new file mode 100644 index 000000000..9df1cf05d --- /dev/null +++ b/docs/changelog/3500.bugfix.rst @@ -0,0 +1,2 @@ +Make tox tests pass with Python 3.14.0a6 +- by :user:`hroncok` diff --git a/tests/config/loader/test_str_convert.py b/tests/config/loader/test_str_convert.py index 8540e2105..b06929f48 100644 --- a/tests/config/loader/test_str_convert.py +++ b/tests/config/loader/test_str_convert.py @@ -77,7 +77,7 @@ def test_str_convert_ok_py39(raw: str, value: Any, of_type: type[Any]) -> None: [ ("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\]"), + ("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"), ], ) From f5f5cb1d7a9269a7a628af9c57eb8f7fbc18cf9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 08:32:19 -0700 Subject: [PATCH 17/20] [pre-commit.ci] pre-commit autoupdate (#3499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/abravalheri/validate-pyproject: v0.23 → v0.24](https://github.com/abravalheri/validate-pyproject/compare/v0.23...v0.24) - [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 597f4cb81..a5ddfda8b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,11 +19,11 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: "v0.23" + rev: "v0.24" hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.9.10" + rev: "v0.11.0" hooks: - id: ruff-format - id: ruff From 5a67ae1a9e350e1e5a0149d6835bd29c517cc3ee Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 11:47:20 -0700 Subject: [PATCH 18/20] [pre-commit.ci] pre-commit autoupdate (#3505) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/abravalheri/validate-pyproject: v0.24 → v0.24.1](https://github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1) - [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a5ddfda8b..59820e00b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,11 +19,11 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: "v0.24" + rev: "v0.24.1" hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.11.0" + rev: "v0.11.2" hooks: - id: ruff-format - id: ruff From 0e6b4ad70b96c750e581ed02ae8dcdcad83cee66 Mon Sep 17 00:00:00 2001 From: Martin Imre Date: Thu, 27 Mar 2025 16:11:42 +0100 Subject: [PATCH 19/20] feat(config): Allow ranges in envlist (#3503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(config): Allow ranges in envlist Implements #3502. Now it is possible to use ranges within the {} of an env specifier such as py3{10-13}. I chose to implement it as a pre-processing string replacement that just replaces the range with a literal enumeration of the range members. This is mainly to avoid more in-depth handling of these ranges when it coto generative environment lists. Also moves CircularChainError from `of_type` to `types` to avoid a circular import error. (kinda ironic :D) * fixup! feat(config): Allow ranges in envlist * fixup! feat(config): Allow ranges in envlist * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixup! feat(config): Allow ranges in envlist * fixup! feat(config): Allow ranges in envlist --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bernát Gábor --- docs/changelog/3502.feature.rst | 1 + docs/config.rst | 45 +++++++++++--- src/tox/config/loader/ini/factor.py | 16 ++++- src/tox/config/loader/replacer.py | 2 +- src/tox/config/loader/str_convert.py | 2 + src/tox/config/of_type.py | 5 +- src/tox/config/source/ini_section.py | 4 +- src/tox/config/types.py | 4 ++ tests/config/loader/ini/test_factor.py | 82 ++++++++++++++++++++++++++ 9 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 docs/changelog/3502.feature.rst diff --git a/docs/changelog/3502.feature.rst b/docs/changelog/3502.feature.rst new file mode 100644 index 000000000..47772b8de --- /dev/null +++ b/docs/changelog/3502.feature.rst @@ -0,0 +1 @@ +Add support for number ranges in generative environments, more details :ref:`here`. - by :user:`mimre25` diff --git a/docs/config.rst b/docs/config.rst index fb099c3d9..86e282688 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1554,6 +1554,8 @@ Conditional settings Here pip will be always installed as the configuration value is not conditional. black is only used for the ``format`` environment, while ``pytest`` is only installed for the ``py310`` and ``py39`` environments. +.. _generative-environment-list: + Generative environment list ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1563,7 +1565,7 @@ If you have a large matrix of dependencies, python versions and/or environments .. code-block:: ini [tox] - env_list = py{311,310,39}-django{41,40}-{sqlite,mysql} + env_list = py3{9-11}-django{41,40}-{sqlite,mysql} [testenv] deps = @@ -1582,24 +1584,49 @@ This will generate the following tox environments: > tox l default environments: - py311-django41-sqlite -> [no description] - py311-django41-mysql -> [no description] - py311-django40-sqlite -> [no description] - py311-django40-mysql -> [no description] - py310-django41-sqlite -> [no description] - py310-django41-mysql -> [no description] - py310-django40-sqlite -> [no description] - py310-django40-mysql -> [no description] py39-django41-sqlite -> [no description] py39-django41-mysql -> [no description] py39-django40-sqlite -> [no description] py39-django40-mysql -> [no description] + py310-django41-sqlite -> [no description] + py310-django41-mysql -> [no description] + py310-django40-sqlite -> [no description] + py310-django40-mysql -> [no description] + py311-django41-sqlite -> [no description] + py311-django41-mysql -> [no description] + py311-django40-sqlite -> [no description] + py311-django40-mysql -> [no description] + +Both enumerations (``{1,2,3}``) and numerical ranges (``{1-3}``) are supported, and can be mixed together: + +.. code-block:: ini + + [tox] + env_list = py3{8-10, 11, 13-14} + +will create the following envs: + +.. code-block:: shell + + > tox l + default environments: + py38 -> [no description] + py39 -> [no description] + py310 -> [no description] + py311 -> [no description] + py313 -> [no description] + py314 -> [no description] + +Negative ranges will also be expanded (``{3-1}`` -> ``{3,2,1}``), however, open ranges such as ``{1-}``, ``{-2}``, ``{a-}``, and ``{-b}`` will not be expanded. + + Generative section names ~~~~~~~~~~~~~~~~~~~~~~~~ Suppose you have some binary packages, and need to run tests both in 32 and 64 bits. You also want an environment to create your virtual env for the developers. +This also supports ranges in the same way as generative environment lists. .. code-block:: ini diff --git a/src/tox/config/loader/ini/factor.py b/src/tox/config/loader/ini/factor.py index 1bb639bbf..11b4fa66d 100644 --- a/src/tox/config/loader/ini/factor.py +++ b/src/tox/config/loader/ini/factor.py @@ -66,7 +66,7 @@ def find_factor_groups(value: str) -> Iterator[list[tuple[str, bool]]]: yield result -_FACTOR_RE = re.compile(r"!?[\w._][\w._-]*") +_FACTOR_RE = re.compile(r"(?:!?[\w._][\w._-]*|^$)") def expand_env_with_negation(value: str) -> Iterator[str]: @@ -93,8 +93,22 @@ def is_negated(factor: str) -> bool: return factor.startswith("!") +def expand_ranges(value: str) -> str: + """Expand ranges in env expressions, eg py3{10-13} -> "py3{10,11,12,13}""" + matches = re.findall(r"((\d+)-(\d+)|\d+)(?:,|})", value) + for src, start_, end_ in matches: + if src and start_ and end_: + start = int(start_) + end = int(end_) + direction = 1 if start < end else -1 + expansion = ",".join(str(x) for x in range(start, end + direction, direction)) + value = value.replace(src, expansion, 1) + return value + + __all__ = ( "expand_factors", + "expand_ranges", "extend_factors", "filter_for_env", "find_envs", diff --git a/src/tox/config/loader/replacer.py b/src/tox/config/loader/replacer.py index 05e3c060b..5d9fd70eb 100644 --- a/src/tox/config/loader/replacer.py +++ b/src/tox/config/loader/replacer.py @@ -8,7 +8,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Final, Sequence, Union -from tox.config.of_type import CircularChainError +from tox.config.types import CircularChainError from tox.execute.request import shell_cmd if TYPE_CHECKING: diff --git a/src/tox/config/loader/str_convert.py b/src/tox/config/loader/str_convert.py index 65dd46dad..248afe268 100644 --- a/src/tox/config/loader/str_convert.py +++ b/src/tox/config/loader/str_convert.py @@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any, Iterator 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: @@ -113,6 +114,7 @@ def to_command(value: str) -> Command | None: def to_env_list(value: str) -> EnvList: from tox.config.loader.ini.factor import extend_factors # noqa: PLC0415 + value = expand_ranges(value) elements = list(chain.from_iterable(extend_factors(expr) for expr in value.split("\n"))) return EnvList(elements) diff --git a/src/tox/config/of_type.py b/src/tox/config/of_type.py index d31922f6c..4d49d16ef 100644 --- a/src/tox/config/of_type.py +++ b/src/tox/config/of_type.py @@ -7,16 +7,13 @@ from typing import TYPE_CHECKING, Callable, Generic, Iterable, TypeVar, cast from tox.config.loader.api import ConfigLoadArgs, Loader +from tox.config.types import CircularChainError if TYPE_CHECKING: from tox.config.loader.convert import Factory from tox.config.main import Config # pragma: no cover -class CircularChainError(ValueError): - """circular chain in config""" - - T = TypeVar("T") V = TypeVar("V") diff --git a/src/tox/config/source/ini_section.py b/src/tox/config/source/ini_section.py index d7d26720f..7ed3267f7 100644 --- a/src/tox/config/source/ini_section.py +++ b/src/tox/config/source/ini_section.py @@ -1,6 +1,6 @@ from __future__ import annotations -from tox.config.loader.ini.factor import extend_factors +from tox.config.loader.ini.factor import expand_ranges, extend_factors from tox.config.loader.section import Section @@ -15,7 +15,7 @@ def is_test_env(self) -> bool: @property def names(self) -> list[str]: - return list(extend_factors(self.name)) + return list(extend_factors(expand_ranges(self.name))) TEST_ENV_PREFIX = "testenv" diff --git a/src/tox/config/types.py b/src/tox/config/types.py index 8d1300aa4..42c2c4814 100644 --- a/src/tox/config/types.py +++ b/src/tox/config/types.py @@ -6,6 +6,10 @@ from tox.execute.request import shell_cmd +class CircularChainError(ValueError): + """circular chain in config""" + + class Command: # noqa: PLW1641 """A command to execute.""" diff --git a/tests/config/loader/ini/test_factor.py b/tests/config/loader/ini/test_factor.py index b73abd03b..5dbc50b57 100644 --- a/tests/config/loader/ini/test_factor.py +++ b/tests/config/loader/ini/test_factor.py @@ -178,6 +178,76 @@ def test_factor_config_no_env_list_creates_env(tox_ini_conf: ToxIniCreator) -> N assert list(config) == ["py37-django15", "py37-django16", "py36"] +@pytest.mark.parametrize( + ("env_list", "expected_envs"), + [ + pytest.param("py3{10-13}", ["py310", "py311", "py312", "py313"], id="Expand positive range"), + pytest.param("py3{10-11},a", ["py310", "py311", "a"], id="Expand range and add additional env"), + pytest.param("py3{10-11},a{1-2}", ["py310", "py311", "a1", "a2"], id="Expand multiple env with ranges"), + pytest.param( + "py3{10-12,14}", + ["py310", "py311", "py312", "py314"], + id="Expand ranges, and allow extra parameter in generator", + ), + pytest.param( + "py3{8-10,12,14-16}", + ["py38", "py39", "py310", "py312", "py314", "py315", "py316"], + id="Expand multiple ranges for one generator", + ), + pytest.param( + "py3{10-11}-django1.{3-5}", + [ + "py310-django1.3", + "py310-django1.4", + "py310-django1.5", + "py311-django1.3", + "py311-django1.4", + "py311-django1.5", + ], + id="Expand ranges and factor multiple environment parts", + ), + pytest.param( + "py3{10-11, 13}-django1.{3-4, 6}", + [ + "py310-django1.3", + "py310-django1.4", + "py310-django1.6", + "py311-django1.3", + "py311-django1.4", + "py311-django1.6", + "py313-django1.3", + "py313-django1.4", + "py313-django1.6", + ], + id="Expand ranges and parameters and factor multiple environment parts", + ), + pytest.param( + "py3{10-11},a{1-2}-b{3-4}", + ["py310", "py311", "a1-b3", "a1-b4", "a2-b3", "a2-b4"], + id="Expand ranges and parameters & factor multiple environment parts for multiple generative environments", + ), + pytest.param("py3{13-11}", ["py313", "py312", "py311"], id="Expand negative ranges"), + pytest.param("3.{10-13}", ["3.10", "3.11", "3.12", "3.13"], id="Expand new-style python envs"), + pytest.param("py3{-11}", ["py3-11"], id="Don't expand left-open numerical range"), + pytest.param("foo{11-}", ["foo11-"], id="Don't expand right-open numerical range"), + pytest.param("foo{a-}", ["fooa-"], id="Don't expand right-open range"), + pytest.param("foo{-a}", ["foo-a"], id="Don't expand left-open range"), + pytest.param("foo{a-11}", ["fooa-11"], id="Don't expand alpha-umerical range"), + pytest.param("foo{13-a}", ["foo13-a"], id="Don't expand numerical-alpha range"), + pytest.param("foo{a-b}", ["fooa-b"], id="Don't expand non-numerical range"), + ], +) +def test_env_list_expands_ranges(env_list: str, expected_envs: list[str], tox_ini_conf: ToxIniCreator) -> None: + config = tox_ini_conf( + f""" + [tox] + env_list = {env_list} + """ + ) + + assert list(config) == expected_envs + + @pytest.mark.parametrize( ("env", "result"), [ @@ -202,6 +272,18 @@ def test_ini_loader_raw_with_factors( assert outcome == result +def test_generative_section_name_with_ranges(tox_ini_conf: ToxIniCreator) -> None: + config = tox_ini_conf( + """ + [testenv:py3{11-13}-{black,lint}] + deps-x = + black: black + lint: flake8 + """, + ) + assert list(config) == ["py311-black", "py311-lint", "py312-black", "py312-lint", "py313-black", "py313-lint"] + + def test_generative_section_name(tox_ini_conf: ToxIniCreator) -> None: config = tox_ini_conf( """ From 3d35559ca1e9411708b9e5f73d610691a4fbdefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Thu, 27 Mar 2025 08:12:21 -0700 Subject: [PATCH 20/20] release 4.25.0 --- docs/changelog.rst | 12 ++++++++++++ docs/changelog/3500.bugfix.rst | 2 -- docs/changelog/3502.feature.rst | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) delete mode 100644 docs/changelog/3500.bugfix.rst delete mode 100644 docs/changelog/3502.feature.rst diff --git a/docs/changelog.rst b/docs/changelog.rst index 623d7a456..fa6077355 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,18 @@ Release History .. towncrier release notes start +v4.25.0 (2025-03-27) +-------------------- + +Features - 4.25.0 +~~~~~~~~~~~~~~~~~ +- Add support for number ranges in generative environments, more details :ref:`here`. - by :user:`mimre25` (:issue:`3502`) + +Bugfixes - 4.25.0 +~~~~~~~~~~~~~~~~~ +- Make tox tests pass with Python 3.14.0a6 + - by :user:`hroncok` (:issue:`3500`) + v4.24.2 (2025-03-07) -------------------- diff --git a/docs/changelog/3500.bugfix.rst b/docs/changelog/3500.bugfix.rst deleted file mode 100644 index 9df1cf05d..000000000 --- a/docs/changelog/3500.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make tox tests pass with Python 3.14.0a6 -- by :user:`hroncok` diff --git a/docs/changelog/3502.feature.rst b/docs/changelog/3502.feature.rst deleted file mode 100644 index 47772b8de..000000000 --- a/docs/changelog/3502.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for number ranges in generative environments, more details :ref:`here`. - by :user:`mimre25`