Easing the pain of updating packages with pip
Python’s ecosystem is blessed with an array of tools for managing packages and dependencies. However if you are more of a DIY programmer, refuse to adopt a package manager, or simply LOVE pip, this article is for you! I will be focusing on the little-known pipdeptree package, and its integration within a bash script to automate the process of updating Python packages.
Before we dive into the script, let’s appreciate the utility of pipdeptree. This command line tool presents a clear, tree-like overview of all your installed Python packages and their dependencies. There are a variety of options on how this information is displayed, but we will be using the standard output.
The bash script
Our script begins by defining our three sets of important packages in separate arrays: primary, secondary, and pipdeptree_packages.
# List of packages to check and upgrade
primary=("pip" "pipenv" "setuptools" "wheel")
secondary=("pipdeptree")
# Get packages from the pipdeptree output
pipdeptree_packages=$(pipdeptree | grep '^[a-zA-Z]' | awk -F '==' '{print $1}')
The primary array consists of some fundamental Python packages, namely pip, pipenv, setuptools, and wheel, which could potentially affect how the rest of the packages are updated. The secondary array only contains the pipdeptree package. Last but definitely not least we have the array derived from pipdeptree’s output.
We then define a function, upgrade_packages(), which checks whether any packages in a given array are outdated. If they are, it upgrades them.
upgrade_packages() {
local outdated_packages=$(python3 -m pip list -o | tail -n +3 | cut -d " " -f 1)
local packages_to_check=("${@:2}")
local packages_to_upgrade=()
for package in "${packages_to_check[@]}"; do
for outdated_pkg in $outdated_packages; do
if [[ $outdated_pkg == $package ]]; then
packages_to_upgrade+=("$package")
fi
done
done
if [ ${#packages_to_upgrade[@]} -ne 0 ]; then
echo "Upgrading ${packages_to_upgrade[@]}"
python3 -m pip install --upgrade "${packages_to_upgrade[@]}"
else
echo "No $1 packages to upgrade."
fi
}
The function is then run for all three arrays sequentially.
upgrade_packages "primary" "${primary[@]}"
upgrade_packages "secondary" "${secondary[@]}"
upgrade_packages "common" "${pipdeptree_packages[@]}"
Done! Here is script in full:
#!/bin/bash
# List of packages to check and upgrade
primary=("pip" "pipenv" "setuptools" "wheel")
secondary=("pipdeptree")
upgrade_packages() {
local outdated_packages=$(python3 -m pip list -o | tail -n +3 | cut -d " " -f 1)
local packages_to_check=("${@:2}")
local packages_to_upgrade=()
for package in "${packages_to_check[@]}"; do
for outdated_pkg in $outdated_packages; do
if [[ $outdated_pkg == $package ]]; then
packages_to_upgrade+=("$package")
fi
done
done
if [ ${#packages_to_upgrade[@]} -ne 0 ]; then
echo "Upgrading ${packages_to_upgrade[@]}"
python3 -m pip install --upgrade "${packages_to_upgrade[@]}"
else
echo "No $1 packages to upgrade."
fi
}
upgrade_packages "primary" "${primary[@]}"
upgrade_packages "secondary" "${secondary[@]}"
# Get packages from the pipdeptree output
pipdeptree_packages=$(pipdeptree | grep '^[a-zA-Z]' | awk -F '==' '{print $1}')
upgrade_packages "common" "${pipdeptree_packages[@]}"
Keep in mind this is a bash script, so you must make it executable before using it. In other words, if you save the script as update_packages.sh in your home directory, your next step is to open up your terminal and change its permissions with this command:
chmod +x update_packages.sh
After which you should be able to run the script like this (again, assuming you are running it from within your system’s home directory):
./update_packages2.sh
Why??
I am indeed a stubborn programmer, specifically when it comes to using python’s built-in pip instead of standalone package managers. However, it has been hell trying to update my home environment manually, so I figured I would write this script, and if it helps at least one more poor soul out there, it has been time well spent!