This article contains affiliate links. See my affiliate disclosure for more information.

Beginners should install Python with the official installers. But the installer workflow isn't very efficient for professional developers working on multiple projects — or any project that supports multiple Python versions.

In this guide, I'll show you how to:

  • Install multiple versions of Python with pyenv.
  • Install global utilities like black and flake8 with pipx.
  • Manage project dependencies with pip-tools.

Install Python With pyenv

pyenv allows you to install multiple versions of Python and switch between them as needed. To install pyenv, run the command below that corresponds to your OS.

Note: pyenv isn't the only option for managing Python versions. Some developers prefer tools like asdf or homebrew. Others use their operating system's native package manager.

Use the automatic installer below, or see the README for more options:

curl | bash

You'll need to restart your shell when you're done:

exec "$SHELL"

Install with PowerShell below, or see the README for more options:

Invoke-WebRequest -UseBasicParsing -Uri "" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"
pyenv is only compatible with Unix/Linux operating systems. Windows users must use the separately maintained pyenv-win fork. pyenv and pyenv-win share the same CLI commands, so this guide works for every OS.

Once pyenv or pyenv-win is installed, run pyenv install 3.11 to install the latest version of Python 3.11:

pyenv install 3.11

At the time of the writing, the latest version is 3.11.1.

Use a different version prefix or a specific version number to install other versions of Python. For example, the following command installs the latest version of Python 3.10:

pyenv install 3.10

To use a specific Python version that you've installed, run python global:

pyenv global 3.11.1

Now your python3 command points to Python 3.11.1.

Tip: Put a text file called .python-version containing a specific Python version number in your project's root directory. Whenever you cd into the project's folder, pyenv will automatically switch your interpreter to the version specified in .python-version.

Install Global Utilities With pipx

pipx is a Python package installer that installs applications and command-line utilities in isolated environments and makes them available globally.

This is particularly helpful for tools like black and flake8, as they can be installed once and used across multiple projects while still taking into account project-specific configuration files.

You can install pipx with pip:

python3 -m pip install pipx

Before you can use pipx, you need to add it to PATH:

python3 -m pipx ensurepath
Important: pipx links applications it installs to the same Python executable that was originally used to install pipx.

Consequently, when you invoke applications installed with pipx, those applications will run using the pipx Python executable. (Except for ipython, which will detect and use project-specific virtual environments.)

This is generally not an issue and may even be beneficial as it isolates tools from project dependencies. But be warned: Uninstalling the pipx Python executable renders any pipx-installed tools pointing to it unusable.

Frustratingly, you must install pipx for every version of Python that you install with pyenv.

Some of the tools I install with pipx are:

  • black: My preferred Python auto-formatter.
  • flake8: My preferred Python code linter.
  • ipython: My preferred Python REPL.

The following commands install all three tools in three separate environments:

pipx install black
pipx install flake8
pipx install ipython

Now you can use these tools as you normally would across all your projects without installing them in every project's environment.

A Note For VS Code Users

If you use Python for VS Code, you'll need to point your flake8 path to the right location. If you don't, you'll see a pop-up that says "flake8 is not installed" every time you open VS Code.

Run pipx list to list every app installed by pipx. The application install path is displayed on the second line of output:

$ pipx list
venvs are in /Users/damos/.local/pipx/venvs
apps are exposed on your $PATH at /Users/damos/.local/bin
   package black 22.12.0, installed using Python 3.11.1
    - black
    - blackd
   package flake8 6.0.0, installed using Python 3.11.1
    - flake8
   package ipython 8.9.0, installed using Python 3.11.1
    - ipython
    - ipython3

In my case, apps are installed in /Users/damos/.local/bin. Now run which flake8 to see which executable the flake8 command points to:

$ which flake8

Copy this path to your clipboard.

Important: The output of which flake8 should show flake8 in the same directory that pipx uses to install applications.

If it doesn't, you may have previously installed flake8 with a Python version that isn't managed by pyenv. In this case, you'll need to uninstall the old version of flake8 or remove the old version of Python.

