Skip to content

Commit

Permalink
Update tox_to_nox to support tox 4 (#687)
Browse files Browse the repository at this point in the history
Co-authored-by: Christopher Wilcox <[email protected]>
  • Loading branch information
frenzymadness and crwilcox authored Feb 20, 2024
1 parent ea9ea27 commit ef54d13
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 61 deletions.
29 changes: 28 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
matrix:
os: [ubuntu-20.04, windows-latest, macos-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
tox-version: ["latest", "<4"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -37,7 +38,33 @@ jobs:
run: |
python -m pip install --disable-pip-version-check .
- name: Run tests on ${{ matrix.os }}
run: nox --non-interactive --error-on-missing-interpreter --session "tests-${{ matrix.python-version }}" -- --full-trace
run: nox --non-interactive --error-on-missing-interpreter --session "tests(python='${{ matrix.python-version }}', tox_version='${{ matrix.tox-version }}')" -- --full-trace
- name: Save coverage report
uses: actions/upload-artifact@v3
with:
name: coverage
path: .coverage.*

coverage:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Nox-under-test
run: |
python -m pip install --disable-pip-version-check .
- name: Download individual coverage reports
uses: actions/download-artifact@v3
with:
name: coverage
- name: Display structure of downloaded files
run: ls -aR
- name: Run coverage
run: nox --non-interactive --session "cover"

lint:
runs-on: ubuntu-latest
Expand Down
33 changes: 33 additions & 0 deletions nox/tox4_to_nox.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import nox

{% for envname, envconfig in config.items()|sort: %}
@nox.session({%- if envconfig.base_python %}python='{{envconfig.base_python}}'{%- endif %})
def {{fixname(envname)}}(session):
{%- if envconfig.description != '' %}
"""{{envconfig.description}}"""
{%- endif %}
{%- set envs = envconfig.get('set_env', {}) -%}
{%- for key, value in envs.items()|sort: %}
session.env['{{key}}'] = '{{value}}'
{%- endfor %}

{%- if envconfig.deps %}
session.install({{envconfig.deps}})
{%- endif %}

{%- if not envconfig.skip_install %}
{%- if envconfig.use_develop %}
session.install('-e', '.')
{%- else %}
session.install('.')
{%- endif -%}
{%- endif %}

{%- if envconfig.change_dir %}
session.chdir('{{envconfig.change_dir}}')
{%- endif %}

{%- for command in envconfig.commands %}
session.run({{command}})
{%- endfor %}
{% endfor %}
85 changes: 76 additions & 9 deletions nox/tox_to_nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,40 @@
from __future__ import annotations

import argparse
import os
import pkgutil
from collections.abc import Iterator
from typing import Any
import re
from configparser import ConfigParser
from pathlib import Path
from subprocess import check_output
from typing import Any, Iterable

import jinja2
import tox.config
from tox import __version__ as TOX_VERSION

_TEMPLATE = jinja2.Template(
pkgutil.get_data(__name__, "tox_to_nox.jinja2").decode("utf-8"), # type: ignore[union-attr]
extensions=["jinja2.ext.do"],
)
TOX4 = TOX_VERSION[0] == "4"

if TOX4:
_TEMPLATE = jinja2.Template(
pkgutil.get_data(__name__, "tox4_to_nox.jinja2").decode("utf-8"), # type: ignore[union-attr]
extensions=["jinja2.ext.do"],
)
else:
_TEMPLATE = jinja2.Template(
pkgutil.get_data(__name__, "tox_to_nox.jinja2").decode("utf-8"), # type: ignore[union-attr]
extensions=["jinja2.ext.do"],
)

def wrapjoin(seq: Iterator[Any]) -> str:

def wrapjoin(seq: Iterable[Any]) -> str:
"""Wrap each item in single quotes and join them with a comma."""
return ", ".join([f"'{item}'" for item in seq])


def fixname(envname: str) -> str:
"""Replace dashes with underscores and check if the result is a valid identifier."""
envname = envname.replace("-", "_")
envname = envname.replace("-", "_").replace("testenv:", "")
if not envname.isidentifier():
print(
f"Environment {envname!r} is not a valid nox session name.\n"
Expand All @@ -58,7 +71,61 @@ def main() -> None:

args = parser.parse_args()

config = tox.config.parseconfig([])
if TOX4:
output = check_output(["tox", "config"], text=True)
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(section["deps"].strip().splitlines())

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([])

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

write_output_to_file(output, args.output)
26 changes: 22 additions & 4 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,40 @@
nox.options.sessions.append("conda_tests")


@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"])
def tests(session: nox.Session) -> None:
@nox.session
@nox.parametrize(
"python, tox_version",
[
(python, tox_version)
for python in ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12")
for tox_version in ("latest", "<4")
],
)
def tests(session: nox.Session, tox_version: str) -> None:
"""Run test suite with pytest."""
# Because there is a dependency conflict between
# argcomplete and the latest tox (both depend on
# a different version of importlibmetadata for Py 3.7)
# pip installs tox 3 as the latest one for Py 3.7.
if session.python == "3.7" and tox_version == "latest":
return

session.create_tmp() # Fixes permission errors on Windows
session.install("-r", "requirements-test.txt")
session.install("-e", ".[tox_to_nox]")
if tox_version != "latest":
session.install(f"tox{tox_version}")
session.run(
"pytest",
"--cov=nox",
"--cov-config",
"pyproject.toml",
"--cov-report=",
*session.posargs,
env={"COVERAGE_FILE": f".coverage.{session.python}"},
env={
"COVERAGE_FILE": f".coverage.{session.python}.tox.{tox_version.lstrip('<')}"
},
)
session.notify("cover")


@nox.session(python=["3.7", "3.8", "3.9", "3.10"], venv_backend="conda")
Expand Down
12 changes: 8 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ dependencies = [
[project.optional-dependencies]
tox_to_nox = [
"jinja2",
"tox<4",
"tox",
]
[project.urls]
bug-tracker = "https://github.com/wntrblm/nox/issues"
Expand All @@ -64,9 +64,6 @@ tox-to-nox = "nox.tox_to_nox:main"
[tool.hatch]
metadata.allow-ambiguous-features = true # disable normalization (tox-to-nox) for back-compat

[tool.ruff]
target-version = "py37"

[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear
Expand All @@ -85,6 +82,12 @@ ignore = [
"ISC001", # Conflicts with formatter
]

[tool.ruff]
target-version = "py37"

[tool.isort]
profile = "black"

[tool.pytest.ini_options]
minversion = "6.0"
addopts = [ "-ra", "--strict-markers", "--strict-config" ]
Expand All @@ -95,6 +98,7 @@ testpaths = [ "tests" ]

[tool.coverage.run]
branch = true
relative_files = true
omit = [ "nox/_typing.py" ]

[tool.coverage.report]
Expand Down
Loading

0 comments on commit ef54d13

Please sign in to comment.