If you are a pip1 and virtualenv user you already know how easy it is
to install python packages. Unlike the bad old days when I started
programming in Python, 9 years ago, it is now easy to add, remove and
manage python modules. In fact we can leverage pip to create an
command for a python program, for example and ease of illustration, a
Table of Contents
- Desired Features
- Basic Idea
- Uses a server controlled by the owner (instead of the Python Package Index).
- Install an arbitrary version of the program.
- Defaults to updating to the newest version of the major release one is tracking.
I often want my programs to update themselves from a specific location.
For instance an internal server or perhaps my github account.
Fortunately pip already supports such nicities with the
-e for the
Additionally, when running a generic update you often want to stay on the same major revision and simply get the bug fixes. However, it is important to provide the option to update to any arbitrary release including tracking the master branch.
Use Pip and the
-e option plus a base URL to automatically update your
software. eg. Have you software run pip for the user.
pip install --upgrade --src="$HOME/.src" -e git+<URL>@<REV>#egg=PACKAGE_NAME
Tracking Major Versions
To track major version updates some care must be taken in setting up the repository. I use branches instead of tags to track major releases. This allows me to push out bug fix updates for every one tracking that release. I tag minor releases to allow users to install a specific version.
When checking out branches using pip you have to supply
pip install --upgrade --src="$HOME/.src" -e git+https://github.com/user/repo.git@origin/branch#egg=PACKAGE_NAME
While when checking out a commit you should not supply origin
pip install --upgrade --src="$HOME/.src" -e git+https://github.com/user/repo.git@COMMIT_ID#egg=PACKAGE_NAME
Why does pip work like this? Because of the commands it executes. For the command:
pip install --upgrade --src="$HOME/.src" -e git+https://github.com/user/repo.git@<VERSION>#egg=PACKAGE_NAME
git fetch -q git reset --hard -q <VERSION>
Store the tracked version in the source
To ensure the update command installs the correct updates I put which release to checkout in the source code. This allows me to "release" a version by creating a branch and then changing the RELEASE constant to point the name of the branch.
Note: This is example code only, you should modify for security and stability of your enviroment.
Note: I didn't include virtualenv support in this code but it is trivial to add.
from subprocess import check_call as run from getopt import getopt, GetoptError RELEASE = 'master' # default release SRC_DIR = "$HOME/.src" # checkout directory UPDATE_CMD = ( # base command 'pip install --src="%s" --upgrade -e ' 'git://github.com/timtadh/swork.git@%s#egg=swork' ) @command def update(args): try: opts, args = getopt(args, 'sr:', ['sudo', 'src=', 'release=', 'commit=']) except GetoptError, err: log(err) usage(error_codes['option']) sudo = False src_dir = SRC_DIR release = RELEASE commit = None for opt, arg in opts: if opt in ('-s', '--sudo'): sudo = True elif opt in ('-r', '--release'): release = arg elif opt in ('--src',): src_dir = arg elif opt in ('--commit',): commit = arg if release.isdigit(): ## Check if it is a version release = 'r' + release release = 'origin/' + release ## assume it is a branch if commit is not None: ## if a commit is supplied use that cmd = UPDATE_CMD % (src_dir, commit) else: cmd = UPDATE_CMD % (src_dir, release) if sudo: run('sudo %s' % cmd) else: run(cmd)