Python, Venv and Cygwin
Update: since originally writing this post, Python has been updated to
create Scripts/activate
by default (see issue 22343).
A few posts back I went over using the venv module to generate Python virtual environments. Unfortunately there are a couple of complications if you use Cygwin.
Virtual environments under Windows
Assuming you are running Python 3.4 or greater, venv
should be part of
Python's standard library. Creating an environment works in the same way
as Unix-like systems:
python -m venv example_env
There are however a few subtle differences, for example bin/
is called
Scripts/
. Environments are also activated/deactivated with either a batch or
PowerShell script:
C:\Users\bob\Desktop> example_env\Scripts\activate.bat
(example_env) C:\Users\bob\Desktop> example_env\Scripts\deactivate.bat
C:\Users\bob\Desktop>
This works fine if you use cmd
or PowerShell. However the bash script to
activate the environment is not present. This is a problem if you want to use
Cygwin.
$ source example_env/Scripts/activate
bash: example_env/Scripts/activate: No such file or directory
Setting environment variables manually
You can work around this problem by manually setting environment variables:
$ export VIRTUAL_ENV="/c/Users/bob/Desktop/example_env"
$ export PATH="${VIRTUAL_ENV}/Scripts:${PATH}"
$ unset PYTHONHOME
Optionally you can also add the environment to the prompt:
$ export PS1="(example_env) ${PS1}"
(example_env) $
Deactivating the environment is then just a case of reverting the environment variables you changed previously:
(example_env) $ export PS1='$ '
$ export PATH="$(echo $PATH | sed "s|${VIRTUAL_ENV}/Scripts:||")"
$ unset VIRTUAL_ENV
Although the method above works, it's not exactly ideal.
Copying the POSIX script with sed
The script templates for venv
can be found in the Python lib/venv/scripts/
directory. The templates are rendered with the following replacements:
text = text.replace('__VENV_DIR__', context.env_dir)
text = text.replace('__VENV_NAME__', context.env_name)
text = text.replace('__VENV_PROMPT__', context.prompt)
text = text.replace('__VENV_BIN_NAME__', context.bin_name)
text = text.replace('__VENV_PYTHON__', context.env_exe)
Of the variables above, only __VENV_DIR__
, __VENV_BIN_NAME__
and
__VENV_PROMPT__
are used in the POSIX activate script. The following
sed command can be used to replace the variables and copy the
template:
sed -e 's|__VENV_DIR__|/c/Users/bob/Desktop/example_env|g' \
-e 's|__VENV_PROMPT__|(example_env) |g' \
-e 's|__VENV_BIN_NAME__|Scripts|g' \
/c/Python36/lib/venv/scripts/posix/activate > /c/Users/bob/Desktop/example_env/Scripts/activate
Note: an absolute Cygwin path to the environment is used.
Once the activate script has been created you can activate/deactivate the environment like you would under any other POSIX system:
$ source /c/Users/bob/Desktop/example_env/Scripts/activate
(example_env) $ deactivate
$
Adding activate with Python
Alternatively a short python script can be used to generate
the activate
script:
$ ./cygwin_venv.py example_env/
Created: "C:\Users\bob\Desktop\example_env\Scripts\activate"
This script uses the ensure_directories
and replace_variables
functions
from the venv
module to generate the activate
script in the correct place.
Note: the script could have used the setup_scripts
function from venv
.
To do this you have to set os.name
to posix
to ensure you copy the correct
files. This is pretty hacky, and the resulting bash scripts will have windows
style line returns (CRLF
), hence it was avoided.