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 bundled version 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 variable called $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.
You can find this script in this gist or in function from my dotfiles repo.
All done!
Changelog
- 2022-05-10 : Added gist and dotfiles link references
surepip
Top comments (0)