Skip to content

Commit

Permalink
First implementation of tox_to_nox for tox 4
Browse files Browse the repository at this point in the history
  • Loading branch information
frenzymadness committed Jan 25, 2023
1 parent 1fe4414 commit b709168
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 7 deletions.
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 %}
83 changes: 77 additions & 6 deletions nox/tox_to_nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,38 @@
from __future__ import annotations

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

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:
return ", ".join([f"'{item}'" for item in seq])


def fixname(envname: str) -> str:
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 @@ -49,7 +63,64 @@ 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 = {}

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

if isinstance(section.get("commands"), str):
config[name]["commands"] = [
wrapjoin(c.split())
for c in section["commands"].strip().splitlines()
]

if isinstance(section.get("deps"), str):
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)

with open(args.output, "w") as outfile:
Expand Down
11 changes: 11 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ def tests(session: nox.Session) -> None:
*session.posargs,
env={"COVERAGE_FILE": f".coverage.{session.python}"},
)
# Test tox_to_nox with older tox
session.install("tox<4")
session.run(
"pytest",
"--cov=nox",
"--cov-config",
"pyproject.toml",
"--cov-report=",
"tests/test_tox_to_nox.py",
env={"COVERAGE_FILE": f".coverage.{session.python}.tox3"},
)
session.notify("cover")


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 @@ classifiers = [
[project.optional-dependencies]
tox_to_nox = [
"jinja2",
"tox<4",
"tox",
]

[project.urls]
Expand Down
4 changes: 4 additions & 0 deletions tests/test_tox_to_nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
import textwrap

import pytest
from tox import __version__ as TOX_VERSION

from nox import tox_to_nox

TOX4 = TOX_VERSION[0] == "4"


@pytest.fixture
def makeconfig(tmpdir):
Expand Down Expand Up @@ -277,6 +280,7 @@ def test_with_dash(session):
)


@pytest.mark.skipif(TOX4, reason="Not supported in tox 4.")
def test_non_identifier_in_envname(makeconfig, capfd):
result = makeconfig(
textwrap.dedent(
Expand Down

0 comments on commit b709168

Please sign in to comment.