Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tox-dev/tox
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4.2.6
Choose a base ref
...
head repository: tox-dev/tox
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4.2.7
Choose a head ref
  • 6 commits
  • 10 files changed
  • 5 contributors

Commits on Jan 7, 2023

  1. Add release notes project URL for quick access in PyPI web (#2835)

    Use "Release Notes" as the link title instead of the document's
    "Release History" title to get a representative icon shown for it.
    
    https://github.com/pypi/warehouse/blob/04ee9be504caa89345617b2795ff53fc22631297/warehouse/templates/packaging/detail.html#L24
    scop authored Jan 7, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    761e461 View commit details

Commits on Jan 8, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    fd25b2b View commit details

Commits on Jan 10, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ce2e8db View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    34b79a2 View commit details

Commits on Jan 11, 2023

  1. Revert to supporting simple Python factors (#2849)

    Co-authored-by: Bernát Gábor <bgabor8@bloomberg.net>
    Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
    Fixes #2657
    Fixes #2848
    stephenfin authored Jan 11, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9f89f51 View commit details
  2. release 4.2.7

    gaborbernat committed Jan 11, 2023

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    b8e47ce View commit details
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/feature-request.md
Original file line number Diff line number Diff line change
@@ -18,7 +18,8 @@ assignees: ""

## Alternative Solutions

<!-- Have you tried to workaround the problem using tox or other tools? Or a different approach to solving this issue? Please elaborate here. -->
<!-- Have you tried to workaround the problem using tox or other tools? Or a different approach to solving this issue?
Please elaborate here. -->

## Additional context

12 changes: 2 additions & 10 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -40,18 +40,10 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.py }}
- name: Pick environment to run
run: |
import os; import platform; import sys; from pathlib import Path
env = f'TOXENV=py{"" if platform.python_implementation() == "CPython" else "py"}3{sys.version_info.minor}'
print(f"Picked: {env} for {sys.version} based of {sys.executable}")
with Path(os.environ["GITHUB_ENV"]).open("ta") as file_handler:
file_handler.write(env)
shell: python
- name: Setup test suite
run: tox r -vv --notest
run: tox r -e py${{ matrix.py }} -vv --notest
- name: Run test suite
run: tox r --skip-pkg-install
run: tox r -e py${{ matrix.py }} --skip-pkg-install
env:
CI_RUN: "yes"
DIFF_AGAINST: HEAD
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ repos:
hooks:
- id: rst-backticks
- repo: https://github.com/tox-dev/tox-ini-fmt
rev: "0.5.2"
rev: "0.6.0"
hooks:
- id: tox-ini-fmt
args: ["-p", "fix"]
@@ -69,7 +69,7 @@ repos:
- "@prettier/plugin-xml@2.2"
args: ["--print-width=120", "--prose-wrap=always"]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.32.2
rev: v0.33.0
hooks:
- id: markdownlint
- repo: local
16 changes: 16 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -4,6 +4,22 @@ Release History

.. towncrier release notes start
v4.2.7 (2023-01-11)
-------------------

Bugfixes - 4.2.7
~~~~~~~~~~~~~~~~
- A testenv with multiple factors, one of which conflicts with a ``base_python`` setting in ``tox.ini``, will now use the
correct Python interpreter version - by :user:`stephenfin`. (:issue:`2838`)
- Explicitly list ``wheel`` as requirement for the tests, as some of the tests error without it. (:issue:`2843`)
- tox has reverted support for Python factors that include PATCH release info (e.g. ``py3.10.1``), build architecture
(e.g. ``pypy3-64``) or do not define a ``py`` prefix or other supported prefix (e.g. ``3.10``). These complex factors
were initially supported with the release of tox 4.0 but has proven complicated to support. Instead, the simple factors
supported by tox 3 e.g. (``py310``, ``pypy3``) or period-separated equivalent (``py3.10``) introduced in tox 4 should be
used. Users who wish to specify more specific Python version information should configure the :ref:`base_python` setting
- by :user:`stephenfin`. (:issue:`2848`)


v4.2.6 (2023-01-06)
-------------------

