When using Python, you’ll encounter various version environments. First, distributions like RedHat and Debian have their own system Python installed, and you can also use Python built from source on a per-account basis as needed. Python has major syntax and built-in library differences between 2.x and 3.x versions, and minor versions can have different behaviors or implementations in some features - a free but sometimes risky situation.
Of course, if you only operate one project on a single system and the version will never change, you may not need to worry about this. However, generally, a single system can have various Python projects, and these may often be implemented based on different Python versions. If you reduce cohesion between projects through dependency isolation, individual projects don’t need to worry about other environments.
I’d like to introduce pyenv as an open-source solution suitable for these needs! Of course, you can control versions without using open-source tools. It’s possible by properly managing environment variables like $PATH and $PYTHON_PATH. However, thinking about dealing with these environment variables in every project seemed like it could be wasteful, so I decided to learn more about pyenv.
Brief Overview of Core Features
pyenv provides the following features:
- Change Global Python Version per user
- Use Python version management features per project
- Allow overriding Python versions through environment variables
- Query multiple Python versions at specific times. This is useful for libraries or CI tools that run tests on various versions.
And here are the differences from similar solutions:
- No dependency on Python. Implemented in pure shell scripts.
- Need to load Pyenv as an environment variable in the shell. In other words, you need to add pyenv’s shim access directory to
$PATH. - Can manage virtualenv. You can use virtualenv directly, or automate the process of creating virtual environments through pyenv-virtualenv.
Understanding How It Works
In Unix/Linux systems, when executing a specific command, the system searches for executable files in the directory list registered in $PATH in order. Common commands like cd and rm work because they’re registered in the $PATH environment variable. If not registered in $PATH, you’d have to specify the executable file with an absolute path like /bin/cd or /bin/rm every time. $PATH searches directories from left to right, and if there are executable files with the same name, the file in the directory found first is used.
When pyenv installation is complete, you initially call eval $(pyenv init -), which registers PATH=$(pyenv root)/shims:$PATH in the environment variable. At this point, the path is dynamically created through $(pyenv root), which pyenv project refers to as Hash (or Rehash).
Afterwards, when you install Python through pyenv, it’s installed in $(pyenv root)/versions/<version> path. Then, binary files are created in the $(pyenv root)/shims directory.
How Shims Work
Installation
Automatic Installation (Recommended)
| |
Manual Installation
For initial installation, clone from the official Github project. Installing at $HOME/.pyenv is most recommended but not required, so clone to an appropriate path.
| |
Set the following environment variables in the appropriate rc file for your shell. (I’m using zsh shell. If using bash shell, modify the .bash_profile file.)
| |
[Caution] When using bash shell, if BASH_ENV calls .bashrc on some systems, adding the above content to .bashrc may cause an infinite loop, so be careful. (Must add to .bash_profile.)
Finally, run the following command to reflect the above changes:
| |
Configurable Environment Variables
PYENV_VERSION Specify the Python version to use.
PYENV_ROOT Specify the root directory where pyenv will be installed. (Default: ~/.pyenv)
PYENV_DEBUG Whether to expose pyenv debug information cf. pyenv --debug <subcommand>
PYENV_HOOK_PATH Define the search path to use in pyenv hooks feature. The pyenv hooks feature is an expert option used when you want a specified script to run at specific times during pyenv commands, so refer to the following wiki for details. pyenv hook wiki
PYENV_DIR Enter the path to find the .python-version file. (Default: $PWD)
PYTHON_BUILD_ARIA2_OPTS If pyenv finds the aria2c binary path in $PATH and it’s executable, it uses aria2 to download Python source. This environment variable passes options for that. You can control bandwidth, number of connections, etc. aria2c options
Selecting Latest or Specific Version
To use the latest commit version that hasn’t been officially released, run the following command:
| |
If you want to use a specific released tag, run the following command. (For example, using v1.0.9 version, using v0.9.4, etc.)
| |
Uninstallation
After removing the installed pyenv directory, remove all environment variables set above.
| |
Understanding Commands
Now that pyenv installation is complete, let’s summarize the commands provided by pyenv. I won’t cover all commands, only frequently used and essential content. To check other commands or commands added in newer versions, refer to the pyenv COMMANDS page.
Installing Python
You can install specific Python versions or see the list of installable Python versions.
- Install specific Python version
| |
Build Options
If you need to set compile options during Python build, you can set them through the CONFIGURE_OPTS environment variable.
| |
If HTTP(S) proxy settings are needed, set http_proxy and https_proxy environment variables beforehand.
For various build issues like required packages/libraries installation and CPU architecture selection, refer to the common build problems wiki page.
- View list of all installable Python versions
| |
Uninstalling Python
Used to delete already installed Python. If you need to delete all versions and no longer use pyenv, you can permanently delete with rm -rf $(pyenv root). However, using the pyenv uninstall command substitutes with another available Python.
| |
Managing Python Versions
pyenv handles various Python versions and can select different versions as needed and even multiple versions by using various environment variables.
First, pyenv has the following priority when selecting Python: $PYENV_VERSION > $PYENV_DIR/.python-version > $PYENV_ROOT/version. Each value can be set through pyenv shell, pyenv local, and pyenv global commands.
In other words: python call -> pyenv hooks command -> get version info according to priority -> execute that version of Python.
Additionally, a unique feature is that you can select multiple Python versions. If you decide to use versions 2.7.13 and 3.4.6? Just set pyenv (shell|local|global) 2.7.13 3.4.6. With this setting, when you call the python command, Python version 2.7.13 is called. If you want to use version 3.4.6 as default? Just change the order to pyenv (shell|local|global) 3.4.6 2.7.13.
Let me summarize the details below.
pyenv shell
This command manages Python versions in the shell. In other words, it sets the $PYENV_VERSION environment variable to specify the version to use, and this environment variable has the highest priority among other setting methods. It’s useful when Python version needs to change per script or when version needs to be determined at execution time.
| |
| |
pyenv local
This command manages Python versions in specific directories. More precisely, it’s a command that defines the Python version to use in $PYENV_DIR/.python-version. If you haven’t specially set the $PYENV_DIR environment variable, by default this file is created in $PWD, i.e., the current directory.
| |
| |
pyenv global
This command manages system Python versions. More precisely, it’s a command to define the version to use in $PYENV_ROOT/version. If you haven’t specially defined the $PYENV_ROOT environment variable, it defaults to ~/.pyenv.
| |
| |
Version Check Commands
| |
Using pyenv-virtualenv
pyenv-virtualenv is a pyenv plugin that manages virtualenv.
Installation
| |
Shell Settings
| |
Creating Virtual Environments
| |
Auto-activation per Project
| |
Practical Examples
Django Project Setup
| |
Using with tox (Multi-version Testing)
| |
CI/CD Usage
| |
Troubleshooting
Common Problems
1. Build Failure
| |
Solution (Ubuntu/Debian):
| |
Solution (macOS):
| |
2. Command Not Found
| |
Solution:
| |
3. Version Not Changing
| |
Solution:
| |
4. Permission Issues
| |
Solution:
| |
Best Practices
1. Commit .python-version per Project
| |
2. Manage with requirements.txt
| |
3. .python-version and runtime.txt
PaaS like Heroku uses runtime.txt:
4. Combining pyenv with poetry
| |