Issue
There are 2 things that annoy me every time when I create a python virtual environment.
NOTE
Below instructions are related to the python’s default venv module.
If you are using any other version/virtual environment management tools,
like Conda, Poetry, Pyenv, below doesn’t apply.
The first one is that I have to type every time:
$ python -m venv ./.venv
$ . ./.venv/bin/activate
# or
# $ source ./.venv/bin/activate
The second thing is that after installing any package I’m getting below pip
version WARNING:
WARNING: You are using pip version 21.1.1; however, version 22.0.4 is available. You should consider upgrading via the '/path/to/project/.venv/bin/python3.8 -m pip install --upgrade pip' command.
It is an expected behavior, ’cause python -m venv
calls python -m ensurepip
to install pip
.
And there is no way to globally upgrade since pip
is installed with bundledversion which is almost out of date. You can check it like below:
>>> import ensurepip
>>> ensurepip.version()
'21.1.1'
Even though you can’t upgrade to the latest version with below commands as mentioned in ensurepip doc:
$ python -m ensurepip --upgrade
Because ensurepip
will only install the bundled version even with the --upgrade
option.
There is no official option to update the bundled pip
and setuptools
, yet.
Solution
Okay, since we are developers, we always find our ways and produce our solutions that suits our needs.
My solution comes from a shell script. You can put it in your shell configuration file, like .zshrc
, .bashrc
, config.fish
.
With below function, when I enter ve
command, it will create my virtual environment — if it doesn’t exist, activate it and upgrade to the latest pip
version.
# 0. If not already in virtualenv:
# 0.1. If virtualenv already exists activate it,
# 0.2. If not create it with global packages, update pip then activate it
# 1. If already in virtualenv: just give info
#
# Usage:
# $ ve
# or
# $ ve python3.9
# or
# $ ve python3.9 ./.venv-diff
ve() {
local py=${1:-python3.8}
local venv="${2:-./.venv}"
local bin="${venv}/bin/activate"
# If not already in virtualenv
# $VIRTUAL_ENV is being set from $venv/bin/activate script
if [ -z "${VIRTUAL_ENV}" ]; then
if [ ! -d ${venv} ]; then
echo "Creating and activating virtual environment ${venv}"
${py} -m venv ${venv} --system-site-package
echo "export PYTHON=${py}" >> ${bin} # overwrite ${python} on .zshenv
source ${bin}
echo "Upgrading pip"
${py} -m pip install --upgrade pip
else
echo "Virtual environment ${venv} already exists, activating..."
source ${bin}
fi
else
echo "Already in a virtual environment!"
fi
}
This function may seem complex, so let me walk through it line by line.
$1
is the first argument, the desired python version, $2
is the second argument which is virtual environment’s name/directory, provided to the function ve()
. If no arguments provided function will use default values: python3.8
and .venv
. You can these default values according to your needs.
ve() {
local py=${1:-python3.8}
local venv="${2:-./.venv}"
local bin="${venv}/bin/activate"
...
}
You can provide positional arguments like:
$ ve python3.9 .venv2
After creating local variables
, if we are already in a virtual environment, it just gives “already in a virtual environment” message. It decides this whether we’re in a virtual environment by checking if there is a environment variablecalled $VIRTUAL_ENV
exported from activate
— .venv/bin/activate
, script.
ve() {
...
if [ -z "${VIRTUAL_ENV}" ]; then
...
else
echo "Already in a virtual environment!"
fi
}
If we’re not in a virtual environment, it will control if .venv
directory exists. If the directory exists, it will be activated:
ve() {
...
if [ -z "${VIRTUAL_ENV}" ]; then
if [ ! -d ${venv} ]; then
...
else
echo "Virtual environment ${venv} already exists, activating..."
source ${bin}
fi
else
...
fi
}
However, if .venv
directory does not exist, it will:
- Create virtual environment
.venv
with global packages included (line 7), - Add exporting desired python version environment variable into
activate
script (line 8) - Activate the virtual environment (line 9)
- Upgrade the current
pip
version to the latest one (line 11)
ve() {
...
if [ -z "${VIRTUAL_ENV}" ]; then
if [ ! -d ${venv} ]; then
echo "Creating and activating virtual environment ${venv}"
${py} -m venv ${venv} --system-site-package
echo "export PYTHON=${py}" >> ${bin} # overwrite ${python} on .zshenv
source ${bin}
echo "Upgrading pip"
${py} -m pip install --upgrade pip
else
...
fi
else
...
fi
}
You probably don’t need line 8, I’m adding this, because using this environment variable in my other
aliases
/functions
/binaries
.
Conclusion
In the end, with this shell script we solved our annoying bugs and make us more productive again.
All done!