Mac: multiple Python versions/virtualenv with brew and pyenv

Although you could use brew to install Python directly, the cleaner way to manage Python versions and isolate Python virtual environments is by using pyenv and pyenv-virtualenv.

pyenv allows you to install and switch between different versions of Python, while pyenv-virtualenv provides isolation of pip modules, for independence between projects.

Install brew package manager

Install brew from the terminal per the official brew instructions.

# install brew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# add brew environment variables to current shell
eval "$(/opt/homebrew/bin/brew shellenv)"

Install pyenv

Instead of using brew to directly install Python, use brew to install pyenv.  This will provide more flexibility in managing multiple Python versions from the same machine.

brew install pyenv pyenv-virtualenv

Configure zsh settings

To persist the proper shell settings, add the following lines to ~/.zprofile

# brew variables and PATH
eval "$(/opt/homebrew/bin/brew shellenv)"
command -v brew >/dev/null 2>&1 || export PATH=/opt/homebrew/bin:$PATH

# for pyenv and pyenv-virtualenv
command -v pyenv >/dev/null 2>&1 || export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

reload terminal with new settings.

exec "$SHELL"

Multiple Python versions with pyenv

Use pyenv to install two different versions of Python.

pyenv install --list | grep 3\.12

# install 2 different versions
pyenv install 3.12.0
pyenv install 3.12.1

# set global Python version as default
pyenv versions; pyenv global 3.12.1; pyenv versions

# validate where python and pip are pointed to
which python; which python3
which pip; which pip3

# validate newest version of python and pip are in use
python --version; python3 --version
pip --version; pip3 --version

# make sure we are using latest version of pip globally
pip3 install --upgrade pip

Distinct virtualenv for pip module isolation

Use virtualenv to create isolated environments that can be used for specific projects.

# create virtualenv for two different versions of Python
pyenv virtualenv 3.12.0 pytest3120
pyenv virtualenv 3.12.1 pytest3121

# list virtualenv available
pyenv virtualenvs

Test pip module isolated to pytest3120 virtualenv.

# enter virtualenv
pyenv activate pytest3120
python --version
pip list
pip install python-string-utils
pip list

# test usage of module
python -c "import string_utils; print(string_utils.is_string('hello'))"

# leave virtualenv
pyenv deactivate

Test pip module isolated to pytest3121 virtualenv.

# enter virtualenv
pyenv activate pytest3121
python --version
pip list
pip install simple_env
pip list

# test usage of module
foo=BAR python -c "import simple_env as se; print(se.get('foo'))"

# leave virtualenv
pyenv deactivate

Auto-loading virtualenv

Because of this line added to ~/.zprofile earlier, we also have the ability to auto-activate a virtualenv simply by changing into a python project directory.

eval "$(pyenv virtualenv-init -)"

Here we auto-load the virtualenv named “pytest3120”.

# create python project directory
mkdir pytest3120 && cd $_

# set virtualenv to use
pyenv local pytest3120
# special file now created, which triggers virtualenv
cat .python-version

# show local pyenv settings
pyenv versions
pyenv which python; pyenv which pip
python --version ; pip --version

# test, should be 3.12.0 version
echo -en '#!/usr/bin/env python\nimport sys\nprint(sys.version)' > main.py
python main.py

cd ..

And here we auto-load the virtualenv named “pytest3121”.

# create python project directory
mkdir pytest3121 && cd $_

# set virtualenv to use
pyenv local pytest3121
# special file now created, which triggers virtualenv
cat .python-version

# show local pyenv settings
pyenv versions
pyenv which python; pyenv which pip
python --version ; pip --version

# test, should be 3.12.1 version
echo -en '#!/usr/bin/env python\nimport sys\nprint(sys.version)' > main.py
python main.py

cd ..

VS Code and virtualenv

VS Code will NOT automatically pick up the virtualenv coming from the “.python-version” file.  You will need to manually specify the Python interpreter.

VS Code installation

If you have not installed VS Code, it can be done with brew.

brew install --cask visual-studio-code

Then open VSCode, and install the “Python” extension.

Select Python Interpreter

From the VS Code menu: File > Open Folder, and select the “pytest3120” folder created earlier from the terminal.

Open the “main.py” file.

From the VS Code menu: View > Command Palette, type “Python: Select Interpreter” and select the “pytest3120” virtualenv from the pull down list.

This value should then be reflected in the lower-right of the VS Code window, and running main.py will show that Python 3.12.0 is being used.

 

REFERENCES

github pyenv

github pyenv-virtualenv

realpython.com, usage of pyenv

Matthew Broberg, using brew to install pyenv (instead of python directly)

stackoverflow, brew rmrec to uninstall dependencies