← Back to DevBytes

macOS Managing Multiple Python Versions

Introduction to Managing Multiple Python Versions on macOS

macOS ships with a system Python, but relying on it for development is a recipe for trouble. As projects evolve, you'll inevitably need different Python versions — one project might require Python 3.8 for legacy compatibility, while another demands the latest 3.12 release. Managing these versions cleanly is an essential skill for any macOS developer.

What Is Python Version Management?

Python version management is the practice of installing, isolating, and switching between multiple Python interpreters on a single machine. Rather than overwriting the system Python or manually juggling binaries, you use specialized tools that let you declare which Python version each project should use. This ensures reproducible environments, smoother collaboration, and fewer "works on my machine" headaches.

Why It Matters on macOS

Popular Tools for Managing Python Versions

Several tools exist for managing multiple Python versions on macOS. The three most popular are pyenv, asdf, and uv. Each has its strengths, but pyenv remains the most widely adopted and beginner-friendly option.

pyenv

pyenv lets you install multiple Python versions side-by-side and switch between them per-project or globally. It works by intercepting Python commands through shim scripts inserted into your PATH.

asdf

asdf is a polyglot version manager — it handles Python, Node.js, Ruby, and many other runtimes through plugins. It's ideal if you work across multiple language ecosystems and want a single tool to manage them all.

uv

uv, from the team behind Ruff, is a fast Rust-based Python package and project manager that also handles Python version installation. It's quickly gaining traction for its speed and modern workflow.

Installing pyenv on macOS

The easiest way to install pyenv is through Homebrew. If you don't have Homebrew installed, install it first from brew.sh.

Step 1: Install pyenv

brew update
brew install pyenv

Step 2: Configure Your Shell

pyenv needs to initialize itself in your shell. The exact configuration depends on your shell. For zsh (the default on modern macOS), add the following to your ~/.zshrc file:

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init - zsh)"' >> ~/.zshrc

For bash users, add the equivalent lines to ~/.bash_profile:

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

Restart your shell or run source ~/.zshrc to apply the changes.

Step 3: Install Build Dependencies

Building Python from source requires several dependencies. Install them with Homebrew:

brew install openssl readline sqlite3 xz zlib tcl-tk

Using pyenv to Manage Python Versions

Viewing Available Versions

To see all Python versions available for installation:

pyenv install --list

This outputs a long list including CPython, PyPy, Anaconda, and others. You can filter it with grep:

pyenv install --list | grep " 3.1[0-2]"

Installing a Python Version

To install a specific version, use the install command:

pyenv install 3.12.1
pyenv install 3.11.7
pyenv install 3.10.13

Installation takes a few minutes as pyenv compiles Python from source. If you encounter build errors, ensure all dependencies from the previous step are installed.

Viewing Installed Versions

pyenv versions

Output looks like this, with an asterisk marking the active version:

* system (set by /Users/you/.pyenv/version)
  3.10.13
  3.11.7
  3.12.1

Setting the Global Python Version

The global version is the default Python used when no project-specific version is set:

pyenv global 3.12.1
python --version
# Python 3.12.1

Setting a Local (Project-Specific) Version

This is where pyenv truly shines. Navigate to your project directory and set a local version:

cd ~/projects/legacy-app
pyenv local 3.10.13

This creates a .python-version file in the directory. Whenever you enter that directory, pyenv automatically switches to the specified version:

cat .python-version
# 3.10.13

python --version
# Python 3.10.13

Setting a Shell-Specific Version

For temporary use within a single shell session:

pyenv shell 3.11.7
python --version
# Python 3.11.7

This overrides both global and local settings until the shell closes.

Uninstalling a Python Version

pyenv uninstall 3.10.13

Combining pyenv with Virtual Environments

Managing Python versions is only half the battle — you also need to isolate project dependencies. Virtual environments solve this. With pyenv, each Python version includes venv out of the box.

Creating a Virtual Environment

cd ~/projects/my-app
pyenv local 3.12.1
python -m venv .venv
source .venv/bin/activate

After activation, your prompt shows the environment name, and python points to the isolated interpreter:

which python
# /Users/you/projects/my-app/.venv/bin/python

pip install requests
pip freeze > requirements.txt

Using pyenv-virtualenv Plugin

The pyenv-virtualenv plugin integrates virtual environment management directly into pyenv:

brew install pyenv-virtualenv

Add this to your ~/.zshrc:

echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.zshrc

Now you can create named virtual environments tied to specific Python versions:

pyenv virtualenv 3.12.1 myapp-env
pyenv activate myapp-env
pyenv deactivate

You can also set a virtual environment as the local version for a project:

cd ~/projects/my-app
pyenv local myapp-env

Alternative: Using uv for Python Version Management

uv is a modern, extremely fast alternative that combines Python version management, package installation, and project management in a single tool.

Installing uv

brew install uv

Installing Python Versions

uv python install 3.12 3.11 3.10

Listing Installed Versions

uv python list

Creating a Project with a Specific Python Version

uv init my-project
cd my-project
uv python pin 3.12
uv add requests

uv creates a .python-version file and a pyproject.toml, managing both the interpreter and dependencies automatically. Its speed makes it especially attractive for large projects or CI pipelines.

Best Practices

Never Use System Python for Development

Always install and use a managed Python version. The system Python may change or disappear with macOS updates, breaking your projects unexpectedly.

Pin Python Versions Per Project

Always commit the .python-version file to version control. This ensures every developer on the team uses the same interpreter, reducing environment-related bugs.

Use Virtual Environments for Every Project

Never install project dependencies globally. Each project should have its own virtual environment, even if it shares the same Python version as another project. This prevents dependency conflicts.

Keep Your Tools Updated

brew upgrade pyenv
pyenv update  # if installed via pyenv-installer

New Python versions are released frequently, and pyenv updates add support for building them.

Clean Up Unused Versions

Over time, you may accumulate many Python versions that consume disk space. Periodically review and remove versions you no longer need:

pyenv versions
pyenv uninstall 3.8.18

Document Your Setup

Include setup instructions in your project's README. Specify the required Python version, the version manager you use, and the steps to create the virtual environment. This onboards new contributors faster.

Consider a Version Manager for Multiple Languages

If you work with Node.js, Ruby, Go, or other languages alongside Python, consider asdf or mise (a Rust-based asdf alternative) to manage all runtimes with a single tool and consistent workflow.

Troubleshooting Common Issues

Build Failures During Installation

If pyenv install fails, the most common cause is missing build dependencies. Ensure you've installed all required Homebrew packages. You can also check the build log:

cat /tmp/python-build.*.log

Python Version Not Switching

If pyenv doesn't switch versions, verify your shell configuration is correct. Run this diagnostic command:

pyenv doctor

Also ensure the pyenv shims directory appears at the front of your PATH:

echo $PATH
# Should show ~/.pyenv/shims near the beginning

Wrong Python After Activation

If activating a virtual environment still shows the wrong Python, make sure you created the virtual environment after setting the correct pyenv version. The virtual environment inherits the Python version active at creation time.

Conclusion

Managing multiple Python versions on macOS doesn't have to be painful. Tools like pyenv and uv provide clean, predictable workflows for installing and switching between interpreters, while virtual environments keep dependencies isolated per project. By adopting these tools and following best practices — pinning versions per project, never touching the system Python, and documenting your setup — you'll eliminate a whole class of environment-related bugs and make your development workflow more robust and reproducible. Whether you choose the battle-tested pyenv or the blazing-fast uv, the key is consistency: pick a tool, configure it properly, and use it for every project.

— Ad —

Google AdSense will appear here after approval

← Back to all articles