7 changes: 7 additions & 0 deletions docs/upgrading.rst
Original file line number Diff line number Diff line change
@@ -54,6 +54,13 @@ Changed INI rules
- tox 4 requires the ``install_command`` to evaluate to a non-empty value for each target environment. In tox 3, an
empty value would be substituted for the default install command.

Known regressions
-----------------

- On Windows, the tty trait of the caller environment is no longer passed through. The most notable impact of this
change is that some tools no longer show colored output. You may need to force colorization to be for such enabled
for such tools. See :issue:`2337` for more details.

New plugin system
-----------------

20 changes: 11 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
build-backend = "hatchling.build"
requires = ["hatchling>=1.12", "hatch-vcs>=0.3"]
requires = ["hatchling>=1.12.2", "hatch-vcs>=0.3"]

[project]
name = "tox"
@@ -12,6 +12,7 @@ license = "MIT"
urls.Homepage = "http://tox.readthedocs.org"
urls.Source = "https://github.com/tox-dev/tox"
urls.Tracker = "https://github.com/tox-dev/tox/issues"
urls."Release Notes" = "https://tox.wiki/en/latest/changelog.html"
authors = [{ name = "Bernát Gábor", email = "gaborjbernat@gmail.com" }]
maintainers = [
{ name = "Anthony Sottile", email = "asottile@umich.edu" },
@@ -21,24 +22,24 @@ maintainers = [
]
requires-python = ">=3.7"
dependencies = [
"cachetools>=5.2",
"cachetools>=5.2.1",
"chardet>=5.1",
"colorama>=0.4.6",
"packaging>=22",
"packaging>=23",
"platformdirs>=2.6.2",
"pluggy>=1",
"pyproject-api>=1.2.1",
"pyproject-api>=1.4",
'tomli>=2.0.1; python_version < "3.11"',
"virtualenv>=20.17.1",
"filelock>=3.9",
'importlib-metadata>=5.2; python_version < "3.8"',
'importlib-metadata>=6; python_version < "3.8"',
'typing-extensions>=4.4; python_version < "3.8"',
]
optional-dependencies.docs = [
"furo>=2022.12.7",
"sphinx>=6",
"sphinx-argparse-cli>=1.10",
"sphinx-autodoc-typehints>=1.19.5",
"sphinx>=6.1.3",
"sphinx-argparse-cli>=1.11",
"sphinx-autodoc-typehints>=1.20.1",
"sphinx-copybutton>=0.5.1",
"sphinx-inline-tabs>=2022.1.2b11",
"sphinxcontrib-towncrier>=0.2.1a0",
@@ -52,13 +53,14 @@ optional-dependencies.testing = [
"distlib>=0.3.6",
"flaky>=3.7",
"hatch-vcs>=0.3",
"hatchling>=1.12",
"hatchling>=1.12.2",
"psutil>=5.9.4",
"pytest>=7.2",
"pytest-cov>=4",
"pytest-mock>=3.10",
"pytest-xdist>=3.1",
"re-assert>=1.1",
"wheel>=0.38.4",
"time-machine>=2.8.2; implementation_name != \"pypy\"",
]
scripts.tox = "tox.run:run"
42 changes: 27 additions & 15 deletions src/tox/tox_env/python/api.py
Original file line number Diff line number Diff line change
@@ -4,12 +4,12 @@
from __future__ import annotations

import logging
import re
import sys
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, List, NamedTuple, cast

from packaging.tags import INTERPRETER_SHORT_NAMES
from virtualenv.discovery.py_spec import PythonSpec

from tox.config.main import Config
@@ -47,6 +47,16 @@ def version_dot(self) -> str:
return f"{self.version_info.major}.{self.version_info.minor}"


PY_FACTORS_RE = re.compile(
r"""
^(?!py$) # don't match 'py' as it doesn't provide any info
(?P<impl>py|pypy|cpython|jython|rustpython|ironpython) # the interpeter; most users will simply use 'py'
(?P<version>[2-9]\.?[0-9]?[0-9]?)?$ # the version; one of: MAJORMINOR, MAJOR.MINOR
""",
re.VERBOSE,
)


class Python(ToxEnv, ABC):
def __init__(self, create_args: ToxEnvCreateArgs) -> None:
self._base_python: PythonInfo | None = None
@@ -127,28 +137,29 @@ def default_base_python(self, conf: Config, env_name: str | None) -> list[str]:
base_python = None if env_name is None else self.extract_base_python(env_name)
return [sys.executable if base_python is None else base_python]

@staticmethod
def extract_base_python(env_name: str) -> str | None:
@classmethod
def extract_base_python(cls, env_name: str) -> str | None:
candidates: list[str] = []
for factor in env_name.split("-"):
spec = PythonSpec.from_string_spec(factor)
impl = spec.implementation or "python"
if impl.lower() in INTERPRETER_SHORT_NAMES and env_name is not None and spec.path is None:
match = PY_FACTORS_RE.match(factor)
if match:
candidates.append(factor)
if candidates:
if len(candidates) > 1:
raise ValueError(f"conflicting factors {', '.join(candidates)} in {env_name}")
return next(iter(candidates))
return None

@staticmethod
def _validate_base_python(env_name: str, base_pythons: list[str], ignore_base_python_conflict: bool) -> list[str]:
elements = {env_name} # match with full env-name
elements.update(env_name.split("-")) # and also any factor
for candidate in elements:
spec_name = PythonSpec.from_string_spec(candidate)
if spec_name.implementation and spec_name.implementation.lower() not in INTERPRETER_SHORT_NAMES:
continue
@classmethod
def _validate_base_python(
cls,
env_name: str,
base_pythons: list[str],
ignore_base_python_conflict: bool,
) -> list[str]:
env_base_python = cls.extract_base_python(env_name)
if env_base_python is not None:
spec_name = PythonSpec.from_string_spec(env_base_python)
for base_python in base_pythons:
spec_base = PythonSpec.from_string_spec(base_python)
if any(
@@ -158,7 +169,8 @@ def _validate_base_python(env_name: str, base_pythons: list[str], ignore_base_py
):
msg = f"env name {env_name} conflicting with base python {base_python}"
if ignore_base_python_conflict:
return [env_name] # ignore the base python settings
# ignore the base python settings and return the thing that looks like a Python version
return [env_base_python]
raise Fail(msg)
return base_pythons

62 changes: 48 additions & 14 deletions tests/tox_env/python/test_python_api.py
Original file line number Diff line number Diff line change
@@ -71,6 +71,36 @@ def test_diff_msg_no_diff() -> None:
assert Python._diff_msg({}, {}) == "python "


@pytest.mark.parametrize(
("env", "base_python"),
[
("py3", "py3"),
("py311", "py311"),
("py3.12", "py3.12"),
("pypy2", "pypy2"),
("rustpython3", "rustpython3"),
("cpython3.8", "cpython3.8"),
("ironpython2.7", "ironpython2.7"),
("functional-py310", "py310"),
("bar-pypy2-foo", "pypy2"),
("py", None),
("django-32", None),
("eslint-8.3", None),
("py-310", None),
("py3000", None),
("4.foo", None),
("310", None),
("5", None),
("2000", None),
("4000", None),
],
ids=lambda a: "|".join(a) if isinstance(a, list) else str(a),
)
def test_extract_base_python(env: str, base_python: str | None) -> None:
result = Python.extract_base_python(env)
assert result == base_python


@pytest.mark.parametrize("ignore_conflict", [True, False])
@pytest.mark.parametrize(
("env", "base_python"),
@@ -87,26 +117,30 @@ def test_base_python_env_no_conflict(env: str, base_python: list[str], ignore_co

@pytest.mark.parametrize("ignore_conflict", [True, False])
@pytest.mark.parametrize(
("env", "base_python", "conflict"),
("env", "base_python", "expected", "conflict"),
[
("cpython", ["pypy"], ["pypy"]),
("pypy", ["cpython"], ["cpython"]),
("pypy2", ["pypy3"], ["pypy3"]),
("py3", ["py2"], ["py2"]),
("py38", ["py39"], ["py39"]),
("py38", ["py38", "py39"], ["py39"]),
("py38", ["python3"], ["python3"]),
("py310", ["py38", "py39"], ["py38", "py39"]),
("py3.11.1", ["py3.11.2"], ["py3.11.2"]),
("py3-64", ["py3-32"], ["py3-32"]),
("py310-magic", ["py39"], ["py39"]),
("pypy", ["cpython"], "pypy", ["cpython"]),
("pypy2", ["pypy3"], "pypy2", ["pypy3"]),
("py3", ["py2"], "py3", ["py2"]),
("py38", ["py39"], "py38", ["py39"]),
("py38", ["py38", "py39"], "py38", ["py39"]),
("py38", ["python3"], "py38", ["python3"]),
("py310", ["py38", "py39"], "py310", ["py38", "py39"]),
("py3.11", ["py310"], "py3.11", ["py310"]),
("py310-magic", ["py39"], "py310", ["py39"]),
],
ids=lambda a: "|".join(a) if isinstance(a, list) else str(a),
)
def test_base_python_env_conflict(env: str, base_python: list[str], conflict: list[str], ignore_conflict: bool) -> None:
def test_base_python_env_conflict(
env: str,
base_python: list[str],
expected: str,
conflict: list[str],
ignore_conflict: bool,
) -> None:
if ignore_conflict:
result = Python._validate_base_python(env, base_python, ignore_conflict)
assert result == [env]
assert result == [expected]
else:
msg = f"env name {env} conflicting with base python {conflict[0]}"
with pytest.raises(Fail, match=msg):
12 changes: 6 additions & 6 deletions tests/tox_env/python/virtual_env/test_virtualenv_api.py
Original file line number Diff line number Diff line change
@@ -33,10 +33,10 @@ def virtualenv_opt(monkeypatch: MonkeyPatch, mocker: MockerFixture) -> VirtualEn

def test_virtualenv_default_settings(tox_project: ToxProjectCreator, virtualenv_opt: VirtualEnvOptions) -> None:
proj = tox_project({"tox.ini": "[testenv]\npackage=skip"})
result = proj.run("r", "-e", "py", "--discover", sys.executable, str(proj.path / "a"))
result = proj.run("r", "-e", "py3", "--discover", sys.executable, str(proj.path / "a"))
result.assert_success()

conf = result.env_conf("py")
conf = result.env_conf("py3")
assert conf["system_site_packages"] is False
assert conf["always_copy"] is False
assert conf["download"] is False
@@ -46,7 +46,7 @@ def test_virtualenv_default_settings(tox_project: ToxProjectCreator, virtualenv_
assert virtualenv_opt.download is False
assert virtualenv_opt.copies is False
assert virtualenv_opt.no_periodic_update is True
assert virtualenv_opt.python == ["py"]
assert virtualenv_opt.python == ["py3"]
assert virtualenv_opt.try_first_with == [str(sys.executable), str(proj.path / "a")]


@@ -60,10 +60,10 @@ def test_virtualenv_flipped_settings(
)
monkeypatch.setenv("VIRTUALENV_CLEAR", "0")

result = proj.run("r", "-e", "py")
result = proj.run("r", "-e", "py3")
result.assert_success()

conf = result.env_conf("py")
conf = result.env_conf("py3")
assert conf["system_site_packages"] is True
assert conf["always_copy"] is True
assert conf["download"] is True
@@ -72,7 +72,7 @@ def test_virtualenv_flipped_settings(
assert virtualenv_opt.system_site is True
assert virtualenv_opt.download is True
assert virtualenv_opt.copies is True
assert virtualenv_opt.python == ["py"]
assert virtualenv_opt.python == ["py3"]


def test_virtualenv_env_ignored_if_set(
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@ description = do a release, required posarg of the version number
skip_install = true
deps =
gitpython>=3.1.30
packaging>=22
packaging>=23
towncrier>=22.12
commands =
python {toxinidir}/tasks/release.py --version {posargs}