How to Update Python Version: The Better Way
We’ve all been there — there comes a time when we must update our Python version to meet a different Python version yet, be it at work or when working on personal projects.
Just a quick search about “update python version” and we will be bombarded with suggestions to run python --version
followed by brew upgrade python3
or sudo apt-get update
.
Okay cool. Problem solved right?
Not really. Probably 9/10 times an upgrade won’t cut it — enter another project. And guess what? It wants a different Python version, maybe an older one just to make our life a little bit more miserable.
So, here we are, stuck between a rock and a hard place, asking ourselves, "Do I downgrade Python now? But what if I need to juggle both projects? I didn't sign up for this symlink or PATH variable wrestling match!”
Let's use pyenv
?
We know it’s not uncommon either to find ourselves in another project needing a different Python version yet.
Now, with some quick search, you can tell most Python folks swear by pyenv
(docs) for managing Python versions.
Don’t get me wrong, it works but it’s just not for me.
Well, given that I work with a lot of CLI tools like go
, node
, terraform
, git
, etc., I prefer the simplicity of using a single tool – asdf
.
In other words, I very much prefer to use asdf
to manage all my programming language or CLI tool versions, rather than dealing with the likes of gvm
, nvm
, and pyenv
separately.
asdf
Python Quick Guide
Beyond Python, asdf
supports various plugins. But, let's focus on Python without delving into exhaustive details.
asdf plugin list all
to list all available plugins.Installation
Easy, just follow asdf-vm.com/guide/getting-started.html based on your system specifications:
- OS (e.g. linux, macOS)
- Package manager (e.g.
brew
,apt
,pacman
, etc.) - Shell (e.g.
zsh
,bash
, etc.)
For instance, on macOS with Homebrew and ZSH:
# Using Homebrew on macOS
brew install asdf
echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc
Setup
Add the Python plugin:
# asdf plugin add <name>: Adds a plugin for managing a specific runtime
# e.g. <name>: python, nodejs, golang
asdf plugin add python
Basic Usage
Let’s install our first Python version:
# asdf install <name> <version>: Installs a specific version of a runtime
asdf install python 3.12.1
What if most of your projects rely on Python 3.12.1? Well, let’s set Python 3.12.1 as our global/default Python version:
# asdf global <name> <version>: Sets a global (default) version of a runtime
asdf global python 3.12.1
cd && python --version # Python 3.12.1
Next, let’s install more Python versions!
asdf install python 3.8.13
asdf install python 3.9.16
asdf install python 3.10.9
asdf install python 3.11.3
Wait, I lost track, how many different Python versions have I installed…?
asdf list python
# 3.10.9
# 3.11.3
# *3.12.1
# 3.8.13
# 3.9.16
"*3.12.1
" in the asdf list python
output indicates that 3.12.1 is the currently active (local) Python version in the current directory.
Now, you can easily switch between different Python versions:
# Go to my project
cd ~/github.com/ngshiheng/burplist
# Project current Python version
python --version # Python 3.12.1
# But, I need Python 3.8.13
asdf local python 3.8.13
# Yay!
python --version # Python 3.8.13
That’s it! You'll likely find yourself using this set of commands about 80% of the time.
Cheatsheet
Here's a quick refresher:
# "How to install a specific Python version?"
asdf install python 3.12.1
# "What versions have I installed?"
asdf list python
# Set version on global level
asdf global python 3.12.1
# Set version on project level
asdf local python 3.12.1
# "What is my current Python version in this dir?"
python --version
.tools-version
Now you may notice that your project directory may contain a file called .tool-versions
. It's used to remember which versions of these tools each project needs (reference).
Should I commit this file to Git?
If having the .tool-versions
file in your project helps everyone on your team use the same versions of tools, then it's a good idea to include it in your source control. It keeps things consistent for everyone.
Not Just Python
This approach isn't limited to Python; it works for managing versions of other tools like Node.js, Go, etc. All you need to do is to replace "python
" with the respective tool/plugin name:
# Same examples, but in golang:
asdf plugin add golang
asdf install golang 1.21.6
asdf list golang
asdf global golang 1.21.6
asdf local golang 1.21.6
go version
# Same examples, but in nodejs:
asdf plugin add nodejs
asdf install nodejs 21.6.1
asdf list nodejs
asdf global nodejs 21.6.1
asdf local nodejs 21.6.1
node --version
Closing Thought
These days, when considering adopting a new tool, I've adopted a systematic approach:
- Firstly, I check
asdf
to see if there's plugin support available (asdf plugin list all
). Vet the plugin first! - If not, I explore whether the specific CLI tool has its own version manager like
gvm
,nvm
,rubyenv
,pyenv
,tfenv
, etc. - If neither option is viable, then only I resort to installing the tool from the source via my package manager like
brew
orapt
Following this decision-making chain has significantly simplified version management for all my tools, saving me considerable time and pain.
References
P/S: A friend recommended an alternative to asdf called mise. I haven't had the chance to check it out yet, but it seems promising. I might give it a try in the near future.