Pipenv is a very useful tool to create and maintain independent Python working environments. Using it feels like a breeze. There are enough online tutorials about it, and I will only talk about one specific thing in this article: how to move a virtual environment to another machine.
The reason I need to make virtual environments movable is that our clients do not usually allow direct Internet access in production environments, therefore we cannot install packages from online sources on production servers. They also often enforce a certain directory structure. So we need to prepare the environment in our test environment, and it would be better if we did not need to worry about where we put the result on the production server. Virtual environments, especially with the help of Pipenv, seem to provide a nice and painless way of achieving this effect—if we can just make the result of pipenv install
movable, or, in the term of virtualenv
, relocatable.
virtualenv
is already able to make most of the virtual environment relocatable. When working with Pipenv, it can be as simple as
virtualenv --relocatable `pipenv --venv`
There are two problems, though:
- The command line above does not make the
activate
script relocatable - At least on some 64-bit Linux distros, a
lib64
symlink needs to be present,1 and it currently points to the absolute path oflib
They are not difficult to solve, and we can conquer them one by one.
As pointed out in the issue discussion, one only needs to replace one line in activate
to make it relocatable. What is originally
VIRTUAL_ENV="/home/yongwei/.local/share/virtualenvs/something--PD5l8nP"
should be changed to
VIRTUAL_ENV=$(cd $(dirname "$BASH_SOURCE"); dirname `pwd`)
To be on the safe side, I would look for exactly the same line and replace it, so some sed
tricks are needed. I also need to take care of the differences between BSD sed
and GNU sed
, but it is a problem already solved before.
The second problem is even easier. Creating a new relative symlink solves the problem.
I’ll share the final result here, a simple script that can make a virtual environment relocatable, as well as creating a tarball from it. The archive has ‘-venv-platform’ as the suffix, but it does not include a root directory. Keep this in mind when you unpack the tarball.
#!/bin/sh case $(sed --version 2>&1) in *GNU*) sed_i () { sed -i "$@"; };; *) sed_i () { sed -i '' "$@"; };; esac sed_escape() { echo $1|sed -e 's/[]\/$*.^[]/\\&/g' } VENV_PATH=`pipenv --venv` if [ $? -ne 0 ]; then exit 1 fi virtualenv --relocatable "$VENV_PATH" VENV_PATH_ESC=`sed_escape "$VENV_PATH"` RUN_PATH=`pwd` BASE_NAME=`basename "$RUN_PATH"` PLATFORM=`python -c 'import sys; print(sys.platform)'` cd "$VENV_PATH" sed_i "s/^VIRTUAL_ENV=\"$VENV_PATH_ESC\"/VIRTUAL_ENV=\$(cd \$(dirname \"\$BASH_SOURCE\"); dirname \`pwd\`)/" bin/activate [ -h lib64 ] && rm -f lib64 && ln -s lib lib64 tar cvfz $RUN_PATH/$BASE_NAME-venv-$PLATFORM.tar.gz .
After running the script, I can copy result tarball to another machine of the same OS, unpack it, and then either use the activate
script or set the PYTHONPATH
environment variable to make my Python program work. Problem solved.
A last note: I have not touched activate.csh
and activate.fish
, as I do not use them. If you did, you would need to update the script accordingly. That would be your homework as an open-source user. 😼
- I tried removing it, and Pipenv was very unhappy. ↩