Skip to content

Commit

Permalink
feat: drop support for tox 3 (#910)
Browse files Browse the repository at this point in the history
* feat: drop support for tox 3

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update ci.yml

* tests: include Python version

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: drop unused code

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
henryiii authored Jan 15, 2025
1 parent 82b5542 commit 12b805a
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 183 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ jobs:
channels: conda-forge/label/python_rc,conda-forge
- name: Install Nox-under-test (uv)
run: uv pip install --system .
- name: Run tests on ${{ matrix.os }} (tox <4)
run: nox --non-interactive --error-on-missing-interpreter --session "tests(python='${{ matrix.python-version }}', tox_version='<4')" -- --full-trace
- name: Run tox-to-nox tests on ${{ matrix.os }} (tox latest)
run: nox --non-interactive --error-on-missing-interpreter --session "tests(python='${{ matrix.python-version }}', tox_version='latest')" -- tests/test_tox_to_nox.py --full-trace
- name: Run tests on ${{ matrix.os }}
run: nox --non-interactive --error-on-missing-interpreter --session "tests-${{ matrix.python-version }}" -- --full-trace
- name: Save coverage report
uses: actions/upload-artifact@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ To just check for lint errors, run:

To run against a particular Python version:

nox --session "tests(python='3.12', tox_version='latest')"
nox --session tests-3.12
nox --session conda_tests
nox --session mamba_tests
nox --session micromamba_tests
Expand Down
36 changes: 0 additions & 36 deletions nox/tox_to_nox.jinja2

This file was deleted.

142 changes: 61 additions & 81 deletions nox/tox_to_nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import re
import sys
from configparser import ConfigParser
from importlib import metadata
from pathlib import Path
from subprocess import check_output
from typing import Any, Iterable

import jinja2
import tox.config

if sys.version_info < (3, 9):
from importlib_resources import files
Expand All @@ -42,22 +40,12 @@ def __dir__() -> list[str]:
return __all__


TOX_VERSION = metadata.version("tox")

TOX4 = int(TOX_VERSION.split(".")[0]) >= 4

DATA = files("nox")

if TOX4:
_TEMPLATE = jinja2.Template(
DATA.joinpath("tox4_to_nox.jinja2").read_text(encoding="utf-8"),
extensions=["jinja2.ext.do"],
)
else:
_TEMPLATE = jinja2.Template(
DATA.joinpath("tox_to_nox.jinja2").read_text(encoding="utf-8"),
extensions=["jinja2.ext.do"],
)
_TEMPLATE = jinja2.Template(
DATA.joinpath("tox4_to_nox.jinja2").read_text(encoding="utf-8"),
extensions=["jinja2.ext.do"],
)


def wrapjoin(seq: Iterable[Any]) -> str:
Expand All @@ -66,14 +54,11 @@ def wrapjoin(seq: Iterable[Any]) -> str:


def fixname(envname: str) -> str:
"""Replace dashes with underscores and check if the result is a valid identifier."""
envname = envname.replace("-", "_").replace("testenv:", "")
if not envname.isidentifier():
print(
f"Environment {envname!r} is not a valid nox session name.\n"
"Manually update the session name in noxfile.py before running nox."
)
return envname
"""
Replace dashes with underscores. Tox 4+ requires valid identifiers for
names already.
"""
return envname.replace("-", "_").replace("testenv:", "")


def write_output_to_file(output: str, filename: str) -> None:
Expand All @@ -88,65 +73,60 @@ def main() -> None:

args = parser.parse_args()

if TOX4:
output = check_output(["tox", "config"], text=True, encoding="utf-8") # noqa: S607
original_config = ConfigParser()
original_config.read_string(output)
config: dict[str, dict[str, Any]] = {}

for name, section in original_config.items():
if name == "DEFAULT":
continue

config[name] = dict(section)
# Convert set_env from string to dict
set_env = {}
for var in section.get("set_env", "").strip().splitlines():
k, v = var.split("=")
if k not in {
"PYTHONHASHSEED",
"PIP_DISABLE_PIP_VERSION_CHECK",
"PYTHONIOENCODING",
}:
set_env[k] = v

config[name]["set_env"] = set_env

config[name]["commands"] = [
wrapjoin(c.split()) for c in section["commands"].strip().splitlines()
output = check_output(["tox", "config"], text=True, encoding="utf-8") # noqa: S607
original_config = ConfigParser()
original_config.read_string(output)
config: dict[str, dict[str, Any]] = {}

for name, section in original_config.items():
if name == "DEFAULT":
continue

config[name] = dict(section)
# Convert set_env from string to dict
set_env = {}
for var in section.get("set_env", "").strip().splitlines():
k, v = var.split("=")
if k not in {
"PYTHONHASHSEED",
"PIP_DISABLE_PIP_VERSION_CHECK",
"PYTHONIOENCODING",
}:
set_env[k] = v

config[name]["set_env"] = set_env

config[name]["commands"] = [
wrapjoin(c.split()) for c in section["commands"].strip().splitlines()
]

config[name]["deps"] = wrapjoin(
[
part
for dep in section["deps"].strip().splitlines()
for part in (dep.split() if dep.startswith("-r") else [dep])
]
)

config[name]["deps"] = wrapjoin([
part for dep
in section["deps"].strip().splitlines()
for part in
(dep.split() if dep.startswith("-r") else [dep])
])

for option in "skip_install", "use_develop":
if section.get(option):
if section[option] == "False":
config[name][option] = False
else:
config[name][option] = True

if os.path.isabs(section["base_python"]) or re.match(
r"py\d+", section["base_python"]
):
impl = (
"python" if section["py_impl"] == "cpython" else section["py_impl"]
)
config[name]["base_python"] = impl + section["py_dot_ver"]

change_dir = Path(section.get("change_dir"))
rel_to_cwd = change_dir.relative_to(Path.cwd())
if str(rel_to_cwd) == ".":
config[name]["change_dir"] = None
else:
config[name]["change_dir"] = rel_to_cwd

else:
config = tox.config.parseconfig([])
for option in "skip_install", "use_develop":
if section.get(option):
if section[option] == "False":
config[name][option] = False
else:
config[name][option] = True

if os.path.isabs(section["base_python"]) or re.match(
r"py\d+", section["base_python"]
):
impl = "python" if section["py_impl"] == "cpython" else section["py_impl"]
config[name]["base_python"] = impl + section["py_dot_ver"]

change_dir = Path(section.get("change_dir"))
rel_to_cwd = change_dir.relative_to(Path.cwd())
if str(rel_to_cwd) == ".":
config[name]["change_dir"] = None
else:
config[name]["change_dir"] = rel_to_cwd

output = _TEMPLATE.render(config=config, wrapjoin=wrapjoin, fixname=fixname)

Expand Down
20 changes: 4 additions & 16 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,16 @@
]


@nox.session
@nox.parametrize(
"python, tox_version",
[
(python, tox_version)
for python in ALL_PYTHONS
for tox_version in ("latest", "<4")
],
)
def tests(session: nox.Session, tox_version: str) -> None:
@nox.session(python=ALL_PYTHONS)
def tests(session: nox.Session) -> None:
"""Run test suite with pytest."""

coverage_file = (
f".coverage.{sys.platform}.{session.python}.tox{tox_version.lstrip('<')}"
)
coverage_file = f".coverage.{sys.platform}.{session.python}"

session.create_tmp() # Fixes permission errors on Windows
session.install(*PYPROJECT["dependency-groups"]["test"], "uv")
session.install("-e.[tox_to_nox]")
if tox_version != "latest":
session.install(f"tox{tox_version}")
extra_env = {"PYTHONWARNDEFAULTENCODING": "1"} if tox_version == "latest" else {}
extra_env = {"PYTHONWARNDEFAULTENCODING": "1"}
session.run(
"pytest",
"--cov",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ dependencies = [
optional-dependencies.tox_to_nox = [
"importlib-resources; python_version<'3.9'",
"jinja2",
"tox",
"tox>=4",
]
optional-dependencies.uv = [
"uv>=0.1.6",
Expand Down
2 changes: 1 addition & 1 deletion requirements-conda-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ argcomplete >=1.9.4,<3.0
colorlog >=2.6.1,<7.0.0
jinja2
pytest
tox<4.0.0
tox>=4.0.0
virtualenv >=20.14.1
46 changes: 3 additions & 43 deletions tests/test_tox_to_nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

tox_to_nox = pytest.importorskip("nox.tox_to_nox")

TOX4 = tox_to_nox.TOX4
PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
PYTHON_VERSION_NODOT = PYTHON_VERSION.replace(".", "")

Expand Down Expand Up @@ -289,44 +288,6 @@ def test_with_dash(session):
)


@pytest.mark.skipif(TOX4, reason="Not supported in tox 4.")
def test_non_identifier_in_envname(
makeconfig: Callable[[str], str], capfd: pytest.CaptureFixture[str]
) -> None:
result = makeconfig(
textwrap.dedent(
f"""
[tox]
envlist = test-with-&
[testenv:test-with-&]
basepython = python{PYTHON_VERSION}
"""
)
)

assert (
result
== textwrap.dedent(
f"""
import nox
@nox.session(python='python{PYTHON_VERSION}')
def test_with_&(session):
session.install('.')
"""
).lstrip()
)

out, _ = capfd.readouterr()

assert (
out == "Environment 'test_with_&' is not a valid nox session name.\n"
"Manually update the session name in noxfile.py before running nox.\n"
)


def test_descriptions_into_docstrings(makeconfig: Callable[[str], str]) -> None:
result = makeconfig(
textwrap.dedent(
Expand Down Expand Up @@ -371,17 +332,16 @@ def test_commands_with_requirements(makeconfig: Callable[[str], str]) -> None:
pytest
pytest-cov
aiohttp: -r requirements/aiohttp.txt
"""
)
""")
)

assert (
result
== textwrap.dedent("""
== textwrap.dedent(f"""
import nox
@nox.session(python='python3.13')
@nox.session(python='python{PYTHON_VERSION}')
def aiohttp(session):
session.install('pytest', 'pytest-cov', '-r', 'requirements/aiohttp.txt')
session.install('-e', '.')
Expand Down

0 comments on commit 12b805a

Please sign in to comment.