Behold, My Stuff

[Home] [Writing] [CV] [GitHub] [Email]

Python Project Setup

Recently I have split some python projects into separate packages, rather than git submodules. In an effort to make these packages useful to myself in a variety of situtations, I want to make the packages available on PyPI and ensure they run on multiple versions of python (at least the versions I have installed on my local machine–3.6 to 3.9). flit makes it easy to publish packages to PyPI if there’s nothing complicated going on (like compiling C extensions or bundling JavaScript). Then I use tox as a generic command runner to run tests on multiple versions of Python, as well as check that code is formatted and has correct static types. This is a living document of the process I use to set up or convert some existing code to a standalone Python package.


  1. (Only if you use pyenv) Set up pyenv to use as many versions as you want to test with tox: pyenv local 3.9.12 3.8.11 3.7.12 3.6.15. The first version should be the one you want to use for your virtual environment.
  2. Make a new virtual environment. It doesn’t matter what version of Python you use–I use the latest version I have installed on my machine.
  3. pip install flit
  4. flit init. Answer the questions.
  5. pip install tox
  6. Make your tox.ini file. pyproject.toml is supported by tox but only as a single string, so for now we are stuck with tox.ini. I hav an example tox.ini at the end of this post.
  7. Install all your dependencies in your virtual environment (pip install pytest black isort mypy # etc.) and develop and write tests as normal.
  8. Run tox and it will create the virtual environments then run the tests, type checks and formatting checks.

Additional Tips

Make isort follow black’s conventions:

profile = "black"

Add coverage to pytest: pip install pytest-cov

addopts = "--cov prelude --cov-report html"

Example tox.ini File

The isolated_build = True is required for pyproject.toml-based projects.

envlist =
isolated_build = True

deps =
commands =

deps =
commands =
  mypy --strict prelude

deps =
commands =
  isort --quiet --check .
  black --quiet --check .

[Relevant link] [Source]

Sam Stevens, 2022