python
Table of content
Virtual Environment or venv
A Virtual Environment (also called venv) helps you keeping your main system clean from packages as these only live in the venv and gives you also the benefit, that you can use different python versions on the same system.
To being able to create a venv, you have to make sure, that you python installation is able to do so. For systems which are using Debain, you can simple do that by installing the python package(s) which are ending with -venv
, for example:
$ apt install python3-venv # will give you the latest python3 venv support
$ apt install python3.12-venv # for a specific minor verion
Creation
To create a venv, simply run the following command:
$ python -m venv /home/myuser/python_venvs/project1
This can be also done with a specific python version:
$ python3.12 -m venv /home/myuser/python_venvs/project2
Enable and disable the venv
After you have sucessfully created the venv, you have to enable/activate it.
This is done with the simple command:
$ source /home/myuser/python_venvs/project2/bin/activate
In your PS1 you will then see that the name (basename) of the venv dir path is placed in addition. Depending on what you are using, it can be at the beginning like wiht a standard PS1 (venv_basename)user@host:~$
or if you have for examplethe powerline, it will be right after the hostname (if you did not reorder it).
To disable/deactiate it, use the command deactivate
which got added while you sourced the bin/activate
file.
Update to higher python version
To upgrade an existing venv, we just run the creation command again, specify the pythion version using the correct binary and add the parameter --upgrade
$ python3.13 -m venv --upgrade /home/myuser/python_venvs/project2
This will add to the /home/myuser/python_venvs/project2/bin
path the specified python3.13
binarie (manualy re-link project2/bin/python
and project2/bin/python3
to the new one), updates the pip binary (also and updates the pyvenv.cfg
.
Old unsupported python versions
Somtimes you have the need, that you have to run a old pythion version which is not supported by your OS version any more.
There it comes handy to use a container, which will spin up an older version of your OS and allows you to intall then the old python version.
Of crouse you don’t want to move all your content/additional applications/scripts/configs and so on into the container, so lets make the venv which is available in the container to the host system.
We already assume, that you have deployed the container, installed python and created a venv with the same user as on the host system beneath its home inside the dir
python-venv
.
In this sample, we need it to support an older
ansible
version as well as apython3.7
on the clients.As container system we use
incus
(give it a try, it is worth it)
This file is also only ment to be sourced and not executed!
# do not exeute this file, only source it
#
# added functions to shell:
# - actiavte_ansible
# - deactivate_ansible
eash_itsa_me="$(whoami)"
eash_incus_path="<<< PATH TO YOUR CONTAINERS >>>"
eash_incus_container="<<< CONTAINER NAMME >>>"
eash_python_venv_main_path="${HOME}/python-venv"
eash_python_main_version="3"
eash_python_minor_version="11"
eash_python_version="${eash_python_main_version}.${eash_python_minor_version}"
eash_python_path="${eash_python_venv_main_path}/${eash_python_version}"
eash_python_bin="/usr/bin/python${eash_python_main_version}"
function deactivate_ansible() {
alias ansible-playbook="ansible-playbook -e \"ansible_python_interpreter=/usr/bin/python${eash_python_main_version}\""
echo -n "[i] Restored backup alias in ansible-playbook: " ; alias ansible-playbook
if [[ -n "${VIRTUAL_ENV}" ]]; then
deactivate
else
echo "[s] Python${eash_python_version} venv already deactivated"
fi
if findmnt "${eash_python_path}" &>/dev/null ; then
sudo umount "${eash_python_path}"
echo "[i] venv bind unmounted"
else
echo "[s] venv bind mount already unmounted"
fi
if findmnt "${HOME}/.ansible" &>/dev/null ; then
sudo umount "${HOME}/.ansible"
echo "[i] .ansible bind unmounted"
else
echo "[s] .ansible bind mount already unmounted"
fi
if readlink "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}" | grep -q -E "${eash_incus_path}" ; then
rm -f "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}"
mv "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}_incus" "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}"
echo "[i] Restored incus python${eash_python_version} version using internal container link"
else
echo "[s] Incus python${eash_python_version} venv bin internal link in place"
fi
if readlink "${eash_python_bin}" | grep -q -E "${eash_python_version}$" ; then
sudo rm -f "${eash_python_bin}"
sudo mv "${eash_python_bin}"_bk "${eash_python_bin}"
echo -n "[i] Restored old python binary: " ; readlink "${eash_python_bin}"
else
echo "[s] Python binary already set back to default"
fi
if incus list "${eash_incus_container}" | grep -q RUNNING ; then
incus stop "${eash_incus_container}"
echo "[i] Stopped incus container(${eash_incus_container})"
else
echo "[s] Incus container(${eash_incus_container}) already stopped"
fi
}
function activate_ansible() {
alias ansible-playbook="${eash_python_path}/bin/ansible-playbook -e \"ansible_python_interpreter=/usr/bin/python${eash_python_main_version}\""
echo -n "[i] Enabled new alias to use python incus venv: " ; alias ansible-playbook
if incus list "${eash_incus_container}" | grep -q RUNNING ; then
echo "[s] Incus container(${eash_incus_container}) already running"
else
incus start "${eash_incus_container}"
echo "[i] Started incus container(${eash_incus_container})"
fi
sudo chgrp incus "${eash_incus_path}/${eash_incus_container}"
sudo chmod g+x "${eash_incus_path}/${eash_incus_container}"
echo "[i] Placed group permissions for contains/fs"
if findmnt "${eash_python_path}" &>/dev/null ; then
echo "[s] venv bind mount in place"
else
sudo mount --bind -o "uid=${eash_itsa_me},gid=${eash_itsa_me}" "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}" "${eash_python_path}"
echo "[i] venv established bind mount at"
fi
if findmnt "${HOME}/.ansible" &>/dev/null ; then
echo "[s] .ansible bind mount in place"
else
sudo mount --bind -o "uid=${eash_itsa_me},gid=${eash_itsa_me}" "${eash_incus_path}/${eash_incus_container}/rootfs${HOME}/.ansible" "${HOME}/.ansible"
echo "[i] .ansible established bind mount at"
fi
if readlink "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}" | grep -q -E "${eash_incus_path}" ; then
echo "[s] Incus python${eash_python_version} venv bin link in place"
else
mv "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}" "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}_incus"
ln -s "${eash_incus_path}/${eash_incus_container}/rootfs/usr/bin/python${eash_python_version}" "${eash_incus_path}/${eash_incus_container}/rootfs${eash_python_path}/bin/python${eash_python_version}"
echo "[i] Enabled incus python${eash_python_version} version through external path"
fi
if readlink "${eash_python_bin}" | grep -q -E "${eash_python_version}$" ; then
echo "[s] Local python binary already set to ${eash_python_version}"
else
sudo mv "${eash_python_bin}" "${eash_python_bin}_bk"
sudo ln -s "$(sed -E "s/[0-9]$/${eash_python_version}/g" <<<"${eash_python_bin}")" "${eash_python_bin}"
echo "[i] Enabled local python version ${eash_python_version} through link"
fi
if [[ -n "${VIRTUAL_ENV}" ]]; then
echo "[s] Python${eash_python_version} venv already activated"
else
echo "[i] Activationg pythion venv"
source "${eash_python_path}/bin/activate"
fi
}
With this sourced to your bash
/zsh
you can call both functions, activate_ansible
and deactiavte_ansible
and they will prepare your host system so that it can use the venv from the container installed in incus
.
Updates and changes to the venv, shold be ofcourse done when you are attached to the container, just to make sure that nothing gets messed up.
Documentaiton on how to pin a container and run commands (like installing creating the user,
pip
,…) will be added soon in theincus
documentation.