On UNIX-like operating systems you can have the Python equivalent of node_modules today, for every Python version, without changing your workflows.

There is tool-envy in the Python community towards communities with all-encompassing packaging tools1 like JavaScript and their npm. One of the commonly desired features is something akin to the node_modules directory instead of managing virtualenvs.

On UNIX-like operating systems2, you can have that thanks to a tool that I can recommend regardless of Python packaging: direnv.

At its core it’s for setting environment variables when entering a directory. But it has a ton of other features, including baked-in support for venv (and more Python-specific tools).

All it takes is the following line in the .envrc in the project’s directory:

layout python python3.9

Now whenever you enter that directory, direnv will look into the local .direnv directory and either activate or create a venv for the exact Python version. As of writing this it would be .direnv/python-3.9.6.

If something breaks, just delete it. The only thing to keep in mind is that it’s usually a good idea to update pip and setuptools whenever direnv creates a new venv for you (aka whenever entering the directory takes unusually long). To clean up stale venvs I just use

$ find . -type d -name "python-OLD-VERSION" -delete

If you use pyenv to manage your Python installations3 and don’t want direnv to create a new venv whenever you update your Python, you can also use it to set the Python version:

layout pyenv 3.9.6

My thanks go to Chris for telling me about this a few months ago.


Windows users will have to wait for Kushal’s PEP 582 to ship or play with his implementation of it. Or take a look at pdm.


  1. A topic I have a blog post draft on. I know about Poetry; please don’t tell me about it. ↩︎

  2. Including WSL, of course. ↩︎

  3. I personally prefer using asdf nowadays, because I can use it for managing all kinds of compilers and runtimes. It uses a well-known per-directory file called .tool-versions. Don’t use Homebrew’s Python↩︎