Open the VS Code settings explorer by going to File > Settings menu or pressing Cmd+, on macOS or Ctrl+, on Linux/Windows.

Type flake8 into the search bar and press Enter. Then paste the path to flake8 into the input box for the Python > Linting: Flake8 Path setting:

Note: You may also set this in settings.json by editing the python.linting.flake8Path setting.

Manage Project Dependencies With pip-tools

I only had to use pip-tools for about 5 minutes to know I needed it all the time. pip-tools helps you where you need it to and gets out of the way everywhere else. The basic usage goes like this.

Start a new Python project in a new folder:

# Create a new folder and change directories to it
mkdir ~/my-project && cd ~/my-project

# Create a new virtual environment
python3 -m venv .venv --prompt my-project

# Activate the virtual environment
source .venv/bin/activate

# Update pip
python -m pip install -U pip

Then install pip-tools into the project's virtual environment using pip:

python -m pip install pip-tools
Important: Although it is possible to install and use pip-tools through pipx, it is not yet well supported. The docs officially recommend installing pip-tools into your project's environment, and I can confirm that this is the more reliable method.

Your project's dependencies go in a file called For example, the file for a Django 3 project might look like this:

# ~/my-project/


You can generate a requirements.txt with fully resolved dependencies using the pip-compile command:

pip-compile --allow-unsafe --resolver=backtracking
Note: The --allow-unsafe and --resolver=backtracking options will both become the default in the next major release of pip-tools. The docs recommend passing these options to adopt the new default behavior.

The requirements.txt file generated by pip-compile will look something like this:

# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#    pip-compile --allow-unsafe --resolver=backtracking
    # via django
    # via -r
    # via django
    # via django

Notice that there is a comment at the top of the file indicating that it was autogenerated by pip-compile (it even includes the command!) and that every version number is pinned.

I usually create a second file called for development dependencies, such as pytest and django-debug-toolbar, and even pip-tools itself:

# ~/my-project/

# Use requrements.txt as a constraint file
-c requirements.txt


Using requirements.txt as a constraint file ensures that any dependencies installed for the packages in are compatible with the package versions specified in requirements.txt.

Run pip-compile a second time to generate a dev-requirements.txt file:

pip-compile --allow-unsafe --resolver=backtracking

Now for the real magic. Run pip-sync to synchronize your virtual environment with the packages in the requirements.txt and dev-requirements.txt files:

pip-sync requirements.txt dev-requirements.txt

To update or add a package, edit the appropriate .in file and then re-compile and re-sync everything.

Note: I check all of my * and *-requirements.txt files into version control.

I often wrap all of this into a Makefile:

	@pip install -r requirements.txt -r dev-requirements.txt

	@pip-compile --allow-unsafe --resolver=backtracking
	@pip-compile --allow-unsafe --resolver=backtracking

	@pip-sync requirements.txt dev-requirements.txt

Collaborators can run make install after cloning the repository to set up their environment, then run make compile && make sync as needed to keep everything synchronized.

Python dependency management tools abound.

I know many Python devs that swear by poetry or pipenv. And I know plenty that still use good ol' pip freeze. Personally, pipenv and poetry are too heavy-handed for my taste. pip freeze feels tedious to me. And the new pdm project looks interesting but still has a long way to go.

Call me Goldilocks, but pip-tools is the porridge that's just right.

Acknowledgment: Much of my current Python tooling philosophy is inspired by Sebastian Witowski's Modern Python Devloper's Toolkit. I encourage everyone who uses Python to check it out.

Other pipx Apps To Consider

Depending on the kind of development you do, the following packages may be useful tools to install as applications with pipx:

  • cookiecutter for scaffolding projects.
  • pre-commit for managing pre-commit hooks.
  • tox for testing and task automation.
  • build frontend for building Python packages.

Dig Deeper

If you write and distribute Python packages, then you need to read Dan Hillard's book Publishing Python Packages.

In addition to covering how to set up, publish, maintain, and scale your package, Dane discusses setting up a professional development environment specifically for package development. It is far more versatile than the environment described in this guide.

Get instant access from Manning, or buy a print version from Amazon.