pyenv is a command-line tool that enables you to manage different Python environments in your
$HOME, in isolation from interpreters located in root-owned directory
/usr, typically called "system" Python interpreters.
Note: This tutorial is part of a series about Python environments. You can go back to the first part with this link: Sane Python environment Part 1, isolation.
In this tutorial, we will assume that you are using Ubuntu or macOS. If you are using macOS, you should make sure to install the Xcode Command Line Tools (
xcode-select --install) and Homebrew.
The installation of pyenv requires some dependencies, so please install those first:
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl git
brew install openssl readline xz
As pyenv is independent of your OS Python, it does not use Python for installation, and relies on a Bash script instead. This Bash script (
bin/pyenv-installer) is provided in the pyenv-installer repo. Install it using the following command:
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
Pitfall: DO NOT use your system package manager to install pyenv! Indeed, your package manager is tightly coupled to other components of your system and this goes against isolation. Moreover, your package manager usually writes files to sub-directories of root-owned directory
/usr/local/Cellarwith Homebrew on macOS), which will almost always cause permissions issues later. While a poweruser will be able to deal with potential regressions introduced by the system package manager, the ordinary user most likely won't. To be convinced that dealing with the OS package manager in Python is difficult, you can google
site:stackoverflow.com brew python, which at time of writing returns 32'000 results.
After downloading pyenv and installing it to
~/.pyenv, you will need to add a few initialization lines to your Shell config file (e.g.
export PATH="$HOME/.pyenv/bin:$PATH" eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)"
These initialization lines include a modification of your
$PATH, which enforces the precedence of pyenv's
bin directory of your
$HOME over the other registered directories, which usually include the
bin sub-directories of
/usr such as
/usr/local/bin. This "precedence trick" is called a shim and enables pyenv to intercept all Python-related commands such as
After updating your Shell config, exit/logout from your terminal (e.g. with
exit) and then open it again. Please note that it's not enough to just
source your Shell config file in many cases!
Pitfall: If your Shell config files contain some lines with
export PATH="SOME_DIR:$PATH"already, make sure the pyenv initialization lines are added afterwards, to make sure
$HOME/.pyenv/bintakes precedence in your
After opening the new terminal, the
pyenv command should be available, and you should be able to run
pyenv versions. This command will show you all the Python environments installed on your computer. As no new environment was installed yet, you should just see the "system" Python of your OS:
➜ pyenv versions * system (set by /home/pierre/.pyenv/version)
Before attempting to install a Python version, first check that your machine is ready for it:
➜ pyenv doctor Cloning /home/pierre/.pyenv/plugins/pyenv-doctor/bin/..... Installing python-pyenv-doctor... Installed python-pyenv-doctor to /tmp/pyenv-doctor.20191103204543.28966/prefix Congratulations! You are ready to build pythons!
pyenv doctor fails, fix the missing installations that it found. Once
pyenv doctor succeeds, we can check which Python versions are available for installation, and install one of them:
➜ pyenv install --list | grep 3.8 3.8.0 3.8-dev 3.8.1 miniconda-3.8.3 miniconda3-3.8.3 ➜ pyenv install 3.8.1 Downloading Python-3.8.1.tar.xz... -> https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tar.xz Installing Python-3.8.1... Installed Python-3.8.1 to /home/pierre/.pyenv/versions/3.8.1 # Make sure the shims are up-to-date ➜ pyenv rehash ➜ pyenv versions * system (set by /home/pierre/.pyenv/version) 3.8.1
As you can see, our new Python version was installed successfully in
~/.pyenv/versions. In the output of the
pyenv versions command, the little star next to the version name indicates the current global version of Python, which is initially
system. Thanks to pyenv, your system Python is still active and fully functional. It was not destroyed by your new Python
To make sure our shims are up-to-date, we also ran
pyenv rehash after installing the interpreter. If you don't run
pyenv rehash, your new interpreter might not be installed correctly, as the GitHub member "dangitall" commented on the project issue tracker. I personally encountered the same issue on on macOS, Ubuntu 18.04 and Ubuntu 19.04 with pyenv 1.2.15.
➜ python Python 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
After screaming out of joy for your preserved system, you can switch to your new version of Python using the
pyenv global command:
➜ pyenv global 3.8.1 ➜ python Python 3.8.1 (default, Jan 7 2020, 23:51:55) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Pitfall: In case your Python version is not updated after running
pyenv global, run
pyenv rehashor exit and re-enter your terminal.
You can then switch back and forth between your versions using the same command:
➜ pyenv global system ➜ python Python 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> ➜ pyenv global 3.8.1 ➜ python Python 3.8.1 (default, Jan 7 2020, 23:51:55) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Finally, be aware that pyenv has the ability to select multiple global Pythons at the same time, even though it is not required for common development use cases. That is, you may see other tutorials on the Internet where there is more than one single star in the output of
Pitfall: As you can see, the notions of
systemPython do not refer to the same thing in pyenv. Your
systemPython is the one distributed by your OS or package manager (located somewhere in
/usr), while your
globalPython is just a pointer to the Python currently selected by pyenv. This pointer is most often pointing to some sub-directory of the
.pyenvdirectory in your
$HOME, but can also refer to the
systemPython. To avoid messing up your system Python, you should always have your global Python pointing to a version installed using pyenv. In our case, the global Python should be pointing to the version
3.8.1that was installed earlier.
If you run into issues with the
$PATH manipulations of pyenv, these StackOverflow answers might help you:
How to correctly set the $PATH? by Gilles and Stéphane Chazelas on StackOverflow Unix
What should/shouldn't go in .zshenv, .zshrc, ...? by Smith Jon on StackOverflow Unix
Choosing between .bashrc, .profile, etc... by Dan Rabinowitz and Gilles on StackOverflow SuperUser
pyenv is self-contained, so it can be removed fairly easily if needed:
pipxfrom the next part of the tutorial, make sure to uninstall it first before uninstalling pyenv.
export PATH=...line containing pyenv
rm -rf ~/.pyenv
Now that you are familiar with pyenv, you can learn about Python tooling isolation in Part 3 of the tutorial.