Nox: An Awesome Tox Alternative

If you’ve been writing much Python you have probably heard of or seen the tox library. tox is a tool that can be used to execute linting, testing, and really any commands you would like, and do so against multiple versions of Python. tox even creates virtual environments specific for its testing purposes so you don’t need to worry about that, and it does all of this with a handy CLI “front end.” All-in-all, tox is pretty great!

But! There is another! Enter nox! nox serves pretty much the same purpose that tox does, however instead of dealing with an ini type configuration file as tox does, nox allows you to simply write more Python code! For me this has been a very welcome change of pace! There is nothing wrong with tox, and nothing particularly cumbersome about the “normal” tox.ini file. If anything the tox.ini file is probably more succinct than a noxfile.py, but, I’m a Python developer and I’m already writing Python, so it feels nice and easy to simply write a little bit more Python!

Here is a simple example of a function that I put in the noxfile.py (which replaces tox.ini) that handles running black against my project:

import nox


@nox.session(python=["3.8"])
def black(session):
    """
    Nox run black

    Args:
        session: nox session

    Returns:
        N/A  # noqa: DAR202

    Raises:
        N/A

    """
    session.install(f"black{DEV_REQUIREMENTS['black']}")
    session.run("black", "--check", ".")

Nice and easy! session is an argument that is passed to this function when invoked with the nox CLI utility. With this session object we can call the install method to install… really whatever we want – in this case I have some additional code elsewhere to parse my requirements.txt file, and I am simply installing black with whatever that version is. Next we simply call the run method – which does pretty much what it sounds – it “runs” the command that we pass it. In this case we are simply running black in check mode against the current directory.

Once you’ve got a noxfile.py setup (the above snippet is all you need to get going) you can run nox -l to list the available nox sessions, or simply run nox without arguments to run all of the sessions in your noxfile.py. For a quick demo here is an example of running just the black session in the scrapli project:

$ nox -l
Sessions defined in /Users/carl/dev/github/scrapli/noxfile.py:

* unit_tests-3.6 -> Nox run unit tests
* unit_tests-3.7 -> Nox run unit tests
* unit_tests-3.8 -> Nox run unit tests
* unit_tests-3.9 -> Nox run unit tests
* isort-3.8 -> Nox run isort
* black-3.8 -> Nox run black
* pylama-3.8 -> Nox run pylama
* pydocstyle-3.8 -> Nox run pydocstyle
* mypy-3.8 -> Nox run mypy
* darglint-3.8 -> Nox run darglint

sessions marked with * are selected, sessions marked with - are skipped.
$ nox -s black
nox > Running session black-3.8
nox > Creating virtual environment (virtualenv) using python3.8 in .nox/black-3-8
nox > pip install black>=20.8b1
nox > black --check .
All done! ✨ 🍰 ✨
152 files would be left unchanged.
nox > Session black-3.8 was successful.

Nothing to it!

I’ve converted all of my projects over from tox to nox as I prefer to just keep writing Python, and I am quite happy with that move! Take nox for a test drive and let me know (or better yet, the author!) what you think!